code: /*
   * Licensed to the Apache Software Foundation (ASF) under one
   * or more contributor license agreements. See the NOTICE file
   * distributed with this work for additional information
   * regarding copyright ownership. The ASF licenses this file
   * to you under the Apache License, Version 2.0 (the  "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  /*
   * $Id$
   */
  package org.apache.xml.serializer;
  
  import java.util.Vector;
  
  import org.xml.sax.Attributes;
  import org.xml.sax.ContentHandler;
  import org.xml.sax.ErrorHandler;
  import org.xml.sax.SAXException;
  import org.xml.sax.SAXParseException;
  import org.xml.sax.ext.LexicalHandler;
  
  /**
   * This class is used to provide a base behavior to be inherited
   * by other To...SAXHandler serializers.
   * 
   * This class is not a public API.
   * 
   * @xsl.usage internal
   */
  public abstract class ToSAXHandler extends SerializerBase 
  {
      public ToSAXHandler()
      {
      }
  
      public ToSAXHandler(
          ContentHandler hdlr,
          LexicalHandler lex,
          String encoding)
      {
          setContentHandler(hdlr);
          setLexHandler(lex);
          setEncoding(encoding);
      }
      public ToSAXHandler(ContentHandler handler, String encoding)
      {
          setContentHandler(handler);
          setEncoding(encoding);
      }
  
      /**
       * Underlying SAX handler. Taken from XSLTC
       */
      protected ContentHandler m_saxHandler;
  
      /**
       * Underlying LexicalHandler. Taken from XSLTC
       */
      protected LexicalHandler m_lexHandler;
  
      /**
       * A startPrefixMapping() call on a ToSAXHandler will pass that call
       * on to the wrapped ContentHandler, but should we also mirror these calls
       * with matching attributes, if so this field is true.
       * For example if this field is true then a call such as
       * startPrefixMapping("prefix1","uri1") will also cause the additional
       * internally generated attribute xmlns:prefix1="uri1" to be effectively added
       * to the attributes passed to the wrapped ContentHandler.
       */ 
      private boolean m_shouldGenerateNSAttribute = true;
      
      /** If this is true, then the content handler wrapped by this
       * serializer implements the TransformState interface which
       * will give the content handler access to the state of
       * the transform. */
      protected TransformStateSetter m_state = null;
  
      /**
       * Pass callback to the SAX Handler
       */
      protected void startDocumentInternal() throws SAXException
      {
          if (m_needToCallStartDocument)  
          {
              super.startDocumentInternal();
  
              m_saxHandler.startDocument();
              m_needToCallStartDocument = false;
          }
      }
      /**
       * Do nothing.
       * @see org.xml.sax.ext.LexicalHandler#startDTD(String, String, String)
       */
      public void startDTD(String arg0, String arg1, String arg2)
          throws SAXException
      {
          // do nothing for now
      }
  
      /**
       * Receive notification of character data.
       *
       * @param characters The string of characters to process.
       *
       * @throws org.xml.sax.SAXException
       *
       * @see ExtendedContentHandler#characters(String)
       */
      public void characters(String characters) throws SAXException
      {
          final int len = characters.length();
          if (len > m_charsBuff.length)
          {
             m_charsBuff = new char[len*2 + 1];             
          }
          characters.getChars(0,len, m_charsBuff, 0);   
          characters(m_charsBuff, 0, len);
      }
  
      /**
       * Receive notification of a comment.
       *
       * @see ExtendedLexicalHandler#comment(String)
       */
      public void comment(String comment) throws SAXException
      {
          flushPending();
  
          // Ignore if a lexical handler has not been set
          if (m_lexHandler != null)
          {
              final int len = comment.length();
              if (len > m_charsBuff.length)
              {
                 m_charsBuff = new char[len*2 + 1];              
              }
              comment.getChars(0,len, m_charsBuff, 0);            
              m_lexHandler.comment(m_charsBuff, 0, len);
              // time to fire off comment event
              if (m_tracer != null)
                  super.fireCommentEvent(m_charsBuff, 0, len);
          }
  
      }
  
      /**
       * Do nothing as this is an abstract class. All subclasses will need to
       * define their behavior if it is different.
       * @see org.xml.sax.ContentHandler#processingInstruction(String, String)
       */
      public void processingInstruction(String target, String data)
          throws SAXException
      {
          // Redefined in SAXXMLOutput
      }
  
      protected void closeStartTag() throws SAXException
      {
      }
  
      protected void closeCDATA() throws SAXException
      {
          // Redefined in SAXXMLOutput
      }
      
      /**
       * Receive notification of the beginning of an element, although this is a
       * SAX method additional namespace or attribute information can occur before
       * or after this call, that is associated with this element.
       *
       * @throws org.xml.sax.SAXException Any SAX exception, possibly
       *            wrapping another exception.
       * @see org.xml.sax.ContentHandler#startElement
       * @see org.xml.sax.ContentHandler#endElement
       * @see org.xml.sax.AttributeList
       *
       * @throws org.xml.sax.SAXException
       *
       * @see org.xml.sax.ContentHandler#startElement(String,String,String,Attributes)
       */
      public void startElement(
          String arg0,
          String arg1,
          String arg2,
          Attributes arg3)
          throws SAXException
      {
          if (m_state != null) {
              m_state.resetState(getTransformer());
          }
  
          // fire off the start element event
          if (m_tracer != null)
              super.fireStartElem(arg2);
      }
  
      /**
       * Sets the LexicalHandler.
       * @param _lexHandler The LexicalHandler to set
       */
      public void setLexHandler(LexicalHandler _lexHandler)
      {
          this.m_lexHandler = _lexHandler;
      }
  
      /**
       * Sets the SAX ContentHandler.
       * @param _saxHandler The ContentHandler to set
       */
      public void setContentHandler(ContentHandler _saxHandler)
      {
          this.m_saxHandler = _saxHandler;
          if (m_lexHandler == null && _saxHandler instanceof LexicalHandler)
          {
              // we are not overwriting an existing LexicalHandler, and _saxHandler
              // is also implements LexicalHandler, so lets use it
              m_lexHandler = (LexicalHandler) _saxHandler;
          }
      }
  
      /**
       * Does nothing. The setting of CDATA section elements has an impact on
       * stream serializers.
       * @see SerializationHandler#setCdataSectionElements(java.util.Vector)
       */
      public void setCdataSectionElements(Vector URI_and_localNames)
      {
          // do nothing
      }
      
      /** Set whether or not namespace declarations (e.g. 
       * xmlns:foo) should appear as attributes of 
       * elements
       * @param doOutputNSAttr whether or not namespace declarations
       * should appear as attributes
       */
      public void setShouldOutputNSAttr(boolean doOutputNSAttr)
      {
          m_shouldGenerateNSAttribute = doOutputNSAttr;
      }
   
      /** 
       * Returns true if namespace declarations from calls such as
       * startPrefixMapping("prefix1","uri1") should
       * also be mirrored with self generated additional attributes of elements 
       * that declare the namespace, for example the attribute xmlns:prefix1="uri1"
       */
      boolean getShouldOutputNSAttr()
      {
          return m_shouldGenerateNSAttribute;
      }
  
      /**
       * This method flushes any pending events, which can be startDocument()
       * closing the opening tag of an element, or closing an open CDATA section.
       */
      public void flushPending() throws SAXException
      {
      
              if (m_needToCallStartDocument)
              {
                  startDocumentInternal();
                  m_needToCallStartDocument = false;
              }
  
              if (m_elemContext.m_startTagOpen)
              {
                  closeStartTag();
                  m_elemContext.m_startTagOpen = false;
              }
              
              if (m_cdataTagOpen)
              {
                  closeCDATA();
                  m_cdataTagOpen = false;
              }
  
      }
  
      /**
       * Pass in a reference to a TransformState object, which
       * can be used during SAX ContentHandler events to obtain
       * information about he state of the transformation. This
       * method will be called  before each startDocument event.
       *
       * @param ts A reference to a TransformState object
       */
      public void setTransformState(TransformStateSetter ts) {
          this.m_state = ts;
      }
      
      /**
       * Receives notification that an element starts, but attributes are not
       * fully known yet.
       *
       * @param uri the URI of the namespace of the element (optional)
       * @param localName the element name, but without prefix (optional)
       * @param qName the element name, with prefix, if any (required)
       *
       * @see ExtendedContentHandler#startElement(String, String, String)
       */
      public void startElement(String uri, String localName, String qName)
          throws SAXException {
              
          if (m_state != null) {
              m_state.resetState(getTransformer());
          }
  
          // fire off the start element event
          if (m_tracer != null)
              super.fireStartElem(qName);         
      }
  
      /**
       * An element starts, but attributes are not fully known yet.
       *
       * @param qName the element name, with prefix (if any).
  
       * @see ExtendedContentHandler#startElement(String)
       */
      public void startElement(String qName) throws SAXException {
          if (m_state != null) {
              m_state.resetState(getTransformer());
          }        
          // fire off the start element event
          if (m_tracer != null)
              super.fireStartElem(qName);              
      }
      
      /**
       * This method gets the node's value as a String and uses that String as if
       * it were an input character notification.
       * @param node the Node to serialize
       * @throws org.xml.sax.SAXException
       */    
      public void characters(org.w3c.dom.Node node)
          throws org.xml.sax.SAXException
      {
          // remember the current node
          if (m_state != null)
          {
              m_state.setCurrentNode(node);
          }
          
          // Get the node's value as a String and use that String as if
          // it were an input character notification.
          String data = node.getNodeValue();
          if (data != null) {
              this.characters(data);
          }
      }    
  
      /**
       * @see org.xml.sax.ErrorHandler#fatalError(SAXParseException)
       */
      public void fatalError(SAXParseException exc) throws SAXException {
          super.fatalError(exc);
          
          m_needToCallStartDocument = false;
          
          if (m_saxHandler instanceof ErrorHandler) {
              ((ErrorHandler)m_saxHandler).fatalError(exc);            
          }
      }
  
      /**
       * @see org.xml.sax.ErrorHandler#error(SAXParseException)
       */
      public void error(SAXParseException exc) throws SAXException {
          super.error(exc);
          
          if (m_saxHandler instanceof ErrorHandler)
              ((ErrorHandler)m_saxHandler).error(exc);        
          
      }
  
      /**
       * @see org.xml.sax.ErrorHandler#warning(SAXParseException)
       */
      public void warning(SAXParseException exc) throws SAXException {
          super.warning(exc);
          
          if (m_saxHandler instanceof ErrorHandler)
              ((ErrorHandler)m_saxHandler).warning(exc);        
      }
      
         
      /**
       * Try's to reset the super class and reset this class for 
       * re-use, so that you don't need to create a new serializer 
       * (mostly for performance reasons).
       * 
       * @return true if the class was successfuly reset.
       * @see Serializer#reset()
       */
      public boolean reset()
      {
          boolean wasReset = false;
          if (super.reset())
          {
              resetToSAXHandler();
              wasReset = true;
          }
          return wasReset;
      }
      
      /**
       * Reset all of the fields owned by ToSAXHandler class
       *
       */
      private void resetToSAXHandler()
      {
          this.m_lexHandler = null;
          this.m_saxHandler = null;
          this.m_state = null;
          this.m_shouldGenerateNSAttribute = false;
      }  
  
      /**
       * Add a unique attribute
       */
      public void addUniqueAttribute(String qName, String value, int flags)
          throws SAXException
      {
          addAttribute(qName, value); 
      }
  }
  