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