0001: /*--
0002:
0003: $Id: SAXOutputter.java,v 1.2 2005/05/03 07:02:04 wittek Exp $
0004:
0005: Copyright (C) 2000-2004 Jason Hunter & Brett McLaughlin.
0006: All rights reserved.
0007:
0008: Redistribution and use in source and binary forms, with or without
0009: modification, are permitted provided that the following conditions
0010: are met:
0011:
0012: 1. Redistributions of source code must retain the above copyright
0013: notice, this list of conditions, and the following disclaimer.
0014:
0015: 2. Redistributions in binary form must reproduce the above copyright
0016: notice, this list of conditions, and the disclaimer that follows
0017: these conditions in the documentation and/or other materials
0018: provided with the distribution.
0019:
0020: 3. The name "JDOM" must not be used to endorse or promote products
0021: derived from this software without prior written permission. For
0022: written permission, please contact <request_AT_jdom_DOT_org>.
0023:
0024: 4. Products derived from this software may not be called "JDOM", nor
0025: may "JDOM" appear in their name, without prior written permission
0026: from the JDOM Project Management <request_AT_jdom_DOT_org>.
0027:
0028: In addition, we request (but do not require) that you include in the
0029: end-user documentation provided with the redistribution and/or in the
0030: software itself an acknowledgement equivalent to the following:
0031: "This product includes software developed by the
0032: JDOM Project (http://www.jdom.org/)."
0033: Alternatively, the acknowledgment may be graphical using the logos
0034: available at http://www.jdom.org/images/logos.
0035:
0036: THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
0037: WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
0038: OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
0039: DISCLAIMED. IN NO EVENT SHALL THE JDOM AUTHORS OR THE PROJECT
0040: CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
0041: SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
0042: LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
0043: USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
0044: ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
0045: OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
0046: OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
0047: SUCH DAMAGE.
0048:
0049: This software consists of voluntary contributions made by many
0050: individuals on behalf of the JDOM Project and was originally
0051: created by Jason Hunter <jhunter_AT_jdom_DOT_org> and
0052: Brett McLaughlin <brett_AT_jdom_DOT_org>. For more information
0053: on the JDOM Project, please see <http://www.jdom.org/>.
0054:
0055: */
0056:
0057: package org.jdom.output;
0058:
0059: import java.io.*;
0060: import java.lang.reflect.*;
0061: import java.util.*;
0062:
0063: import org.jdom.*;
0064: import org.xml.sax.*;
0065: import org.xml.sax.ext.*;
0066: import org.xml.sax.helpers.*;
0067:
0068: /**
0069: * Outputs a JDOM document as a stream of SAX2 events.
0070: * <p>
0071: * Most ContentHandler callbacks are supported. Both
0072: * <code>ignorableWhitespace()</code> and <code>skippedEntity()</code> have not
0073: * been implemented. The <code>{@link JDOMLocator}</code> class returned by
0074: * <code>{@link #getLocator}</code> exposes the current node being operated
0075: * upon.
0076: * <p>
0077: * At this time, it is not possible to access notations and unparsed entity
0078: * references in a DTD from JDOM. Therefore, <code>DTDHandler</code> callbacks
0079: * have not been implemented yet.
0080: * <p>
0081: * The <code>ErrorHandler</code> callbacks have not been implemented, since
0082: * these are supposed to be invoked when the document is parsed and at this
0083: * point the document exists in memory and is known to have no errors. </p>
0084: *
0085: * @version $Revision: 1.2 $, $Date: 2005/05/03 07:02:04 $
0086: * @author Brett McLaughlin
0087: * @author Jason Hunter
0088: * @author Fred Trimble
0089: * @author Bradley S. Huffman
0090: */
0091: public class SAXOutputter {
0092:
0093: private static final String CVS_ID = "@(#) $RCSfile: SAXOutputter.java,v $ $Revision: 1.2 $ $Date: 2005/05/03 07:02:04 $ $Name: $";
0094:
0095: /** Shortcut for SAX namespaces core feature */
0096: private static final String NAMESPACES_SAX_FEATURE = "http://xml.org/sax/features/namespaces";
0097:
0098: /** Shortcut for SAX namespace-prefixes core feature */
0099: private static final String NS_PREFIXES_SAX_FEATURE = "http://xml.org/sax/features/namespace-prefixes";
0100:
0101: /** Shortcut for SAX validation core feature */
0102: private static final String VALIDATION_SAX_FEATURE = "http://xml.org/sax/features/validation";
0103:
0104: /** Shortcut for SAX-ext. lexical handler property */
0105: private static final String LEXICAL_HANDLER_SAX_PROPERTY = "http://xml.org/sax/properties/lexical-handler";
0106:
0107: /** Shortcut for SAX-ext. declaration handler property */
0108: private static final String DECL_HANDLER_SAX_PROPERTY = "http://xml.org/sax/properties/declaration-handler";
0109:
0110: /**
0111: * Shortcut for SAX-ext. lexical handler alternate property.
0112: * Although this property URI is not the one defined by the SAX
0113: * "standard", some parsers use it instead of the official one.
0114: */
0115: private static final String LEXICAL_HANDLER_ALT_PROPERTY = "http://xml.org/sax/handlers/LexicalHandler";
0116:
0117: /** Shortcut for SAX-ext. declaration handler alternate property */
0118: private static final String DECL_HANDLER_ALT_PROPERTY = "http://xml.org/sax/handlers/DeclHandler";
0119:
0120: /**
0121: * Array to map JDOM attribute type (as entry index) to SAX
0122: * attribute type names.
0123: */
0124: private static final String[] attrTypeToNameMap = new String[] {
0125: "CDATA", // Attribute.UNDEFINED_ATTRIBUTE, as per SAX 2.0 spec.
0126: "CDATA", // Attribute.CDATA_TYPE
0127: "ID", // Attribute.ID_TYPE
0128: "IDREF", // Attribute.IDREF_TYPE
0129: "IDREFS", // Attribute.IDREFS_TYPE
0130: "ENTITY", // Attribute.ENTITY_TYPE
0131: "ENTITIES", // Attribute.ENTITIES_TYPE
0132: "NMTOKEN", // Attribute.NMTOKEN_TYPE
0133: "NMTOKENS", // Attribute.NMTOKENS_TYPE
0134: "NOTATION", // Attribute.NOTATION_TYPE
0135: "NMTOKEN", // Attribute.ENUMERATED_TYPE, as per SAX 2.0 spec.
0136: };
0137:
0138: /** registered <code>ContentHandler</code> */
0139: private ContentHandler contentHandler;
0140:
0141: /** registered <code>ErrorHandler</code> */
0142: private ErrorHandler errorHandler;
0143:
0144: /** registered <code>DTDHandler</code> */
0145: private DTDHandler dtdHandler;
0146:
0147: /** registered <code>EntityResolver</code> */
0148: private EntityResolver entityResolver;
0149:
0150: /** registered <code>LexicalHandler</code> */
0151: private LexicalHandler lexicalHandler;
0152:
0153: /** registered <code>DeclHandler</code> */
0154: private DeclHandler declHandler;
0155:
0156: /**
0157: * Whether to report attribute namespace declarations as xmlns attributes.
0158: * Defaults to <code>false</code> as per SAX specifications.
0159: *
0160: * @see <a href="http://www.megginson.com/SAX/Java/namespaces.html">
0161: * SAX namespace specifications</a>
0162: */
0163: private boolean declareNamespaces = false;
0164:
0165: /**
0166: * Whether to report DTD events to DeclHandlers and LexicalHandlers.
0167: * Defaults to <code>true</code>.
0168: */
0169: private boolean reportDtdEvents = true;
0170:
0171: /**
0172: * A SAX Locator that points at the JDOM node currently being
0173: * outputted.
0174: */
0175: private JDOMLocator locator = null;
0176:
0177: /**
0178: * This will create a <code>SAXOutputter</code> without any
0179: * registered handler. The application is then responsible for
0180: * registering them using the <code>setXxxHandler()</code> methods.
0181: */
0182: public SAXOutputter() {
0183: }
0184:
0185: /**
0186: * This will create a <code>SAXOutputter</code> with the
0187: * specified <code>ContentHandler</code>.
0188: *
0189: * @param contentHandler contains <code>ContentHandler</code>
0190: * callback methods
0191: */
0192: public SAXOutputter(ContentHandler contentHandler) {
0193: this (contentHandler, null, null, null, null);
0194: }
0195:
0196: /**
0197: * This will create a <code>SAXOutputter</code> with the
0198: * specified SAX2 handlers. At this time, only <code>ContentHandler</code>
0199: * and <code>EntityResolver</code> are supported.
0200: *
0201: * @param contentHandler contains <code>ContentHandler</code>
0202: * callback methods
0203: * @param errorHandler contains <code>ErrorHandler</code> callback methods
0204: * @param dtdHandler contains <code>DTDHandler</code> callback methods
0205: * @param entityResolver contains <code>EntityResolver</code>
0206: * callback methods
0207: */
0208: public SAXOutputter(ContentHandler contentHandler,
0209: ErrorHandler errorHandler, DTDHandler dtdHandler,
0210: EntityResolver entityResolver) {
0211: this (contentHandler, errorHandler, dtdHandler, entityResolver,
0212: null);
0213: }
0214:
0215: /**
0216: * This will create a <code>SAXOutputter</code> with the
0217: * specified SAX2 handlers. At this time, only <code>ContentHandler</code>
0218: * and <code>EntityResolver</code> are supported.
0219: *
0220: * @param contentHandler contains <code>ContentHandler</code>
0221: * callback methods
0222: * @param errorHandler contains <code>ErrorHandler</code> callback methods
0223: * @param dtdHandler contains <code>DTDHandler</code> callback methods
0224: * @param entityResolver contains <code>EntityResolver</code>
0225: * callback methods
0226: * @param lexicalHandler contains <code>LexicalHandler</code> callbacks.
0227: */
0228: public SAXOutputter(ContentHandler contentHandler,
0229: ErrorHandler errorHandler, DTDHandler dtdHandler,
0230: EntityResolver entityResolver, LexicalHandler lexicalHandler) {
0231: this .contentHandler = contentHandler;
0232: this .errorHandler = errorHandler;
0233: this .dtdHandler = dtdHandler;
0234: this .entityResolver = entityResolver;
0235: this .lexicalHandler = lexicalHandler;
0236: }
0237:
0238: /**
0239: * This will set the <code>ContentHandler</code>.
0240: *
0241: * @param contentHandler contains <code>ContentHandler</code>
0242: * callback methods.
0243: */
0244: public void setContentHandler(ContentHandler contentHandler) {
0245: this .contentHandler = contentHandler;
0246: }
0247:
0248: /**
0249: * Returns the registered <code>ContentHandler</code>.
0250: *
0251: * @return the current <code>ContentHandler</code> or
0252: * <code>null</code> if none was registered.
0253: */
0254: public ContentHandler getContentHandler() {
0255: return this .contentHandler;
0256: }
0257:
0258: /**
0259: * This will set the <code>ErrorHandler</code>.
0260: *
0261: * @param errorHandler contains <code>ErrorHandler</code> callback methods.
0262: */
0263: public void setErrorHandler(ErrorHandler errorHandler) {
0264: this .errorHandler = errorHandler;
0265: }
0266:
0267: /**
0268: * Return the registered <code>ErrorHandler</code>.
0269: *
0270: * @return the current <code>ErrorHandler</code> or
0271: * <code>null</code> if none was registered.
0272: */
0273: public ErrorHandler getErrorHandler() {
0274: return this .errorHandler;
0275: }
0276:
0277: /**
0278: * This will set the <code>DTDHandler</code>.
0279: *
0280: * @param dtdHandler contains <code>DTDHandler</code> callback methods.
0281: */
0282: public void setDTDHandler(DTDHandler dtdHandler) {
0283: this .dtdHandler = dtdHandler;
0284: }
0285:
0286: /**
0287: * Return the registered <code>DTDHandler</code>.
0288: *
0289: * @return the current <code>DTDHandler</code> or
0290: * <code>null</code> if none was registered.
0291: */
0292: public DTDHandler getDTDHandler() {
0293: return this .dtdHandler;
0294: }
0295:
0296: /**
0297: * This will set the <code>EntityResolver</code>.
0298: *
0299: * @param entityResolver contains EntityResolver callback methods.
0300: */
0301: public void setEntityResolver(EntityResolver entityResolver) {
0302: this .entityResolver = entityResolver;
0303: }
0304:
0305: /**
0306: * Return the registered <code>EntityResolver</code>.
0307: *
0308: * @return the current <code>EntityResolver</code> or
0309: * <code>null</code> if none was registered.
0310: */
0311: public EntityResolver getEntityResolver() {
0312: return this .entityResolver;
0313: }
0314:
0315: /**
0316: * This will set the <code>LexicalHandler</code>.
0317: *
0318: * @param lexicalHandler contains lexical callback methods.
0319: */
0320: public void setLexicalHandler(LexicalHandler lexicalHandler) {
0321: this .lexicalHandler = lexicalHandler;
0322: }
0323:
0324: /**
0325: * Return the registered <code>LexicalHandler</code>.
0326: *
0327: * @return the current <code>LexicalHandler</code> or
0328: * <code>null</code> if none was registered.
0329: */
0330: public LexicalHandler getLexicalHandler() {
0331: return this .lexicalHandler;
0332: }
0333:
0334: /**
0335: * This will set the <code>DeclHandler</code>.
0336: *
0337: * @param declHandler contains declaration callback methods.
0338: */
0339: public void setDeclHandler(DeclHandler declHandler) {
0340: this .declHandler = declHandler;
0341: }
0342:
0343: /**
0344: * Return the registered <code>DeclHandler</code>.
0345: *
0346: * @return the current <code>DeclHandler</code> or
0347: * <code>null</code> if none was registered.
0348: */
0349: public DeclHandler getDeclHandler() {
0350: return this .declHandler;
0351: }
0352:
0353: /**
0354: * Returns whether attribute namespace declarations shall be reported as
0355: * "xmlns" attributes.
0356: *
0357: * @return whether attribute namespace declarations shall be reported as
0358: * "xmlns" attributes.
0359: */
0360: public boolean getReportNamespaceDeclarations() {
0361: return declareNamespaces;
0362: }
0363:
0364: /**
0365: * This will define whether attribute namespace declarations shall be
0366: * reported as "xmlns" attributes. This flag defaults to <code>false</code>
0367: * and behaves as the "namespace-prefixes" SAX core feature.
0368: *
0369: * @param declareNamespaces whether attribute namespace declarations
0370: * shall be reported as "xmlns" attributes.
0371: */
0372: public void setReportNamespaceDeclarations(boolean declareNamespaces) {
0373: this .declareNamespaces = declareNamespaces;
0374: }
0375:
0376: /**
0377: * Returns whether DTD events will be reported.
0378: *
0379: * @return whether DTD events will be reported
0380: */
0381: public boolean getReportDTDEvents() {
0382: return reportDtdEvents;
0383: }
0384:
0385: /**
0386: * This will define whether to report DTD events to SAX DeclHandlers
0387: * and LexicalHandlers if these handlers are registered and the
0388: * document to output includes a DocType declaration.
0389: *
0390: * @param reportDtdEvents whether to notify DTD events.
0391: */
0392: public void setReportDTDEvents(boolean reportDtdEvents) {
0393: this .reportDtdEvents = reportDtdEvents;
0394: }
0395:
0396: /**
0397: * This will set the state of a SAX feature.
0398: * <p>
0399: * All XMLReaders are required to support setting to true and to false.
0400: * </p>
0401: * <p>
0402: * SAXOutputter currently supports the following SAX core features:
0403: * <dl>
0404: * <dt><code>http://xml.org/sax/features/namespaces</code></dt>
0405: * <dd><strong>description:</strong> <code>true</code> indicates
0406: * namespace URIs and unprefixed local names for element and
0407: * attribute names will be available</dd>
0408: * <dd><strong>access:</strong> read/write, but always
0409: * <code>true</code>!</dd>
0410: * <dt><code>http://xml.org/sax/features/namespace-prefixes</code></dt>
0411: * <dd><strong>description:</strong> <code>true</code> indicates
0412: * XML 1.0 names (with prefixes) and attributes (including xmlns*
0413: * attributes) will be available</dd>
0414: * <dd><strong>access:</strong> read/write</dd>
0415: * <dt><code>http://xml.org/sax/features/validation</code></dt>
0416: * <dd><strong>description:</strong> controls whether SAXOutputter
0417: * is reporting DTD-related events; if <code>true</code>, the
0418: * DocType internal subset will be parsed to fire DTD events</dd>
0419: * <dd><strong>access:</strong> read/write, defaults to
0420: * <code>true</code></dd>
0421: * </dl>
0422: * </p>
0423: *
0424: * @param name <code>String</code> the feature name, which is a
0425: * fully-qualified URI.
0426: * @param value <code>boolean</code> the requested state of the
0427: * feature (true or false).
0428: *
0429: * @throws SAXNotRecognizedException when SAXOutputter does not
0430: * recognize the feature name.
0431: * @throws SAXNotSupportedException when SAXOutputter recognizes
0432: * the feature name but cannot set the requested value.
0433: */
0434: public void setFeature(String name, boolean value)
0435: throws SAXNotRecognizedException, SAXNotSupportedException {
0436: if (NS_PREFIXES_SAX_FEATURE.equals(name)) {
0437: // Namespace prefix declarations.
0438: this .setReportNamespaceDeclarations(value);
0439: } else {
0440: if (NAMESPACES_SAX_FEATURE.equals(name)) {
0441: if (value != true) {
0442: // Namespaces feature always supported by SAXOutputter.
0443: throw new SAXNotSupportedException(name);
0444: }
0445: // Else: true is OK!
0446: } else {
0447: if (VALIDATION_SAX_FEATURE.equals(name)) {
0448: // Report DTD events.
0449: this .setReportDTDEvents(value);
0450: } else {
0451: // NotElement a supported feature.
0452: throw new SAXNotRecognizedException(name);
0453: }
0454: }
0455: }
0456: }
0457:
0458: /**
0459: * This will look up the value of a SAX feature.
0460: *
0461: * @param name <code>String</code> the feature name, which is a
0462: * fully-qualified URI.
0463: * @return <code>boolean</code> the current state of the feature
0464: * (true or false).
0465: *
0466: * @throws SAXNotRecognizedException when SAXOutputter does not
0467: * recognize the feature name.
0468: * @throws SAXNotSupportedException when SAXOutputter recognizes
0469: * the feature name but determine its value at this time.
0470: */
0471: public boolean getFeature(String name)
0472: throws SAXNotRecognizedException, SAXNotSupportedException {
0473: if (NS_PREFIXES_SAX_FEATURE.equals(name)) {
0474: // Namespace prefix declarations.
0475: return (this .declareNamespaces);
0476: } else {
0477: if (NAMESPACES_SAX_FEATURE.equals(name)) {
0478: // Namespaces feature always supported by SAXOutputter.
0479: return (true);
0480: } else {
0481: if (VALIDATION_SAX_FEATURE.equals(name)) {
0482: // Report DTD events.
0483: return (this .reportDtdEvents);
0484: } else {
0485: // NotElement a supported feature.
0486: throw new SAXNotRecognizedException(name);
0487: }
0488: }
0489: }
0490: }
0491:
0492: /**
0493: * This will set the value of a SAX property.
0494: * This method is also the standard mechanism for setting extended
0495: * handlers.
0496: * <p>
0497: * SAXOutputter currently supports the following SAX properties:
0498: * <dl>
0499: * <dt><code>http://xml.org/sax/properties/lexical-handler</code></dt>
0500: * <dd><strong>data type:</strong>
0501: * <code>org.xml.sax.ext.LexicalHandler</code></dd>
0502: * <dd><strong>description:</strong> An optional extension handler for
0503: * lexical events like comments.</dd>
0504: * <dd><strong>access:</strong> read/write</dd>
0505: * <dt><code>http://xml.org/sax/properties/declaration-handler</code></dt>
0506: * <dd><strong>data type:</strong>
0507: * <code>org.xml.sax.ext.DeclHandler</code></dd>
0508: * <dd><strong>description:</strong> An optional extension handler for
0509: * DTD-related events other than notations and unparsed entities.</dd>
0510: * <dd><strong>access:</strong> read/write</dd>
0511: * </dl>
0512: * </p>
0513: *
0514: * @param name <code>String</code> the property name, which is a
0515: * fully-qualified URI.
0516: * @param value <code>Object</code> the requested value for the property.
0517: *
0518: * @throws SAXNotRecognizedException when SAXOutputter does not recognize
0519: * the property name.
0520: * @throws SAXNotSupportedException when SAXOutputter recognizes the
0521: * property name but cannot set the requested value.
0522: */
0523: public void setProperty(String name, Object value)
0524: throws SAXNotRecognizedException, SAXNotSupportedException {
0525: if ((LEXICAL_HANDLER_SAX_PROPERTY.equals(name))
0526: || (LEXICAL_HANDLER_ALT_PROPERTY.equals(name))) {
0527: this .setLexicalHandler((LexicalHandler) value);
0528: } else {
0529: if ((DECL_HANDLER_SAX_PROPERTY.equals(name))
0530: || (DECL_HANDLER_ALT_PROPERTY.equals(name))) {
0531: this .setDeclHandler((DeclHandler) value);
0532: } else {
0533: throw new SAXNotRecognizedException(name);
0534: }
0535: }
0536: }
0537:
0538: /**
0539: * This will look up the value of a SAX property.
0540: *
0541: * @param name <code>String</code> the property name, which is a
0542: * fully-qualified URI.
0543: * @return <code>Object</code> the current value of the property.
0544: *
0545: * @throws SAXNotRecognizedException when SAXOutputter does not recognize
0546: * the property name.
0547: * @throws SAXNotSupportedException when SAXOutputter recognizes the
0548: * property name but cannot determine its value at this time.
0549: */
0550: public Object getProperty(String name)
0551: throws SAXNotRecognizedException, SAXNotSupportedException {
0552: if ((LEXICAL_HANDLER_SAX_PROPERTY.equals(name))
0553: || (LEXICAL_HANDLER_ALT_PROPERTY.equals(name))) {
0554: return this .getLexicalHandler();
0555: } else {
0556: if ((DECL_HANDLER_SAX_PROPERTY.equals(name))
0557: || (DECL_HANDLER_ALT_PROPERTY.equals(name))) {
0558: return this .getDeclHandler();
0559: } else {
0560: throw new SAXNotRecognizedException(name);
0561: }
0562: }
0563: }
0564:
0565: /**
0566: * This will output the <code>JDOM Document</code>, firing off the
0567: * SAX events that have been registered.
0568: *
0569: * @param document <code>JDOM Document</code> to output.
0570: *
0571: * @throws JDOMException if any error occurred.
0572: */
0573: public void output(Document document) throws JDOMException {
0574: if (document == null) {
0575: return;
0576: }
0577:
0578: // contentHandler.setDocumentLocator()
0579: documentLocator(document);
0580:
0581: // contentHandler.startDocument()
0582: startDocument();
0583:
0584: // Fire DTD events
0585: if (this .reportDtdEvents) {
0586: dtdEvents(document);
0587: }
0588:
0589: // Handle root element, as well as any root level
0590: // processing instructions and comments
0591: Iterator i = document.getContent().iterator();
0592: while (i.hasNext()) {
0593: Object obj = i.next();
0594:
0595: // update locator
0596: locator.setNode(obj);
0597:
0598: if (obj instanceof Element) {
0599: // process root element and its content
0600: element(document.getRootElement(), new NamespaceStack());
0601: } else if (obj instanceof ProcessingInstruction) {
0602: // contentHandler.processingInstruction()
0603: processingInstruction((ProcessingInstruction) obj);
0604: } else if (obj instanceof Comment) {
0605: // lexicalHandler.comment()
0606: comment(((Comment) obj).getText());
0607: }
0608: }
0609:
0610: // contentHandler.endDocument()
0611: endDocument();
0612: }
0613:
0614: /**
0615: * This will output a list of JDOM nodes as a document, firing
0616: * off the SAX events that have been registered.
0617: * <p>
0618: * <strong>Warning</strong>: This method may output ill-formed XML
0619: * documents if the list contains top-level objects that are not
0620: * legal at the document level (e.g. Text or CDATA nodes, multiple
0621: * Element nodes, etc.). Thus, it should only be used to output
0622: * document portions towards ContentHandlers capable of accepting
0623: * such ill-formed documents (such as XSLT processors).</p>
0624: *
0625: * @param nodes <code>List</code> of JDOM nodes to output.
0626: *
0627: * @throws JDOMException if any error occurred.
0628: *
0629: * @see #output(org.jdom.Document)
0630: */
0631: public void output(List nodes) throws JDOMException {
0632: if ((nodes == null) || (nodes.size() == 0)) {
0633: return;
0634: }
0635:
0636: // contentHandler.setDocumentLocator()
0637: documentLocator(null);
0638:
0639: // contentHandler.startDocument()
0640: startDocument();
0641:
0642: // Process node list.
0643: elementContent(nodes, new NamespaceStack());
0644:
0645: // contentHandler.endDocument()
0646: endDocument();
0647: }
0648:
0649: /**
0650: * This will output a single JDOM element as a document, firing
0651: * off the SAX events that have been registered.
0652: *
0653: * @param node the <code>Element</code> node to output.
0654: *
0655: * @throws JDOMException if any error occurred.
0656: */
0657: public void output(Element node) throws JDOMException {
0658: if (node == null) {
0659: return;
0660: }
0661:
0662: // contentHandler.setDocumentLocator()
0663: documentLocator(null);
0664:
0665: // contentHandler.startDocument()
0666: startDocument();
0667:
0668: // Output node.
0669: elementContent(node, new NamespaceStack());
0670:
0671: // contentHandler.endDocument()
0672: endDocument();
0673: }
0674:
0675: /**
0676: * This will output a list of JDOM nodes as a fragment of an XML
0677: * document, firing off the SAX events that have been registered.
0678: * <p>
0679: * <strong>Warning</strong>: This method does not call the
0680: * {@link ContentHandler#setDocumentLocator},
0681: * {@link ContentHandler#startDocument} and
0682: * {@link ContentHandler#endDocument} callbacks on the
0683: * {@link #setContentHandler ContentHandler}. The user shall
0684: * invoke these methods directly prior/after outputting the
0685: * document fragments.</p>
0686: *
0687: * @param nodes <code>List</code> of JDOM nodes to output.
0688: *
0689: * @throws JDOMException if any error occurred.
0690: *
0691: * @see #outputFragment(org.jdom.Content)
0692: */
0693: public void outputFragment(List nodes) throws JDOMException {
0694: if ((nodes == null) || (nodes.size() == 0)) {
0695: return;
0696: }
0697:
0698: // Output node list as a document fragment.
0699: elementContent(nodes, new NamespaceStack());
0700: }
0701:
0702: /**
0703: * This will output a single JDOM nodes as a fragment of an XML
0704: * document, firing off the SAX events that have been registered.
0705: * <p>
0706: * <strong>Warning</strong>: This method does not call the
0707: * {@link ContentHandler#setDocumentLocator},
0708: * {@link ContentHandler#startDocument} and
0709: * {@link ContentHandler#endDocument} callbacks on the
0710: * {@link #setContentHandler ContentHandler}. The user shall
0711: * invoke these methods directly prior/after outputting the
0712: * document fragments.</p>
0713: *
0714: * @param node the <code>Content</code> node to output.
0715: *
0716: * @throws JDOMException if any error occurred.
0717: *
0718: * @see #outputFragment(java.util.List)
0719: */
0720: public void outputFragment(Content node) throws JDOMException {
0721: if (node == null) {
0722: return;
0723: }
0724:
0725: // Output single node as a document fragment.
0726: elementContent(node, new NamespaceStack());
0727: }
0728:
0729: /**
0730: * This parses a DTD declaration to fire the related events towards
0731: * the registered handlers.
0732: *
0733: * @param document <code>JDOM Document</code> the DocType is to
0734: * process.
0735: */
0736: private void dtdEvents(Document document) throws JDOMException {
0737: DocType docType = document.getDocType();
0738:
0739: // Fire DTD-related events only if handlers have been registered.
0740: if ((docType != null)
0741: && ((dtdHandler != null) || (declHandler != null))) {
0742:
0743: // Build a dummy XML document that only references the DTD...
0744: String dtdDoc = new XMLOutputter().outputString(docType);
0745:
0746: try {
0747: // And parse it to fire DTD events.
0748: createDTDParser().parse(
0749: new InputSource(new StringReader(dtdDoc)));
0750:
0751: // We should never reach this point as the document is
0752: // ill-formed; it does not have any root element.
0753: } catch (SAXParseException e) {
0754: // Expected exception: There's no root element in document.
0755: } catch (SAXException e) {
0756: throw new JDOMException("DTD parsing error", e);
0757: } catch (IOException e) {
0758: throw new JDOMException("DTD parsing error", e);
0759: }
0760: }
0761: }
0762:
0763: /**
0764: * <p>
0765: * This method tells you the line of the XML file being parsed.
0766: * For an in-memory document, it's meaningless. The location
0767: * is only valid for the current parsing lifecycle, but
0768: * the document has already been parsed. Therefore, it returns
0769: * -1 for both line and column numbers.
0770: * </p>
0771: *
0772: * @param document JDOM <code>Document</code>.
0773: */
0774: private void documentLocator(Document document) {
0775: locator = new JDOMLocator();
0776: String publicID = null;
0777: String systemID = null;
0778:
0779: if (document != null) {
0780: DocType docType = document.getDocType();
0781: if (docType != null) {
0782: publicID = docType.getPublicID();
0783: systemID = docType.getSystemID();
0784: }
0785: }
0786: locator.setPublicId(publicID);
0787: locator.setSystemId(systemID);
0788: locator.setLineNumber(-1);
0789: locator.setColumnNumber(-1);
0790:
0791: contentHandler.setDocumentLocator(locator);
0792: }
0793:
0794: /**
0795: * <p>
0796: * This method is always the second method of all callbacks in
0797: * all handlers to be invoked (setDocumentLocator is always first).
0798: * </p>
0799: */
0800: private void startDocument() throws JDOMException {
0801: try {
0802: contentHandler.startDocument();
0803: } catch (SAXException se) {
0804: throw new JDOMException("Exception in startDocument", se);
0805: }
0806: }
0807:
0808: /**
0809: * <p>
0810: * Always the last method of all callbacks in all handlers
0811: * to be invoked.
0812: * </p>
0813: */
0814: private void endDocument() throws JDOMException {
0815: try {
0816: contentHandler.endDocument();
0817:
0818: // reset locator
0819: locator = null;
0820: } catch (SAXException se) {
0821: throw new JDOMException("Exception in endDocument", se);
0822: }
0823: }
0824:
0825: /**
0826: * <p>
0827: * This will invoke the <code>ContentHandler.processingInstruction</code>
0828: * callback when a processing instruction is encountered.
0829: * </p>
0830: *
0831: * @param pi <code>ProcessingInstruction</code> containing target and data.
0832: */
0833: private void processingInstruction(ProcessingInstruction pi)
0834: throws JDOMException {
0835: if (pi != null) {
0836: String target = pi.getTarget();
0837: String data = pi.getData();
0838: try {
0839: contentHandler.processingInstruction(target, data);
0840: } catch (SAXException se) {
0841: throw new JDOMException(
0842: "Exception in processingInstruction", se);
0843: }
0844: }
0845: }
0846:
0847: /**
0848: * <p>
0849: * This will recursively invoke all of the callbacks for a particular
0850: * element.
0851: * </p>
0852: *
0853: * @param element <code>Element</code> used in callbacks.
0854: * @param namespaces <code>List</code> stack of Namespaces in scope.
0855: */
0856: private void element(Element element, NamespaceStack namespaces)
0857: throws JDOMException {
0858: // used to check endPrefixMapping
0859: int previouslyDeclaredNamespaces = namespaces.size();
0860:
0861: // contentHandler.startPrefixMapping()
0862: Attributes nsAtts = startPrefixMapping(element, namespaces);
0863:
0864: // contentHandler.startElement()
0865: startElement(element, nsAtts);
0866:
0867: // handle content in the element
0868: elementContent(element.getContent(), namespaces);
0869:
0870: // update locator
0871: locator.setNode(element);
0872:
0873: // contentHandler.endElement()
0874: endElement(element);
0875:
0876: // contentHandler.endPrefixMapping()
0877: endPrefixMapping(namespaces, previouslyDeclaredNamespaces);
0878: }
0879:
0880: /**
0881: * <p>
0882: * This will invoke the <code>ContentHandler.startPrefixMapping</code>
0883: * callback
0884: * when a new namespace is encountered in the <code>Document</code>.
0885: * </p>
0886: *
0887: * @param element <code>Element</code> used in callbacks.
0888: * @param namespaces <code>List</code> stack of Namespaces in scope.
0889: *
0890: * @return <code>Attributes</code> declaring the namespaces local to
0891: * <code>element</code> or <code>null</code>.
0892: */
0893: private Attributes startPrefixMapping(Element element,
0894: NamespaceStack namespaces) throws JDOMException {
0895: AttributesImpl nsAtts = null; // The namespaces as xmlns attributes
0896:
0897: Namespace ns = element.getNamespace();
0898: if (ns != Namespace.XML_NAMESPACE) {
0899: String prefix = ns.getPrefix();
0900: String uri = namespaces.getURI(prefix);
0901: if (!ns.getURI().equals(uri)) {
0902: namespaces.push(ns);
0903: nsAtts = this .addNsAttribute(nsAtts, ns);
0904: try {
0905: contentHandler.startPrefixMapping(prefix, ns
0906: .getURI());
0907: } catch (SAXException se) {
0908: throw new JDOMException(
0909: "Exception in startPrefixMapping", se);
0910: }
0911: }
0912: }
0913:
0914: // Fire additional namespace declarations
0915: List additionalNamespaces = element.getAdditionalNamespaces();
0916: if (additionalNamespaces != null) {
0917: Iterator itr = additionalNamespaces.iterator();
0918: while (itr.hasNext()) {
0919: ns = (Namespace) itr.next();
0920: String prefix = ns.getPrefix();
0921: String uri = namespaces.getURI(prefix);
0922: if (!ns.getURI().equals(uri)) {
0923: namespaces.push(ns);
0924: nsAtts = this .addNsAttribute(nsAtts, ns);
0925: try {
0926: contentHandler.startPrefixMapping(prefix, ns
0927: .getURI());
0928: } catch (SAXException se) {
0929: throw new JDOMException(
0930: "Exception in startPrefixMapping", se);
0931: }
0932: }
0933: }
0934: }
0935: return nsAtts;
0936: }
0937:
0938: /**
0939: * <p>
0940: * This will invoke the <code>endPrefixMapping</code> callback in the
0941: * <code>ContentHandler</code> when a namespace is goes out of scope
0942: * in the <code>Document</code>.
0943: * </p>
0944: *
0945: * @param namespaces <code>List</code> stack of Namespaces in scope.
0946: * @param previouslyDeclaredNamespaces number of previously declared
0947: * namespaces
0948: */
0949: private void endPrefixMapping(NamespaceStack namespaces,
0950: int previouslyDeclaredNamespaces) throws JDOMException {
0951: while (namespaces.size() > previouslyDeclaredNamespaces) {
0952: String prefix = namespaces.pop();
0953: try {
0954: contentHandler.endPrefixMapping(prefix);
0955: } catch (SAXException se) {
0956: throw new JDOMException(
0957: "Exception in endPrefixMapping", se);
0958: }
0959: }
0960: }
0961:
0962: /**
0963: * <p>
0964: * This will invoke the <code>startElement</code> callback
0965: * in the <code>ContentHandler</code>.
0966: * </p>
0967: *
0968: * @param element <code>Element</code> used in callbacks.
0969: * @param nsAtts <code>List</code> of namespaces to declare with
0970: * the element or <code>null</code>.
0971: */
0972: private void startElement(Element element, Attributes nsAtts)
0973: throws JDOMException {
0974: String namespaceURI = element.getNamespaceURI();
0975: String localName = element.getName();
0976: String rawName = element.getQualifiedName();
0977:
0978: // Allocate attribute list.
0979: AttributesImpl atts = (nsAtts != null) ? new AttributesImpl(
0980: nsAtts) : new AttributesImpl();
0981:
0982: List attributes = element.getAttributes();
0983: Iterator i = attributes.iterator();
0984: while (i.hasNext()) {
0985: Attribute a = (Attribute) i.next();
0986: atts.addAttribute(a.getNamespaceURI(), a.getName(), a
0987: .getQualifiedName(), getAttributeTypeName(a
0988: .getAttributeType()), a.getValue());
0989: }
0990:
0991: try {
0992: contentHandler.startElement(namespaceURI, localName,
0993: rawName, atts);
0994: } catch (SAXException se) {
0995: throw new JDOMException("Exception in startElement", se);
0996: }
0997: }
0998:
0999: /**
1000: * <p>
1001: * This will invoke the <code>endElement</code> callback
1002: * in the <code>ContentHandler</code>.
1003: * </p>
1004: *
1005: * @param element <code>Element</code> used in callbacks.
1006: */
1007: private void endElement(Element element) throws JDOMException {
1008: String namespaceURI = element.getNamespaceURI();
1009: String localName = element.getName();
1010: String rawName = element.getQualifiedName();
1011:
1012: try {
1013: contentHandler.endElement(namespaceURI, localName, rawName);
1014: } catch (SAXException se) {
1015: throw new JDOMException("Exception in endElement", se);
1016: }
1017: }
1018:
1019: /**
1020: * <p>
1021: * This will invoke the callbacks for the content of an element.
1022: * </p>
1023: *
1024: * @param content element content as a <code>List</code> of nodes.
1025: * @param namespaces <code>List</code> stack of Namespaces in scope.
1026: */
1027: private void elementContent(List content, NamespaceStack namespaces)
1028: throws JDOMException {
1029: for (Iterator i = content.iterator(); i.hasNext();) {
1030: Object obj = i.next();
1031:
1032: if (obj instanceof Content) {
1033: this .elementContent((Content) obj, namespaces);
1034: } else {
1035: // NotElement a valid element child. This could happen with
1036: // application-provided lists which may contain non
1037: // JDOM objects.
1038: handleError(new JDOMException(
1039: "Invalid element content: " + obj));
1040: }
1041: }
1042: }
1043:
1044: /**
1045: * <p>
1046: * This will invoke the callbacks for the content of an element.
1047: * </p>
1048: *
1049: * @param node a <code>Content</code> node.
1050: * @param namespaces <code>List</code> stack of Namespaces in scope.
1051: */
1052: private void elementContent(Content node, NamespaceStack namespaces)
1053: throws JDOMException {
1054: // update locator
1055: locator.setNode(node);
1056:
1057: if (node instanceof Element) {
1058: element((Element) node, namespaces);
1059: } else if (node instanceof CDATA) {
1060: cdata(((CDATA) node).getText());
1061: } else if (node instanceof Text) {
1062: // contentHandler.characters()
1063: characters(((Text) node).getText());
1064: } else if (node instanceof ProcessingInstruction) {
1065: // contentHandler.processingInstruction()
1066: processingInstruction((ProcessingInstruction) node);
1067: } else if (node instanceof Comment) {
1068: // lexicalHandler.comment()
1069: comment(((Comment) node).getText());
1070: } else if (node instanceof EntityRef) {
1071: // contentHandler.skippedEntity()
1072: entityRef((EntityRef) node);
1073: } else {
1074: // NotElement a valid element child. This could happen with
1075: // application-provided lists which may contain non
1076: // JDOM objects.
1077: handleError(new JDOMException("Invalid element content: "
1078: + node));
1079: }
1080: }
1081:
1082: /**
1083: * <p>
1084: * This will be called for each chunk of CDATA section encountered.
1085: * </p>
1086: *
1087: * @param cdataText all text in the CDATA section, including whitespace.
1088: */
1089: private void cdata(String cdataText) throws JDOMException {
1090: try {
1091: if (lexicalHandler != null) {
1092: lexicalHandler.startCDATA();
1093: characters(cdataText);
1094: lexicalHandler.endCDATA();
1095: } else {
1096: characters(cdataText);
1097: }
1098: } catch (SAXException se) {
1099: throw new JDOMException("Exception in CDATA", se);
1100: }
1101: }
1102:
1103: /**
1104: * <p>
1105: * This will be called for each chunk of character data encountered.
1106: * </p>
1107: *
1108: * @param elementText all text in an element, including whitespace.
1109: */
1110: private void characters(String elementText) throws JDOMException {
1111: char[] c = elementText.toCharArray();
1112: try {
1113: contentHandler.characters(c, 0, c.length);
1114: } catch (SAXException se) {
1115: throw new JDOMException("Exception in characters", se);
1116: }
1117: }
1118:
1119: /**
1120: * <p>
1121: * This will be called for each chunk of comment data encontered.
1122: * </p>
1123: *
1124: * @param commentText all text in a comment, including whitespace.
1125: */
1126: private void comment(String commentText) throws JDOMException {
1127: if (lexicalHandler != null) {
1128: char[] c = commentText.toCharArray();
1129: try {
1130: lexicalHandler.comment(c, 0, c.length);
1131: } catch (SAXException se) {
1132: throw new JDOMException("Exception in comment", se);
1133: }
1134: }
1135: }
1136:
1137: /**
1138: * <p>
1139: * This will invoke the <code>ContentHandler.skippedEntity</code>
1140: * callback when an entity reference is encountered.
1141: * </p>
1142: *
1143: * @param entity <code>EntityRef</code>.
1144: */
1145: private void entityRef(EntityRef entity) throws JDOMException {
1146: if (entity != null) {
1147: try {
1148: // No need to worry about appending a '%' character as
1149: // we do not support parameter entities
1150: contentHandler.skippedEntity(entity.getName());
1151: } catch (SAXException se) {
1152: throw new JDOMException("Exception in entityRef", se);
1153: }
1154: }
1155: }
1156:
1157: /**
1158: * <p>
1159: * Appends a namespace declaration in the form of a xmlns attribute to
1160: * an attribute list, crerating this latter if needed.
1161: * </p>
1162: *
1163: * @param atts <code>AttributeImpl</code> where to add the attribute.
1164: * @param ns <code>Namespace</code> the namespace to declare.
1165: *
1166: * @return <code>AttributeImpl</code> the updated attribute list.
1167: */
1168: private AttributesImpl addNsAttribute(AttributesImpl atts,
1169: Namespace ns) {
1170: if (this .declareNamespaces) {
1171: if (atts == null) {
1172: atts = new AttributesImpl();
1173: }
1174: atts.addAttribute("", // namespace
1175: "", // local name
1176: "xmlns:" + ns.getPrefix(), // qualified name
1177: "CDATA", // type
1178: ns.getURI()); // value
1179: }
1180: return atts;
1181: }
1182:
1183: /**
1184: * <p>
1185: * Returns the SAX 2.0 attribute type string from the type of
1186: * a JDOM Attribute.
1187: * </p>
1188: *
1189: * @param type <code>int</code> the type of the JDOM attribute.
1190: *
1191: * @return <code>String</code> the SAX 2.0 attribute type string.
1192: *
1193: * @see org.jdom.Attribute#getAttributeType
1194: * @see org.xml.sax.Attributes#getType
1195: */
1196: private static String getAttributeTypeName(int type) {
1197: if ((type < 0) || (type >= attrTypeToNameMap.length)) {
1198: type = Attribute.UNDECLARED_TYPE;
1199: }
1200: return attrTypeToNameMap[type];
1201: }
1202:
1203: /**
1204: * <p>
1205: * Notifies the registered {@link ErrorHandler SAX error handler}
1206: * (if any) of an input processing error. The error handler can
1207: * choose to absorb the error and let the processing continue.
1208: * </p>
1209: *
1210: * @param exception <code>JDOMException</code> containing the
1211: * error information; will be wrapped in a
1212: * {@link SAXParseException} when reported to
1213: * the SAX error handler.
1214: *
1215: * @throws JDOMException if no error handler has been registered
1216: * or if the error handler fired a
1217: * {@link SAXException}.
1218: */
1219: private void handleError(JDOMException exception)
1220: throws JDOMException {
1221: if (errorHandler != null) {
1222: try {
1223: errorHandler.error(new SAXParseException(exception
1224: .getMessage(), null, exception));
1225: } catch (SAXException se) {
1226: if (se.getException() instanceof JDOMException) {
1227: throw (JDOMException) (se.getException());
1228: } else {
1229: throw new JDOMException(se.getMessage(), se);
1230: }
1231: }
1232: } else {
1233: throw exception;
1234: }
1235: }
1236:
1237: /**
1238: * <p>
1239: * Creates a SAX XMLReader.
1240: * </p>
1241: *
1242: * @return <code>XMLReader</code> a SAX2 parser.
1243: *
1244: * @throws Exception if no parser can be created.
1245: */
1246: protected XMLReader createParser() throws Exception {
1247: XMLReader parser = null;
1248:
1249: // Try using JAXP...
1250: // Note we need JAXP 1.1, and if JAXP 1.0 is all that's
1251: // available then the getXMLReader call fails and we skip
1252: // to the hard coded default parser
1253: try {
1254: Class factoryClass = Class
1255: .forName("javax.xml.parsers.SAXParserFactory");
1256:
1257: // factory = SAXParserFactory.newInstance();
1258: Method newParserInstance = factoryClass.getMethod(
1259: "newInstance", null);
1260: Object factory = newParserInstance.invoke(null, null);
1261:
1262: // jaxpParser = factory.newSAXParser();
1263: Method newSAXParser = factoryClass.getMethod(
1264: "newSAXParser", null);
1265: Object jaxpParser = newSAXParser.invoke(factory, null);
1266:
1267: // parser = jaxpParser.getXMLReader();
1268: Class parserClass = jaxpParser.getClass();
1269: Method getXMLReader = parserClass.getMethod("getXMLReader",
1270: null);
1271: parser = (XMLReader) getXMLReader.invoke(jaxpParser, null);
1272: } catch (ClassNotFoundException e) {
1273: //e.printStackTrace();
1274: } catch (InvocationTargetException e) {
1275: //e.printStackTrace();
1276: } catch (NoSuchMethodException e) {
1277: //e.printStackTrace();
1278: } catch (IllegalAccessException e) {
1279: //e.printStackTrace();
1280: }
1281:
1282: // Check to see if we got a parser yet, if not, try to use a
1283: // hard coded default
1284: if (parser == null) {
1285: parser = XMLReaderFactory
1286: .createXMLReader("org.apache.xerces.parsers.SAXParser");
1287: }
1288: return parser;
1289: }
1290:
1291: /**
1292: * <p>
1293: * This will create a SAX XMLReader capable of parsing a DTD and
1294: * configure it so that the DTD parsing events are routed to the
1295: * handlers registered onto this SAXOutputter.
1296: * </p>
1297: *
1298: * @return <code>XMLReader</code> a SAX2 parser.
1299: *
1300: * @throws JDOMException if no parser can be created.
1301: */
1302: private XMLReader createDTDParser() throws JDOMException {
1303: XMLReader parser = null;
1304:
1305: // Get a parser instance
1306: try {
1307: parser = createParser();
1308: } catch (Exception ex1) {
1309: throw new JDOMException("Error in SAX parser allocation",
1310: ex1);
1311: }
1312:
1313: // Register handlers
1314: if (this .getDTDHandler() != null) {
1315: parser.setDTDHandler(this .getDTDHandler());
1316: }
1317: if (this .getEntityResolver() != null) {
1318: parser.setEntityResolver(this .getEntityResolver());
1319: }
1320: if (this .getLexicalHandler() != null) {
1321: try {
1322: parser.setProperty(LEXICAL_HANDLER_SAX_PROPERTY, this
1323: .getLexicalHandler());
1324: } catch (SAXException ex1) {
1325: try {
1326: parser.setProperty(LEXICAL_HANDLER_ALT_PROPERTY,
1327: this .getLexicalHandler());
1328: } catch (SAXException ex2) {
1329: // Forget it!
1330: }
1331: }
1332: }
1333: if (this .getDeclHandler() != null) {
1334: try {
1335: parser.setProperty(DECL_HANDLER_SAX_PROPERTY, this
1336: .getDeclHandler());
1337: } catch (SAXException ex1) {
1338: try {
1339: parser.setProperty(DECL_HANDLER_ALT_PROPERTY, this
1340: .getDeclHandler());
1341: } catch (SAXException ex2) {
1342: // Forget it!
1343: }
1344: }
1345: }
1346:
1347: // Absorb errors as much as possible, per Laurent
1348: parser.setErrorHandler(new DefaultHandler());
1349:
1350: return parser;
1351: }
1352:
1353: /**
1354: * Returns a JDOMLocator object referencing the node currently
1355: * being processed by this outputter. The returned object is a
1356: * snapshot of the location information and can thus safely be
1357: * memorized for later use.
1358: * <p>
1359: * This method allows direct access to the location information
1360: * maintained by SAXOutputter without requiring to implement
1361: * <code>XMLFilter</code>. (In SAX, locators are only available
1362: * though the <code>ContentHandler</code> interface).</p>
1363: * <p>
1364: * Note that location information is only available while
1365: * SAXOutputter is outputting nodes. Hence this method should
1366: * only be used by objects taking part in the output processing
1367: * such as <code>ErrorHandler</code>s.
1368: *
1369: * @return a JDOMLocator object referencing the node currently
1370: * being processed or <code>null</code> if no output
1371: * operation is being performed.
1372: */
1373: public JDOMLocator getLocator() {
1374: return (locator != null) ? new JDOMLocator(locator) : null;
1375: }
1376: }
|