| /* |
| * The Apache Software License, Version 1.1 |
| * |
| * |
| * Copyright (c) 2001-2003 The Apache Software Foundation. All rights |
| * reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in |
| * the documentation and/or other materials provided with the |
| * distribution. |
| * |
| * 3. The end-user documentation included with the redistribution, |
| * if any, must include the following acknowledgment: |
| * "This product includes software developed by the |
| * Apache Software Foundation (http://www.apache.org/)." |
| * Alternately, this acknowledgment may appear in the software itself, |
| * if and wherever such third-party acknowledgments normally appear. |
| * |
| * 4. The names "Xalan" and "Apache Software Foundation" must |
| * not be used to endorse or promote products derived from this |
| * software without prior written permission. For written |
| * permission, please contact apache@apache.org. |
| * |
| * 5. Products derived from this software may not be called "Apache", |
| * nor may "Apache" appear in their name, without prior written |
| * permission of the Apache Software Foundation. |
| * |
| * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED |
| * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
| * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR |
| * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES{} LOSS OF |
| * USE, DATA, OR PROFITS{} OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
| * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT |
| * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| * SUCH DAMAGE. |
| * ==================================================================== |
| * |
| * This software consists of voluntary contributions made by many |
| * individuals on behalf of the Apache Software Foundation and was |
| * originally based on software copyright (c) 2001, Sun |
| * Microsystems., http://www.sun.com. For more |
| * information on the Apache Software Foundation, please see |
| * <http://www.apache.org/>. |
| * |
| * @author Santiago Pericas-Geertsen |
| * @author G. Todd Miller |
| * |
| */ |
| package org.apache.xml.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 org.apache.xml.res.XMLErrorResources; |
| import org.apache.xml.res.XMLMessages; |
| import org.xml.sax.SAXException; |
| |
| public class ToXMLStream extends ToStream |
| { |
| |
| /** |
| * remembers if we need to write out "]]>" to close the CDATA |
| */ |
| private boolean m_cdataTagOpen = false; |
| |
| |
| /** |
| * Map that tells which XML characters should have special treatment, and it |
| * provides character to entity name lookup. |
| */ |
| protected static CharInfo m_xmlcharInfo = |
| // new CharInfo(CharInfo.XML_ENTITIES_RESOURCE); |
| CharInfo.getCharInfo(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_ispreserve = xmlListener.m_ispreserve; |
| m_preserves = xmlListener.m_preserves; |
| 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_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 (m_inEntityRef) |
| return; |
| |
| m_needToOutputDocTypeDecl = true; |
| m_startNewLine = false; |
| |
| 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("?>"); |
| 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 |
| { |
| 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="preserve"</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 |
| { |
| |
| // Not sure this is really what we want. -sb |
| m_preserves.push(true); |
| |
| m_ispreserve = true; |
| } |
| |
| /** |
| * Ends a whitespace preserving section. |
| * |
| * @see #startPreserving |
| * |
| * @throws org.xml.sax.SAXException |
| */ |
| public void endPreserving() throws org.xml.sax.SAXException |
| { |
| |
| // Not sure this is really what we want. -sb |
| m_ispreserve = m_preserves.isEmpty() ? false : m_preserves.pop(); |
| } |
| |
| /** |
| * 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 (m_inEntityRef) |
| return; |
| |
| 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; |
| } |
| |
| 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('>'); |
| |
| // Always output a newline char if not inside of an |
| // element. The whitespace is not significant in that |
| // case. |
| if (m_elemContext.m_currentElemDepth <= 0) |
| writer.write(m_lineSep, 0, m_lineSepLen); |
| |
| 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); |
| } |
| } |
| } |
| |
| public void addAttribute( |
| String uri, |
| String localName, |
| String rawName, |
| String type, |
| String value) |
| throws SAXException |
| { |
| if (m_elemContext.m_startTagOpen) |
| { |
| if (!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); |
| } |
| 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 = XMLMessages.createXMLMessage( |
| XMLErrorResources.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 org.apache.xml.serializer.ExtendedContentHandler#endElement(String) |
| */ |
| public void endElement(String elemName) throws SAXException |
| { |
| endElement(null, null, elemName); |
| } |
| |
| /** |
| * From XSLTC |
| * Related to startPrefixMapping ??? |
| */ |
| 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; |
| |
| } |
| |
| } |