Source for gnu.xml.util.SAXNullTransformerFactory

   1: /* SAXNullTransformerFactory.java --
   2:    Copyright (C) 2001 Free Software Foundation, Inc.
   3: 
   4: This file is part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2, or (at your option)
   9: any later version.
  10: 
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; see the file COPYING.  If not, write to the
  18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19: 02110-1301 USA.
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module.  An independent module is a module which is not derived from
  33: or based on this library.  If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version. */
  37: 
  38: package gnu.xml.util;
  39: 
  40: import java.io.FileOutputStream;
  41: import java.io.IOException;
  42: import java.io.OutputStream;
  43: import java.net.URL;
  44: import java.net.URLConnection;
  45: import java.util.Hashtable;
  46: import java.util.Properties;
  47: 
  48: import gnu.xml.dom.Consumer;
  49: import gnu.xml.dom.DomDocument;
  50: import gnu.xml.pipeline.DomConsumer;
  51: import gnu.xml.pipeline.EventFilter;
  52: 
  53: import javax.xml.transform.*;
  54: import javax.xml.transform.dom.*;
  55: import javax.xml.transform.sax.*;
  56: import javax.xml.transform.stream.*;
  57: 
  58: import org.xml.sax.*;
  59: import org.xml.sax.helpers.XMLReaderFactory;
  60: import org.xml.sax.helpers.LocatorImpl;
  61: 
  62: 
  63: /**
  64:  * Implements null transforms. XSLT stylesheets are not supported.
  65:  * This class provides a way to translate three representations of
  66:  * XML data (SAX event stream, DOM tree, and XML text) into each other.
  67:  * In essence it's a thinnish wrapper around basic SAX event
  68:  * <a href="../pipeline/package-summary.html">pipeline</a> facilities, which
  69:  * exposes only limited functionality.  The <em>javax.xml.transform</em>
  70:  * functionality is implemented as follows: <ul>
  71:  *
  72:  * <li>The {@link javax.xml.transform.sax.SAXSource SAXSource} class
  73:  * just wraps an {@link XMLReader} and {@link InputSource}, while the
  74:  * {@link javax.xml.transform.sax.SAXResult SAXResult} class is less
  75:  * functional than a {@link gnu.xml.pipeline.EventConsumer EventConsumer}.
  76:  * (Notably, it drops all but one declaration from any DTD.)</li>
  77:  *
  78:  * <li>The {@link javax.xml.transform.dom.DOMSource DOMSource} class
  79:  * corresponds to special SAX parsers like {@link DomParser}, and the
  80:  * {@link javax.xml.transform.dom.DOMResult DOMResult} class corresponds
  81:  * to a {@link gnu.xml.pipeline.DomConsumer DomConsumer}.</li>
  82:  *
  83:  * <li>The {@link javax.xml.transform.stream.StreamSource StreamSource}
  84:  * class corresponds to a SAX {@link InputSource}, and the
  85:  * {@link javax.xml.transform.stream.StreamResult StreamResult} class
  86:  * corresponds to a {@link gnu.xml.pipeline.TextConsumer TextConsumer}.</li>
  87:  *
  88:  * </ul>
  89:  *
  90:  * <p><em>This implementation is preliminary.</em>
  91:  *
  92:  * @see gnu.xml.pipeline.XsltFilter
  93:  *
  94:  * @author David Brownell
  95:  */
  96: public class SAXNullTransformerFactory extends SAXTransformerFactory
  97: {
  98: 
  99:   private ErrorListener errListener;
 100:   private URIResolver           uriResolver;
 101: 
 102:   /** Default constructor */
 103:   public SAXNullTransformerFactory () { }
 104: 
 105:   //
 106:   // only has stuff that makes sense with null transforms
 107:   //
 108: 
 109:   /**
 110:    * Returns true if the requested feature is supported.
 111:    * All three kinds of input and output are accepted:
 112:    * XML text, SAX events, and DOM nodes.
 113:    */
 114:   public boolean getFeature (String feature)
 115:   {
 116:     return SAXTransformerFactory.FEATURE.equals (feature)
 117:       || SAXResult.FEATURE.equals (feature)
 118:       || SAXSource.FEATURE.equals (feature)
 119:       || DOMResult.FEATURE.equals (feature)
 120:       || DOMSource.FEATURE.equals (feature)
 121:       || StreamResult.FEATURE.equals (feature)
 122:       || StreamSource.FEATURE.equals (feature)
 123:       ;
 124:   }
 125: 
 126:   public void setFeature(String name, boolean value)
 127:     throws TransformerConfigurationException
 128:   {
 129:     throw new TransformerConfigurationException(name);
 130:   }
 131: 
 132: 
 133:   /** Throws an exception (no implementation attributes are supported) */
 134:   public void setAttribute (String key, Object value)
 135:   {
 136:     throw new IllegalArgumentException ();
 137:   }
 138: 
 139:   /** Throws an exception (no implementation attributes are supported) */
 140:   public Object getAttribute (String key)
 141:   {
 142:     throw new IllegalArgumentException ();
 143:   }
 144: 
 145:   /** (not yet implemented) */
 146:   public Source getAssociatedStylesheet (Source source,
 147:                                          String media,
 148:                                          String title,
 149:                                          String charset)
 150:     throws TransformerConfigurationException
 151:   {
 152:     // parse, and find the appropriate xsl-stylesheet PI contents
 153:     throw new IllegalArgumentException ();
 154:   }
 155: 
 156:   public Transformer newTransformer ()
 157:     throws TransformerConfigurationException
 158:   {
 159:     return new NullTransformer ();
 160:   }
 161: 
 162:   /**
 163:    * Returns a TransformerHandler that knows how to generate output
 164:    * in all three standard formats.  Output text is generated using
 165:    * {@link XMLWriter}, and the GNU implementation of
 166:    * {@link DomDocument DOM} is used.
 167:    *
 168:    * @see SAXResult
 169:    * @see StreamResult
 170:    * @see DOMResult
 171:    */
 172:   public TransformerHandler newTransformerHandler ()
 173:     throws TransformerConfigurationException
 174:   {
 175:     NullTransformer     transformer = new NullTransformer ();
 176:     return transformer.handler;
 177:   }
 178: 
 179:   //
 180:   // Stuff that depends on XSLT support, which we don't provide
 181:   //
 182:   private static final String           noXSLT = "No XSLT support";
 183: 
 184:   /** Throws an exception (XSLT is not supported). */
 185:   public Transformer newTransformer (Source stylesheet)
 186:     throws TransformerConfigurationException
 187:   {
 188:     throw new TransformerConfigurationException (noXSLT);
 189:   }
 190: 
 191:   /** Throws an exception (XSLT is not supported). */
 192:   public Templates newTemplates (Source stylesheet)
 193:     throws TransformerConfigurationException
 194:   {
 195:     throw new TransformerConfigurationException (noXSLT);
 196:   }
 197: 
 198:   /** Throws an exception (XSLT is not supported). */
 199:   public TemplatesHandler newTemplatesHandler ()
 200:     throws TransformerConfigurationException
 201:   {
 202:     throw new TransformerConfigurationException (noXSLT);
 203:   }
 204: 
 205:   /** Throws an exception (XSLT is not supported). */
 206:   public TransformerHandler newTransformerHandler (Source stylesheet)
 207:     throws TransformerConfigurationException
 208:   {
 209:     throw new TransformerConfigurationException (noXSLT);
 210:   }
 211: 
 212:   /** Throws an exception (XSLT is not supported). */
 213:   public TransformerHandler newTransformerHandler (Templates stylesheet)
 214:     throws TransformerConfigurationException
 215:   {
 216:     throw new TransformerConfigurationException (noXSLT);
 217:   }
 218: 
 219:   /** Throws an exception (XSLT is not supported). */
 220:   public XMLFilter newXMLFilter (Source stylesheet)
 221:     throws TransformerConfigurationException
 222:   {
 223:     throw new TransformerConfigurationException (noXSLT);
 224:   }
 225: 
 226:   /** Throws an exception (XSLT is not supported). */
 227:   public XMLFilter newXMLFilter (Templates stylesheet)
 228:     throws TransformerConfigurationException
 229:   {
 230:     throw new TransformerConfigurationException (noXSLT);
 231:   }
 232: 
 233:   /** Returns the value assigned by {@link #setErrorListener}.  */
 234:   public ErrorListener getErrorListener ()
 235:   {
 236:     return errListener;
 237:   }
 238: 
 239:   /** Assigns a value that would be used when parsing stylesheets */
 240:   public void setErrorListener (ErrorListener e)
 241:   {
 242:     errListener = e;
 243:   }
 244: 
 245:   /** Returns the value assigned by {@link #setURIResolver}.  */
 246:   public URIResolver getURIResolver ()
 247:   {
 248:     return uriResolver;
 249:   }
 250: 
 251:   /** Assigns a value that would be used when parsing stylesheets */
 252:   public void setURIResolver (URIResolver u)
 253:   {
 254:     uriResolver = u;
 255:   }
 256: 
 257: 
 258:   //
 259:   // Helper classes.  These might in theory be subclassed
 260:   // by an XSLT implementation, if they were exported.
 261:   //
 262: 
 263:   static class DomTerminus
 264:     extends DomConsumer
 265:   {
 266: 
 267:     DomTerminus (DOMResult result)
 268:       throws SAXException
 269:     {
 270:       // won't really throw SAXException
 271:       super (DomDocument.class);
 272:       setHandler (new DomHandler (this, result));
 273:     }
 274: 
 275:   }
 276: 
 277:   static class DomHandler
 278:     extends Consumer.Backdoor
 279:   {
 280: 
 281:     private DOMResult   result;
 282: 
 283:     DomHandler (DomConsumer c, DOMResult r)
 284:       throws SAXException
 285:     {
 286:       // won't really throw SAXException
 287:       super (c);
 288:       result = r;
 289:     }
 290: 
 291:     public void endDocument ()
 292:       throws SAXException
 293:     {
 294:       super.endDocument ();
 295:       result.setNode (getDocument ());
 296:     }
 297: 
 298:   }
 299: 
 300:   private static OutputStream getOutputStream (String uri)
 301:     throws IOException
 302:   {
 303:     // JDK stupidity:  file "protocol does not support output" ...
 304:     if (uri.startsWith ("file:"))
 305:       return new FileOutputStream (uri.substring (5));
 306: 
 307:     // Otherwise ...
 308:     URL         url = new URL (uri);
 309:     URLConnection       conn = url.openConnection ();
 310: 
 311:     conn.setDoOutput (true);
 312:     return conn.getOutputStream ();
 313:   }
 314: 
 315: 
 316:   static class NullHandler
 317:     extends EventFilter
 318:     implements TransformerHandler
 319:   {
 320: 
 321:     private String              systemId;
 322:     private Transformer transformer;
 323: 
 324:     NullHandler (Transformer t)
 325:     {
 326:       transformer = t;
 327:     }
 328: 
 329:     public Transformer getTransformer ()
 330:     {
 331:       return transformer;
 332:     }
 333: 
 334:     public String getSystemId ()
 335:     {
 336:       return systemId;
 337:     }
 338: 
 339:     public void setSystemId (String id)
 340:     {
 341:       systemId = id;
 342:     }
 343: 
 344:     public void setResult (Result result)
 345:     {
 346:       if (result.getSystemId () != null)
 347:         systemId = result.getSystemId ();
 348: 
 349:       try
 350:         {
 351: 
 352:           // output to partial SAX event stream?
 353:           if (result instanceof SAXResult)
 354:             {
 355:               SAXResult         r = (SAXResult) result;
 356: 
 357:               setContentHandler (r.getHandler ());
 358:               setProperty (LEXICAL_HANDLER, r.getLexicalHandler ());
 359:               // DTD info is filtered out by javax.transform
 360: 
 361:               // output to DOM tree?
 362:             }
 363:           else if (result instanceof DOMResult)
 364:             {
 365:               DomTerminus       out = new DomTerminus ((DOMResult) result);
 366: 
 367:               setContentHandler (out.getContentHandler ());
 368:               setProperty (LEXICAL_HANDLER,
 369:                            out.getProperty (LEXICAL_HANDLER));
 370:               // save DTD-derived info, if any.
 371:               setDTDHandler (out.getDTDHandler ());
 372:               setProperty (DECL_HANDLER,
 373:                            out.getProperty (DECL_HANDLER));
 374: 
 375:               // node is saved into result on endDocument()
 376: 
 377:               // output to (XML) text?
 378:             }
 379:           else if (result instanceof StreamResult)
 380:             {
 381:               StreamResult      r = (StreamResult) result;
 382:               XMLWriter         out;
 383: 
 384:               // FIXME:  when do output properties take effect?
 385:               // encoding, standalone decl, xml/xhtml/... ...
 386: 
 387:               // FIXME:  maybe put nsfix filter up front
 388: 
 389:               try
 390:                 {
 391:                   if (r.getWriter () != null)
 392:                     out = new XMLWriter (r.getWriter ());
 393:                   else if (r.getOutputStream () != null)
 394:                     out = new XMLWriter (r.getOutputStream ());
 395:                   else if (r.getSystemId () != null)
 396:                     out = new XMLWriter (
 397:                                          getOutputStream (r.getSystemId ()));
 398:                   else
 399:                     throw new IllegalArgumentException (
 400:                                                         "bad StreamResult");
 401:                 }
 402:               catch (IOException e)
 403:                 {
 404:                   e.printStackTrace ();
 405:                   // on jdk 1.4, pass the root cause ...
 406:                   throw new IllegalArgumentException (e.getMessage ());
 407:                 }
 408: 
 409:               // out.setExpandingEntities (true);
 410:               // out.setPrettyPrinting (true);
 411:               // out.setXhtml (true);
 412: 
 413:               setContentHandler (out);
 414:               setProperty (LEXICAL_HANDLER, out);
 415:               // save DTD info, if any; why not?
 416:               setDTDHandler (out);
 417:               setProperty (DECL_HANDLER, out);
 418:             }
 419: 
 420:         }
 421:       catch (SAXException e)
 422:         {
 423:           // SAXNotSupportedException or SAXNotRecognizedException:
 424:           // "can't happen" ... but SAXException for DOM build probs
 425:           // could happen, so ...
 426:           // on jdk 1.4, pass the root cause ...
 427:           throw new IllegalArgumentException (e.getMessage ());
 428:         }
 429:     }
 430:   }
 431: 
 432:   // an interface that adds no value
 433:   static class LocatorAdapter
 434:     extends LocatorImpl
 435:     implements SourceLocator
 436:   {
 437: 
 438:     LocatorAdapter (SAXParseException e)
 439:     {
 440:       setSystemId (e.getSystemId ());
 441:       setPublicId (e.getPublicId ());
 442:       setLineNumber (e.getLineNumber ());
 443:       setColumnNumber (e.getColumnNumber ());
 444:     }
 445: 
 446:   }
 447: 
 448:   // another interface that adds no value
 449:   static class ListenerAdapter
 450:     implements ErrorHandler
 451:   {
 452: 
 453:     NullTransformer     transformer;
 454: 
 455:     ListenerAdapter (NullTransformer t)
 456:     {
 457:       transformer = t;
 458:     }
 459: 
 460:     private TransformerException map (SAXParseException e)
 461:     {
 462:       return new TransformerException (
 463:                                        e.getMessage (),
 464:                                        new LocatorAdapter (e),
 465:                                        e);
 466:     }
 467: 
 468:     public void error (SAXParseException e)
 469:       throws SAXParseException
 470:     {
 471:       try
 472:         {
 473:           if (transformer.errListener != null)
 474:             transformer.errListener.error (map (e));
 475:         }
 476:       catch (TransformerException ex)
 477:         {
 478:           transformer.ex = ex;
 479:           throw e;
 480:         }
 481:     }
 482: 
 483:     public void fatalError (SAXParseException e)
 484:       throws SAXParseException
 485:     {
 486:       try
 487:         {
 488:           if (transformer.errListener != null)
 489:             transformer.errListener.fatalError (map (e));
 490:           else
 491:             throw map (e);
 492:         } catch (TransformerException ex) {
 493:           transformer.ex = ex;
 494:           throw e;
 495:         }
 496:     }
 497: 
 498:     public void warning (SAXParseException e)
 499:       throws SAXParseException
 500:     {
 501:       try
 502:         {
 503:           if (transformer.errListener != null)
 504:             transformer.errListener.warning (map (e));
 505:         }
 506:       catch (TransformerException ex)
 507:         {
 508:           transformer.ex = ex;
 509:           throw e;
 510:         }
 511:     }
 512:   }
 513: 
 514:   static class NullTransformer
 515:     extends Transformer
 516:   {
 517: 
 518:     private URIResolver         uriResolver;
 519:     private Properties          props = new Properties ();
 520:     private Hashtable           params = new Hashtable (7);
 521: 
 522:     ErrorListener                       errListener = null;
 523:     TransformerException                ex = null;
 524:     NullHandler                 handler;
 525: 
 526:     NullTransformer ()
 527:     {
 528:       super ();
 529:       handler = new NullHandler (this);
 530:     }
 531: 
 532:     public ErrorListener getErrorListener ()
 533:     {
 534:       return errListener;
 535:     }
 536: 
 537:     public void setErrorListener (ErrorListener e)
 538:     {
 539:       errListener = e;
 540:     }
 541: 
 542:     public URIResolver getURIResolver ()
 543:     {
 544:       return uriResolver;
 545:     }
 546: 
 547:     public void setURIResolver (URIResolver u)
 548:     {
 549:       uriResolver = u;
 550:     }
 551: 
 552:     public void setOutputProperties (Properties p)
 553:     {
 554:       props = (Properties) p.clone ();
 555:     }
 556: 
 557:     public Properties getOutputProperties ()
 558:     {
 559:       return (Properties) props.clone ();
 560:     }
 561: 
 562:     public void setOutputProperty (String name, String value)
 563:     {
 564:       props.setProperty (name, value);
 565:     }
 566: 
 567:     public String getOutputProperty (String name)
 568:     {
 569:       return props.getProperty (name);
 570:     }
 571: 
 572:     public void clearParameters ()
 573:     {
 574:       params.clear ();
 575:     }
 576: 
 577:     public void setParameter (String name, Object value)
 578:     {
 579:       props.put (name, value);
 580:     }
 581: 
 582:     public Object getParameter (String name)
 583:     {
 584:       return props.get (name);
 585:     }
 586: 
 587:     public void transform (Source in, Result out)
 588:       throws TransformerException
 589:     {
 590:       try
 591:         {
 592:           XMLReader             producer;
 593:           InputSource           input;
 594: 
 595:           // Input from DOM?
 596:           if (in instanceof DOMSource)
 597:             {
 598:               DOMSource source = (DOMSource) in;
 599: 
 600:               if (source.getNode () == null)
 601:                 throw new IllegalArgumentException ("no DOM node");
 602:               producer = new DomParser (source.getNode ());
 603:               input = null;
 604: 
 605:               // Input from SAX?
 606:             }
 607:           else if (in instanceof SAXSource)
 608:             {
 609:               SAXSource source = (SAXSource) in;
 610: 
 611:               producer = source.getXMLReader ();
 612:               if (producer == null)
 613:                 producer = XMLReaderFactory.createXMLReader ();
 614: 
 615:               input = source.getInputSource ();
 616:               if (input == null)
 617:                 {
 618:                   if (source.getSystemId () != null)
 619:                     input = new InputSource (source.getSystemId ());
 620:                   else
 621:                     throw new IllegalArgumentException (
 622:                                                         "missing SAX input");
 623:                 }
 624: 
 625:               // Input from a stream or something?
 626:             }
 627:           else
 628:             {
 629:               producer = XMLReaderFactory.createXMLReader ();
 630:               input = SAXSource.sourceToInputSource (in);
 631:               if (input == null)
 632:                 throw new IllegalArgumentException ("missing input");
 633:             }
 634: 
 635:           // preserve original namespace prefixes
 636:           try
 637:             {
 638:               producer.setFeature(EventFilter.FEATURE_URI +
 639:                                   "namespace-prefixes",
 640:                                   true);
 641:             }
 642:           catch (Exception e)
 643:             {
 644:               /* ignore */
 645:               // FIXME if we couldn't, "NsFix" stage before the output ..
 646:             }
 647: 
 648:           // arrange the output
 649:           handler.setResult (out);
 650:           EventFilter.bind (producer, handler);
 651: 
 652:           // then parse ... single element pipeline
 653:           producer.parse (input);
 654: 
 655:         }
 656:       catch (IOException e)
 657:         {
 658:           throw new TransformerException ("transform failed", e);
 659: 
 660:         }
 661:       catch (SAXException e)
 662:         {
 663:           if (ex == null && ex.getCause () == e)
 664:             throw ex;
 665:           else
 666:             throw new TransformerException ("transform failed", e);
 667: 
 668:         }
 669:       finally
 670:         {
 671:           ex = null;
 672:         }
 673:     }
 674:   }
 675: 
 676: }