/* * 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: SerializerBase.java 471981 2006-11-07 04:28:00Z minchau $ */ package org.apache.xml.serializer; import java.io.IOException; import java.util.HashMap; import java.util.Set; import javax.xml.transform.OutputKeys; import javax.xml.transform.SourceLocator; import javax.xml.transform.Transformer; import org.apache.xml.serializer.utils.MsgKey; import org.apache.xml.serializer.utils.Utils; import org.xml.sax.Attributes; import org.xml.sax.ContentHandler; import org.xml.sax.Locator; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; /** * This class acts as a base class for the XML "serializers" * and the stream serializers. * It contains a number of common fields and methods. * * @xsl.usage internal */ public abstract class SerializerBase implements SerializationHandler, SerializerConstants { SerializerBase() { return; } /** * The name of the package that this class is in. *
* Not a public API. */ public static final String PKG_NAME; /** * The same as the name of the package that this class is in * except that '.' are replaced with '/'. *
* Not a public API. */ public static final String PKG_PATH; static { String fullyQualifiedName = SerializerBase.class.getName(); int lastDot = fullyQualifiedName.lastIndexOf('.'); if (lastDot < 0) { PKG_NAME = ""; } else { PKG_NAME = fullyQualifiedName.substring(0, lastDot); } StringBuffer sb = new StringBuffer(); for (int i = 0; i < PKG_NAME.length(); i++) { char ch = PKG_NAME.charAt(i); if (ch == '.') sb.append('/'); else sb.append(ch); } PKG_PATH = sb.toString(); } /** * To fire off the end element trace event * @param name Name of element */ protected void fireEndElem(String name) throws org.xml.sax.SAXException { if (m_tracer != null) { flushMyWriter(); m_tracer.fireGenerateEvent(SerializerTrace.EVENTTYPE_ENDELEMENT,name, (Attributes)null); } } /** * Report the characters trace event * @param chars content of characters * @param start starting index of characters to output * @param length number of characters to output */ protected void fireCharEvent(char[] chars, int start, int length) throws org.xml.sax.SAXException { if (m_tracer != null) { flushMyWriter(); m_tracer.fireGenerateEvent(SerializerTrace.EVENTTYPE_CHARACTERS, chars, start,length); } } /** * true if we still need to call startDocumentInternal() */ protected boolean m_needToCallStartDocument = true; /** True if a trailing "]]>" still needs to be written to be * written out. Used to merge adjacent CDATA sections */ protected boolean m_cdataTagOpen = false; /** * All the attributes of the current element, collected from * startPrefixMapping() calls, or addAddtribute() calls, or * from the SAX attributes in a startElement() call. */ protected AttributesImplSerializer m_attributes = new AttributesImplSerializer(); /** * Tells if we're in an EntityRef event. */ protected boolean m_inEntityRef = false; /** This flag is set while receiving events from the external DTD */ protected boolean m_inExternalDTD = false; /** * The System ID for the doc type. */ protected String m_doctypeSystem; /** * The public ID for the doc type. */ protected String m_doctypePublic; /** * Flag to tell that we need to add the doctype decl, which we can't do * until the first element is encountered. */ boolean m_needToOutputDocTypeDecl = true; /** * Tells if we should write the XML declaration. */ protected boolean m_shouldNotWriteXMLHeader = false; /** * The standalone value for the doctype. */ private String m_standalone; /** * True if standalone was specified. */ protected boolean m_standaloneWasSpecified = false; /** * Flag to tell if indenting (pretty-printing) is on. */ protected boolean m_doIndent = false; /** * Amount to indent. */ protected int m_indentAmount = 0; /** * Tells the XML version, for writing out to the XML decl. */ protected String m_version = null; /** * The mediatype. Not used right now. */ protected String m_mediatype; /** * The transformer that was around when this output handler was created (if * any). */ private Transformer m_transformer; /** * Namespace support, that keeps track of currently defined * prefix/uri mappings. As processed elements come and go, so do * the associated mappings for that element. */ protected NamespaceMappings m_prefixMap; /** * Handle for firing generate events. This interface may be implemented * by the referenced transformer object. */ protected SerializerTrace m_tracer; protected SourceLocator m_sourceLocator; /** * The writer to send output to. This field is only used in the ToStream * serializers, but exists here just so that the fireStartDoc() and * other fire... methods can flush this writer when tracing. */ protected java.io.Writer m_writer = null; /** * A reference to "stack frame" corresponding to * the current element. Such a frame is pushed at a startElement() * and popped at an endElement(). This frame contains information about * the element, such as its namespace URI. */ protected ElemContext m_elemContext = new ElemContext(); /** * A utility buffer for converting Strings passed to * character() methods to character arrays. * Reusing this buffer means not creating a new character array * everytime and it runs faster. */ protected char[] m_charsBuff = new char[60]; /** * A utility buffer for converting Strings passed to * attribute methods to character arrays. * Reusing this buffer means not creating a new character array * everytime and it runs faster. */ protected char[] m_attrBuff = new char[30]; /** * Receive notification of a comment. * * @see ExtendedLexicalHandler#comment(String) */ public void comment(String data) throws SAXException { m_docIsEmpty = false; final int length = data.length(); if (length > m_charsBuff.length) { m_charsBuff = new char[length * 2 + 1]; } data.getChars(0, length, m_charsBuff, 0); comment(m_charsBuff, 0, length); } /** * If at runtime, when the qname of the attribute is * known, another prefix is specified for the attribute, then we can * patch or hack the name with this method. For * a qname of the form "ns?:otherprefix:name", this function patches the * qname by simply ignoring "otherprefix". * TODO: This method is a HACK! We do not have access to the * XML file, it sometimes generates a NS prefix of the form "ns?" for * an attribute. */ protected String patchName(String qname) { final int lastColon = qname.lastIndexOf(':'); if (lastColon > 0) { final int firstColon = qname.indexOf(':'); final String prefix = qname.substring(0, firstColon); final String localName = qname.substring(lastColon + 1); // If uri is "" then ignore prefix final String uri = m_prefixMap.lookupNamespace(prefix); if (uri != null && uri.length() == 0) { return localName; } else if (firstColon != lastColon) { return prefix + ':' + localName; } } return qname; } /** * Returns the local name of a qualified name. If the name has no prefix, * then it works as the identity (SAX2). * @param qname the qualified name * @return the name, but excluding any prefix and colon. */ protected static String getLocalName(String qname) { final int col = qname.lastIndexOf(':'); return (col > 0) ? qname.substring(col + 1) : qname; } /** * Receive an object for locating the origin of SAX document events. * * @param locator An object that can return the location of any SAX document * event. * * Receive an object for locating the origin of SAX document events. * *
SAX parsers are strongly encouraged (though not absolutely * required) to supply a locator: if it does so, it must supply * the locator to the application by invoking this method before * invoking any of the other methods in the DocumentHandler * interface.
* *The locator allows the application to determine the end * position of any document-related event, even if the parser is * not reporting an error. Typically, the application will * use this information for reporting its own errors (such as * character content that does not match an application's * business rules). The information returned by the locator * is probably not sufficient for use with a search engine.
* *Note that the locator will return correct information only * during the invocation of the events in this interface. The * application should not attempt to use it at any other time.
*/ public void setDocumentLocator(Locator locator) { return; // I don't do anything with this yet. } /** * Adds the given attribute to the set of collected attributes , but only if * there is a currently open element. * * An element is currently open if a startElement() notification has * occured but the start of the element has not yet been written to the * output. In the stream case this means that we have not yet been forced * to close the elements opening tag by another notification, such as a * character notification. * * @param uri the URI of the attribute * @param localName the local name of the attribute * @param rawName the qualified name of the attribute * @param type the type of the attribute (probably CDATA) * @param value the value of the attribute * @param XSLAttribute true if this attribute is coming from an xsl:attriute element * @see ExtendedContentHandler#addAttribute(String, String, String, String, String) */ public void addAttribute( String uri, String localName, String rawName, String type, String value, boolean XSLAttribute) throws SAXException { if (m_elemContext.m_startTagOpen) { addAttributeAlways(uri, localName, rawName, type, value, XSLAttribute); } } /** * Adds the given attribute to the set of attributes, even if there is * no currently open element. This is useful if a SAX startPrefixMapping() * should need to add an attribute before the element name is seen. * * @param uri the URI of the attribute * @param localName the local name of the attribute * @param rawName the qualified name of the attribute * @param type the type of the attribute (probably CDATA) * @param value the value of the attribute * @param XSLAttribute true if this attribute is coming from an xsl:attribute element * @return true if the attribute was added, * false if an existing value was replaced. */ public boolean addAttributeAlways( String uri, String localName, String rawName, String type, String value, boolean XSLAttribute) { boolean was_added; // final int index = // (localName == null || uri == null) ? // m_attributes.getIndex(rawName):m_attributes.getIndex(uri, localName); int index; // if (localName == null || uri == null){ // index = m_attributes.getIndex(rawName); // } // else { // index = m_attributes.getIndex(uri, localName); // } if (localName == null || uri == null || uri.length() == 0) index = m_attributes.getIndex(rawName); else { index = m_attributes.getIndex(uri,localName); } if (index >= 0) { /* We've seen the attribute before. * We may have a null uri or localName, but all * we really want to re-set is the value anyway. */ m_attributes.setValue(index,value); was_added = false; } else { // the attribute doesn't exist yet, create it m_attributes.addAttribute(uri, localName, rawName, type, value); was_added = true; } return was_added; } /** * Adds the given attribute to the set of collected attributes, * but only if there is a currently open element. * * @param name the attribute's qualified name * @param value the value of the attribute */ public void addAttribute(String name, final String value) { if (m_elemContext.m_startTagOpen) { final String patchedName = patchName(name); final String localName = getLocalName(patchedName); final String uri = getNamespaceURI(patchedName, false); addAttributeAlways(uri,localName, patchedName, "CDATA", value, false); } } /** * Adds the given xsl:attribute to the set of collected attributes, * but only if there is a currently open element. * * @param name the attribute's qualified name (prefix:localName) * @param value the value of the attribute * @param uri the URI that the prefix of the name points to */ public void addXSLAttribute(String name, final String value, final String uri) { if (m_elemContext.m_startTagOpen) { final String patchedName = patchName(name); final String localName = getLocalName(patchedName); addAttributeAlways(uri,localName, patchedName, "CDATA", value, true); } } /** * Add the given attributes to the currently collected ones. These * attributes are always added, regardless of whether on not an element * is currently open. * @param atts List of attributes to add to this list */ public void addAttributes(Attributes atts) throws SAXException { int nAtts = atts.getLength(); for (int i = 0; i < nAtts; i++) { String uri = atts.getURI(i); if (null == uri) uri = ""; addAttributeAlways( uri, atts.getLocalName(i), atts.getQName(i), atts.getType(i), atts.getValue(i), false); } } /** * Return a {@link ContentHandler} interface into this serializer. * If the serializer does not support the {@link ContentHandler} * interface, it should return null. * * @return A {@link ContentHandler} interface into this serializer, * or null if the serializer is not SAX 2 capable * @throws IOException An I/O exception occured */ public ContentHandler asContentHandler() throws IOException { return this; } /** * Report the end of an entity. * * @param name The name of the entity that is ending. * @throws org.xml.sax.SAXException The application may raise an exception. * @see #startEntity */ public void endEntity(String name) throws org.xml.sax.SAXException { if (name.equals("[dtd]")) m_inExternalDTD = false; m_inEntityRef = false; if (m_tracer != null) this.fireEndEntity(name); } /** * Flush and close the underlying java.io.Writer. This method applies to * ToStream serializers, not ToSAXHandler serializers. * @see ToStream */ public void close() { // do nothing (base behavior) } /** * Initialize global variables */ protected void initCDATA() { // CDATA stack // _cdataStack = new Stack(); // _cdataStack.push(new Integer(-1)); // push dummy value } /** * Returns the character encoding to be used in the output document. * @return the character encoding to be used in the output document. */ public String getEncoding() { return getOutputProperty(OutputKeys.ENCODING); } /** * Sets the character encoding coming from the xsl:output encoding stylesheet attribute. * @param m_encoding the character encoding */ public void setEncoding(String encoding) { setOutputProperty(OutputKeys.ENCODING,encoding); } /** * Sets the value coming from the xsl:output omit-xml-declaration stylesheet attribute * @param b true if the XML declaration is to be omitted from the output * document. */ public void setOmitXMLDeclaration(boolean b) { String val = b ? "yes":"no"; setOutputProperty(OutputKeys.OMIT_XML_DECLARATION,val); } /** * @return true if the XML declaration is to be omitted from the output * document. */ public boolean getOmitXMLDeclaration() { return m_shouldNotWriteXMLHeader; } /** * Returns the previously set value of the value to be used as the public * identifier in the document type declaration (DTD). * *@return the public identifier to be used in the DOCTYPE declaration in the * output document. */ public String getDoctypePublic() { return m_doctypePublic; } /** Set the value coming from the xsl:output doctype-public stylesheet attribute. * @param doctypePublic the public identifier to be used in the DOCTYPE * declaration in the output document. */ public void setDoctypePublic(String doctypePublic) { setOutputProperty(OutputKeys.DOCTYPE_PUBLIC, doctypePublic); } /** * Returns the previously set value of the value to be used * as the system identifier in the document type declaration (DTD). * @return the system identifier to be used in the DOCTYPE declaration in * the output document. * */ public String getDoctypeSystem() { return m_doctypeSystem; } /** Set the value coming from the xsl:output doctype-system stylesheet attribute. * @param doctypeSystem the system identifier to be used in the DOCTYPE * declaration in the output document. */ public void setDoctypeSystem(String doctypeSystem) { setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, doctypeSystem); } /** Set the value coming from the xsl:output doctype-public and doctype-system stylesheet properties * @param doctypeSystem the system identifier to be used in the DOCTYPE * declaration in the output document. * @param doctypePublic the public identifier to be used in the DOCTYPE * declaration in the output document. */ public void setDoctype(String doctypeSystem, String doctypePublic) { setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, doctypeSystem); setOutputProperty(OutputKeys.DOCTYPE_PUBLIC, doctypePublic); } /** * Sets the value coming from the xsl:output standalone stylesheet attribute. * @param standalone a value of "yes" indicates that the *standalone
delaration is to be included in the output
* document. This method remembers if the value was explicitly set using
* this method, verses if the value is the default value.
*/
public void setStandalone(String standalone)
{
setOutputProperty(OutputKeys.STANDALONE, standalone);
}
/**
* Sets the XSL standalone attribute, but does not remember if this is a
* default or explicite setting.
* @param standalone "yes" | "no"
*/
protected void setStandaloneInternal(String standalone)
{
if ("yes".equals(standalone))
m_standalone = "yes";
else
m_standalone = "no";
}
/**
* Gets the XSL standalone attribute
* @return a value of "yes" if the standalone
delaration is to
* be included in the output document.
* @see XSLOutputAttributes#getStandalone()
*/
public String getStandalone()
{
return m_standalone;
}
/**
* @return true if the output document should be indented to visually
* indicate its structure.
*/
public boolean getIndent()
{
return m_doIndent;
}
/**
* Gets the mediatype the media-type or MIME type associated with the output
* document.
* @return the mediatype the media-type or MIME type associated with the
* output document.
*/
public String getMediaType()
{
return m_mediatype;
}
/**
* Gets the version of the output format.
* @return the version of the output format.
*/
public String getVersion()
{
return m_version;
}
/**
* Sets the value coming from the xsl:output version attribute.
* @param version the version of the output format.
* @see SerializationHandler#setVersion(String)
*/
public void setVersion(String version)
{
setOutputProperty(OutputKeys.VERSION, version);
}
/**
* Sets the value coming from the xsl:output media-type stylesheet attribute.
* @param mediaType the non-null media-type or MIME type associated with the
* output document.
* @see javax.xml.transform.OutputKeys#MEDIA_TYPE
* @see SerializationHandler#setMediaType(String)
*/
public void setMediaType(String mediaType)
{
setOutputProperty(OutputKeys.MEDIA_TYPE,mediaType);
}
/**
* @return the number of spaces to indent for each indentation level.
*/
public int getIndentAmount()
{
return m_indentAmount;
}
/**
* Sets the indentation amount.
* @param m_indentAmount The m_indentAmount to set
*/
public void setIndentAmount(int m_indentAmount)
{
this.m_indentAmount = m_indentAmount;
}
/**
* Sets the value coming from the xsl:output indent stylesheet
* attribute.
* @param doIndent true if the output document should be indented to
* visually indicate its structure.
* @see XSLOutputAttributes#setIndent(boolean)
*/
public void setIndent(boolean doIndent)
{
String val = doIndent ? "yes":"no";
setOutputProperty(OutputKeys.INDENT,val);
}
/**
* This method is used when a prefix/uri namespace mapping
* is indicated after the element was started with a
* startElement() and before and endElement().
* startPrefixMapping(prefix,uri) would be used before the
* startElement() call.
* @param uri the URI of the namespace
* @param prefix the prefix associated with the given URI.
*
* @see ExtendedContentHandler#namespaceAfterStartElement(String, String)
*/
public void namespaceAfterStartElement(String uri, String prefix)
throws SAXException
{
// default behavior is to do nothing
}
/**
* Return a {@link DOMSerializer} interface into this serializer. If the
* serializer does not support the {@link DOMSerializer} interface, it should
* return null.
*
* @return A {@link DOMSerializer} interface into this serializer, or null
* if the serializer is not DOM capable
* @throws IOException An I/O exception occured
* @see Serializer#asDOMSerializer()
*/
public DOMSerializer asDOMSerializer() throws IOException
{
return this;
}
/**
* Tell if two strings are equal, without worry if the first string is null.
*
* @param p String reference, which may be null.
* @param t String reference, which may be null.
*
* @return true if strings are equal.
*/
private static final boolean subPartMatch(String p, String t)
{
return (p == t) || ((null != p) && (p.equals(t)));
}
/**
* Returns the local name of a qualified name.
* If the name has no prefix,
* then it works as the identity (SAX2).
*
* @param qname a qualified name
* @return returns the prefix of the qualified name,
* or null if there is no prefix.
*/
protected static final String getPrefixPart(String qname)
{
final int col = qname.indexOf(':');
return (col > 0) ? qname.substring(0, col) : null;
//return (col > 0) ? qname.substring(0,col) : "";
}
/**
* Some users of the serializer may need the current namespace mappings
* @return the current namespace mappings (prefix/uri)
* @see ExtendedContentHandler#getNamespaceMappings()
*/
public NamespaceMappings getNamespaceMappings()
{
return m_prefixMap;
}
/**
* Returns the prefix currently pointing to the given URI (if any).
* @param namespaceURI the uri of the namespace in question
* @return a prefix pointing to the given URI (if any).
* @see ExtendedContentHandler#getPrefix(String)
*/
public String getPrefix(String namespaceURI)
{
String prefix = m_prefixMap.lookupPrefix(namespaceURI);
return prefix;
}
/**
* Returns the URI of an element or attribute. Note that default namespaces
* do not apply directly to attributes.
* @param qname a qualified name
* @param isElement true if the qualified name is the name of
* an element.
* @return returns the namespace URI associated with the qualified name.
*/
public String getNamespaceURI(String qname, boolean isElement)
{
String uri = EMPTYSTRING;
int col = qname.lastIndexOf(':');
final String prefix = (col > 0) ? qname.substring(0, col) : EMPTYSTRING;
if (!EMPTYSTRING.equals(prefix) || isElement)
{
if (m_prefixMap != null)
{
uri = m_prefixMap.lookupNamespace(prefix);
if (uri == null && !prefix.equals(XMLNS_PREFIX))
{
throw new RuntimeException(
Utils.messages.createMessage(
MsgKey.ER_NAMESPACE_PREFIX,
new Object[] { qname.substring(0, col) } ));
}
}
}
return uri;
}
/**
* Returns the URI of prefix (if any)
*
* @param prefix the prefix whose URI is searched for
* @return the namespace URI currently associated with the
* prefix, null if the prefix is undefined.
*/
public String getNamespaceURIFromPrefix(String prefix)
{
String uri = null;
if (m_prefixMap != null)
uri = m_prefixMap.lookupNamespace(prefix);
return uri;
}
/**
* Entity reference event.
*
* @param name Name of entity
*
* @throws org.xml.sax.SAXException
*/
public void entityReference(String name) throws org.xml.sax.SAXException
{
flushPending();
startEntity(name);
endEntity(name);
if (m_tracer != null)
fireEntityReference(name);
}
/**
* Sets the transformer associated with this serializer
* @param t the transformer associated with this serializer.
* @see SerializationHandler#setTransformer(Transformer)
*/
public void setTransformer(Transformer t)
{
m_transformer = t;
// If this transformer object implements the SerializerTrace interface
// then assign m_tracer to the transformer object so it can be used
// to fire trace events.
if ((m_transformer instanceof SerializerTrace) &&
(((SerializerTrace) m_transformer).hasTraceListeners())) {
m_tracer = (SerializerTrace) m_transformer;
} else {
m_tracer = null;
}
}
/**
* Gets the transformer associated with this serializer
* @return returns the transformer associated with this serializer.
* @see SerializationHandler#getTransformer()
*/
public Transformer getTransformer()
{
return m_transformer;
}
/**
* This method gets the nodes 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
{
flushPending();
String data = node.getNodeValue();
if (data != null)
{
final int length = data.length();
if (length > m_charsBuff.length)
{
m_charsBuff = new char[length * 2 + 1];
}
data.getChars(0, length, m_charsBuff, 0);
characters(m_charsBuff, 0, length);
}
}
/**
* @see org.xml.sax.ErrorHandler#error(SAXParseException)
*/
public void error(SAXParseException exc) throws SAXException {
}
/**
* @see org.xml.sax.ErrorHandler#fatalError(SAXParseException)
*/
public void fatalError(SAXParseException exc) throws SAXException {
m_elemContext.m_startTagOpen = false;
}
/**
* @see org.xml.sax.ErrorHandler#warning(SAXParseException)
*/
public void warning(SAXParseException exc) throws SAXException
{
}
/**
* To fire off start entity trace event
* @param name Name of entity
*/
protected void fireStartEntity(String name)
throws org.xml.sax.SAXException
{
if (m_tracer != null)
{
flushMyWriter();
m_tracer.fireGenerateEvent(SerializerTrace.EVENTTYPE_ENTITYREF, name);
}
}
/**
* Report the characters event
* @param chars content of characters
* @param start starting index of characters to output
* @param length number of characters to output
*/
// protected void fireCharEvent(char[] chars, int start, int length)
// throws org.xml.sax.SAXException
// {
// if (m_tracer != null)
// m_tracer.fireGenerateEvent(SerializerTrace.EVENTTYPE_CHARACTERS, chars, start,length);
// }
//
/**
* This method is only used internally when flushing the writer from the
* various fire...() trace events. Due to the writer being wrapped with
* SerializerTraceWriter it may cause the flush of these trace events:
* EVENTTYPE_OUTPUT_PSEUDO_CHARACTERS
* EVENTTYPE_OUTPUT_CHARACTERS
* which trace the output written to the output stream.
*
*/
private void flushMyWriter()
{
if (m_writer != null)
{
try
{
m_writer.flush();
}
catch(IOException ioe)
{
}
}
}
/**
* Report the CDATA trace event
* @param chars content of CDATA
* @param start starting index of characters to output
* @param length number of characters to output
*/
protected void fireCDATAEvent(char[] chars, int start, int length)
throws org.xml.sax.SAXException
{
if (m_tracer != null)
{
flushMyWriter();
m_tracer.fireGenerateEvent(SerializerTrace.EVENTTYPE_CDATA, chars, start,length);
}
}
/**
* Report the comment trace event
* @param chars content of comment
* @param start starting index of comment to output
* @param length number of characters to output
*/
protected void fireCommentEvent(char[] chars, int start, int length)
throws org.xml.sax.SAXException
{
if (m_tracer != null)
{
flushMyWriter();
m_tracer.fireGenerateEvent(SerializerTrace.EVENTTYPE_COMMENT, new String(chars, start, length));
}
}
/**
* To fire off end entity trace event
* @param name Name of entity
*/
public void fireEndEntity(String name)
throws org.xml.sax.SAXException
{
if (m_tracer != null)
flushMyWriter();
// we do not need to handle this.
}
/**
* To fire off start document trace event
*/
protected void fireStartDoc()
throws org.xml.sax.SAXException
{
if (m_tracer != null)
{
flushMyWriter();
m_tracer.fireGenerateEvent(SerializerTrace.EVENTTYPE_STARTDOCUMENT);
}
}
/**
* To fire off end document trace event
*/
protected void fireEndDoc()
throws org.xml.sax.SAXException
{
if (m_tracer != null)
{
flushMyWriter();
m_tracer.fireGenerateEvent(SerializerTrace.EVENTTYPE_ENDDOCUMENT);
}
}
/**
* Report the start element trace event. This trace method needs to be
* called just before the attributes are cleared.
*
* @param elemName the qualified name of the element
*
*/
protected void fireStartElem(String elemName)
throws org.xml.sax.SAXException
{
if (m_tracer != null)
{
flushMyWriter();
m_tracer.fireGenerateEvent(SerializerTrace.EVENTTYPE_STARTELEMENT,
elemName, m_attributes);
}
}
/**
* To fire off the end element event
* @param name Name of element
*/
// protected void fireEndElem(String name)
// throws org.xml.sax.SAXException
// {
// if (m_tracer != null)
// m_tracer.fireGenerateEvent(SerializerTrace.EVENTTYPE_ENDELEMENT,name, (Attributes)null);
// }
/**
* To fire off the PI trace event
* @param name Name of PI
*/
protected void fireEscapingEvent(String name, String data)
throws org.xml.sax.SAXException
{
if (m_tracer != null)
{
flushMyWriter();
m_tracer.fireGenerateEvent(SerializerTrace.EVENTTYPE_PI,name, data);
}
}
/**
* To fire off the entity reference trace event
* @param name Name of entity reference
*/
protected void fireEntityReference(String name)
throws org.xml.sax.SAXException
{
if (m_tracer != null)
{
flushMyWriter();
m_tracer.fireGenerateEvent(SerializerTrace.EVENTTYPE_ENTITYREF,name, (Attributes)null);
}
}
/**
* Receive notification of the beginning of a document.
* This method is never a self generated call,
* but only called externally.
*
* The SAX parser will invoke this method only once, before any * other methods in this interface or in DTDHandler (except for * setDocumentLocator).
* * @throws org.xml.sax.SAXException Any SAX exception, possibly * wrapping another exception. * * @throws org.xml.sax.SAXException */ public void startDocument() throws org.xml.sax.SAXException { // if we do get called with startDocument(), handle it right away startDocumentInternal(); m_needToCallStartDocument = false; return; } /** * This method handles what needs to be done at a startDocument() call, * whether from an external caller, or internally called in the * serializer. For historical reasons the serializer is flexible to * startDocument() not always being called. * Even if no external call is * made into startDocument() this method will always be called as a self * generated internal startDocument, it handles what needs to be done at a * startDocument() call. * * This method exists just to make sure that startDocument() is only ever * called from an external caller, which in principle is just a matter of * style. * * @throws SAXException */ protected void startDocumentInternal() throws org.xml.sax.SAXException { if (m_tracer != null) this.fireStartDoc(); } /** * This method is used to set the source locator, which might be used to * generated an error message. * @param locator the source locator * * @see ExtendedContentHandler#setSourceLocator(javax.xml.transform.SourceLocator) */ public void setSourceLocator(SourceLocator locator) { m_sourceLocator = locator; } /** * Used only by TransformerSnapshotImpl to restore the serialization * to a previous state. * * @param mappings NamespaceMappings */ public void setNamespaceMappings(NamespaceMappings mappings) { m_prefixMap = mappings; } public boolean reset() { resetSerializerBase(); return true; } /** * Reset all of the fields owned by SerializerBase * */ private void resetSerializerBase() { this.m_attributes.clear(); this.m_CdataElems = null; this.m_cdataTagOpen = false; this.m_docIsEmpty = true; this.m_doctypePublic = null; this.m_doctypeSystem = null; this.m_doIndent = false; this.m_elemContext = new ElemContext(); this.m_indentAmount = 0; this.m_inEntityRef = false; this.m_inExternalDTD = false; this.m_mediatype = null; this.m_needToCallStartDocument = true; this.m_needToOutputDocTypeDecl = false; if (m_OutputProps != null) this.m_OutputProps.clear(); if (m_OutputPropsDefault != null) this.m_OutputPropsDefault.clear(); if (this.m_prefixMap != null) this.m_prefixMap.reset(); this.m_shouldNotWriteXMLHeader = false; this.m_sourceLocator = null; this.m_standalone = null; this.m_standaloneWasSpecified = false; this.m_StringOfCDATASections = null; this.m_tracer = null; this.m_transformer = null; this.m_version = null; // don't set writer to null, so that it might be re-used //this.m_writer = null; } /** * Returns true if the serializer is used for temporary output rather than * final output. * * This concept is made clear in the XSLT 2.0 draft. */ final boolean inTemporaryOutputState() { /* This is a hack. We should really be letting the serializer know * that it is in temporary output state with an explicit call, but * from a pragmatic point of view (for now anyways) having no output * encoding at all, not even the default UTF-8 indicates that the serializer * is being used for temporary RTF. */ return (getEncoding() == null); } /** * This method adds an attribute the the current element, * but should not be used for an xsl:attribute child. * @see ExtendedContentHandler#addAttribute(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String) */ public void addAttribute(String uri, String localName, String rawName, String type, String value) throws SAXException { if (m_elemContext.m_startTagOpen) { addAttributeAlways(uri, localName, rawName, type, value, false); } } /** * @see org.xml.sax.DTDHandler#notationDecl(java.lang.String, java.lang.String, java.lang.String) */ public void notationDecl(String arg0, String arg1, String arg2) throws SAXException { // This method just provides a definition to satisfy the interface // A particular sub-class of SerializerBase provides the implementation (if desired) } /** * @see org.xml.sax.DTDHandler#unparsedEntityDecl(java.lang.String, java.lang.String, java.lang.String, java.lang.String) */ public void unparsedEntityDecl( String arg0, String arg1, String arg2, String arg3) throws SAXException { // This method just provides a definition to satisfy the interface // A particular sub-class of SerializerBase provides the implementation (if desired) } /** * If set to false the serializer does not expand DTD entities, * but leaves them as is, the default value is true. */ public void setDTDEntityExpansion(boolean expand) { // This method just provides a definition to satisfy the interface // A particular sub-class of SerializerBase provides the implementation (if desired) } /** * The CDATA section names stored in a whitespace separateed list with * each element being a word of the form "{uri}localName" This list * comes from the cdata-section-elements attribute. * * This field replaces m_cdataSectionElements Vector. */ protected String m_StringOfCDATASections = null; boolean m_docIsEmpty = true; void initCdataElems(String s) { if (s != null) { int max = s.length(); // true if we are in the middle of a pair of curly braces that delimit a URI boolean inCurly = false; // true if we found a URI but haven't yet processed the local name boolean foundURI = false; StringBuffer buf = new StringBuffer(); String uri = null; String localName = null; // parse through string, breaking on whitespaces. I do this instead // of a tokenizer so I can track whitespace inside of curly brackets, // which theoretically shouldn't happen if they contain legal URLs. for (int i = 0; i < max; i++) { char c = s.charAt(i); if (Character.isWhitespace(c)) { if (!inCurly) { if (buf.length() > 0) { localName = buf.toString(); if (!foundURI) uri = ""; addCDATAElement(uri,localName); buf.setLength(0); foundURI = false; } continue; } else buf.append(c); // add whitespace to the URI } else if ('{' == c) // starting a URI inCurly = true; else if ('}' == c) { // we just ended a URI, add the URI to the vector foundURI = true; uri = buf.toString(); buf.setLength(0); inCurly = false; } else { // append non-whitespace, non-curly to current URI or localName being gathered. buf.append(c); } } if (buf.length() > 0) { // We have one last localName to process. localName = buf.toString(); if (!foundURI) uri = ""; addCDATAElement(uri,localName); } } } protected java.util.Hashtable m_CdataElems = null; private void addCDATAElement(String uri, String localName) { if (m_CdataElems == null) { m_CdataElems = new java.util.Hashtable(); } java.util.Hashtable h = (java.util.Hashtable) m_CdataElems.get(localName); if (h == null) { h = new java.util.Hashtable(); m_CdataElems.put(localName,h); } h.put(uri,uri); } /** * Return true if nothing has been sent to this result tree yet. *
* This is not a public API.
*
* @xsl.usage internal
*/
public boolean documentIsEmpty() {
// If we haven't called startDocument() yet, then this document is empty
return m_docIsEmpty && (m_elemContext.m_currentElemDepth == 0);
}
/**
* Return true if the current element in m_elemContext
* is a CDATA section.
* CDATA sections are specified in the
* This method is not a public API, but is only used internally by the serializer.
*/
protected boolean isCdataSection()
{
boolean b = false;
if (null != m_StringOfCDATASections)
{
if (m_elemContext.m_elementLocalName == null)
{
String localName = getLocalName(m_elemContext.m_elementName);
m_elemContext.m_elementLocalName = localName;
}
if ( m_elemContext.m_elementURI == null) {
m_elemContext.m_elementURI = getElementURI();
}
else if ( m_elemContext.m_elementURI.length() == 0) {
if ( m_elemContext.m_elementName == null) {
m_elemContext.m_elementName = m_elemContext.m_elementLocalName;
// leave URI as "", meaning in no namespace
}
else if (m_elemContext.m_elementLocalName.length() < m_elemContext.m_elementName.length()){
// We were told the URI was "", yet the name has a prefix since the name is longer than the localname.
// So we will fix that incorrect information here.
m_elemContext.m_elementURI = getElementURI();
}
}
java.util.Hashtable h = (java.util.Hashtable) m_CdataElems.get(m_elemContext.m_elementLocalName);
if (h != null)
{
Object obj = h.get(m_elemContext.m_elementURI);
if (obj != null)
b = true;
}
}
return b;
}
/**
* Before this call m_elementContext.m_elementURI is null,
* which means it is not yet known. After this call it
* is non-null, but possibly "" meaning that it is in the
* default namespace.
*
* @return The URI of the element, never null, but possibly "".
*/
private String getElementURI() {
String uri = null;
// At this point in processing we have received all the
// namespace mappings
// As we still don't know the elements namespace,
// we now figure it out.
String prefix = getPrefixPart(m_elemContext.m_elementName);
if (prefix == null) {
// no prefix so lookup the URI of the default namespace
uri = m_prefixMap.lookupNamespace("");
} else {
uri = m_prefixMap.lookupNamespace(prefix);
}
if (uri == null) {
// We didn't find the namespace for the
// prefix ... ouch, that shouldn't happen.
// This is a hack, we really don't know
// the namespace
uri = EMPTYSTRING;
}
return uri;
}
/**
* Get the value of an output property,
* the explicit value, if any, otherwise the
* default value, if any, otherwise null.
*/
public String getOutputProperty(String name) {
String val = getOutputPropertyNonDefault(name);
// If no explicit value, try to get the default value
if (val == null)
val = getOutputPropertyDefault(name);
return val;
}
/**
* Get the value of an output property,
* not the default value. If there is a default
* value, but no non-default value this method
* will return null.
*
*
*/
public String getOutputPropertyNonDefault(String name )
{
return getProp(name,false);
}
/**
* Return a {@link DOM3Serializer} interface into this serializer. If the
* serializer does not support the {@link DOM3Serializer} interface, it should
* return null.
*
* @return A {@link DOM3Serializer} interface into this serializer, or null
* if the serializer is not DOM capable
* @throws IOException An I/O exception occured
* @see org.apache.xml.serializer.Serializer#asDOM3Serializer()
*/
public Object asDOM3Serializer() throws IOException
{
return new org.apache.xml.serializer.dom3.DOM3SerializerImpl(this);
}
/**
* Get the default value of an xsl:output property,
* which would be null only if no default value exists
* for the property.
*/
public String getOutputPropertyDefault(String name) {
return getProp(name, true);
}
/**
* Set the value for the output property, typically from
* an xsl:output element, but this does not change what
* the default value is.
*/
public void setOutputProperty(String name, String val) {
setProp(name,val,false);
}
/**
* Set the default value for an output property, but this does
* not impact any explicitly set value.
*/
public void setOutputPropertyDefault(String name, String val) {
setProp(name,val,true);
}
/**
* A mapping of keys to explicitly set values, for example if
* and
* "{uri1}localName1 {uri2}localName2 . . . "
*
*
*