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