Home | History | Annotate | Download | only in serializer
      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: ToSAXHandler.java 468654 2006-10-28 07:09:23Z minchau $
     20  */
     21 package org.apache.xml.serializer;
     22 
     23 import java.util.Vector;
     24 
     25 import org.xml.sax.Attributes;
     26 import org.xml.sax.ContentHandler;
     27 import org.xml.sax.ErrorHandler;
     28 import org.xml.sax.SAXException;
     29 import org.xml.sax.SAXParseException;
     30 import org.xml.sax.ext.LexicalHandler;
     31 
     32 /**
     33  * This class is used to provide a base behavior to be inherited
     34  * by other To...SAXHandler serializers.
     35  *
     36  * This class is not a public API.
     37  *
     38  * @xsl.usage internal
     39  */
     40 public abstract class ToSAXHandler extends SerializerBase
     41 {
     42     public ToSAXHandler()
     43     {
     44     }
     45 
     46     public ToSAXHandler(
     47         ContentHandler hdlr,
     48         LexicalHandler lex,
     49         String encoding)
     50     {
     51         setContentHandler(hdlr);
     52         setLexHandler(lex);
     53         setEncoding(encoding);
     54     }
     55     public ToSAXHandler(ContentHandler handler, String encoding)
     56     {
     57         setContentHandler(handler);
     58         setEncoding(encoding);
     59     }
     60 
     61     /**
     62      * Underlying SAX handler. Taken from XSLTC
     63      */
     64     protected ContentHandler m_saxHandler;
     65 
     66     /**
     67      * Underlying LexicalHandler. Taken from XSLTC
     68      */
     69     protected LexicalHandler m_lexHandler;
     70 
     71     /**
     72      * A startPrefixMapping() call on a ToSAXHandler will pass that call
     73      * on to the wrapped ContentHandler, but should we also mirror these calls
     74      * with matching attributes, if so this field is true.
     75      * For example if this field is true then a call such as
     76      * startPrefixMapping("prefix1","uri1") will also cause the additional
     77      * internally generated attribute xmlns:prefix1="uri1" to be effectively added
     78      * to the attributes passed to the wrapped ContentHandler.
     79      */
     80     private boolean m_shouldGenerateNSAttribute = true;
     81 
     82     /** If this is true, then the content handler wrapped by this
     83      * serializer implements the TransformState interface which
     84      * will give the content handler access to the state of
     85      * the transform. */
     86     protected TransformStateSetter m_state = null;
     87 
     88     /**
     89      * Pass callback to the SAX Handler
     90      */
     91     protected void startDocumentInternal() throws SAXException
     92     {
     93         if (m_needToCallStartDocument)
     94         {
     95             super.startDocumentInternal();
     96 
     97             m_saxHandler.startDocument();
     98             m_needToCallStartDocument = false;
     99         }
    100     }
    101     /**
    102      * Do nothing.
    103      * @see org.xml.sax.ext.LexicalHandler#startDTD(String, String, String)
    104      */
    105     public void startDTD(String arg0, String arg1, String arg2)
    106         throws SAXException
    107     {
    108         // do nothing for now
    109     }
    110 
    111     /**
    112      * Receive notification of character data.
    113      *
    114      * @param characters The string of characters to process.
    115      *
    116      * @throws org.xml.sax.SAXException
    117      *
    118      * @see ExtendedContentHandler#characters(String)
    119      */
    120     public void characters(String characters) throws SAXException
    121     {
    122         final int len = characters.length();
    123         if (len > m_charsBuff.length)
    124         {
    125            m_charsBuff = new char[len*2 + 1];
    126         }
    127         characters.getChars(0,len, m_charsBuff, 0);
    128         characters(m_charsBuff, 0, len);
    129     }
    130 
    131     /**
    132      * Receive notification of a comment.
    133      *
    134      * @see ExtendedLexicalHandler#comment(String)
    135      */
    136     public void comment(String comment) throws SAXException
    137     {
    138         flushPending();
    139 
    140         // Ignore if a lexical handler has not been set
    141         if (m_lexHandler != null)
    142         {
    143             final int len = comment.length();
    144             if (len > m_charsBuff.length)
    145             {
    146                m_charsBuff = new char[len*2 + 1];
    147             }
    148             comment.getChars(0,len, m_charsBuff, 0);
    149             m_lexHandler.comment(m_charsBuff, 0, len);
    150             // time to fire off comment event
    151             if (m_tracer != null)
    152                 super.fireCommentEvent(m_charsBuff, 0, len);
    153         }
    154 
    155     }
    156 
    157     /**
    158      * Do nothing as this is an abstract class. All subclasses will need to
    159      * define their behavior if it is different.
    160      * @see org.xml.sax.ContentHandler#processingInstruction(String, String)
    161      */
    162     public void processingInstruction(String target, String data)
    163         throws SAXException
    164     {
    165         // Redefined in SAXXMLOutput
    166     }
    167 
    168     protected void closeStartTag() throws SAXException
    169     {
    170     }
    171 
    172     protected void closeCDATA() throws SAXException
    173     {
    174         // Redefined in SAXXMLOutput
    175     }
    176 
    177     /**
    178      * Receive notification of the beginning of an element, although this is a
    179      * SAX method additional namespace or attribute information can occur before
    180      * or after this call, that is associated with this element.
    181      *
    182      * @throws org.xml.sax.SAXException Any SAX exception, possibly
    183      *            wrapping another exception.
    184      * @see org.xml.sax.ContentHandler#startElement
    185      * @see org.xml.sax.ContentHandler#endElement
    186      * @see org.xml.sax.AttributeList
    187      *
    188      * @throws org.xml.sax.SAXException
    189      *
    190      * @see org.xml.sax.ContentHandler#startElement(String,String,String,Attributes)
    191      */
    192     public void startElement(
    193         String arg0,
    194         String arg1,
    195         String arg2,
    196         Attributes arg3)
    197         throws SAXException
    198     {
    199         if (m_state != null) {
    200             m_state.resetState(getTransformer());
    201         }
    202 
    203         // fire off the start element event
    204         if (m_tracer != null)
    205             super.fireStartElem(arg2);
    206     }
    207 
    208     /**
    209      * Sets the LexicalHandler.
    210      * @param _lexHandler The LexicalHandler to set
    211      */
    212     public void setLexHandler(LexicalHandler _lexHandler)
    213     {
    214         this.m_lexHandler = _lexHandler;
    215     }
    216 
    217     /**
    218      * Sets the SAX ContentHandler.
    219      * @param _saxHandler The ContentHandler to set
    220      */
    221     public void setContentHandler(ContentHandler _saxHandler)
    222     {
    223         this.m_saxHandler = _saxHandler;
    224         if (m_lexHandler == null && _saxHandler instanceof LexicalHandler)
    225         {
    226             // we are not overwriting an existing LexicalHandler, and _saxHandler
    227             // is also implements LexicalHandler, so lets use it
    228             m_lexHandler = (LexicalHandler) _saxHandler;
    229         }
    230     }
    231 
    232     /**
    233      * Does nothing. The setting of CDATA section elements has an impact on
    234      * stream serializers.
    235      * @see SerializationHandler#setCdataSectionElements(java.util.Vector)
    236      */
    237     public void setCdataSectionElements(Vector URI_and_localNames)
    238     {
    239         // do nothing
    240     }
    241 
    242     /** Set whether or not namespace declarations (e.g.
    243      * xmlns:foo) should appear as attributes of
    244      * elements
    245      * @param doOutputNSAttr whether or not namespace declarations
    246      * should appear as attributes
    247      */
    248     public void setShouldOutputNSAttr(boolean doOutputNSAttr)
    249     {
    250         m_shouldGenerateNSAttribute = doOutputNSAttr;
    251     }
    252 
    253     /**
    254      * Returns true if namespace declarations from calls such as
    255      * startPrefixMapping("prefix1","uri1") should
    256      * also be mirrored with self generated additional attributes of elements
    257      * that declare the namespace, for example the attribute xmlns:prefix1="uri1"
    258      */
    259     boolean getShouldOutputNSAttr()
    260     {
    261         return m_shouldGenerateNSAttribute;
    262     }
    263 
    264     /**
    265      * This method flushes any pending events, which can be startDocument()
    266      * closing the opening tag of an element, or closing an open CDATA section.
    267      */
    268     public void flushPending() throws SAXException
    269     {
    270 
    271             if (m_needToCallStartDocument)
    272             {
    273                 startDocumentInternal();
    274                 m_needToCallStartDocument = false;
    275             }
    276 
    277             if (m_elemContext.m_startTagOpen)
    278             {
    279                 closeStartTag();
    280                 m_elemContext.m_startTagOpen = false;
    281             }
    282 
    283             if (m_cdataTagOpen)
    284             {
    285                 closeCDATA();
    286                 m_cdataTagOpen = false;
    287             }
    288 
    289     }
    290 
    291     /**
    292      * Pass in a reference to a TransformState object, which
    293      * can be used during SAX ContentHandler events to obtain
    294      * information about he state of the transformation. This
    295      * method will be called  before each startDocument event.
    296      *
    297      * @param ts A reference to a TransformState object
    298      */
    299     public void setTransformState(TransformStateSetter ts) {
    300         this.m_state = ts;
    301     }
    302 
    303     /**
    304      * Receives notification that an element starts, but attributes are not
    305      * fully known yet.
    306      *
    307      * @param uri the URI of the namespace of the element (optional)
    308      * @param localName the element name, but without prefix (optional)
    309      * @param qName the element name, with prefix, if any (required)
    310      *
    311      * @see ExtendedContentHandler#startElement(String, String, String)
    312      */
    313     public void startElement(String uri, String localName, String qName)
    314         throws SAXException {
    315 
    316         if (m_state != null) {
    317             m_state.resetState(getTransformer());
    318         }
    319 
    320         // fire off the start element event
    321         if (m_tracer != null)
    322             super.fireStartElem(qName);
    323     }
    324 
    325     /**
    326      * An element starts, but attributes are not fully known yet.
    327      *
    328      * @param qName the element name, with prefix (if any).
    329 
    330      * @see ExtendedContentHandler#startElement(String)
    331      */
    332     public void startElement(String qName) throws SAXException {
    333         if (m_state != null) {
    334             m_state.resetState(getTransformer());
    335         }
    336         // fire off the start element event
    337         if (m_tracer != null)
    338             super.fireStartElem(qName);
    339     }
    340 
    341     /**
    342      * This method gets the node's value as a String and uses that String as if
    343      * it were an input character notification.
    344      * @param node the Node to serialize
    345      * @throws org.xml.sax.SAXException
    346      */
    347     public void characters(org.w3c.dom.Node node)
    348         throws org.xml.sax.SAXException
    349     {
    350         // remember the current node
    351         if (m_state != null)
    352         {
    353             m_state.setCurrentNode(node);
    354         }
    355 
    356         // Get the node's value as a String and use that String as if
    357         // it were an input character notification.
    358         String data = node.getNodeValue();
    359         if (data != null) {
    360             this.characters(data);
    361         }
    362     }
    363 
    364     /**
    365      * @see org.xml.sax.ErrorHandler#fatalError(SAXParseException)
    366      */
    367     public void fatalError(SAXParseException exc) throws SAXException {
    368         super.fatalError(exc);
    369 
    370         m_needToCallStartDocument = false;
    371 
    372         if (m_saxHandler instanceof ErrorHandler) {
    373             ((ErrorHandler)m_saxHandler).fatalError(exc);
    374         }
    375     }
    376 
    377     /**
    378      * @see org.xml.sax.ErrorHandler#error(SAXParseException)
    379      */
    380     public void error(SAXParseException exc) throws SAXException {
    381         super.error(exc);
    382 
    383         if (m_saxHandler instanceof ErrorHandler)
    384             ((ErrorHandler)m_saxHandler).error(exc);
    385 
    386     }
    387 
    388     /**
    389      * @see org.xml.sax.ErrorHandler#warning(SAXParseException)
    390      */
    391     public void warning(SAXParseException exc) throws SAXException {
    392         super.warning(exc);
    393 
    394         if (m_saxHandler instanceof ErrorHandler)
    395             ((ErrorHandler)m_saxHandler).warning(exc);
    396     }
    397 
    398 
    399     /**
    400      * Try's to reset the super class and reset this class for
    401      * re-use, so that you don't need to create a new serializer
    402      * (mostly for performance reasons).
    403      *
    404      * @return true if the class was successfuly reset.
    405      * @see Serializer#reset()
    406      */
    407     public boolean reset()
    408     {
    409         boolean wasReset = false;
    410         if (super.reset())
    411         {
    412             resetToSAXHandler();
    413             wasReset = true;
    414         }
    415         return wasReset;
    416     }
    417 
    418     /**
    419      * Reset all of the fields owned by ToSAXHandler class
    420      *
    421      */
    422     private void resetToSAXHandler()
    423     {
    424         this.m_lexHandler = null;
    425         this.m_saxHandler = null;
    426         this.m_state = null;
    427         this.m_shouldGenerateNSAttribute = false;
    428     }
    429 
    430     /**
    431      * Add a unique attribute
    432      */
    433     public void addUniqueAttribute(String qName, String value, int flags)
    434         throws SAXException
    435     {
    436         addAttribute(qName, value);
    437     }
    438 }
    439