src/java.xml/share/classes/com/sun/org/apache/xml/internal/serializer/ToXMLStream.java
changeset 47216 71c04702a3d5
parent 45035 4b44e48d8ff1
child 55575 25165403c62e
child 58678 9cf78a70fa4f
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.xml/share/classes/com/sun/org/apache/xml/internal/serializer/ToXMLStream.java	Tue Sep 12 19:03:39 2017 +0200
@@ -0,0 +1,657 @@
+/*
+ * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
+ */
+/*
+ * 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.
+ */
+
+ package com.sun.org.apache.xml.internal.serializer;
+
+import java.io.IOException;
+
+import javax.xml.transform.ErrorListener;
+import javax.xml.transform.Result;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+
+import com.sun.org.apache.xml.internal.serializer.utils.MsgKey;
+import com.sun.org.apache.xml.internal.serializer.utils.Utils;
+import org.xml.sax.SAXException;
+
+/**
+ * This class converts SAX or SAX-like calls to a
+ * serialized xml document.  The xsl:output method is "xml".
+ *
+ * This class is used explicitly in code generated by XSLTC,
+ * so it is "public", but it should
+ * be viewed as internal or package private, this is not an API.
+ *
+ * @xsl.usage internal
+ */
+public final class ToXMLStream extends ToStream
+{
+
+    /**
+     * remembers if we need to write out "]]>" to close the CDATA
+     */
+    boolean m_cdataTagOpen = false;
+
+
+    /**
+     * Map that tells which XML characters should have special treatment, and it
+     *  provides character to entity name lookup.
+     */
+    private static CharInfo m_xmlcharInfo =
+//      new CharInfo(CharInfo.XML_ENTITIES_RESOURCE);
+        CharInfo.getCharInfoInternal(CharInfo.XML_ENTITIES_RESOURCE, Method.XML);
+
+    /**
+     * Default constructor.
+     */
+    public ToXMLStream()
+    {
+        m_charInfo = m_xmlcharInfo;
+
+        initCDATA();
+        // initialize namespaces
+        m_prefixMap = new NamespaceMappings();
+
+    }
+
+    /**
+     * Copy properties from another SerializerToXML.
+     *
+     * @param xmlListener non-null reference to a SerializerToXML object.
+     */
+    public void CopyFrom(ToXMLStream xmlListener)
+    {
+
+        m_writer = xmlListener.m_writer;
+
+
+        // m_outputStream = xmlListener.m_outputStream;
+        String encoding = xmlListener.getEncoding();
+        setEncoding(encoding);
+
+        setOmitXMLDeclaration(xmlListener.getOmitXMLDeclaration());
+
+        m_ispreserveSpace = xmlListener.m_ispreserveSpace;
+        m_preserveSpaces = xmlListener.m_preserveSpaces;
+        m_childNodeNum = xmlListener.m_childNodeNum;
+        m_childNodeNumStack = xmlListener.m_childNodeNumStack;
+        m_charactersBuffer = xmlListener.m_charactersBuffer;
+        m_inEntityRef = xmlListener.m_inEntityRef;
+        m_isprevtext = xmlListener.m_isprevtext;
+        m_doIndent = xmlListener.m_doIndent;
+        setIndentAmount(xmlListener.getIndentAmount());
+        m_startNewLine = xmlListener.m_startNewLine;
+        m_needToOutputDocTypeDecl = xmlListener.m_needToOutputDocTypeDecl;
+        setDoctypeSystem(xmlListener.getDoctypeSystem());
+        setDoctypePublic(xmlListener.getDoctypePublic());
+        setStandalone(xmlListener.getStandalone());
+        setMediaType(xmlListener.getMediaType());
+        m_maxCharacter = xmlListener.m_maxCharacter;
+        m_encodingInfo = xmlListener.m_encodingInfo;
+        m_spaceBeforeClose = xmlListener.m_spaceBeforeClose;
+        m_cdataStartCalled = xmlListener.m_cdataStartCalled;
+
+    }
+
+    /**
+     * Receive notification of the beginning of a document.
+     *
+     * @throws org.xml.sax.SAXException Any SAX exception, possibly
+     *            wrapping another exception.
+     *
+     * @throws org.xml.sax.SAXException
+     */
+    public void startDocumentInternal() throws org.xml.sax.SAXException
+    {
+
+        if (m_needToCallStartDocument)
+        {
+            super.startDocumentInternal();
+            m_needToCallStartDocument = false;
+
+            if (isInEntityRef())
+                return;
+
+            m_needToOutputDocTypeDecl = true;
+            m_startNewLine = false;
+            /* The call to getXMLVersion() might emit an error message
+             * and we should emit this message regardless of if we are
+             * writing out an XML header or not.
+             */
+            if (getOmitXMLDeclaration() == false)
+            {
+                String encoding = Encodings.getMimeEncoding(getEncoding());
+                String version = getVersion();
+                if (version == null)
+                    version = "1.0";
+                String standalone;
+
+                if (m_standaloneWasSpecified)
+                {
+                    standalone = " standalone=\"" + getStandalone() + "\"";
+                }
+                else
+                {
+                    standalone = "";
+                }
+
+                try
+                {
+                    final java.io.Writer writer = m_writer;
+                    writer.write("<?xml version=\"");
+                    writer.write(version);
+                    writer.write("\" encoding=\"");
+                    writer.write(encoding);
+                    writer.write('\"');
+                    writer.write(standalone);
+                    writer.write("?>");
+                    if (m_doIndent) {
+                        if (m_standaloneWasSpecified
+                                || getDoctypePublic() != null
+                                || getDoctypeSystem() != null
+                                || m_isStandalone) {
+                            // We almost never put a newline after the XML
+                            // header because this XML could be used as
+                            // an extenal general parsed entity
+                            // and we don't know the context into which it
+                            // will be used in the future.  Only when
+                            // standalone, or a doctype system or public is
+                            // specified are we free to insert a new line
+                            // after the header.  Is it even worth bothering
+                            // in these rare cases?
+                            writer.write(m_lineSep, 0, m_lineSepLen);
+                        }
+                    }
+                }
+                catch(IOException e)
+                {
+                    throw new SAXException(e);
+                }
+
+            }
+        }
+    }
+
+    /**
+     * Receive notification of the end of a document.
+     *
+     * @throws org.xml.sax.SAXException Any SAX exception, possibly
+     *            wrapping another exception.
+     *
+     * @throws org.xml.sax.SAXException
+     */
+    public void endDocument() throws org.xml.sax.SAXException
+    {
+        if (m_doIndent) {
+            flushCharactersBuffer();
+        }
+        flushPending();
+        if (m_doIndent && !m_isprevtext)
+        {
+            try
+            {
+            outputLineSep();
+            }
+            catch(IOException e)
+            {
+                throw new SAXException(e);
+            }
+        }
+
+        flushWriter();
+
+        if (m_tracer != null)
+            super.fireEndDoc();
+    }
+
+    /**
+     * Starts a whitespace preserving section. All characters printed
+     * within a preserving section are printed without indentation and
+     * without consolidating multiple spaces. This is equivalent to
+     * the <tt>xml:space=&quot;preserve&quot;</tt> attribute. Only XML
+     * and HTML serializers need to support this method.
+     * <p>
+     * The contents of the whitespace preserving section will be delivered
+     * through the regular <tt>characters</tt> event.
+     *
+     * @throws org.xml.sax.SAXException
+     */
+    public void startPreserving() throws org.xml.sax.SAXException
+    {
+    }
+
+    /**
+     * Ends a whitespace preserving section.
+     *
+     * @see #startPreserving
+     *
+     * @throws org.xml.sax.SAXException
+     */
+    public void endPreserving() throws org.xml.sax.SAXException
+    {
+    }
+
+    /**
+     * Receive notification of a processing instruction.
+     *
+     * @param target The processing instruction target.
+     * @param data The processing instruction data, or null if
+     *        none was supplied.
+     * @throws org.xml.sax.SAXException Any SAX exception, possibly
+     *            wrapping another exception.
+     *
+     * @throws org.xml.sax.SAXException
+     */
+    public void processingInstruction(String target, String data)
+        throws org.xml.sax.SAXException
+    {
+        if (isInEntityRef())
+            return;
+
+        if (m_doIndent) {
+            m_childNodeNum++;
+            flushCharactersBuffer();
+        }
+        flushPending();
+
+        if (target.equals(Result.PI_DISABLE_OUTPUT_ESCAPING))
+        {
+            startNonEscaping();
+        }
+        else if (target.equals(Result.PI_ENABLE_OUTPUT_ESCAPING))
+        {
+            endNonEscaping();
+        }
+        else
+        {
+            try
+            {
+                if (m_elemContext.m_startTagOpen)
+                {
+                    closeStartTag();
+                    m_elemContext.m_startTagOpen = false;
+                }
+                else if (m_needToCallStartDocument)
+                    startDocumentInternal();
+
+                if (shouldIndent())
+                    indent();
+
+                final java.io.Writer writer = m_writer;
+                writer.write("<?");
+                writer.write(target);
+
+                if (data.length() > 0
+                    && !Character.isSpaceChar(data.charAt(0)))
+                    writer.write(' ');
+
+                int indexOfQLT = data.indexOf("?>");
+
+                if (indexOfQLT >= 0)
+                {
+
+                    // See XSLT spec on error recovery of "?>" in PIs.
+                    if (indexOfQLT > 0)
+                    {
+                        writer.write(data.substring(0, indexOfQLT));
+                    }
+
+                    writer.write("? >"); // add space between.
+
+                    if ((indexOfQLT + 2) < data.length())
+                    {
+                        writer.write(data.substring(indexOfQLT + 2));
+                    }
+                }
+                else
+                {
+                    writer.write(data);
+                }
+
+                writer.write('?');
+                writer.write('>');
+
+                /**
+                 * Before Xalan 1497, a newline char was printed out if not inside of an
+                 * element. The whitespace is not significant is the output is standalone
+                */
+                if (m_elemContext.m_currentElemDepth <= 0 && m_isStandalone)
+                    writer.write(m_lineSep, 0, m_lineSepLen);
+
+
+                /*
+                 * Don't write out any indentation whitespace now,
+                 * because there may be non-whitespace text after this.
+                 *
+                 * Simply mark that at this point if we do decide
+                 * to indent that we should
+                 * add a newline on the end of the current line before
+                 * the indentation at the start of the next line.
+                 */
+                m_startNewLine = true;
+            }
+            catch(IOException e)
+            {
+                throw new SAXException(e);
+            }
+        }
+
+        if (m_tracer != null)
+            super.fireEscapingEvent(target, data);
+    }
+
+    /**
+     * Receive notivication of a entityReference.
+     *
+     * @param name The name of the entity.
+     *
+     * @throws org.xml.sax.SAXException
+     */
+    public void entityReference(String name) throws org.xml.sax.SAXException
+    {
+        if (m_elemContext.m_startTagOpen)
+        {
+            closeStartTag();
+            m_elemContext.m_startTagOpen = false;
+        }
+
+        try
+        {
+            if (shouldIndent())
+                indent();
+
+            final java.io.Writer writer = m_writer;
+            writer.write('&');
+            writer.write(name);
+            writer.write(';');
+        }
+        catch(IOException e)
+        {
+            throw new SAXException(e);
+        }
+
+        if (m_tracer != null)
+            super.fireEntityReference(name);
+    }
+
+    /**
+     * This method is used to add an attribute to the currently open element.
+     * The caller has guaranted that this attribute is unique, which means that it
+     * not been seen before and will not be seen again.
+     *
+     * @param name the qualified name of the attribute
+     * @param value the value of the attribute which can contain only
+     * ASCII printable characters characters in the range 32 to 127 inclusive.
+     * @param flags the bit values of this integer give optimization information.
+     */
+    public void addUniqueAttribute(String name, String value, int flags)
+        throws SAXException
+    {
+        if (m_elemContext.m_startTagOpen)
+        {
+
+            try
+            {
+                final String patchedName = patchName(name);
+                final java.io.Writer writer = m_writer;
+                if ((flags & NO_BAD_CHARS) > 0 && m_xmlcharInfo.onlyQuotAmpLtGt)
+                {
+                    // "flags" has indicated that the characters
+                    // '>'  '<'   '&'  and '"' are not in the value and
+                    // m_htmlcharInfo has recorded that there are no other
+                    // entities in the range 32 to 127 so we write out the
+                    // value directly
+
+                    writer.write(' ');
+                    writer.write(patchedName);
+                    writer.write("=\"");
+                    writer.write(value);
+                    writer.write('"');
+                }
+                else
+                {
+                    writer.write(' ');
+                    writer.write(patchedName);
+                    writer.write("=\"");
+                    writeAttrString(writer, value, this.getEncoding());
+                    writer.write('"');
+                }
+            } catch (IOException e) {
+                throw new SAXException(e);
+            }
+        }
+    }
+
+    /**
+     * Add an attribute to the current element.
+     * @param uri the URI associated with the element name
+     * @param localName local part of the attribute name
+     * @param rawName   prefix:localName
+     * @param type
+     * @param value the value of the attribute
+     * @param xslAttribute true if this attribute is from an xsl:attribute,
+     * false if declared within the elements opening tag.
+     * @throws SAXException
+     */
+    public void addAttribute(
+        String uri,
+        String localName,
+        String rawName,
+        String type,
+        String value,
+        boolean xslAttribute)
+        throws SAXException
+    {
+        if (m_elemContext.m_startTagOpen)
+        {
+            boolean was_added = addAttributeAlways(uri, localName, rawName, type, value, xslAttribute);
+
+
+            /*
+             * We don't run this block of code if:
+             * 1. The attribute value was only replaced (was_added is false).
+             * 2. The attribute is from an xsl:attribute element (that is handled
+             *    in the addAttributeAlways() call just above.
+             * 3. The name starts with "xmlns", i.e. it is a namespace declaration.
+             */
+            if (was_added && !xslAttribute && !rawName.startsWith("xmlns"))
+            {
+                String prefixUsed =
+                    ensureAttributesNamespaceIsDeclared(
+                        uri,
+                        localName,
+                        rawName);
+                if (prefixUsed != null
+                    && rawName != null
+                    && !rawName.startsWith(prefixUsed))
+                {
+                    // use a different raw name, with the prefix used in the
+                    // generated namespace declaration
+                    rawName = prefixUsed + ":" + localName;
+
+                }
+            }
+            addAttributeAlways(uri, localName, rawName, type, value, xslAttribute);
+        }
+        else
+        {
+            /*
+             * The startTag is closed, yet we are adding an attribute?
+             *
+             * Section: 7.1.3 Creating Attributes Adding an attribute to an
+             * element after a PI (for example) has been added to it is an
+             * error. The attributes can be ignored. The spec doesn't explicitly
+             * say this is disallowed, as it does for child elements, but it
+             * makes sense to have the same treatment.
+             *
+             * We choose to ignore the attribute which is added too late.
+             */
+            // Generate a warning of the ignored attributes
+
+            // Create the warning message
+            String msg = Utils.messages.createMessage(
+                    MsgKey.ER_ILLEGAL_ATTRIBUTE_POSITION,new Object[]{ localName });
+
+            try {
+                // Prepare to issue the warning message
+                Transformer tran = super.getTransformer();
+                ErrorListener errHandler = tran.getErrorListener();
+
+
+                // Issue the warning message
+                if (null != errHandler && m_sourceLocator != null)
+                  errHandler.warning(new TransformerException(msg, m_sourceLocator));
+                else
+                  System.out.println(msg);
+                }
+            catch (Exception e){}
+        }
+    }
+
+    /**
+     * @see ExtendedContentHandler#endElement(String)
+     */
+    public void endElement(String elemName) throws SAXException
+    {
+        endElement(null, null, elemName);
+    }
+
+    /**
+     * This method is used to notify the serializer of a namespace mapping (or node)
+     * that applies to the current element whose startElement() call has already been seen.
+     * The official SAX startPrefixMapping(prefix,uri) is to define a mapping for a child
+     * element that is soon to be seen with a startElement() call. The official SAX call
+     * does not apply to the current element, hence the reason for this method.
+     */
+    public void namespaceAfterStartElement(
+        final String prefix,
+        final String uri)
+        throws SAXException
+    {
+
+        // hack for XSLTC with finding URI for default namespace
+        if (m_elemContext.m_elementURI == null)
+        {
+            String prefix1 = getPrefixPart(m_elemContext.m_elementName);
+            if (prefix1 == null && EMPTYSTRING.equals(prefix))
+            {
+                // the elements URI is not known yet, and it
+                // doesn't have a prefix, and we are currently
+                // setting the uri for prefix "", so we have
+                // the uri for the element... lets remember it
+                m_elemContext.m_elementURI = uri;
+            }
+        }
+        startPrefixMapping(prefix,uri,false);
+        return;
+
+    }
+
+    /**
+     * From XSLTC
+     * Declare a prefix to point to a namespace URI. Inform SAX handler
+     * if this is a new prefix mapping.
+     */
+    protected boolean pushNamespace(String prefix, String uri)
+    {
+        try
+        {
+            if (m_prefixMap.pushNamespace(
+                prefix, uri, m_elemContext.m_currentElemDepth))
+            {
+                startPrefixMapping(prefix, uri);
+                return true;
+            }
+        }
+        catch (SAXException e)
+        {
+            // falls through
+        }
+        return false;
+    }
+    /**
+     * 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.
+     */
+    public boolean reset()
+    {
+        boolean wasReset = false;
+        if (super.reset())
+        {
+            resetToXMLStream();
+            wasReset = true;
+        }
+        return wasReset;
+    }
+
+    /**
+     * Reset all of the fields owned by ToStream class
+     *
+     */
+    private void resetToXMLStream()
+    {
+        this.m_cdataTagOpen = false;
+
+    }
+
+    /**
+     * This method checks for the XML version of output document.
+     * If XML version of output document is not specified, then output
+     * document is of version XML 1.0.
+     * If XML version of output doucment is specified, but it is not either
+     * XML 1.0 or XML 1.1, a warning message is generated, the XML Version of
+     * output document is set to XML 1.0 and processing continues.
+     * @return string (XML version)
+     */
+    private String getXMLVersion()
+    {
+        String xmlVersion = getVersion();
+        if(xmlVersion == null || xmlVersion.equals(XMLVERSION10))
+        {
+            xmlVersion = XMLVERSION10;
+        }
+        else if(xmlVersion.equals(XMLVERSION11))
+        {
+            xmlVersion = XMLVERSION11;
+        }
+        else
+        {
+            String msg = Utils.messages.createMessage(
+                               MsgKey.ER_XML_VERSION_NOT_SUPPORTED,new Object[]{ xmlVersion });
+            try
+            {
+                // Prepare to issue the warning message
+                Transformer tran = super.getTransformer();
+                ErrorListener errHandler = tran.getErrorListener();
+                // Issue the warning message
+                if (null != errHandler && m_sourceLocator != null)
+                    errHandler.warning(new TransformerException(msg, m_sourceLocator));
+                else
+                    System.out.println(msg);
+            }
+            catch (Exception e){}
+            xmlVersion = XMLVERSION10;
+        }
+        return xmlVersion;
+    }
+}