blob: d91e8f0e9273f354b26a361de1e1c9c766b48494 [file] [log] [blame]
/*
* 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=&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
{
// 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;
}
}