diff options
Diffstat (limited to 'libjava/classpath/gnu/xml/pipeline/DomConsumer.java')
-rw-r--r-- | libjava/classpath/gnu/xml/pipeline/DomConsumer.java | 1374 |
1 files changed, 687 insertions, 687 deletions
diff --git a/libjava/classpath/gnu/xml/pipeline/DomConsumer.java b/libjava/classpath/gnu/xml/pipeline/DomConsumer.java index 53f7f24..141f36e 100644 --- a/libjava/classpath/gnu/xml/pipeline/DomConsumer.java +++ b/libjava/classpath/gnu/xml/pipeline/DomConsumer.java @@ -1,4 +1,4 @@ -/* DomConsumer.java -- +/* DomConsumer.java -- Copyright (C) 1999,2000,2001 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -105,17 +105,17 @@ import org.w3c.dom.Text; */ public class DomConsumer implements EventConsumer { - private Class domImpl; + private Class domImpl; - private boolean hidingCDATA = true; - private boolean hidingComments = true; - private boolean hidingWhitespace = true; - private boolean hidingReferences = true; + private boolean hidingCDATA = true; + private boolean hidingComments = true; + private boolean hidingWhitespace = true; + private boolean hidingReferences = true; - private Handler handler; - private ErrorHandler errHandler; + private Handler handler; + private ErrorHandler errHandler; - private EventConsumer next; + private EventConsumer next; // FIXME: this can't be a generic pipeline stage just now, // since its input became a Class not a String (to be turned @@ -127,16 +127,16 @@ public class DomConsumer implements EventConsumer * of DOM when constructing its result value. * * @param impl class implementing {@link org.w3c.dom.Document Document} - * which publicly exposes a default constructor + * which publicly exposes a default constructor * * @exception SAXException when there is a problem creating an - * empty DOM document using the specified implementation + * empty DOM document using the specified implementation */ public DomConsumer (Class impl) throws SAXException { - domImpl = impl; - handler = new Handler (this); + domImpl = impl; + handler = new Handler (this); } /** @@ -148,22 +148,22 @@ public class DomConsumer implements EventConsumer */ protected void setHandler (Handler h) { - handler = h; + handler = h; } private Document emptyDocument () throws SAXException { - try { - return (Document) domImpl.newInstance (); - } catch (IllegalAccessException e) { - throw new SAXException ("can't access constructor: " - + e.getMessage ()); - } catch (InstantiationException e) { - throw new SAXException ("can't instantiate Document: " - + e.getMessage ()); - } + try { + return (Document) domImpl.newInstance (); + } catch (IllegalAccessException e) { + throw new SAXException ("can't access constructor: " + + e.getMessage ()); + } catch (InstantiationException e) { + throw new SAXException ("can't instantiate Document: " + + e.getMessage ()); + } } @@ -178,18 +178,18 @@ public class DomConsumer implements EventConsumer * copy of the input event stream, use a {@link TeeConsumer}. * * @param impl class implementing {@link org.w3c.dom.Document Document} - * which publicly exposes a default constructor + * which publicly exposes a default constructor * @param next receives a "replayed" sequence of parse events when - * the <em>endDocument</em> method is invoked. + * the <em>endDocument</em> method is invoked. * * @exception SAXException when there is a problem creating an - * empty DOM document using the specified DOM implementation + * empty DOM document using the specified DOM implementation */ public DomConsumer (Class impl, EventConsumer n) throws SAXException { - this (impl); - next = n; + this (impl); + next = n; } @@ -197,16 +197,16 @@ public class DomConsumer implements EventConsumer * Returns the document constructed from the preceding * sequence of events. This method should not be * used again until another sequence of events has been - * given to this EventConsumer. + * given to this EventConsumer. */ final public Document getDocument () { - return handler.clearDocument (); + return handler.clearDocument (); } public void setErrorHandler (ErrorHandler handler) { - errHandler = handler; + errHandler = handler; } @@ -219,8 +219,8 @@ public class DomConsumer implements EventConsumer * * @see #setHidingReferences */ - final public boolean isHidingReferences () - { return hidingReferences; } + final public boolean isHidingReferences () + { return hidingReferences; } /** * Controls whether the consumer will hide entity expansions, @@ -229,9 +229,9 @@ public class DomConsumer implements EventConsumer * @see #isHidingReferences * @param flag False if entity reference nodes will appear */ - final public void setHidingReferences (boolean flag) - { hidingReferences = flag; } - + final public void setHidingReferences (boolean flag) + { hidingReferences = flag; } + /** * Returns true if the consumer is hiding comments (the default), @@ -240,7 +240,7 @@ public class DomConsumer implements EventConsumer * @see #setHidingComments */ public final boolean isHidingComments () - { return hidingComments; } + { return hidingComments; } /** * Controls whether the consumer is hiding comments. @@ -248,7 +248,7 @@ public class DomConsumer implements EventConsumer * @see #isHidingComments */ public final void setHidingComments (boolean flag) - { hidingComments = flag; } + { hidingComments = flag; } /** @@ -259,7 +259,7 @@ public class DomConsumer implements EventConsumer * @see #setHidingWhitespace */ public final boolean isHidingWhitespace () - { return hidingWhitespace; } + { return hidingWhitespace; } /** * Controls whether the consumer hides ignorable whitespace @@ -267,7 +267,7 @@ public class DomConsumer implements EventConsumer * @see #isHidingComments */ public final void setHidingWhitespace (boolean flag) - { hidingWhitespace = flag; } + { hidingWhitespace = flag; } /** @@ -276,28 +276,28 @@ public class DomConsumer implements EventConsumer * * @see #setHidingCDATA */ - final public boolean isHidingCDATA () - { return hidingCDATA; } + final public boolean isHidingCDATA () + { return hidingCDATA; } /** * Controls whether the consumer will save CDATA boundaries. * * @see #isHidingCDATA * @param flag True to treat CDATA text differently from other - * text nodes + * text nodes */ - final public void setHidingCDATA (boolean flag) - { hidingCDATA = flag; } - + final public void setHidingCDATA (boolean flag) + { hidingCDATA = flag; } + /** Returns the document handler being used. */ final public ContentHandler getContentHandler () - { return handler; } + { return handler; } /** Returns the DTD handler being used. */ final public DTDHandler getDTDHandler () - { return handler; } + { return handler; } /** * Returns the lexical handler being used. @@ -306,11 +306,11 @@ public class DomConsumer implements EventConsumer final public Object getProperty (String id) throws SAXNotRecognizedException { - if ("http://xml.org/sax/properties/lexical-handler".equals (id)) - return handler; - if ("http://xml.org/sax/properties/declaration-handler".equals (id)) - return handler; - throw new SAXNotRecognizedException (id); + if ("http://xml.org/sax/properties/lexical-handler".equals (id)) + return handler; + if ("http://xml.org/sax/properties/declaration-handler".equals (id)) + return handler; + throw new SAXNotRecognizedException (id); } EventConsumer getNext () { return next; } @@ -320,7 +320,7 @@ public class DomConsumer implements EventConsumer /** * Class used to intercept various parsing events and use them to * populate a DOM document. Subclasses would typically know and use - * backdoors into specific DOM implementations, used to implement + * backdoors into specific DOM implementations, used to implement * DTD-related functionality. * * <p> Note that if this ever throws a DOMException (runtime exception) @@ -329,639 +329,639 @@ public class DomConsumer implements EventConsumer * accepted illegal input data). </p> */ public static class Handler - implements ContentHandler, LexicalHandler, - DTDHandler, DeclHandler + implements ContentHandler, LexicalHandler, + DTDHandler, DeclHandler { - protected DomConsumer consumer; - - private DOMImplementation impl; - private Document document; - private boolean isL2; - - private Locator locator; - private Node top; - private boolean inCDATA; - private boolean mergeCDATA; - private boolean inDTD; - private String currentEntity; - - private boolean recreatedAttrs; - private AttributesImpl attributes = new AttributesImpl (); - - /** - * Subclasses may use SAX2 events to provide additional - * behaviors in the resulting DOM. - */ - protected Handler (DomConsumer consumer) - throws SAXException - { - this.consumer = consumer; - document = consumer.emptyDocument (); - impl = document.getImplementation (); - isL2 = impl.hasFeature ("XML", "2.0"); - } - - private void fatal (String message, Exception x) - throws SAXException - { - SAXParseException e; - ErrorHandler errHandler = consumer.getErrorHandler (); - - if (locator == null) - e = new SAXParseException (message, null, null, -1, -1, x); - else - e = new SAXParseException (message, locator, x); - if (errHandler != null) - errHandler.fatalError (e); - throw e; - } - - /** - * Returns and forgets the document produced. If the handler is - * reused, a new document may be created. - */ - Document clearDocument () - { - Document retval = document; - document = null; - locator = null; - return retval; - } - - /** - * Returns the document under construction. - */ - protected Document getDocument () - { return document; } - - /** - * Returns the current node being populated. This is usually - * an Element or Document, but it might be an EntityReference - * node if some implementation-specific code knows how to put - * those into the result tree and later mark them as readonly. - */ - protected Node getTop () - { return top; } - - - // SAX1 - public void setDocumentLocator (Locator locator) - { - this.locator = locator; - } - - // SAX1 - public void startDocument () - throws SAXException - { - if (document == null) - try { - if (isL2) { - // couple to original implementation - document = impl.createDocument (null, "foo", null); - document.removeChild (document.getFirstChild ()); - } else { - document = consumer.emptyDocument (); - } - } catch (Exception e) { - fatal ("DOM create document", e); - } - top = document; - } - - // SAX1 - public void endDocument () - throws SAXException - { - try { - if (consumer.getNext () != null && document != null) { - DomParser parser = new DomParser (document); - - EventFilter.bind (parser, consumer.getNext ()); - parser.parse ("ignored"); - } - } finally { - top = null; - } - } - - // SAX1 - public void processingInstruction (String target, String data) - throws SAXException - { - // we can't create populated entity ref nodes using - // only public DOM APIs (they've got to be readonly) - if (currentEntity != null) - return; - - ProcessingInstruction pi; - - if (isL2 - // && consumer.isUsingNamespaces () - && target.indexOf (':') != -1) - namespaceError ( - "PI target name is namespace nonconformant: " - + target); - if (inDTD) - return; - pi = document.createProcessingInstruction (target, data); - top.appendChild (pi); - } - - /** - * Subclasses may overrride this method to provide a more efficient - * way to construct text nodes. - * Typically, copying the text into a single character array will - * be more efficient than doing that as well as allocating other - * needed for a String, including an internal StringBuffer. - * Those additional memory and CPU costs can be incurred later, - * if ever needed. - * Unfortunately the standard DOM factory APIs encourage those costs - * to be incurred early. - */ - protected Text createText ( - boolean isCDATA, - char ch [], - int start, - int length - ) { - String value = new String (ch, start, length); - - if (isCDATA) - return document.createCDATASection (value); - else - return document.createTextNode (value); - } - - // SAX1 - public void characters (char ch [], int start, int length) - throws SAXException - { - // we can't create populated entity ref nodes using - // only public DOM APIs (they've got to be readonly - // at creation time) - if (currentEntity != null) - return; - - Node lastChild = top.getLastChild (); - - // merge consecutive text or CDATA nodes if appropriate. - if (lastChild instanceof Text) { - if (consumer.isHidingCDATA () - // consecutive Text content ... always merge - || (!inCDATA - && !(lastChild instanceof CDATASection)) - // consecutive CDATASection content ... don't - // merge between sections, only within them - || (inCDATA && mergeCDATA - && lastChild instanceof CDATASection) - ) { - CharacterData last = (CharacterData) lastChild; - String value = new String (ch, start, length); - - last.appendData (value); - return; - } - } - if (inCDATA && !consumer.isHidingCDATA ()) { - top.appendChild (createText (true, ch, start, length)); - mergeCDATA = true; - } else - top.appendChild (createText (false, ch, start, length)); - } - - // SAX2 - public void skippedEntity (String name) - throws SAXException - { - // this callback is useless except to report errors, since - // we can't know if the ref was in content, within an - // attribute, within a declaration ... only one of those - // cases supports more intelligent action than a panic. - fatal ("skipped entity: " + name, null); - } - - // SAX2 - public void startPrefixMapping (String prefix, String uri) - throws SAXException - { - // reconstruct "xmlns" attributes deleted by all - // SAX2 parsers without "namespace-prefixes" = true - if ("".equals (prefix)) - attributes.addAttribute ("", "", "xmlns", - "CDATA", uri); - else - attributes.addAttribute ("", "", "xmlns:" + prefix, - "CDATA", uri); - recreatedAttrs = true; - } - - // SAX2 - public void endPrefixMapping (String prefix) - throws SAXException - { } - - // SAX2 - public void startElement ( - String uri, - String localName, - String qName, - Attributes atts - ) throws SAXException - { - // we can't create populated entity ref nodes using - // only public DOM APIs (they've got to be readonly) - if (currentEntity != null) - return; - - // parser discarded basic information; DOM tree isn't writable - // without massaging to assign prefixes to all nodes. - // the "NSFilter" class does that massaging. - if (qName.length () == 0) - qName = localName; - - - Element element; - int length = atts.getLength (); - - if (!isL2) { - element = document.createElement (qName); - - // first the explicit attributes ... - length = atts.getLength (); - for (int i = 0; i < length; i++) - element.setAttribute (atts.getQName (i), - atts.getValue (i)); - // ... then any recreated ones (DOM deletes duplicates) - if (recreatedAttrs) { - recreatedAttrs = false; - length = attributes.getLength (); - for (int i = 0; i < length; i++) - element.setAttribute (attributes.getQName (i), - attributes.getValue (i)); - attributes.clear (); - } - - top.appendChild (element); - top = element; - return; - } - - // For an L2 DOM when namespace use is enabled, use - // createElementNS/createAttributeNS except when - // (a) it's an element in the default namespace, or - // (b) it's an attribute with no prefix - String namespace; - - if (localName.length () != 0) - namespace = (uri.length () == 0) ? null : uri; - else - namespace = getNamespace (getPrefix (qName), atts); - - if (namespace == null) - element = document.createElement (qName); - else - element = document.createElementNS (namespace, qName); - - populateAttributes (element, atts); - if (recreatedAttrs) { - recreatedAttrs = false; - // ... DOM deletes any duplicates - populateAttributes (element, attributes); - attributes.clear (); - } - - top.appendChild (element); - top = element; - } - - final static String xmlnsURI = "http://www.w3.org/2000/xmlns/"; - - private void populateAttributes (Element element, Attributes attrs) - throws SAXParseException - { - int length = attrs.getLength (); - - for (int i = 0; i < length; i++) { - String type = attrs.getType (i); - String value = attrs.getValue (i); - String name = attrs.getQName (i); - String local = attrs.getLocalName (i); - String uri = attrs.getURI (i); - - // parser discarded basic information, DOM tree isn't writable - if (name.length () == 0) - name = local; - - // all attribute types other than these three may not - // contain scoped names... enumerated attributes get - // reported as NMTOKEN, except for NOTATION values - if (!("CDATA".equals (type) - || "NMTOKEN".equals (type) - || "NMTOKENS".equals (type))) { - if (value.indexOf (':') != -1) { - namespaceError ( - "namespace nonconformant attribute value: " - + "<" + element.getNodeName () - + " " + name + "='" + value + "' ...>"); - } - } - - // xmlns="" is legal (undoes default NS) - // xmlns:foo="" is illegal - String prefix = getPrefix (name); - String namespace; - - if ("xmlns".equals (prefix)) { - if ("".equals (value)) - namespaceError ("illegal null namespace decl, " + name); - namespace = xmlnsURI; - } else if ("xmlns".equals (name)) - namespace = xmlnsURI; - - else if (prefix == null) - namespace = null; - else if (!"".equals(uri) && uri.length () != 0) - namespace = uri; - else - namespace = getNamespace (prefix, attrs); - - if (namespace == null) - element.setAttribute (name, value); - else - element.setAttributeNS (namespace, name, value); - } - } - - private String getPrefix (String name) - { - int temp; - - if ((temp = name.indexOf (':')) > 0) - return name.substring (0, temp); - return null; - } - - // used with SAX1-level parser output - private String getNamespace (String prefix, Attributes attrs) - throws SAXParseException - { - String namespace; - String decl; - - // defaulting - if (prefix == null) { - decl = "xmlns"; - namespace = attrs.getValue (decl); - if ("".equals (namespace)) - return null; - else if (namespace != null) - return namespace; - - // "xmlns" is like a keyword - // ... according to the Namespace REC, but DOM L2 CR2+ - // and Infoset violate that by assigning a namespace. - // that conflict is resolved elsewhere. - } else if ("xmlns".equals (prefix)) - return null; - - // "xml" prefix is fixed - else if ("xml".equals (prefix)) - return "http://www.w3.org/XML/1998/namespace"; - - // otherwise, expect a declaration - else { - decl = "xmlns:" + prefix; - namespace = attrs.getValue (decl); - } - - // if we found a local declaration, great - if (namespace != null) - return namespace; - - - // ELSE ... search up the tree we've been building - for (Node n = top; - n != null && n.getNodeType () != Node.DOCUMENT_NODE; - n = n.getParentNode ()) { - if (n.getNodeType () == Node.ENTITY_REFERENCE_NODE) - continue; - Element e = (Element) n; - Attr attr = e.getAttributeNode (decl); - if (attr != null) - return attr.getNodeValue (); - } - // see above re "xmlns" as keyword - if ("xmlns".equals (decl)) - return null; - - namespaceError ("Undeclared namespace prefix: " + prefix); - return null; - } - - // SAX2 - public void endElement (String uri, String localName, String qName) - throws SAXException - { - // we can't create populated entity ref nodes using - // only public DOM APIs (they've got to be readonly) - if (currentEntity != null) - return; - - top = top.getParentNode (); - } - - // SAX1 (mandatory reporting if validating) - public void ignorableWhitespace (char ch [], int start, int length) - throws SAXException - { - if (consumer.isHidingWhitespace ()) - return; - characters (ch, start, length); - } - - // SAX2 lexical event - public void startCDATA () - throws SAXException - { - inCDATA = true; - // true except for the first fragment of a cdata section - mergeCDATA = false; - } - - // SAX2 lexical event - public void endCDATA () - throws SAXException - { - inCDATA = false; - } - - // SAX2 lexical event - // - // this SAX2 callback merges two unrelated things: - // - Declaration of the root element type ... belongs with - // the other DTD declaration methods, NOT HERE. - // - IDs for the optional external subset ... belongs here - // with other lexical information. - // - // ...and it doesn't include the internal DTD subset, desired - // both to support DOM L2 and to enable "pass through" processing - // - public void startDTD (String name, String publicId, String SystemId) - throws SAXException - { - // need to filter out comments and PIs within the DTD - inDTD = true; - } - - // SAX2 lexical event - public void endDTD () - throws SAXException - { - inDTD = false; - } - - // SAX2 lexical event - public void comment (char ch [], int start, int length) - throws SAXException - { - Node comment; - - // we can't create populated entity ref nodes using - // only public DOM APIs (they've got to be readonly) - if (consumer.isHidingComments () - || inDTD - || currentEntity != null) - return; - comment = document.createComment (new String (ch, start, length)); - top.appendChild (comment); - } - - /** - * May be overridden by subclasses to return true, indicating - * that entity reference nodes can be populated and then made - * read-only. - */ - public boolean canPopulateEntityRefs () - { return false; } - - // SAX2 lexical event - public void startEntity (String name) - throws SAXException - { - // are we ignoring what would be contents of an - // entity ref, since we can't populate it? - if (currentEntity != null) - return; - - // Are we hiding all entity boundaries? - if (consumer.isHidingReferences ()) - return; - - // SAX2 shows parameter entities; DOM hides them - if (name.charAt (0) == '%' || "[dtd]".equals (name)) - return; - - // Since we can't create a populated entity ref node in any - // standard way, we create an unpopulated one. - EntityReference ref = document.createEntityReference (name); - top.appendChild (ref); - top = ref; - - // ... allowing subclasses to populate them - if (!canPopulateEntityRefs ()) - currentEntity = name; - } - - // SAX2 lexical event - public void endEntity (String name) - throws SAXException - { - if (name.charAt (0) == '%' || "[dtd]".equals (name)) - return; - if (name.equals (currentEntity)) - currentEntity = null; - if (!consumer.isHidingReferences ()) - top = top.getParentNode (); - } - - - // SAX1 DTD event - public void notationDecl ( - String name, - String publicId, String SystemId - ) throws SAXException - { - /* IGNORE -- no public DOM API lets us store these - * into the doctype node - */ - } - - // SAX1 DTD event - public void unparsedEntityDecl ( - String name, - String publicId, String SystemId, - String notationName - ) throws SAXException - { - /* IGNORE -- no public DOM API lets us store these - * into the doctype node - */ - } - - // SAX2 declaration event - public void elementDecl (String name, String model) - throws SAXException - { - /* IGNORE -- no content model support in DOM L2 */ - } - - // SAX2 declaration event - public void attributeDecl ( - String eName, - String aName, - String type, - String mode, - String value - ) throws SAXException - { - /* IGNORE -- no attribute model support in DOM L2 */ - } - - // SAX2 declaration event - public void internalEntityDecl (String name, String value) - throws SAXException - { - /* IGNORE -- no public DOM API lets us store these - * into the doctype node - */ - } - - // SAX2 declaration event - public void externalEntityDecl ( - String name, - String publicId, - String SystemId - ) throws SAXException - { - /* IGNORE -- no public DOM API lets us store these - * into the doctype node - */ - } - - // - // These really should offer the option of nonfatal handling, - // like other validity errors, though that would cause major - // chaos in the DOM data structures. DOM is already spec'd - // to treat many of these as fatal, so this is consistent. - // - private void namespaceError (String description) - throws SAXParseException - { - SAXParseException err; - - err = new SAXParseException (description, locator); - throw err; - } + protected DomConsumer consumer; + + private DOMImplementation impl; + private Document document; + private boolean isL2; + + private Locator locator; + private Node top; + private boolean inCDATA; + private boolean mergeCDATA; + private boolean inDTD; + private String currentEntity; + + private boolean recreatedAttrs; + private AttributesImpl attributes = new AttributesImpl (); + + /** + * Subclasses may use SAX2 events to provide additional + * behaviors in the resulting DOM. + */ + protected Handler (DomConsumer consumer) + throws SAXException + { + this.consumer = consumer; + document = consumer.emptyDocument (); + impl = document.getImplementation (); + isL2 = impl.hasFeature ("XML", "2.0"); + } + + private void fatal (String message, Exception x) + throws SAXException + { + SAXParseException e; + ErrorHandler errHandler = consumer.getErrorHandler (); + + if (locator == null) + e = new SAXParseException (message, null, null, -1, -1, x); + else + e = new SAXParseException (message, locator, x); + if (errHandler != null) + errHandler.fatalError (e); + throw e; + } + + /** + * Returns and forgets the document produced. If the handler is + * reused, a new document may be created. + */ + Document clearDocument () + { + Document retval = document; + document = null; + locator = null; + return retval; + } + + /** + * Returns the document under construction. + */ + protected Document getDocument () + { return document; } + + /** + * Returns the current node being populated. This is usually + * an Element or Document, but it might be an EntityReference + * node if some implementation-specific code knows how to put + * those into the result tree and later mark them as readonly. + */ + protected Node getTop () + { return top; } + + + // SAX1 + public void setDocumentLocator (Locator locator) + { + this.locator = locator; + } + + // SAX1 + public void startDocument () + throws SAXException + { + if (document == null) + try { + if (isL2) { + // couple to original implementation + document = impl.createDocument (null, "foo", null); + document.removeChild (document.getFirstChild ()); + } else { + document = consumer.emptyDocument (); + } + } catch (Exception e) { + fatal ("DOM create document", e); + } + top = document; + } + + // SAX1 + public void endDocument () + throws SAXException + { + try { + if (consumer.getNext () != null && document != null) { + DomParser parser = new DomParser (document); + + EventFilter.bind (parser, consumer.getNext ()); + parser.parse ("ignored"); + } + } finally { + top = null; + } + } + + // SAX1 + public void processingInstruction (String target, String data) + throws SAXException + { + // we can't create populated entity ref nodes using + // only public DOM APIs (they've got to be readonly) + if (currentEntity != null) + return; + + ProcessingInstruction pi; + + if (isL2 + // && consumer.isUsingNamespaces () + && target.indexOf (':') != -1) + namespaceError ( + "PI target name is namespace nonconformant: " + + target); + if (inDTD) + return; + pi = document.createProcessingInstruction (target, data); + top.appendChild (pi); + } + + /** + * Subclasses may overrride this method to provide a more efficient + * way to construct text nodes. + * Typically, copying the text into a single character array will + * be more efficient than doing that as well as allocating other + * needed for a String, including an internal StringBuffer. + * Those additional memory and CPU costs can be incurred later, + * if ever needed. + * Unfortunately the standard DOM factory APIs encourage those costs + * to be incurred early. + */ + protected Text createText ( + boolean isCDATA, + char ch [], + int start, + int length + ) { + String value = new String (ch, start, length); + + if (isCDATA) + return document.createCDATASection (value); + else + return document.createTextNode (value); + } + + // SAX1 + public void characters (char ch [], int start, int length) + throws SAXException + { + // we can't create populated entity ref nodes using + // only public DOM APIs (they've got to be readonly + // at creation time) + if (currentEntity != null) + return; + + Node lastChild = top.getLastChild (); + + // merge consecutive text or CDATA nodes if appropriate. + if (lastChild instanceof Text) { + if (consumer.isHidingCDATA () + // consecutive Text content ... always merge + || (!inCDATA + && !(lastChild instanceof CDATASection)) + // consecutive CDATASection content ... don't + // merge between sections, only within them + || (inCDATA && mergeCDATA + && lastChild instanceof CDATASection) + ) { + CharacterData last = (CharacterData) lastChild; + String value = new String (ch, start, length); + + last.appendData (value); + return; + } + } + if (inCDATA && !consumer.isHidingCDATA ()) { + top.appendChild (createText (true, ch, start, length)); + mergeCDATA = true; + } else + top.appendChild (createText (false, ch, start, length)); + } + + // SAX2 + public void skippedEntity (String name) + throws SAXException + { + // this callback is useless except to report errors, since + // we can't know if the ref was in content, within an + // attribute, within a declaration ... only one of those + // cases supports more intelligent action than a panic. + fatal ("skipped entity: " + name, null); + } + + // SAX2 + public void startPrefixMapping (String prefix, String uri) + throws SAXException + { + // reconstruct "xmlns" attributes deleted by all + // SAX2 parsers without "namespace-prefixes" = true + if ("".equals (prefix)) + attributes.addAttribute ("", "", "xmlns", + "CDATA", uri); + else + attributes.addAttribute ("", "", "xmlns:" + prefix, + "CDATA", uri); + recreatedAttrs = true; + } + + // SAX2 + public void endPrefixMapping (String prefix) + throws SAXException + { } + + // SAX2 + public void startElement ( + String uri, + String localName, + String qName, + Attributes atts + ) throws SAXException + { + // we can't create populated entity ref nodes using + // only public DOM APIs (they've got to be readonly) + if (currentEntity != null) + return; + + // parser discarded basic information; DOM tree isn't writable + // without massaging to assign prefixes to all nodes. + // the "NSFilter" class does that massaging. + if (qName.length () == 0) + qName = localName; + + + Element element; + int length = atts.getLength (); + + if (!isL2) { + element = document.createElement (qName); + + // first the explicit attributes ... + length = atts.getLength (); + for (int i = 0; i < length; i++) + element.setAttribute (atts.getQName (i), + atts.getValue (i)); + // ... then any recreated ones (DOM deletes duplicates) + if (recreatedAttrs) { + recreatedAttrs = false; + length = attributes.getLength (); + for (int i = 0; i < length; i++) + element.setAttribute (attributes.getQName (i), + attributes.getValue (i)); + attributes.clear (); + } + + top.appendChild (element); + top = element; + return; + } + + // For an L2 DOM when namespace use is enabled, use + // createElementNS/createAttributeNS except when + // (a) it's an element in the default namespace, or + // (b) it's an attribute with no prefix + String namespace; + + if (localName.length () != 0) + namespace = (uri.length () == 0) ? null : uri; + else + namespace = getNamespace (getPrefix (qName), atts); + + if (namespace == null) + element = document.createElement (qName); + else + element = document.createElementNS (namespace, qName); + + populateAttributes (element, atts); + if (recreatedAttrs) { + recreatedAttrs = false; + // ... DOM deletes any duplicates + populateAttributes (element, attributes); + attributes.clear (); + } + + top.appendChild (element); + top = element; + } + + final static String xmlnsURI = "http://www.w3.org/2000/xmlns/"; + + private void populateAttributes (Element element, Attributes attrs) + throws SAXParseException + { + int length = attrs.getLength (); + + for (int i = 0; i < length; i++) { + String type = attrs.getType (i); + String value = attrs.getValue (i); + String name = attrs.getQName (i); + String local = attrs.getLocalName (i); + String uri = attrs.getURI (i); + + // parser discarded basic information, DOM tree isn't writable + if (name.length () == 0) + name = local; + + // all attribute types other than these three may not + // contain scoped names... enumerated attributes get + // reported as NMTOKEN, except for NOTATION values + if (!("CDATA".equals (type) + || "NMTOKEN".equals (type) + || "NMTOKENS".equals (type))) { + if (value.indexOf (':') != -1) { + namespaceError ( + "namespace nonconformant attribute value: " + + "<" + element.getNodeName () + + " " + name + "='" + value + "' ...>"); + } + } + + // xmlns="" is legal (undoes default NS) + // xmlns:foo="" is illegal + String prefix = getPrefix (name); + String namespace; + + if ("xmlns".equals (prefix)) { + if ("".equals (value)) + namespaceError ("illegal null namespace decl, " + name); + namespace = xmlnsURI; + } else if ("xmlns".equals (name)) + namespace = xmlnsURI; + + else if (prefix == null) + namespace = null; + else if (!"".equals(uri) && uri.length () != 0) + namespace = uri; + else + namespace = getNamespace (prefix, attrs); + + if (namespace == null) + element.setAttribute (name, value); + else + element.setAttributeNS (namespace, name, value); + } + } + + private String getPrefix (String name) + { + int temp; + + if ((temp = name.indexOf (':')) > 0) + return name.substring (0, temp); + return null; + } + + // used with SAX1-level parser output + private String getNamespace (String prefix, Attributes attrs) + throws SAXParseException + { + String namespace; + String decl; + + // defaulting + if (prefix == null) { + decl = "xmlns"; + namespace = attrs.getValue (decl); + if ("".equals (namespace)) + return null; + else if (namespace != null) + return namespace; + + // "xmlns" is like a keyword + // ... according to the Namespace REC, but DOM L2 CR2+ + // and Infoset violate that by assigning a namespace. + // that conflict is resolved elsewhere. + } else if ("xmlns".equals (prefix)) + return null; + + // "xml" prefix is fixed + else if ("xml".equals (prefix)) + return "http://www.w3.org/XML/1998/namespace"; + + // otherwise, expect a declaration + else { + decl = "xmlns:" + prefix; + namespace = attrs.getValue (decl); + } + + // if we found a local declaration, great + if (namespace != null) + return namespace; + + + // ELSE ... search up the tree we've been building + for (Node n = top; + n != null && n.getNodeType () != Node.DOCUMENT_NODE; + n = n.getParentNode ()) { + if (n.getNodeType () == Node.ENTITY_REFERENCE_NODE) + continue; + Element e = (Element) n; + Attr attr = e.getAttributeNode (decl); + if (attr != null) + return attr.getNodeValue (); + } + // see above re "xmlns" as keyword + if ("xmlns".equals (decl)) + return null; + + namespaceError ("Undeclared namespace prefix: " + prefix); + return null; + } + + // SAX2 + public void endElement (String uri, String localName, String qName) + throws SAXException + { + // we can't create populated entity ref nodes using + // only public DOM APIs (they've got to be readonly) + if (currentEntity != null) + return; + + top = top.getParentNode (); + } + + // SAX1 (mandatory reporting if validating) + public void ignorableWhitespace (char ch [], int start, int length) + throws SAXException + { + if (consumer.isHidingWhitespace ()) + return; + characters (ch, start, length); + } + + // SAX2 lexical event + public void startCDATA () + throws SAXException + { + inCDATA = true; + // true except for the first fragment of a cdata section + mergeCDATA = false; + } + + // SAX2 lexical event + public void endCDATA () + throws SAXException + { + inCDATA = false; + } + + // SAX2 lexical event + // + // this SAX2 callback merges two unrelated things: + // - Declaration of the root element type ... belongs with + // the other DTD declaration methods, NOT HERE. + // - IDs for the optional external subset ... belongs here + // with other lexical information. + // + // ...and it doesn't include the internal DTD subset, desired + // both to support DOM L2 and to enable "pass through" processing + // + public void startDTD (String name, String publicId, String SystemId) + throws SAXException + { + // need to filter out comments and PIs within the DTD + inDTD = true; + } + + // SAX2 lexical event + public void endDTD () + throws SAXException + { + inDTD = false; + } + + // SAX2 lexical event + public void comment (char ch [], int start, int length) + throws SAXException + { + Node comment; + + // we can't create populated entity ref nodes using + // only public DOM APIs (they've got to be readonly) + if (consumer.isHidingComments () + || inDTD + || currentEntity != null) + return; + comment = document.createComment (new String (ch, start, length)); + top.appendChild (comment); + } + + /** + * May be overridden by subclasses to return true, indicating + * that entity reference nodes can be populated and then made + * read-only. + */ + public boolean canPopulateEntityRefs () + { return false; } + + // SAX2 lexical event + public void startEntity (String name) + throws SAXException + { + // are we ignoring what would be contents of an + // entity ref, since we can't populate it? + if (currentEntity != null) + return; + + // Are we hiding all entity boundaries? + if (consumer.isHidingReferences ()) + return; + + // SAX2 shows parameter entities; DOM hides them + if (name.charAt (0) == '%' || "[dtd]".equals (name)) + return; + + // Since we can't create a populated entity ref node in any + // standard way, we create an unpopulated one. + EntityReference ref = document.createEntityReference (name); + top.appendChild (ref); + top = ref; + + // ... allowing subclasses to populate them + if (!canPopulateEntityRefs ()) + currentEntity = name; + } + + // SAX2 lexical event + public void endEntity (String name) + throws SAXException + { + if (name.charAt (0) == '%' || "[dtd]".equals (name)) + return; + if (name.equals (currentEntity)) + currentEntity = null; + if (!consumer.isHidingReferences ()) + top = top.getParentNode (); + } + + + // SAX1 DTD event + public void notationDecl ( + String name, + String publicId, String SystemId + ) throws SAXException + { + /* IGNORE -- no public DOM API lets us store these + * into the doctype node + */ + } + + // SAX1 DTD event + public void unparsedEntityDecl ( + String name, + String publicId, String SystemId, + String notationName + ) throws SAXException + { + /* IGNORE -- no public DOM API lets us store these + * into the doctype node + */ + } + + // SAX2 declaration event + public void elementDecl (String name, String model) + throws SAXException + { + /* IGNORE -- no content model support in DOM L2 */ + } + + // SAX2 declaration event + public void attributeDecl ( + String eName, + String aName, + String type, + String mode, + String value + ) throws SAXException + { + /* IGNORE -- no attribute model support in DOM L2 */ + } + + // SAX2 declaration event + public void internalEntityDecl (String name, String value) + throws SAXException + { + /* IGNORE -- no public DOM API lets us store these + * into the doctype node + */ + } + + // SAX2 declaration event + public void externalEntityDecl ( + String name, + String publicId, + String SystemId + ) throws SAXException + { + /* IGNORE -- no public DOM API lets us store these + * into the doctype node + */ + } + + // + // These really should offer the option of nonfatal handling, + // like other validity errors, though that would cause major + // chaos in the DOM data structures. DOM is already spec'd + // to treat many of these as fatal, so this is consistent. + // + private void namespaceError (String description) + throws SAXParseException + { + SAXParseException err; + + err = new SAXParseException (description, locator); + throw err; + } } } |