0001: /*
0002: * Copyright 2001-2004 The Apache Software Foundation.
0003: *
0004: * Licensed under the Apache License, Version 2.0 (the "License");
0005: * you may not use this file except in compliance with the License.
0006: * You may obtain a copy of the License at
0007: *
0008: * http://www.apache.org/licenses/LICENSE-2.0
0009: *
0010: * Unless required by applicable law or agreed to in writing, software
0011: * distributed under the License is distributed on an "AS IS" BASIS,
0012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0013: * See the License for the specific language governing permissions and
0014: * limitations under the License.
0015: */
0016: /*
0017: * $Id: SerializerBase.java,v 1.20 2005/04/07 04:29:03 minchau Exp $
0018: */
0019: package org.apache.xml.serializer;
0020:
0021: import java.io.IOException;
0022: import java.util.Vector;
0023:
0024: import javax.xml.transform.SourceLocator;
0025: import javax.xml.transform.Transformer;
0026:
0027: import org.apache.xml.serializer.utils.MsgKey;
0028: import org.apache.xml.serializer.utils.Utils;
0029: import org.xml.sax.Attributes;
0030: import org.xml.sax.ContentHandler;
0031: import org.xml.sax.Locator;
0032: import org.xml.sax.SAXException;
0033: import org.xml.sax.SAXParseException;
0034:
0035: /**
0036: * This class acts as a base class for the XML "serializers"
0037: * and the stream serializers.
0038: * It contains a number of common fields and methods.
0039: *
0040: * @xsl.usage internal
0041: */
0042: public abstract class SerializerBase implements SerializationHandler,
0043: SerializerConstants {
0044:
0045: /**
0046: * To fire off the end element trace event
0047: * @param name Name of element
0048: */
0049: protected void fireEndElem(String name)
0050: throws org.xml.sax.SAXException {
0051: if (m_tracer != null) {
0052: flushMyWriter();
0053: m_tracer.fireGenerateEvent(
0054: SerializerTrace.EVENTTYPE_ENDELEMENT, name,
0055: (Attributes) null);
0056: }
0057: }
0058:
0059: /**
0060: * Report the characters trace event
0061: * @param chars content of characters
0062: * @param start starting index of characters to output
0063: * @param length number of characters to output
0064: */
0065: protected void fireCharEvent(char[] chars, int start, int length)
0066: throws org.xml.sax.SAXException {
0067: if (m_tracer != null) {
0068: flushMyWriter();
0069: m_tracer.fireGenerateEvent(
0070: SerializerTrace.EVENTTYPE_CHARACTERS, chars, start,
0071: length);
0072: }
0073: }
0074:
0075: /**
0076: * true if we still need to call startDocumentInternal()
0077: */
0078: protected boolean m_needToCallStartDocument = true;
0079:
0080: /** True if a trailing "]]>" still needs to be written to be
0081: * written out. Used to merge adjacent CDATA sections
0082: */
0083: protected boolean m_cdataTagOpen = false;
0084:
0085: /**
0086: * All the attributes of the current element, collected from
0087: * startPrefixMapping() calls, or addAddtribute() calls, or
0088: * from the SAX attributes in a startElement() call.
0089: */
0090: protected AttributesImplSerializer m_attributes = new AttributesImplSerializer();
0091:
0092: /**
0093: * Tells if we're in an EntityRef event.
0094: */
0095: protected boolean m_inEntityRef = false;
0096:
0097: /** This flag is set while receiving events from the external DTD */
0098: protected boolean m_inExternalDTD = false;
0099:
0100: /**
0101: * The System ID for the doc type.
0102: */
0103: private String m_doctypeSystem;
0104:
0105: /**
0106: * The public ID for the doc type.
0107: */
0108: private String m_doctypePublic;
0109:
0110: /**
0111: * Flag to tell that we need to add the doctype decl, which we can't do
0112: * until the first element is encountered.
0113: */
0114: boolean m_needToOutputDocTypeDecl = true;
0115:
0116: /**
0117: * The character encoding. Must match the encoding used for the
0118: * printWriter.
0119: */
0120: private String m_encoding = null;
0121:
0122: /**
0123: * Tells if we should write the XML declaration.
0124: */
0125: private boolean m_shouldNotWriteXMLHeader = false;
0126:
0127: /**
0128: * The standalone value for the doctype.
0129: */
0130: private String m_standalone;
0131:
0132: /**
0133: * True if standalone was specified.
0134: */
0135: protected boolean m_standaloneWasSpecified = false;
0136:
0137: /**
0138: * Flag to tell if indenting (pretty-printing) is on.
0139: */
0140: protected boolean m_doIndent = false;
0141: /**
0142: * Amount to indent.
0143: */
0144: protected int m_indentAmount = 0;
0145:
0146: /**
0147: * Tells the XML version, for writing out to the XML decl.
0148: */
0149: private String m_version = null;
0150:
0151: /**
0152: * The mediatype. Not used right now.
0153: */
0154: private String m_mediatype;
0155:
0156: /**
0157: * The transformer that was around when this output handler was created (if
0158: * any).
0159: */
0160: private Transformer m_transformer;
0161:
0162: /**
0163: * Pairs of local names and corresponding URIs of CDATA sections. This list
0164: * comes from the cdata-section-elements attribute. Every second one is a
0165: * local name, and every other second one is the URI for the local name.
0166: */
0167: protected Vector m_cdataSectionElements = null;
0168:
0169: /**
0170: * Namespace support, that keeps track of currently defined
0171: * prefix/uri mappings. As processed elements come and go, so do
0172: * the associated mappings for that element.
0173: */
0174: protected NamespaceMappings m_prefixMap;
0175:
0176: /**
0177: * Handle for firing generate events. This interface may be implemented
0178: * by the referenced transformer object.
0179: */
0180: protected SerializerTrace m_tracer;
0181:
0182: protected SourceLocator m_sourceLocator;
0183:
0184: /**
0185: * The writer to send output to. This field is only used in the ToStream
0186: * serializers, but exists here just so that the fireStartDoc() and
0187: * other fire... methods can flush this writer when tracing.
0188: */
0189: protected java.io.Writer m_writer = null;
0190:
0191: /**
0192: * A reference to "stack frame" corresponding to
0193: * the current element. Such a frame is pushed at a startElement()
0194: * and popped at an endElement(). This frame contains information about
0195: * the element, such as its namespace URI.
0196: */
0197: protected ElemContext m_elemContext = new ElemContext();
0198:
0199: /**
0200: * A utility buffer for converting Strings passed to
0201: * character() methods to character arrays.
0202: * Reusing this buffer means not creating a new character array
0203: * everytime and it runs faster.
0204: */
0205: protected char[] m_charsBuff = new char[60];
0206:
0207: /**
0208: * A utility buffer for converting Strings passed to
0209: * attribute methods to character arrays.
0210: * Reusing this buffer means not creating a new character array
0211: * everytime and it runs faster.
0212: */
0213: protected char[] m_attrBuff = new char[30];
0214:
0215: /**
0216: * Receive notification of a comment.
0217: *
0218: * @see ExtendedLexicalHandler#comment(String)
0219: */
0220: public void comment(String data) throws SAXException {
0221: final int length = data.length();
0222: if (length > m_charsBuff.length) {
0223: m_charsBuff = new char[length * 2 + 1];
0224: }
0225: data.getChars(0, length, m_charsBuff, 0);
0226: comment(m_charsBuff, 0, length);
0227: }
0228:
0229: /**
0230: * If at runtime, when the qname of the attribute is
0231: * known, another prefix is specified for the attribute, then we can
0232: * patch or hack the name with this method. For
0233: * a qname of the form "ns?:otherprefix:name", this function patches the
0234: * qname by simply ignoring "otherprefix".
0235: * TODO: This method is a HACK! We do not have access to the
0236: * XML file, it sometimes generates a NS prefix of the form "ns?" for
0237: * an attribute.
0238: */
0239: protected String patchName(String qname) {
0240:
0241: final int lastColon = qname.lastIndexOf(':');
0242:
0243: if (lastColon > 0) {
0244: final int firstColon = qname.indexOf(':');
0245: final String prefix = qname.substring(0, firstColon);
0246: final String localName = qname.substring(lastColon + 1);
0247:
0248: // If uri is "" then ignore prefix
0249: final String uri = m_prefixMap.lookupNamespace(prefix);
0250: if (uri != null && uri.length() == 0) {
0251: return localName;
0252: } else if (firstColon != lastColon) {
0253: return prefix + ':' + localName;
0254: }
0255: }
0256: return qname;
0257: }
0258:
0259: /**
0260: * Returns the local name of a qualified name. If the name has no prefix,
0261: * then it works as the identity (SAX2).
0262: * @param qname the qualified name
0263: * @return the name, but excluding any prefix and colon.
0264: */
0265: protected static String getLocalName(String qname) {
0266: final int col = qname.lastIndexOf(':');
0267: return (col > 0) ? qname.substring(col + 1) : qname;
0268: }
0269:
0270: /**
0271: * Receive an object for locating the origin of SAX document events.
0272: *
0273: * @param locator An object that can return the location of any SAX document
0274: * event.
0275: *
0276: * Receive an object for locating the origin of SAX document events.
0277: *
0278: * <p>SAX parsers are strongly encouraged (though not absolutely
0279: * required) to supply a locator: if it does so, it must supply
0280: * the locator to the application by invoking this method before
0281: * invoking any of the other methods in the DocumentHandler
0282: * interface.</p>
0283: *
0284: * <p>The locator allows the application to determine the end
0285: * position of any document-related event, even if the parser is
0286: * not reporting an error. Typically, the application will
0287: * use this information for reporting its own errors (such as
0288: * character content that does not match an application's
0289: * business rules). The information returned by the locator
0290: * is probably not sufficient for use with a search engine.</p>
0291: *
0292: * <p>Note that the locator will return correct information only
0293: * during the invocation of the events in this interface. The
0294: * application should not attempt to use it at any other time.</p>
0295: */
0296: public void setDocumentLocator(Locator locator) {
0297: return;
0298:
0299: // I don't do anything with this yet.
0300: }
0301:
0302: /**
0303: * Adds the given attribute to the set of collected attributes , but only if
0304: * there is a currently open element.
0305: *
0306: * An element is currently open if a startElement() notification has
0307: * occured but the start of the element has not yet been written to the
0308: * output. In the stream case this means that we have not yet been forced
0309: * to close the elements opening tag by another notification, such as a
0310: * character notification.
0311: *
0312: * @param uri the URI of the attribute
0313: * @param localName the local name of the attribute
0314: * @param rawName the qualified name of the attribute
0315: * @param type the type of the attribute (probably CDATA)
0316: * @param value the value of the attribute
0317: * @param XSLAttribute true if this attribute is coming from an xsl:attriute element
0318: * @see ExtendedContentHandler#addAttribute(String, String, String, String, String)
0319: */
0320: public void addAttribute(String uri, String localName,
0321: String rawName, String type, String value,
0322: boolean XSLAttribute) throws SAXException {
0323: if (m_elemContext.m_startTagOpen) {
0324: addAttributeAlways(uri, localName, rawName, type, value,
0325: XSLAttribute);
0326: }
0327:
0328: }
0329:
0330: /**
0331: * Adds the given attribute to the set of attributes, even if there is
0332: * no currently open element. This is useful if a SAX startPrefixMapping()
0333: * should need to add an attribute before the element name is seen.
0334: *
0335: * @param uri the URI of the attribute
0336: * @param localName the local name of the attribute
0337: * @param rawName the qualified name of the attribute
0338: * @param type the type of the attribute (probably CDATA)
0339: * @param value the value of the attribute
0340: * @param XSLAttribute true if this attribute is coming from an xsl:attribute element
0341: * @return true if the attribute was added,
0342: * false if an existing value was replaced.
0343: */
0344: public boolean addAttributeAlways(String uri, String localName,
0345: String rawName, String type, String value,
0346: boolean XSLAttribute) {
0347: boolean was_added;
0348: // final int index =
0349: // (localName == null || uri == null) ?
0350: // m_attributes.getIndex(rawName):m_attributes.getIndex(uri, localName);
0351: int index;
0352: // if (localName == null || uri == null){
0353: // index = m_attributes.getIndex(rawName);
0354: // }
0355: // else {
0356: // index = m_attributes.getIndex(uri, localName);
0357: // }
0358: if (localName == null || uri == null || uri.length() == 0)
0359: index = m_attributes.getIndex(rawName);
0360: else {
0361: index = m_attributes.getIndex(uri, localName);
0362: }
0363: if (index >= 0) {
0364: /* We've seen the attribute before.
0365: * We may have a null uri or localName, but all
0366: * we really want to re-set is the value anyway.
0367: */
0368: m_attributes.setValue(index, value);
0369: was_added = false;
0370: } else {
0371: // the attribute doesn't exist yet, create it
0372: m_attributes.addAttribute(uri, localName, rawName, type,
0373: value);
0374: was_added = true;
0375: }
0376: return was_added;
0377:
0378: }
0379:
0380: /**
0381: * Adds the given attribute to the set of collected attributes,
0382: * but only if there is a currently open element.
0383: *
0384: * @param name the attribute's qualified name
0385: * @param value the value of the attribute
0386: */
0387: public void addAttribute(String name, final String value) {
0388: if (m_elemContext.m_startTagOpen) {
0389: final String patchedName = patchName(name);
0390: final String localName = getLocalName(patchedName);
0391: final String uri = getNamespaceURI(patchedName, false);
0392:
0393: addAttributeAlways(uri, localName, patchedName, "CDATA",
0394: value, false);
0395: }
0396: }
0397:
0398: /**
0399: * Adds the given xsl:attribute to the set of collected attributes,
0400: * but only if there is a currently open element.
0401: *
0402: * @param name the attribute's qualified name (prefix:localName)
0403: * @param value the value of the attribute
0404: * @param uri the URI that the prefix of the name points to
0405: */
0406: public void addXSLAttribute(String name, final String value,
0407: final String uri) {
0408: if (m_elemContext.m_startTagOpen) {
0409: final String patchedName = patchName(name);
0410: final String localName = getLocalName(patchedName);
0411:
0412: addAttributeAlways(uri, localName, patchedName, "CDATA",
0413: value, true);
0414: }
0415: }
0416:
0417: /**
0418: * Add the given attributes to the currently collected ones. These
0419: * attributes are always added, regardless of whether on not an element
0420: * is currently open.
0421: * @param atts List of attributes to add to this list
0422: */
0423: public void addAttributes(Attributes atts) throws SAXException {
0424:
0425: int nAtts = atts.getLength();
0426:
0427: for (int i = 0; i < nAtts; i++) {
0428: String uri = atts.getURI(i);
0429:
0430: if (null == uri)
0431: uri = "";
0432:
0433: addAttributeAlways(uri, atts.getLocalName(i), atts
0434: .getQName(i), atts.getType(i), atts.getValue(i),
0435: false);
0436:
0437: }
0438: }
0439:
0440: /**
0441: * Return a {@link ContentHandler} interface into this serializer.
0442: * If the serializer does not support the {@link ContentHandler}
0443: * interface, it should return null.
0444: *
0445: * @return A {@link ContentHandler} interface into this serializer,
0446: * or null if the serializer is not SAX 2 capable
0447: * @throws IOException An I/O exception occured
0448: */
0449: public ContentHandler asContentHandler() throws IOException {
0450: return this ;
0451: }
0452:
0453: /**
0454: * Report the end of an entity.
0455: *
0456: * @param name The name of the entity that is ending.
0457: * @throws org.xml.sax.SAXException The application may raise an exception.
0458: * @see #startEntity
0459: */
0460: public void endEntity(String name) throws org.xml.sax.SAXException {
0461: if (name.equals("[dtd]"))
0462: m_inExternalDTD = false;
0463: m_inEntityRef = false;
0464:
0465: if (m_tracer != null)
0466: this .fireEndEntity(name);
0467: }
0468:
0469: /**
0470: * Flush and close the underlying java.io.Writer. This method applies to
0471: * ToStream serializers, not ToSAXHandler serializers.
0472: * @see ToStream
0473: */
0474: public void close() {
0475: // do nothing (base behavior)
0476: }
0477:
0478: /**
0479: * Initialize global variables
0480: */
0481: protected void initCDATA() {
0482: // CDATA stack
0483: // _cdataStack = new Stack();
0484: // _cdataStack.push(new Integer(-1)); // push dummy value
0485: }
0486:
0487: /**
0488: * Returns the character encoding to be used in the output document.
0489: * @return the character encoding to be used in the output document.
0490: */
0491: public String getEncoding() {
0492: return m_encoding;
0493: }
0494:
0495: /**
0496: * Sets the character encoding coming from the xsl:output encoding stylesheet attribute.
0497: * @param m_encoding the character encoding
0498: */
0499: public void setEncoding(String m_encoding) {
0500: this .m_encoding = m_encoding;
0501: }
0502:
0503: /**
0504: * Sets the value coming from the xsl:output omit-xml-declaration stylesheet attribute
0505: * @param b true if the XML declaration is to be omitted from the output
0506: * document.
0507: */
0508: public void setOmitXMLDeclaration(boolean b) {
0509: this .m_shouldNotWriteXMLHeader = b;
0510: }
0511:
0512: /**
0513: * @return true if the XML declaration is to be omitted from the output
0514: * document.
0515: */
0516: public boolean getOmitXMLDeclaration() {
0517: return m_shouldNotWriteXMLHeader;
0518: }
0519:
0520: /**
0521: * Returns the previously set value of the value to be used as the public
0522: * identifier in the document type declaration (DTD).
0523: *
0524: *@return the public identifier to be used in the DOCTYPE declaration in the
0525: * output document.
0526: */
0527: public String getDoctypePublic() {
0528: return m_doctypePublic;
0529: }
0530:
0531: /** Set the value coming from the xsl:output doctype-public stylesheet attribute.
0532: * @param doctypePublic the public identifier to be used in the DOCTYPE
0533: * declaration in the output document.
0534: */
0535: public void setDoctypePublic(String doctypePublic) {
0536: this .m_doctypePublic = doctypePublic;
0537: }
0538:
0539: /**
0540: * Returns the previously set value of the value to be used
0541: * as the system identifier in the document type declaration (DTD).
0542: * @return the system identifier to be used in the DOCTYPE declaration in
0543: * the output document.
0544: *
0545: */
0546: public String getDoctypeSystem() {
0547: return m_doctypeSystem;
0548: }
0549:
0550: /** Set the value coming from the xsl:output doctype-system stylesheet attribute.
0551: * @param doctypeSystem the system identifier to be used in the DOCTYPE
0552: * declaration in the output document.
0553: */
0554: public void setDoctypeSystem(String doctypeSystem) {
0555: this .m_doctypeSystem = doctypeSystem;
0556: }
0557:
0558: /** Set the value coming from the xsl:output doctype-public and doctype-system stylesheet properties
0559: * @param doctypeSystem the system identifier to be used in the DOCTYPE
0560: * declaration in the output document.
0561: * @param doctypePublic the public identifier to be used in the DOCTYPE
0562: * declaration in the output document.
0563: */
0564: public void setDoctype(String doctypeSystem, String doctypePublic) {
0565: this .m_doctypeSystem = doctypeSystem;
0566: this .m_doctypePublic = doctypePublic;
0567: }
0568:
0569: /**
0570: * Sets the value coming from the xsl:output standalone stylesheet attribute.
0571: * @param standalone a value of "yes" indicates that the
0572: * <code>standalone</code> delaration is to be included in the output
0573: * document. This method remembers if the value was explicitly set using
0574: * this method, verses if the value is the default value.
0575: */
0576: public void setStandalone(String standalone) {
0577: if (standalone != null) {
0578: m_standaloneWasSpecified = true;
0579: setStandaloneInternal(standalone);
0580: }
0581: }
0582:
0583: /**
0584: * Sets the XSL standalone attribute, but does not remember if this is a
0585: * default or explicite setting.
0586: * @param standalone "yes" | "no"
0587: */
0588: protected void setStandaloneInternal(String standalone) {
0589: if ("yes".equals(standalone))
0590: m_standalone = "yes";
0591: else
0592: m_standalone = "no";
0593:
0594: }
0595:
0596: /**
0597: * Gets the XSL standalone attribute
0598: * @return a value of "yes" if the <code>standalone</code> delaration is to
0599: * be included in the output document.
0600: * @see XSLOutputAttributes#getStandalone()
0601: */
0602: public String getStandalone() {
0603: return m_standalone;
0604: }
0605:
0606: /**
0607: * @return true if the output document should be indented to visually
0608: * indicate its structure.
0609: */
0610: public boolean getIndent() {
0611: return m_doIndent;
0612: }
0613:
0614: /**
0615: * Gets the mediatype the media-type or MIME type associated with the output
0616: * document.
0617: * @return the mediatype the media-type or MIME type associated with the
0618: * output document.
0619: */
0620: public String getMediaType() {
0621: return m_mediatype;
0622: }
0623:
0624: /**
0625: * Gets the version of the output format.
0626: * @return the version of the output format.
0627: */
0628: public String getVersion() {
0629: return m_version;
0630: }
0631:
0632: /**
0633: * Sets the value coming from the xsl:output version attribute.
0634: * @param version the version of the output format.
0635: * @see SerializationHandler#setVersion(String)
0636: */
0637: public void setVersion(String version) {
0638: m_version = version;
0639: }
0640:
0641: /**
0642: * Sets the value coming from the xsl:output media-type stylesheet attribute.
0643: * @param mediaType the non-null media-type or MIME type associated with the
0644: * output document.
0645: * @see javax.xml.transform.OutputKeys#MEDIA_TYPE
0646: * @see SerializationHandler#setMediaType(String)
0647: */
0648: public void setMediaType(String mediaType) {
0649: m_mediatype = mediaType;
0650: }
0651:
0652: /**
0653: * @return the number of spaces to indent for each indentation level.
0654: */
0655: public int getIndentAmount() {
0656: return m_indentAmount;
0657: }
0658:
0659: /**
0660: * Sets the indentation amount.
0661: * @param m_indentAmount The m_indentAmount to set
0662: */
0663: public void setIndentAmount(int m_indentAmount) {
0664: this .m_indentAmount = m_indentAmount;
0665: }
0666:
0667: /**
0668: * Sets the value coming from the xsl:output indent stylesheet
0669: * attribute.
0670: * @param doIndent true if the output document should be indented to
0671: * visually indicate its structure.
0672: * @see XSLOutputAttributes#setIndent(boolean)
0673: */
0674: public void setIndent(boolean doIndent) {
0675: m_doIndent = doIndent;
0676: }
0677:
0678: /**
0679: * This method is used when a prefix/uri namespace mapping
0680: * is indicated after the element was started with a
0681: * startElement() and before and endElement().
0682: * startPrefixMapping(prefix,uri) would be used before the
0683: * startElement() call.
0684: * @param uri the URI of the namespace
0685: * @param prefix the prefix associated with the given URI.
0686: *
0687: * @see ExtendedContentHandler#namespaceAfterStartElement(String, String)
0688: */
0689: public void namespaceAfterStartElement(String uri, String prefix)
0690: throws SAXException {
0691: // default behavior is to do nothing
0692: }
0693:
0694: /**
0695: * Return a {@link DOMSerializer} interface into this serializer. If the
0696: * serializer does not support the {@link DOMSerializer} interface, it should
0697: * return null.
0698: *
0699: * @return A {@link DOMSerializer} interface into this serializer, or null
0700: * if the serializer is not DOM capable
0701: * @throws IOException An I/O exception occured
0702: * @see Serializer#asDOMSerializer()
0703: */
0704: public DOMSerializer asDOMSerializer() throws IOException {
0705: return this ;
0706: }
0707:
0708: /**
0709: * Push a boolean state based on if the name of the current element
0710: * is found in the list of qnames. A state is only pushed if
0711: * there were some cdata-section-names were specified.
0712: * <p>
0713: * Hidden parameters are the vector of qualified elements specified in
0714: * cdata-section-names attribute, and the m_cdataSectionStates stack
0715: * onto which whether the current element is in the list is pushed (true or
0716: * false). Other hidden parameters are the current elements namespaceURI,
0717: * localName and qName
0718: */
0719: protected boolean isCdataSection() {
0720:
0721: boolean b = false;
0722:
0723: if (null != m_cdataSectionElements) {
0724: if (m_elemContext.m_elementLocalName == null)
0725: m_elemContext.m_elementLocalName = getLocalName(m_elemContext.m_elementName);
0726: if (m_elemContext.m_elementURI == null) {
0727: String prefix = getPrefixPart(m_elemContext.m_elementName);
0728: if (prefix != null)
0729: m_elemContext.m_elementURI = m_prefixMap
0730: .lookupNamespace(prefix);
0731:
0732: }
0733:
0734: if ((null != m_elemContext.m_elementURI)
0735: && m_elemContext.m_elementURI.length() == 0)
0736: m_elemContext.m_elementURI = null;
0737:
0738: int nElems = m_cdataSectionElements.size();
0739:
0740: // loop through 2 at a time, as these are pairs of URI and localName
0741: for (int i = 0; i < nElems; i += 2) {
0742: String uri = (String) m_cdataSectionElements
0743: .elementAt(i);
0744: String loc = (String) m_cdataSectionElements
0745: .elementAt(i + 1);
0746: if (loc.equals(m_elemContext.m_elementLocalName)
0747: && subPartMatch(m_elemContext.m_elementURI, uri)) {
0748: b = true;
0749:
0750: break;
0751: }
0752: }
0753: }
0754: return b;
0755: }
0756:
0757: /**
0758: * Tell if two strings are equal, without worry if the first string is null.
0759: *
0760: * @param p String reference, which may be null.
0761: * @param t String reference, which may be null.
0762: *
0763: * @return true if strings are equal.
0764: */
0765: private static final boolean subPartMatch(String p, String t) {
0766: return (p == t) || ((null != p) && (p.equals(t)));
0767: }
0768:
0769: /**
0770: * Returns the local name of a qualified name.
0771: * If the name has no prefix,
0772: * then it works as the identity (SAX2).
0773: *
0774: * @param qname a qualified name
0775: * @return returns the prefix of the qualified name,
0776: * or null if there is no prefix.
0777: */
0778: protected static final String getPrefixPart(String qname) {
0779: final int col = qname.indexOf(':');
0780: return (col > 0) ? qname.substring(0, col) : null;
0781: //return (col > 0) ? qname.substring(0,col) : "";
0782: }
0783:
0784: /**
0785: * Some users of the serializer may need the current namespace mappings
0786: * @return the current namespace mappings (prefix/uri)
0787: * @see ExtendedContentHandler#getNamespaceMappings()
0788: */
0789: public NamespaceMappings getNamespaceMappings() {
0790: return m_prefixMap;
0791: }
0792:
0793: /**
0794: * Returns the prefix currently pointing to the given URI (if any).
0795: * @param namespaceURI the uri of the namespace in question
0796: * @return a prefix pointing to the given URI (if any).
0797: * @see ExtendedContentHandler#getPrefix(String)
0798: */
0799: public String getPrefix(String namespaceURI) {
0800: String prefix = m_prefixMap.lookupPrefix(namespaceURI);
0801: return prefix;
0802: }
0803:
0804: /**
0805: * Returns the URI of an element or attribute. Note that default namespaces
0806: * do not apply directly to attributes.
0807: * @param qname a qualified name
0808: * @param isElement true if the qualified name is the name of
0809: * an element.
0810: * @return returns the namespace URI associated with the qualified name.
0811: */
0812: public String getNamespaceURI(String qname, boolean isElement) {
0813: String uri = EMPTYSTRING;
0814: int col = qname.lastIndexOf(':');
0815: final String prefix = (col > 0) ? qname.substring(0, col)
0816: : EMPTYSTRING;
0817:
0818: if (!EMPTYSTRING.equals(prefix) || isElement) {
0819: if (m_prefixMap != null) {
0820: uri = m_prefixMap.lookupNamespace(prefix);
0821: if (uri == null && !prefix.equals(XMLNS_PREFIX)) {
0822: throw new RuntimeException(Utils.messages
0823: .createMessage(MsgKey.ER_NAMESPACE_PREFIX,
0824: new Object[] { qname.substring(0,
0825: col) }));
0826: }
0827: }
0828: }
0829: return uri;
0830: }
0831:
0832: /**
0833: * Returns the URI of prefix (if any)
0834: *
0835: * @param prefix the prefix whose URI is searched for
0836: * @return the namespace URI currently associated with the
0837: * prefix, null if the prefix is undefined.
0838: */
0839: public String getNamespaceURIFromPrefix(String prefix) {
0840: String uri = null;
0841: if (m_prefixMap != null)
0842: uri = m_prefixMap.lookupNamespace(prefix);
0843: return uri;
0844: }
0845:
0846: /**
0847: * Entity reference event.
0848: *
0849: * @param name Name of entity
0850: *
0851: * @throws org.xml.sax.SAXException
0852: */
0853: public void entityReference(String name)
0854: throws org.xml.sax.SAXException {
0855:
0856: flushPending();
0857:
0858: startEntity(name);
0859: endEntity(name);
0860:
0861: if (m_tracer != null)
0862: fireEntityReference(name);
0863: }
0864:
0865: /**
0866: * Sets the transformer associated with this serializer
0867: * @param t the transformer associated with this serializer.
0868: * @see SerializationHandler#setTransformer(Transformer)
0869: */
0870: public void setTransformer(Transformer t) {
0871: m_transformer = t;
0872:
0873: // If this transformer object implements the SerializerTrace interface
0874: // then assign m_tracer to the transformer object so it can be used
0875: // to fire trace events.
0876: if ((m_transformer instanceof SerializerTrace)
0877: && (((SerializerTrace) m_transformer)
0878: .hasTraceListeners())) {
0879: m_tracer = (SerializerTrace) m_transformer;
0880: } else {
0881: m_tracer = null;
0882: }
0883: }
0884:
0885: /**
0886: * Gets the transformer associated with this serializer
0887: * @return returns the transformer associated with this serializer.
0888: * @see SerializationHandler#getTransformer()
0889: */
0890: public Transformer getTransformer() {
0891: return m_transformer;
0892: }
0893:
0894: /**
0895: * This method gets the nodes value as a String and uses that String as if
0896: * it were an input character notification.
0897: * @param node the Node to serialize
0898: * @throws org.xml.sax.SAXException
0899: */
0900: public void characters(org.w3c.dom.Node node)
0901: throws org.xml.sax.SAXException {
0902: flushPending();
0903: String data = node.getNodeValue();
0904: if (data != null) {
0905: final int length = data.length();
0906: if (length > m_charsBuff.length) {
0907: m_charsBuff = new char[length * 2 + 1];
0908: }
0909: data.getChars(0, length, m_charsBuff, 0);
0910: characters(m_charsBuff, 0, length);
0911: }
0912: }
0913:
0914: /**
0915: * @see org.xml.sax.ErrorHandler#error(SAXParseException)
0916: */
0917: public void error(SAXParseException exc) throws SAXException {
0918: }
0919:
0920: /**
0921: * @see org.xml.sax.ErrorHandler#fatalError(SAXParseException)
0922: */
0923: public void fatalError(SAXParseException exc) throws SAXException {
0924:
0925: m_elemContext.m_startTagOpen = false;
0926:
0927: }
0928:
0929: /**
0930: * @see org.xml.sax.ErrorHandler#warning(SAXParseException)
0931: */
0932: public void warning(SAXParseException exc) throws SAXException {
0933: }
0934:
0935: /**
0936: * To fire off start entity trace event
0937: * @param name Name of entity
0938: */
0939: protected void fireStartEntity(String name)
0940: throws org.xml.sax.SAXException {
0941: if (m_tracer != null) {
0942: flushMyWriter();
0943: m_tracer.fireGenerateEvent(
0944: SerializerTrace.EVENTTYPE_ENTITYREF, name);
0945: }
0946: }
0947:
0948: /**
0949: * Report the characters event
0950: * @param chars content of characters
0951: * @param start starting index of characters to output
0952: * @param length number of characters to output
0953: */
0954: // protected void fireCharEvent(char[] chars, int start, int length)
0955: // throws org.xml.sax.SAXException
0956: // {
0957: // if (m_tracer != null)
0958: // m_tracer.fireGenerateEvent(SerializerTrace.EVENTTYPE_CHARACTERS, chars, start,length);
0959: // }
0960: //
0961: /**
0962: * This method is only used internally when flushing the writer from the
0963: * various fire...() trace events. Due to the writer being wrapped with
0964: * SerializerTraceWriter it may cause the flush of these trace events:
0965: * EVENTTYPE_OUTPUT_PSEUDO_CHARACTERS
0966: * EVENTTYPE_OUTPUT_CHARACTERS
0967: * which trace the output written to the output stream.
0968: *
0969: */
0970: private void flushMyWriter() {
0971: if (m_writer != null) {
0972: try {
0973: m_writer.flush();
0974: } catch (IOException ioe) {
0975:
0976: }
0977: }
0978: }
0979:
0980: /**
0981: * Report the CDATA trace event
0982: * @param chars content of CDATA
0983: * @param start starting index of characters to output
0984: * @param length number of characters to output
0985: */
0986: protected void fireCDATAEvent(char[] chars, int start, int length)
0987: throws org.xml.sax.SAXException {
0988: if (m_tracer != null) {
0989: flushMyWriter();
0990: m_tracer.fireGenerateEvent(SerializerTrace.EVENTTYPE_CDATA,
0991: chars, start, length);
0992: }
0993: }
0994:
0995: /**
0996: * Report the comment trace event
0997: * @param chars content of comment
0998: * @param start starting index of comment to output
0999: * @param length number of characters to output
1000: */
1001: protected void fireCommentEvent(char[] chars, int start, int length)
1002: throws org.xml.sax.SAXException {
1003: if (m_tracer != null) {
1004: flushMyWriter();
1005: m_tracer.fireGenerateEvent(
1006: SerializerTrace.EVENTTYPE_COMMENT, new String(
1007: chars, start, length));
1008: }
1009: }
1010:
1011: /**
1012: * To fire off end entity trace event
1013: * @param name Name of entity
1014: */
1015: public void fireEndEntity(String name)
1016: throws org.xml.sax.SAXException {
1017: if (m_tracer != null)
1018: flushMyWriter();
1019: // we do not need to handle this.
1020: }
1021:
1022: /**
1023: * To fire off start document trace event
1024: */
1025: protected void fireStartDoc() throws org.xml.sax.SAXException {
1026: if (m_tracer != null) {
1027: flushMyWriter();
1028: m_tracer
1029: .fireGenerateEvent(SerializerTrace.EVENTTYPE_STARTDOCUMENT);
1030: }
1031: }
1032:
1033: /**
1034: * To fire off end document trace event
1035: */
1036: protected void fireEndDoc() throws org.xml.sax.SAXException {
1037: if (m_tracer != null) {
1038: flushMyWriter();
1039: m_tracer
1040: .fireGenerateEvent(SerializerTrace.EVENTTYPE_ENDDOCUMENT);
1041: }
1042: }
1043:
1044: /**
1045: * Report the start element trace event. This trace method needs to be
1046: * called just before the attributes are cleared.
1047: *
1048: * @param elemName the qualified name of the element
1049: *
1050: */
1051: protected void fireStartElem(String elemName)
1052: throws org.xml.sax.SAXException {
1053: if (m_tracer != null) {
1054: flushMyWriter();
1055: m_tracer.fireGenerateEvent(
1056: SerializerTrace.EVENTTYPE_STARTELEMENT, elemName,
1057: m_attributes);
1058: }
1059: }
1060:
1061: /**
1062: * To fire off the end element event
1063: * @param name Name of element
1064: */
1065: // protected void fireEndElem(String name)
1066: // throws org.xml.sax.SAXException
1067: // {
1068: // if (m_tracer != null)
1069: // m_tracer.fireGenerateEvent(SerializerTrace.EVENTTYPE_ENDELEMENT,name, (Attributes)null);
1070: // }
1071:
1072: /**
1073: * To fire off the PI trace event
1074: * @param name Name of PI
1075: */
1076: protected void fireEscapingEvent(String name, String data)
1077: throws org.xml.sax.SAXException {
1078:
1079: if (m_tracer != null) {
1080: flushMyWriter();
1081: m_tracer.fireGenerateEvent(SerializerTrace.EVENTTYPE_PI,
1082: name, data);
1083: }
1084: }
1085:
1086: /**
1087: * To fire off the entity reference trace event
1088: * @param name Name of entity reference
1089: */
1090: protected void fireEntityReference(String name)
1091: throws org.xml.sax.SAXException {
1092: if (m_tracer != null) {
1093: flushMyWriter();
1094: m_tracer.fireGenerateEvent(
1095: SerializerTrace.EVENTTYPE_ENTITYREF, name,
1096: (Attributes) null);
1097: }
1098: }
1099:
1100: /**
1101: * Receive notification of the beginning of a document.
1102: * This method is never a self generated call,
1103: * but only called externally.
1104: *
1105: * <p>The SAX parser will invoke this method only once, before any
1106: * other methods in this interface or in DTDHandler (except for
1107: * setDocumentLocator).</p>
1108: *
1109: * @throws org.xml.sax.SAXException Any SAX exception, possibly
1110: * wrapping another exception.
1111: *
1112: * @throws org.xml.sax.SAXException
1113: */
1114: public void startDocument() throws org.xml.sax.SAXException {
1115:
1116: // if we do get called with startDocument(), handle it right away
1117: startDocumentInternal();
1118: m_needToCallStartDocument = false;
1119: return;
1120: }
1121:
1122: /**
1123: * This method handles what needs to be done at a startDocument() call,
1124: * whether from an external caller, or internally called in the
1125: * serializer. For historical reasons the serializer is flexible to
1126: * startDocument() not always being called.
1127: * Even if no external call is
1128: * made into startDocument() this method will always be called as a self
1129: * generated internal startDocument, it handles what needs to be done at a
1130: * startDocument() call.
1131: *
1132: * This method exists just to make sure that startDocument() is only ever
1133: * called from an external caller, which in principle is just a matter of
1134: * style.
1135: *
1136: * @throws SAXException
1137: */
1138: protected void startDocumentInternal()
1139: throws org.xml.sax.SAXException {
1140: if (m_tracer != null)
1141: this .fireStartDoc();
1142: }
1143:
1144: /**
1145: * This method is used to set the source locator, which might be used to
1146: * generated an error message.
1147: * @param locator the source locator
1148: *
1149: * @see ExtendedContentHandler#setSourceLocator(javax.xml.transform.SourceLocator)
1150: */
1151: public void setSourceLocator(SourceLocator locator) {
1152: m_sourceLocator = locator;
1153: }
1154:
1155: /**
1156: * Used only by TransformerSnapshotImpl to restore the serialization
1157: * to a previous state.
1158: *
1159: * @param mappings NamespaceMappings
1160: */
1161: public void setNamespaceMappings(NamespaceMappings mappings) {
1162: m_prefixMap = mappings;
1163: }
1164:
1165: public boolean reset() {
1166: resetSerializerBase();
1167: return true;
1168: }
1169:
1170: /**
1171: * Reset all of the fields owned by SerializerBase
1172: *
1173: */
1174: private void resetSerializerBase() {
1175: this .m_attributes.clear();
1176: this .m_cdataSectionElements = null;
1177: this .m_elemContext = new ElemContext();
1178: this .m_doctypePublic = null;
1179: this .m_doctypeSystem = null;
1180: this .m_doIndent = false;
1181: this .m_encoding = null;
1182: this .m_indentAmount = 0;
1183: this .m_inEntityRef = false;
1184: this .m_inExternalDTD = false;
1185: this .m_mediatype = null;
1186: this .m_needToCallStartDocument = true;
1187: this .m_needToOutputDocTypeDecl = false;
1188: if (this .m_prefixMap != null)
1189: this .m_prefixMap.reset();
1190: this .m_shouldNotWriteXMLHeader = false;
1191: this .m_sourceLocator = null;
1192: this .m_standalone = null;
1193: this .m_standaloneWasSpecified = false;
1194: this .m_tracer = null;
1195: this .m_transformer = null;
1196: this .m_version = null;
1197: // don't set writer to null, so that it might be re-used
1198: //this.m_writer = null;
1199: }
1200:
1201: /**
1202: * Returns true if the serializer is used for temporary output rather than
1203: * final output.
1204: *
1205: * This concept is made clear in the XSLT 2.0 draft.
1206: */
1207: final boolean inTemporaryOutputState() {
1208: /* This is a hack. We should really be letting the serializer know
1209: * that it is in temporary output state with an explicit call, but
1210: * from a pragmatic point of view (for now anyways) having no output
1211: * encoding at all, not even the default UTF-8 indicates that the serializer
1212: * is being used for temporary RTF.
1213: */
1214: return (getEncoding() == null);
1215:
1216: }
1217:
1218: /**
1219: * This method adds an attribute the the current element,
1220: * but should not be used for an xsl:attribute child.
1221: * @see ExtendedContentHandler#addAttribute(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String)
1222: */
1223: public void addAttribute(String uri, String localName,
1224: String rawName, String type, String value)
1225: throws SAXException {
1226: if (m_elemContext.m_startTagOpen) {
1227: addAttributeAlways(uri, localName, rawName, type, value,
1228: false);
1229: }
1230: }
1231:
1232: /**
1233: * @see org.xml.sax.DTDHandler#notationDecl(java.lang.String, java.lang.String, java.lang.String)
1234: */
1235: public void notationDecl(String arg0, String arg1, String arg2)
1236: throws SAXException {
1237: // This method just provides a definition to satisfy the interface
1238: // A particular sub-class of SerializerBase provides the implementation (if desired)
1239: }
1240:
1241: /**
1242: * @see org.xml.sax.DTDHandler#unparsedEntityDecl(java.lang.String, java.lang.String, java.lang.String, java.lang.String)
1243: */
1244: public void unparsedEntityDecl(String arg0, String arg1,
1245: String arg2, String arg3) throws SAXException {
1246: // This method just provides a definition to satisfy the interface
1247: // A particular sub-class of SerializerBase provides the implementation (if desired)
1248: }
1249:
1250: /**
1251: * If set to false the serializer does not expand DTD entities,
1252: * but leaves them as is, the default value is true.
1253: */
1254: public void setDTDEntityExpansion(boolean expand) {
1255: // This method just provides a definition to satisfy the interface
1256: // A particular sub-class of SerializerBase provides the implementation (if desired)
1257: }
1258:
1259: }
|