Home | History | Annotate | Download | only in transformer
      1 /*
      2  * Licensed to the Apache Software Foundation (ASF) under one
      3  * or more contributor license agreements. See the NOTICE file
      4  * distributed with this work for additional information
      5  * regarding copyright ownership. The ASF licenses this file
      6  * to you under the Apache License, Version 2.0 (the  "License");
      7  * you may not use this file except in compliance with the License.
      8  * You may obtain a copy of the License at
      9  *
     10  *     http://www.apache.org/licenses/LICENSE-2.0
     11  *
     12  * Unless required by applicable law or agreed to in writing, software
     13  * distributed under the License is distributed on an "AS IS" BASIS,
     14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     15  * See the License for the specific language governing permissions and
     16  * limitations under the License.
     17  */
     18 /*
     19  * $Id: TransformerIdentityImpl.java 575747 2007-09-14 16:28:37Z kcormier $
     20  */
     21 package org.apache.xalan.transformer;
     22 
     23 import java.io.IOException;
     24 import java.util.Hashtable;
     25 import java.util.Properties;
     26 
     27 import javax.xml.XMLConstants;
     28 import javax.xml.parsers.DocumentBuilder;
     29 import javax.xml.parsers.DocumentBuilderFactory;
     30 import javax.xml.parsers.ParserConfigurationException;
     31 import javax.xml.transform.ErrorListener;
     32 import javax.xml.transform.OutputKeys;
     33 import javax.xml.transform.Result;
     34 import javax.xml.transform.Source;
     35 import javax.xml.transform.Transformer;
     36 import javax.xml.transform.TransformerException;
     37 import javax.xml.transform.URIResolver;
     38 import javax.xml.transform.dom.DOMResult;
     39 import javax.xml.transform.dom.DOMSource;
     40 import javax.xml.transform.sax.SAXResult;
     41 import javax.xml.transform.sax.SAXSource;
     42 import javax.xml.transform.sax.TransformerHandler;
     43 import javax.xml.transform.stream.StreamSource;
     44 import javax.xml.transform.stream.StreamResult;
     45 
     46 import org.apache.xalan.res.XSLMessages;
     47 import org.apache.xalan.res.XSLTErrorResources;
     48 import org.apache.xalan.templates.OutputProperties;
     49 import org.apache.xml.serializer.Serializer;
     50 import org.apache.xml.serializer.SerializerFactory;
     51 import org.apache.xml.serializer.Method;
     52 import org.apache.xml.utils.DOMBuilder;
     53 import org.apache.xml.utils.XMLReaderManager;
     54 
     55 import org.w3c.dom.Document;
     56 import org.w3c.dom.DocumentFragment;
     57 import org.w3c.dom.Node;
     58 
     59 import org.xml.sax.Attributes;
     60 import org.xml.sax.ContentHandler;
     61 import org.xml.sax.DTDHandler;
     62 import org.xml.sax.InputSource;
     63 import org.xml.sax.Locator;
     64 import org.xml.sax.SAXException;
     65 import org.xml.sax.XMLReader;
     66 import org.xml.sax.ext.DeclHandler;
     67 import org.xml.sax.ext.LexicalHandler;
     68 
     69 /**
     70  * This class implements an identity transformer for
     71  * {@link javax.xml.transform.sax.SAXTransformerFactory#newTransformerHandler()}
     72  * and {@link javax.xml.transform.TransformerFactory#newTransformer()}.  It
     73  * simply feeds SAX events directly to a serializer ContentHandler, if the
     74  * result is a stream.  If the result is a DOM, it will send the events to
     75  * {@link org.apache.xml.utils.DOMBuilder}.  If the result is another
     76  * content handler, it will simply pass the events on.
     77  */
     78 public class TransformerIdentityImpl extends Transformer
     79         implements TransformerHandler, DeclHandler
     80 {
     81 
     82   /**
     83    * Constructor TransformerIdentityImpl creates an identity transform.
     84    *
     85    */
     86   public TransformerIdentityImpl(boolean isSecureProcessing)
     87   {
     88     m_outputFormat = new OutputProperties(Method.XML);
     89     m_isSecureProcessing = isSecureProcessing;
     90   }
     91 
     92   /**
     93    * Constructor TransformerIdentityImpl creates an identity transform.
     94    *
     95    */
     96   public TransformerIdentityImpl()
     97   {
     98     this(false);
     99   }
    100 
    101   /**
    102    * Enables the user of the TransformerHandler to set the
    103    * to set the Result for the transformation.
    104    *
    105    * @param result A Result instance, should not be null.
    106    *
    107    * @throws IllegalArgumentException if result is invalid for some reason.
    108    */
    109   public void setResult(Result result) throws IllegalArgumentException
    110   {
    111     if(null == result)
    112       throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_RESULT_NULL, null)); //"Result should not be null");
    113     m_result = result;
    114   }
    115 
    116   /**
    117    * Set the base ID (URI or system ID) from where relative
    118    * URLs will be resolved.
    119    * @param systemID Base URI for the source tree.
    120    */
    121   public void setSystemId(String systemID)
    122   {
    123     m_systemID = systemID;
    124   }
    125 
    126   /**
    127    * Get the base ID (URI or system ID) from where relative
    128    * URLs will be resolved.
    129    * @return The systemID that was set with {@link #setSystemId}.
    130    */
    131   public String getSystemId()
    132   {
    133     return m_systemID;
    134   }
    135 
    136   /**
    137    * Get the Transformer associated with this handler, which
    138    * is needed in order to set parameters and output properties.
    139    *
    140    * @return non-null reference to the transformer.
    141    */
    142   public Transformer getTransformer()
    143   {
    144     return this;
    145   }
    146 
    147   /**
    148    * Reset the status of the transformer.
    149    */
    150   public void reset()
    151   {
    152     m_flushedStartDoc = false;
    153     m_foundFirstElement = false;
    154     m_outputStream = null;
    155     clearParameters();
    156     m_result = null;
    157     m_resultContentHandler = null;
    158     m_resultDeclHandler = null;
    159     m_resultDTDHandler = null;
    160     m_resultLexicalHandler = null;
    161     m_serializer = null;
    162     m_systemID = null;
    163     m_URIResolver = null;
    164     m_outputFormat = new OutputProperties(Method.XML);
    165   }
    166 
    167   /**
    168    * Create a result ContentHandler from a Result object, based
    169    * on the current OutputProperties.
    170    *
    171    * @param outputTarget Where the transform result should go,
    172    * should not be null.
    173    *
    174    * @return A valid ContentHandler that will create the
    175    * result tree when it is fed SAX events.
    176    *
    177    * @throws TransformerException
    178    */
    179   private void createResultContentHandler(Result outputTarget)
    180           throws TransformerException
    181   {
    182 
    183     if (outputTarget instanceof SAXResult)
    184     {
    185       SAXResult saxResult = (SAXResult) outputTarget;
    186 
    187       m_resultContentHandler = saxResult.getHandler();
    188       m_resultLexicalHandler = saxResult.getLexicalHandler();
    189 
    190       if (m_resultContentHandler instanceof Serializer)
    191       {
    192 
    193         // Dubious but needed, I think.
    194         m_serializer = (Serializer) m_resultContentHandler;
    195       }
    196     }
    197     else if (outputTarget instanceof DOMResult)
    198     {
    199       DOMResult domResult = (DOMResult) outputTarget;
    200       Node outputNode = domResult.getNode();
    201       Node nextSibling = domResult.getNextSibling();
    202       Document doc;
    203       short type;
    204 
    205       if (null != outputNode)
    206       {
    207         type = outputNode.getNodeType();
    208         doc = (Node.DOCUMENT_NODE == type)
    209               ? (Document) outputNode : outputNode.getOwnerDocument();
    210       }
    211       else
    212       {
    213         try
    214         {
    215           DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
    216 
    217           dbf.setNamespaceAware(true);
    218 
    219           if (m_isSecureProcessing)
    220           {
    221             try
    222             {
    223               dbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
    224             }
    225             catch (ParserConfigurationException pce) {}
    226           }
    227 
    228           DocumentBuilder db = dbf.newDocumentBuilder();
    229 
    230           doc = db.newDocument();
    231         }
    232         catch (ParserConfigurationException pce)
    233         {
    234           throw new TransformerException(pce);
    235         }
    236 
    237         outputNode = doc;
    238         type = outputNode.getNodeType();
    239 
    240         ((DOMResult) outputTarget).setNode(outputNode);
    241       }
    242 
    243       DOMBuilder domBuilder =
    244         (Node.DOCUMENT_FRAGMENT_NODE == type)
    245         ? new DOMBuilder(doc, (DocumentFragment) outputNode)
    246         : new DOMBuilder(doc, outputNode);
    247 
    248       if (nextSibling != null)
    249         domBuilder.setNextSibling(nextSibling);
    250 
    251       m_resultContentHandler = domBuilder;
    252       m_resultLexicalHandler = domBuilder;
    253     }
    254     else if (outputTarget instanceof StreamResult)
    255     {
    256       StreamResult sresult = (StreamResult) outputTarget;
    257 
    258       try
    259       {
    260         Serializer serializer =
    261           SerializerFactory.getSerializer(m_outputFormat.getProperties());
    262 
    263         m_serializer = serializer;
    264 
    265         if (null != sresult.getWriter())
    266           serializer.setWriter(sresult.getWriter());
    267         else if (null != sresult.getOutputStream())
    268           serializer.setOutputStream(sresult.getOutputStream());
    269         else if (null != sresult.getSystemId())
    270         {
    271           String fileURL = sresult.getSystemId();
    272 
    273           if (fileURL.startsWith("file:///")) {
    274             if (fileURL.substring(8).indexOf(":") >0) {
    275               fileURL = fileURL.substring(8);
    276             } else  {
    277               fileURL = fileURL.substring(7);
    278             }
    279           } else if (fileURL.startsWith("file:/")) {
    280             if (fileURL.substring(6).indexOf(":") >0) {
    281               fileURL = fileURL.substring(6);
    282             } else {
    283               fileURL = fileURL.substring(5);
    284             }
    285           }
    286 
    287           m_outputStream = new java.io.FileOutputStream(fileURL);
    288           serializer.setOutputStream(m_outputStream);
    289         }
    290         else
    291           throw new TransformerException(XSLMessages.createMessage(XSLTErrorResources.ER_NO_OUTPUT_SPECIFIED, null)); //"No output specified!");
    292 
    293         m_resultContentHandler = serializer.asContentHandler();
    294       }
    295       catch (IOException ioe)
    296       {
    297         throw new TransformerException(ioe);
    298       }
    299     }
    300     else
    301     {
    302       throw new TransformerException(XSLMessages.createMessage(XSLTErrorResources.ER_CANNOT_TRANSFORM_TO_RESULT_TYPE, new Object[]{outputTarget.getClass().getName()})); //"Can't transform to a Result of type "
    303                                     // + outputTarget.getClass().getName()
    304                                     // + "!");
    305     }
    306 
    307     if (m_resultContentHandler instanceof DTDHandler)
    308       m_resultDTDHandler = (DTDHandler) m_resultContentHandler;
    309 
    310     if (m_resultContentHandler instanceof DeclHandler)
    311       m_resultDeclHandler = (DeclHandler) m_resultContentHandler;
    312 
    313     if (m_resultContentHandler instanceof LexicalHandler)
    314       m_resultLexicalHandler = (LexicalHandler) m_resultContentHandler;
    315   }
    316 
    317   /**
    318    * Process the source tree to the output result.
    319    * @param source  The input for the source tree.
    320    *
    321    * @param outputTarget The output target.
    322    *
    323    * @throws TransformerException If an unrecoverable error occurs
    324    * during the course of the transformation.
    325    */
    326   public void transform(Source source, Result outputTarget)
    327           throws TransformerException
    328   {
    329 
    330     createResultContentHandler(outputTarget);
    331 
    332     /*
    333      * According to JAXP1.2, new SAXSource()/StreamSource()
    334      * should create an empty input tree, with a default root node.
    335      * new DOMSource()creates an empty document using DocumentBuilder.
    336      * newDocument(); Use DocumentBuilder.newDocument() for all 3 situations,
    337      * since there is no clear spec. how to create an empty tree when
    338      * both SAXSource() and StreamSource() are used.
    339      */
    340     if ((source instanceof StreamSource && source.getSystemId()==null &&
    341        ((StreamSource)source).getInputStream()==null &&
    342        ((StreamSource)source).getReader()==null)||
    343        (source instanceof SAXSource &&
    344        ((SAXSource)source).getInputSource()==null &&
    345        ((SAXSource)source).getXMLReader()==null )||
    346        (source instanceof DOMSource && ((DOMSource)source).getNode()==null)){
    347       try {
    348         DocumentBuilderFactory builderF = DocumentBuilderFactory.newInstance();
    349         DocumentBuilder builder = builderF.newDocumentBuilder();
    350         String systemID = source.getSystemId();
    351         source = new DOMSource(builder.newDocument());
    352 
    353         // Copy system ID from original, empty Source to new Source
    354         if (systemID != null) {
    355           source.setSystemId(systemID);
    356         }
    357       } catch (ParserConfigurationException e){
    358         throw new TransformerException(e.getMessage());
    359       }
    360     }
    361 
    362     try
    363     {
    364       if (source instanceof DOMSource)
    365       {
    366         DOMSource dsource = (DOMSource) source;
    367 
    368         m_systemID = dsource.getSystemId();
    369 
    370         Node dNode = dsource.getNode();
    371 
    372         if (null != dNode)
    373         {
    374           try
    375           {
    376             if(dNode.getNodeType() == Node.ATTRIBUTE_NODE)
    377               this.startDocument();
    378             try
    379             {
    380               if(dNode.getNodeType() == Node.ATTRIBUTE_NODE)
    381               {
    382                 String data = dNode.getNodeValue();
    383                 char[] chars = data.toCharArray();
    384                 characters(chars, 0, chars.length);
    385               }
    386               else
    387               {
    388                 org.apache.xml.serializer.TreeWalker walker;
    389                 walker = new org.apache.xml.serializer.TreeWalker(this, m_systemID);
    390                 walker.traverse(dNode);
    391               }
    392             }
    393             finally
    394             {
    395               if(dNode.getNodeType() == Node.ATTRIBUTE_NODE)
    396                 this.endDocument();
    397             }
    398           }
    399           catch (SAXException se)
    400           {
    401             throw new TransformerException(se);
    402           }
    403 
    404           return;
    405         }
    406         else
    407         {
    408           String messageStr = XSLMessages.createMessage(
    409             XSLTErrorResources.ER_ILLEGAL_DOMSOURCE_INPUT, null);
    410 
    411           throw new IllegalArgumentException(messageStr);
    412         }
    413       }
    414 
    415       InputSource xmlSource = SAXSource.sourceToInputSource(source);
    416 
    417       if (null == xmlSource)
    418       {
    419         throw new TransformerException(XSLMessages.createMessage(XSLTErrorResources.ER_CANNOT_TRANSFORM_SOURCE_TYPE, new Object[]{source.getClass().getName()})); //"Can't transform a Source of type "
    420                                        //+ source.getClass().getName() + "!");
    421       }
    422 
    423       if (null != xmlSource.getSystemId())
    424         m_systemID = xmlSource.getSystemId();
    425 
    426       XMLReader reader = null;
    427       boolean managedReader = false;
    428 
    429       try
    430       {
    431         if (source instanceof SAXSource) {
    432           reader = ((SAXSource) source).getXMLReader();
    433         }
    434 
    435         if (null == reader) {
    436           try {
    437             reader = XMLReaderManager.getInstance().getXMLReader();
    438             managedReader = true;
    439           } catch (SAXException se) {
    440             throw new TransformerException(se);
    441           }
    442         } else {
    443           try {
    444             reader.setFeature("http://xml.org/sax/features/namespace-prefixes",
    445                               true);
    446           } catch (org.xml.sax.SAXException se) {
    447             // We don't care.
    448           }
    449         }
    450 
    451         // Get the input content handler, which will handle the
    452         // parse events and create the source tree.
    453         ContentHandler inputHandler = this;
    454 
    455         reader.setContentHandler(inputHandler);
    456 
    457         if (inputHandler instanceof org.xml.sax.DTDHandler)
    458           reader.setDTDHandler((org.xml.sax.DTDHandler) inputHandler);
    459 
    460         try
    461         {
    462           if (inputHandler instanceof org.xml.sax.ext.LexicalHandler)
    463             reader.setProperty("http://xml.org/sax/properties/lexical-handler",
    464                                inputHandler);
    465 
    466           if (inputHandler instanceof org.xml.sax.ext.DeclHandler)
    467             reader.setProperty(
    468               "http://xml.org/sax/properties/declaration-handler",
    469               inputHandler);
    470         }
    471         catch (org.xml.sax.SAXException se){}
    472 
    473         try
    474         {
    475           if (inputHandler instanceof org.xml.sax.ext.LexicalHandler)
    476             reader.setProperty("http://xml.org/sax/handlers/LexicalHandler",
    477                                inputHandler);
    478 
    479           if (inputHandler instanceof org.xml.sax.ext.DeclHandler)
    480             reader.setProperty("http://xml.org/sax/handlers/DeclHandler",
    481                                inputHandler);
    482         }
    483         catch (org.xml.sax.SAXNotRecognizedException snre){}
    484 
    485         reader.parse(xmlSource);
    486       }
    487       catch (org.apache.xml.utils.WrappedRuntimeException wre)
    488       {
    489         Throwable throwable = wre.getException();
    490 
    491         while (throwable
    492                instanceof org.apache.xml.utils.WrappedRuntimeException)
    493         {
    494           throwable =
    495             ((org.apache.xml.utils.WrappedRuntimeException) throwable).getException();
    496         }
    497 
    498         throw new TransformerException(wre.getException());
    499       }
    500       catch (org.xml.sax.SAXException se)
    501       {
    502         throw new TransformerException(se);
    503       }
    504       catch (IOException ioe)
    505       {
    506         throw new TransformerException(ioe);
    507       } finally {
    508         if (managedReader) {
    509           XMLReaderManager.getInstance().releaseXMLReader(reader);
    510         }
    511       }
    512     }
    513     finally
    514     {
    515       if(null != m_outputStream)
    516       {
    517         try
    518         {
    519           m_outputStream.close();
    520         }
    521         catch(IOException ioe){}
    522         m_outputStream = null;
    523       }
    524     }
    525   }
    526 
    527   /**
    528    * Add a parameter for the transformation.
    529    *
    530    * <p>Pass a qualified name as a two-part string, the namespace URI
    531    * enclosed in curly braces ({}), followed by the local name. If the
    532    * name has a null URL, the String only contain the local name. An
    533    * application can safely check for a non-null URI by testing to see if the first
    534    * character of the name is a '{' character.</p>
    535    * <p>For example, if a URI and local name were obtained from an element
    536    * defined with &lt;xyz:foo xmlns:xyz="http://xyz.foo.com/yada/baz.html"/&gt;,
    537    * then the qualified name would be "{http://xyz.foo.com/yada/baz.html}foo". Note that
    538    * no prefix is used.</p>
    539    *
    540    * @param name The name of the parameter, which may begin with a namespace URI
    541    * in curly braces ({}).
    542    * @param value The value object.  This can be any valid Java object. It is
    543    * up to the processor to provide the proper object coersion or to simply
    544    * pass the object on for use in an extension.
    545    */
    546   public void setParameter(String name, Object value)
    547   {
    548     if (value == null) {
    549       throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_INVALID_SET_PARAM_VALUE, new Object[]{name}));
    550     }
    551 
    552     if (null == m_params)
    553     {
    554       m_params = new Hashtable();
    555     }
    556 
    557     m_params.put(name, value);
    558   }
    559 
    560   /**
    561    * Get a parameter that was explicitly set with setParameter
    562    * or setParameters.
    563    *
    564    * <p>This method does not return a default parameter value, which
    565    * cannot be determined until the node context is evaluated during
    566    * the transformation process.
    567    *
    568    *
    569    * @param name Name of the parameter.
    570    * @return A parameter that has been set with setParameter.
    571    */
    572   public Object getParameter(String name)
    573   {
    574 
    575     if (null == m_params)
    576       return null;
    577 
    578     return m_params.get(name);
    579   }
    580 
    581   /**
    582    * Clear all parameters set with setParameter.
    583    */
    584   public void clearParameters()
    585   {
    586 
    587     if (null == m_params)
    588       return;
    589 
    590     m_params.clear();
    591   }
    592 
    593   /**
    594    * Set an object that will be used to resolve URIs used in
    595    * document().
    596    *
    597    * <p>If the resolver argument is null, the URIResolver value will
    598    * be cleared, and the default behavior will be used.</p>
    599    *
    600    * @param resolver An object that implements the URIResolver interface,
    601    * or null.
    602    */
    603   public void setURIResolver(URIResolver resolver)
    604   {
    605     m_URIResolver = resolver;
    606   }
    607 
    608   /**
    609    * Get an object that will be used to resolve URIs used in
    610    * document(), etc.
    611    *
    612    * @return An object that implements the URIResolver interface,
    613    * or null.
    614    */
    615   public URIResolver getURIResolver()
    616   {
    617     return m_URIResolver;
    618   }
    619 
    620   /**
    621    * Set the output properties for the transformation.  These
    622    * properties will override properties set in the Templates
    623    * with xsl:output.
    624    *
    625    * <p>If argument to this function is null, any properties
    626    * previously set are removed, and the value will revert to the value
    627    * defined in the templates object.</p>
    628    *
    629    * <p>Pass a qualified property key name as a two-part string, the namespace URI
    630    * enclosed in curly braces ({}), followed by the local name. If the
    631    * name has a null URL, the String only contain the local name. An
    632    * application can safely check for a non-null URI by testing to see if the first
    633    * character of the name is a '{' character.</p>
    634    * <p>For example, if a URI and local name were obtained from an element
    635    * defined with &lt;xyz:foo xmlns:xyz="http://xyz.foo.com/yada/baz.html"/&gt;,
    636    * then the qualified name would be "{http://xyz.foo.com/yada/baz.html}foo". Note that
    637    * no prefix is used.</p>
    638    *
    639    * @param oformat A set of output properties that will be
    640    * used to override any of the same properties in affect
    641    * for the transformation.
    642    *
    643    * @see javax.xml.transform.OutputKeys
    644    * @see java.util.Properties
    645    *
    646    * @throws IllegalArgumentException if any of the argument keys are not
    647    * recognized and are not namespace qualified.
    648    */
    649   public void setOutputProperties(Properties oformat)
    650           throws IllegalArgumentException
    651   {
    652 
    653     if (null != oformat)
    654     {
    655 
    656       // See if an *explicit* method was set.
    657       String method = (String) oformat.get(OutputKeys.METHOD);
    658 
    659       if (null != method)
    660         m_outputFormat = new OutputProperties(method);
    661       else
    662         m_outputFormat = new OutputProperties();
    663 
    664       m_outputFormat.copyFrom(oformat);
    665     }
    666     else {
    667       // if oformat is null JAXP says that any props previously set are removed
    668       // and we are to revert back to those in the templates object (i.e. Stylesheet).
    669       m_outputFormat = null;
    670     }
    671   }
    672 
    673   /**
    674    * Get a copy of the output properties for the transformation.
    675    *
    676    * <p>The properties returned should contain properties set by the user,
    677    * and properties set by the stylesheet, and these properties
    678    * are "defaulted" by default properties specified by <a href="http://www.w3.org/TR/xslt#output">section 16 of the
    679    * XSL Transformations (XSLT) W3C Recommendation</a>.  The properties that
    680    * were specifically set by the user or the stylesheet should be in the base
    681    * Properties list, while the XSLT default properties that were not
    682    * specifically set should be the default Properties list.  Thus,
    683    * getOutputProperties().getProperty(String key) will obtain any
    684    * property in that was set by {@link #setOutputProperty},
    685    * {@link #setOutputProperties}, in the stylesheet, <em>or</em> the default
    686    * properties, while
    687    * getOutputProperties().get(String key) will only retrieve properties
    688    * that were explicitly set by {@link #setOutputProperty},
    689    * {@link #setOutputProperties}, or in the stylesheet.</p>
    690    *
    691    * <p>Note that mutation of the Properties object returned will not
    692    * effect the properties that the transformation contains.</p>
    693    *
    694    * <p>If any of the argument keys are not recognized and are not
    695    * namespace qualified, the property will be ignored.  In other words the
    696    * behaviour is not orthogonal with setOutputProperties.</p>
    697    *
    698    * @return A copy of the set of output properties in effect
    699    * for the next transformation.
    700    *
    701    * @see javax.xml.transform.OutputKeys
    702    * @see java.util.Properties
    703    */
    704   public Properties getOutputProperties()
    705   {
    706     return (Properties) m_outputFormat.getProperties().clone();
    707   }
    708 
    709   /**
    710    * Set an output property that will be in effect for the
    711    * transformation.
    712    *
    713    * <p>Pass a qualified property name as a two-part string, the namespace URI
    714    * enclosed in curly braces ({}), followed by the local name. If the
    715    * name has a null URL, the String only contain the local name. An
    716    * application can safely check for a non-null URI by testing to see if the first
    717    * character of the name is a '{' character.</p>
    718    * <p>For example, if a URI and local name were obtained from an element
    719    * defined with &lt;xyz:foo xmlns:xyz="http://xyz.foo.com/yada/baz.html"/&gt;,
    720    * then the qualified name would be "{http://xyz.foo.com/yada/baz.html}foo". Note that
    721    * no prefix is used.</p>
    722    *
    723    * <p>The Properties object that was passed to {@link #setOutputProperties} won't
    724    * be effected by calling this method.</p>
    725    *
    726    * @param name A non-null String that specifies an output
    727    * property name, which may be namespace qualified.
    728    * @param value The non-null string value of the output property.
    729    *
    730    * @throws IllegalArgumentException If the property is not supported, and is
    731    * not qualified with a namespace.
    732    *
    733    * @see javax.xml.transform.OutputKeys
    734    */
    735   public void setOutputProperty(String name, String value)
    736           throws IllegalArgumentException
    737   {
    738 
    739     if (!OutputProperties.isLegalPropertyKey(name))
    740       throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_OUTPUT_PROPERTY_NOT_RECOGNIZED, new Object[]{name})); //"output property not recognized: "
    741                                          //+ name);
    742 
    743     m_outputFormat.setProperty(name, value);
    744   }
    745 
    746   /**
    747    * Get an output property that is in effect for the
    748    * transformation.  The property specified may be a property
    749    * that was set with setOutputProperty, or it may be a
    750    * property specified in the stylesheet.
    751    *
    752    * @param name A non-null String that specifies an output
    753    * property name, which may be namespace qualified.
    754    *
    755    * @return The string value of the output property, or null
    756    * if no property was found.
    757    *
    758    * @throws IllegalArgumentException If the property is not supported.
    759    *
    760    * @see javax.xml.transform.OutputKeys
    761    */
    762   public String getOutputProperty(String name) throws IllegalArgumentException
    763   {
    764 
    765     String value = null;
    766     OutputProperties props = m_outputFormat;
    767 
    768     value = props.getProperty(name);
    769 
    770     if (null == value)
    771     {
    772       if (!OutputProperties.isLegalPropertyKey(name))
    773         throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_OUTPUT_PROPERTY_NOT_RECOGNIZED, new Object[]{name})); //"output property not recognized: "
    774                                           // + name);
    775     }
    776 
    777     return value;
    778   }
    779 
    780   /**
    781    * Set the error event listener in effect for the transformation.
    782    *
    783    * @param listener The new error listener.
    784    * @throws IllegalArgumentException if listener is null.
    785    */
    786   public void setErrorListener(ErrorListener listener)
    787           throws IllegalArgumentException
    788   {
    789       if (listener == null)
    790         throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_NULL_ERROR_HANDLER, null));
    791       else
    792         m_errorListener = listener;
    793   }
    794 
    795   /**
    796    * Get the error event handler in effect for the transformation.
    797    *
    798    * @return The current error handler, which should never be null.
    799    */
    800   public ErrorListener getErrorListener()
    801   {
    802     return m_errorListener;
    803   }
    804 
    805   ////////////////////////////////////////////////////////////////////
    806   // Default implementation of DTDHandler interface.
    807   ////////////////////////////////////////////////////////////////////
    808 
    809   /**
    810    * Receive notification of a notation declaration.
    811    *
    812    * <p>By default, do nothing.  Application writers may override this
    813    * method in a subclass if they wish to keep track of the notations
    814    * declared in a document.</p>
    815    *
    816    * @param name The notation name.
    817    * @param publicId The notation public identifier, or null if not
    818    *                 available.
    819    * @param systemId The notation system identifier.
    820    * @throws org.xml.sax.SAXException Any SAX exception, possibly
    821    *            wrapping another exception.
    822    * @see org.xml.sax.DTDHandler#notationDecl
    823    *
    824    * @throws SAXException
    825    */
    826   public void notationDecl(String name, String publicId, String systemId)
    827           throws SAXException
    828   {
    829     if (null != m_resultDTDHandler)
    830       m_resultDTDHandler.notationDecl(name, publicId, systemId);
    831   }
    832 
    833   /**
    834    * Receive notification of an unparsed entity declaration.
    835    *
    836    * <p>By default, do nothing.  Application writers may override this
    837    * method in a subclass to keep track of the unparsed entities
    838    * declared in a document.</p>
    839    *
    840    * @param name The entity name.
    841    * @param publicId The entity public identifier, or null if not
    842    *                 available.
    843    * @param systemId The entity system identifier.
    844    * @param notationName The name of the associated notation.
    845    * @throws org.xml.sax.SAXException Any SAX exception, possibly
    846    *            wrapping another exception.
    847    * @see org.xml.sax.DTDHandler#unparsedEntityDecl
    848    *
    849    * @throws SAXException
    850    */
    851   public void unparsedEntityDecl(
    852           String name, String publicId, String systemId, String notationName)
    853             throws SAXException
    854   {
    855 
    856     if (null != m_resultDTDHandler)
    857       m_resultDTDHandler.unparsedEntityDecl(name, publicId, systemId,
    858                                             notationName);
    859   }
    860 
    861   ////////////////////////////////////////////////////////////////////
    862   // Default implementation of ContentHandler interface.
    863   ////////////////////////////////////////////////////////////////////
    864 
    865   /**
    866    * Receive a Locator object for document events.
    867    *
    868    * <p>By default, do nothing.  Application writers may override this
    869    * method in a subclass if they wish to store the locator for use
    870    * with other document events.</p>
    871    *
    872    * @param locator A locator for all SAX document events.
    873    * @see org.xml.sax.ContentHandler#setDocumentLocator
    874    * @see org.xml.sax.Locator
    875    */
    876   public void setDocumentLocator(Locator locator)
    877   {
    878     try
    879     {
    880       if (null == m_resultContentHandler)
    881         createResultContentHandler(m_result);
    882     }
    883     catch (TransformerException te)
    884     {
    885       throw new org.apache.xml.utils.WrappedRuntimeException(te);
    886     }
    887 
    888     m_resultContentHandler.setDocumentLocator(locator);
    889   }
    890 
    891   /**
    892    * Receive notification of the beginning of the document.
    893    *
    894    * <p>By default, do nothing.  Application writers may override this
    895    * method in a subclass to take specific actions at the beginning
    896    * of a document (such as allocating the root node of a tree or
    897    * creating an output file).</p>
    898    *
    899    * @throws org.xml.sax.SAXException Any SAX exception, possibly
    900    *            wrapping another exception.
    901    * @see org.xml.sax.ContentHandler#startDocument
    902    *
    903    * @throws SAXException
    904    */
    905   public void startDocument() throws SAXException
    906   {
    907 
    908     try
    909     {
    910       if (null == m_resultContentHandler)
    911         createResultContentHandler(m_result);
    912     }
    913     catch (TransformerException te)
    914     {
    915       throw new SAXException(te.getMessage(), te);
    916     }
    917 
    918     // Reset for multiple transforms with this transformer.
    919     m_flushedStartDoc = false;
    920     m_foundFirstElement = false;
    921   }
    922 
    923   boolean m_flushedStartDoc = false;
    924 
    925   protected final void flushStartDoc()
    926      throws SAXException
    927   {
    928     if(!m_flushedStartDoc)
    929     {
    930       if (m_resultContentHandler == null)
    931       {
    932         try
    933         {
    934           createResultContentHandler(m_result);
    935         }
    936         catch(TransformerException te)
    937         {
    938             throw new SAXException(te);
    939         }
    940       }
    941       m_resultContentHandler.startDocument();
    942       m_flushedStartDoc = true;
    943     }
    944   }
    945 
    946   /**
    947    * Receive notification of the end of the document.
    948    *
    949    * <p>By default, do nothing.  Application writers may override this
    950    * method in a subclass to take specific actions at the end
    951    * of a document (such as finalising a tree or closing an output
    952    * file).</p>
    953    *
    954    * @throws org.xml.sax.SAXException Any SAX exception, possibly
    955    *            wrapping another exception.
    956    * @see org.xml.sax.ContentHandler#endDocument
    957    *
    958    * @throws SAXException
    959    */
    960   public void endDocument() throws SAXException
    961   {
    962     flushStartDoc();
    963     m_resultContentHandler.endDocument();
    964   }
    965 
    966   /**
    967    * Receive notification of the start of a Namespace mapping.
    968    *
    969    * <p>By default, do nothing.  Application writers may override this
    970    * method in a subclass to take specific actions at the start of
    971    * each Namespace prefix scope (such as storing the prefix mapping).</p>
    972    *
    973    * @param prefix The Namespace prefix being declared.
    974    * @param uri The Namespace URI mapped to the prefix.
    975    * @throws org.xml.sax.SAXException Any SAX exception, possibly
    976    *            wrapping another exception.
    977    * @see org.xml.sax.ContentHandler#startPrefixMapping
    978    *
    979    * @throws SAXException
    980    */
    981   public void startPrefixMapping(String prefix, String uri)
    982           throws SAXException
    983   {
    984     flushStartDoc();
    985     m_resultContentHandler.startPrefixMapping(prefix, uri);
    986   }
    987 
    988   /**
    989    * Receive notification of the end of a Namespace mapping.
    990    *
    991    * <p>By default, do nothing.  Application writers may override this
    992    * method in a subclass to take specific actions at the end of
    993    * each prefix mapping.</p>
    994    *
    995    * @param prefix The Namespace prefix being declared.
    996    * @throws org.xml.sax.SAXException Any SAX exception, possibly
    997    *            wrapping another exception.
    998    * @see org.xml.sax.ContentHandler#endPrefixMapping
    999    *
   1000    * @throws SAXException
   1001    */
   1002   public void endPrefixMapping(String prefix) throws SAXException
   1003   {
   1004     flushStartDoc();
   1005     m_resultContentHandler.endPrefixMapping(prefix);
   1006   }
   1007 
   1008   /**
   1009    * Receive notification of the start of an element.
   1010    *
   1011    * <p>By default, do nothing.  Application writers may override this
   1012    * method in a subclass to take specific actions at the start of
   1013    * each element (such as allocating a new tree node or writing
   1014    * output to a file).</p>
   1015    *
   1016    * @param uri The Namespace URI, or the empty string if the
   1017    *        element has no Namespace URI or if Namespace
   1018    *        processing is not being performed.
   1019    * @param localName The local name (without prefix), or the
   1020    *        empty string if Namespace processing is not being
   1021    *        performed.
   1022    * @param qName The qualified name (with prefix), or the
   1023    *        empty string if qualified names are not available.
   1024    * @param attributes The specified or defaulted attributes.
   1025    * @throws org.xml.sax.SAXException Any SAX exception, possibly
   1026    *            wrapping another exception.
   1027    * @see org.xml.sax.ContentHandler#startElement
   1028    *
   1029    * @throws SAXException
   1030    */
   1031   public void startElement(
   1032           String uri, String localName, String qName, Attributes attributes)
   1033             throws SAXException
   1034   {
   1035 
   1036     if (!m_foundFirstElement && null != m_serializer)
   1037     {
   1038       m_foundFirstElement = true;
   1039 
   1040       Serializer newSerializer;
   1041 
   1042       try
   1043       {
   1044         newSerializer = SerializerSwitcher.switchSerializerIfHTML(uri,
   1045                 localName, m_outputFormat.getProperties(), m_serializer);
   1046       }
   1047       catch (TransformerException te)
   1048       {
   1049         throw new SAXException(te);
   1050       }
   1051 
   1052       if (newSerializer != m_serializer)
   1053       {
   1054         try
   1055         {
   1056           m_resultContentHandler = newSerializer.asContentHandler();
   1057         }
   1058         catch (IOException ioe)  // why?
   1059         {
   1060           throw new SAXException(ioe);
   1061         }
   1062 
   1063         if (m_resultContentHandler instanceof DTDHandler)
   1064           m_resultDTDHandler = (DTDHandler) m_resultContentHandler;
   1065 
   1066         if (m_resultContentHandler instanceof LexicalHandler)
   1067           m_resultLexicalHandler = (LexicalHandler) m_resultContentHandler;
   1068 
   1069         m_serializer = newSerializer;
   1070       }
   1071     }
   1072     flushStartDoc();
   1073     m_resultContentHandler.startElement(uri, localName, qName, attributes);
   1074   }
   1075 
   1076   /**
   1077    * Receive notification of the end of an element.
   1078    *
   1079    * <p>By default, do nothing.  Application writers may override this
   1080    * method in a subclass to take specific actions at the end of
   1081    * each element (such as finalising a tree node or writing
   1082    * output to a file).</p>
   1083    *
   1084    * @param uri The Namespace URI, or the empty string if the
   1085    *        element has no Namespace URI or if Namespace
   1086    *        processing is not being performed.
   1087    * @param localName The local name (without prefix), or the
   1088    *        empty string if Namespace processing is not being
   1089    *        performed.
   1090    * @param qName The qualified name (with prefix), or the
   1091    *        empty string if qualified names are not available.
   1092    *
   1093    * @throws org.xml.sax.SAXException Any SAX exception, possibly
   1094    *            wrapping another exception.
   1095    * @see org.xml.sax.ContentHandler#endElement
   1096    *
   1097    * @throws SAXException
   1098    */
   1099   public void endElement(String uri, String localName, String qName)
   1100           throws SAXException
   1101   {
   1102     m_resultContentHandler.endElement(uri, localName, qName);
   1103   }
   1104 
   1105   /**
   1106    * Receive notification of character data inside an element.
   1107    *
   1108    * <p>By default, do nothing.  Application writers may override this
   1109    * method to take specific actions for each chunk of character data
   1110    * (such as adding the data to a node or buffer, or printing it to
   1111    * a file).</p>
   1112    *
   1113    * @param ch The characters.
   1114    * @param start The start position in the character array.
   1115    * @param length The number of characters to use from the
   1116    *               character array.
   1117    * @throws org.xml.sax.SAXException Any SAX exception, possibly
   1118    *            wrapping another exception.
   1119    * @see org.xml.sax.ContentHandler#characters
   1120    *
   1121    * @throws SAXException
   1122    */
   1123   public void characters(char ch[], int start, int length) throws SAXException
   1124   {
   1125     flushStartDoc();
   1126     m_resultContentHandler.characters(ch, start, length);
   1127   }
   1128 
   1129   /**
   1130    * Receive notification of ignorable whitespace in element content.
   1131    *
   1132    * <p>By default, do nothing.  Application writers may override this
   1133    * method to take specific actions for each chunk of ignorable
   1134    * whitespace (such as adding data to a node or buffer, or printing
   1135    * it to a file).</p>
   1136    *
   1137    * @param ch The whitespace characters.
   1138    * @param start The start position in the character array.
   1139    * @param length The number of characters to use from the
   1140    *               character array.
   1141    * @throws org.xml.sax.SAXException Any SAX exception, possibly
   1142    *            wrapping another exception.
   1143    * @see org.xml.sax.ContentHandler#ignorableWhitespace
   1144    *
   1145    * @throws SAXException
   1146    */
   1147   public void ignorableWhitespace(char ch[], int start, int length)
   1148           throws SAXException
   1149   {
   1150     m_resultContentHandler.ignorableWhitespace(ch, start, length);
   1151   }
   1152 
   1153   /**
   1154    * Receive notification of a processing instruction.
   1155    *
   1156    * <p>By default, do nothing.  Application writers may override this
   1157    * method in a subclass to take specific actions for each
   1158    * processing instruction, such as setting status variables or
   1159    * invoking other methods.</p>
   1160    *
   1161    * @param target The processing instruction target.
   1162    * @param data The processing instruction data, or null if
   1163    *             none is supplied.
   1164    * @throws org.xml.sax.SAXException Any SAX exception, possibly
   1165    *            wrapping another exception.
   1166    * @see org.xml.sax.ContentHandler#processingInstruction
   1167    *
   1168    * @throws SAXException
   1169    */
   1170   public void processingInstruction(String target, String data)
   1171           throws SAXException
   1172   {
   1173     flushStartDoc();
   1174     m_resultContentHandler.processingInstruction(target, data);
   1175   }
   1176 
   1177   /**
   1178    * Receive notification of a skipped entity.
   1179    *
   1180    * <p>By default, do nothing.  Application writers may override this
   1181    * method in a subclass to take specific actions for each
   1182    * processing instruction, such as setting status variables or
   1183    * invoking other methods.</p>
   1184    *
   1185    * @param name The name of the skipped entity.
   1186    * @throws org.xml.sax.SAXException Any SAX exception, possibly
   1187    *            wrapping another exception.
   1188    * @see org.xml.sax.ContentHandler#processingInstruction
   1189    *
   1190    * @throws SAXException
   1191    */
   1192   public void skippedEntity(String name) throws SAXException
   1193   {
   1194     flushStartDoc();
   1195     m_resultContentHandler.skippedEntity(name);
   1196   }
   1197 
   1198   /**
   1199    * Report the start of DTD declarations, if any.
   1200    *
   1201    * <p>Any declarations are assumed to be in the internal subset
   1202    * unless otherwise indicated by a {@link #startEntity startEntity}
   1203    * event.</p>
   1204    *
   1205    * <p>Note that the start/endDTD events will appear within
   1206    * the start/endDocument events from ContentHandler and
   1207    * before the first startElement event.</p>
   1208    *
   1209    * @param name The document type name.
   1210    * @param publicId The declared public identifier for the
   1211    *        external DTD subset, or null if none was declared.
   1212    * @param systemId The declared system identifier for the
   1213    *        external DTD subset, or null if none was declared.
   1214    * @throws SAXException The application may raise an
   1215    *            exception.
   1216    * @see #endDTD
   1217    * @see #startEntity
   1218    */
   1219   public void startDTD(String name, String publicId, String systemId)
   1220           throws SAXException
   1221   {
   1222     flushStartDoc();
   1223     if (null != m_resultLexicalHandler)
   1224       m_resultLexicalHandler.startDTD(name, publicId, systemId);
   1225   }
   1226 
   1227   /**
   1228    * Report the end of DTD declarations.
   1229    *
   1230    * @throws SAXException The application may raise an exception.
   1231    * @see #startDTD
   1232    */
   1233   public void endDTD() throws SAXException
   1234   {
   1235     if (null != m_resultLexicalHandler)
   1236       m_resultLexicalHandler.endDTD();
   1237   }
   1238 
   1239   /**
   1240    * Report the beginning of an entity in content.
   1241    *
   1242    * <p><strong>NOTE:</entity> entity references in attribute
   1243    * values -- and the start and end of the document entity --
   1244    * are never reported.</p>
   1245    *
   1246    * <p>The start and end of the external DTD subset are reported
   1247    * using the pseudo-name "[dtd]".  All other events must be
   1248    * properly nested within start/end entity events.</p>
   1249    *
   1250    * <p>Note that skipped entities will be reported through the
   1251    * {@link org.xml.sax.ContentHandler#skippedEntity skippedEntity}
   1252    * event, which is part of the ContentHandler interface.</p>
   1253    *
   1254    * @param name The name of the entity.  If it is a parameter
   1255    *        entity, the name will begin with '%'.
   1256    * @throws SAXException The application may raise an exception.
   1257    * @see #endEntity
   1258    * @see org.xml.sax.ext.DeclHandler#internalEntityDecl
   1259    * @see org.xml.sax.ext.DeclHandler#externalEntityDecl
   1260    */
   1261   public void startEntity(String name) throws SAXException
   1262   {
   1263     if (null != m_resultLexicalHandler)
   1264       m_resultLexicalHandler.startEntity(name);
   1265   }
   1266 
   1267   /**
   1268    * Report the end of an entity.
   1269    *
   1270    * @param name The name of the entity that is ending.
   1271    * @throws SAXException The application may raise an exception.
   1272    * @see #startEntity
   1273    */
   1274   public void endEntity(String name) throws SAXException
   1275   {
   1276     if (null != m_resultLexicalHandler)
   1277       m_resultLexicalHandler.endEntity(name);
   1278   }
   1279 
   1280   /**
   1281    * Report the start of a CDATA section.
   1282    *
   1283    * <p>The contents of the CDATA section will be reported through
   1284    * the regular {@link org.xml.sax.ContentHandler#characters
   1285    * characters} event.</p>
   1286    *
   1287    * @throws SAXException The application may raise an exception.
   1288    * @see #endCDATA
   1289    */
   1290   public void startCDATA() throws SAXException
   1291   {
   1292     if (null != m_resultLexicalHandler)
   1293       m_resultLexicalHandler.startCDATA();
   1294   }
   1295 
   1296   /**
   1297    * Report the end of a CDATA section.
   1298    *
   1299    * @throws SAXException The application may raise an exception.
   1300    * @see #startCDATA
   1301    */
   1302   public void endCDATA() throws SAXException
   1303   {
   1304     if (null != m_resultLexicalHandler)
   1305       m_resultLexicalHandler.endCDATA();
   1306   }
   1307 
   1308   /**
   1309    * Report an XML comment anywhere in the document.
   1310    *
   1311    * <p>This callback will be used for comments inside or outside the
   1312    * document element, including comments in the external DTD
   1313    * subset (if read).</p>
   1314    *
   1315    * @param ch An array holding the characters in the comment.
   1316    * @param start The starting position in the array.
   1317    * @param length The number of characters to use from the array.
   1318    * @throws SAXException The application may raise an exception.
   1319    */
   1320   public void comment(char ch[], int start, int length) throws SAXException
   1321   {
   1322     flushStartDoc();
   1323     if (null != m_resultLexicalHandler)
   1324       m_resultLexicalHandler.comment(ch, start, length);
   1325   }
   1326 
   1327   // Implement DeclHandler
   1328 
   1329   /**
   1330      * Report an element type declaration.
   1331      *
   1332      * <p>The content model will consist of the string "EMPTY", the
   1333      * string "ANY", or a parenthesised group, optionally followed
   1334      * by an occurrence indicator.  The model will be normalized so
   1335      * that all whitespace is removed,and will include the enclosing
   1336      * parentheses.</p>
   1337      *
   1338      * @param name The element type name.
   1339      * @param model The content model as a normalized string.
   1340      * @exception SAXException The application may raise an exception.
   1341      */
   1342     public void elementDecl (String name, String model)
   1343         throws SAXException
   1344     {
   1345                         if (null != m_resultDeclHandler)
   1346                                 m_resultDeclHandler.elementDecl(name, model);
   1347     }
   1348 
   1349 
   1350     /**
   1351      * Report an attribute type declaration.
   1352      *
   1353      * <p>Only the effective (first) declaration for an attribute will
   1354      * be reported.  The type will be one of the strings "CDATA",
   1355      * "ID", "IDREF", "IDREFS", "NMTOKEN", "NMTOKENS", "ENTITY",
   1356      * "ENTITIES", or "NOTATION", or a parenthesized token group with
   1357      * the separator "|" and all whitespace removed.</p>
   1358      *
   1359      * @param eName The name of the associated element.
   1360      * @param aName The name of the attribute.
   1361      * @param type A string representing the attribute type.
   1362      * @param valueDefault A string representing the attribute default
   1363      *        ("#IMPLIED", "#REQUIRED", or "#FIXED") or null if
   1364      *        none of these applies.
   1365      * @param value A string representing the attribute's default value,
   1366      *        or null if there is none.
   1367      * @exception SAXException The application may raise an exception.
   1368      */
   1369     public void attributeDecl (String eName,
   1370                                         String aName,
   1371                                         String type,
   1372                                         String valueDefault,
   1373                                         String value)
   1374         throws SAXException
   1375     {
   1376       if (null != m_resultDeclHandler)
   1377                                 m_resultDeclHandler.attributeDecl(eName, aName, type, valueDefault, value);
   1378     }
   1379 
   1380 
   1381     /**
   1382      * Report an internal entity declaration.
   1383      *
   1384      * <p>Only the effective (first) declaration for each entity
   1385      * will be reported.</p>
   1386      *
   1387      * @param name The name of the entity.  If it is a parameter
   1388      *        entity, the name will begin with '%'.
   1389      * @param value The replacement text of the entity.
   1390      * @exception SAXException The application may raise an exception.
   1391      * @see #externalEntityDecl
   1392      * @see org.xml.sax.DTDHandler#unparsedEntityDecl
   1393      */
   1394     public void internalEntityDecl (String name, String value)
   1395         throws SAXException
   1396     {
   1397       if (null != m_resultDeclHandler)
   1398                                 m_resultDeclHandler.internalEntityDecl(name, value);
   1399     }
   1400 
   1401 
   1402     /**
   1403      * Report a parsed external entity declaration.
   1404      *
   1405      * <p>Only the effective (first) declaration for each entity
   1406      * will be reported.</p>
   1407      *
   1408      * @param name The name of the entity.  If it is a parameter
   1409      *        entity, the name will begin with '%'.
   1410      * @param publicId The declared public identifier of the entity, or
   1411      *        null if none was declared.
   1412      * @param systemId The declared system identifier of the entity.
   1413      * @exception SAXException The application may raise an exception.
   1414      * @see #internalEntityDecl
   1415      * @see org.xml.sax.DTDHandler#unparsedEntityDecl
   1416      */
   1417     public void externalEntityDecl (String name, String publicId,
   1418                                              String systemId)
   1419         throws SAXException
   1420     {
   1421       if (null != m_resultDeclHandler)
   1422                                 m_resultDeclHandler.externalEntityDecl(name, publicId, systemId);
   1423     }
   1424 
   1425   /**
   1426    * This is null unless we own the stream.
   1427    */
   1428   private java.io.FileOutputStream m_outputStream = null;
   1429 
   1430   /** The content handler where result events will be sent. */
   1431   private ContentHandler m_resultContentHandler;
   1432 
   1433   /** The lexical handler where result events will be sent. */
   1434   private LexicalHandler m_resultLexicalHandler;
   1435 
   1436   /** The DTD handler where result events will be sent. */
   1437   private DTDHandler m_resultDTDHandler;
   1438 
   1439   /** The Decl handler where result events will be sent. */
   1440   private DeclHandler m_resultDeclHandler;
   1441 
   1442   /** The Serializer, which may or may not be null. */
   1443   private Serializer m_serializer;
   1444 
   1445   /** The Result object. */
   1446   private Result m_result;
   1447 
   1448   /**
   1449    * The system ID, which is unused, but must be returned to fullfill the
   1450    *  TransformerHandler interface.
   1451    */
   1452   private String m_systemID;
   1453 
   1454   /**
   1455    * The parameters, which is unused, but must be returned to fullfill the
   1456    *  Transformer interface.
   1457    */
   1458   private Hashtable m_params;
   1459 
   1460   /** The error listener for TrAX errors and warnings. */
   1461   private ErrorListener m_errorListener =
   1462     new org.apache.xml.utils.DefaultErrorHandler(false);
   1463 
   1464   /**
   1465    * The URIResolver, which is unused, but must be returned to fullfill the
   1466    *  TransformerHandler interface.
   1467    */
   1468   URIResolver m_URIResolver;
   1469 
   1470   /** The output properties. */
   1471   private OutputProperties m_outputFormat;
   1472 
   1473   /** Flag to set if we've found the first element, so we can tell if we have
   1474    *  to check to see if we should create an HTML serializer.      */
   1475   boolean m_foundFirstElement;
   1476 
   1477   /**
   1478    * State of the secure processing feature.
   1479    */
   1480   private boolean m_isSecureProcessing = false;
   1481 }
   1482