OSDN Git Service

libjava/classpath/ChangeLog.gcj:
[pf3gnuchains/gcc-fork.git] / libjava / classpath / gnu / xml / pipeline / DomConsumer.java
1 /* DomConsumer.java -- 
2    Copyright (C) 1999,2000,2001 Free Software Foundation, Inc.
3
4 This file is part of GNU Classpath.
5
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING.  If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
20
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library.  Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
25
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module.  An independent module is a module which is not derived from
33 or based on this library.  If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so.  If you do not wish to do so, delete this
36 exception statement from your version. */
37
38 package gnu.xml.pipeline;
39
40 import gnu.xml.util.DomParser;
41
42 import org.xml.sax.Attributes;
43 import org.xml.sax.ContentHandler;
44 import org.xml.sax.DTDHandler;
45 import org.xml.sax.ErrorHandler;
46 import org.xml.sax.Locator;
47 import org.xml.sax.SAXException;
48 import org.xml.sax.SAXNotRecognizedException;
49 import org.xml.sax.SAXParseException;
50 import org.xml.sax.ext.DeclHandler;
51 import org.xml.sax.ext.LexicalHandler;
52 import org.xml.sax.helpers.AttributesImpl;
53 import org.w3c.dom.Attr;
54 import org.w3c.dom.CDATASection;
55 import org.w3c.dom.CharacterData;
56 import org.w3c.dom.Document;
57 import org.w3c.dom.DOMImplementation;
58 import org.w3c.dom.Element;
59 import org.w3c.dom.EntityReference;
60 import org.w3c.dom.Node;
61 import org.w3c.dom.ProcessingInstruction;
62 import org.w3c.dom.Text;
63
64 /**
65  * This consumer builds a DOM Document from its input, acting either as a
66  * pipeline terminus or as an intermediate buffer.  When a document's worth
67  * of events has been delivered to this consumer, that document is read with
68  * a {@link DomParser} and sent to the next consumer.  It is also available
69  * as a read-once property.
70  *
71  * <p>The DOM tree is constructed as faithfully as possible.  There are some
72  * complications since a DOM should expose behaviors that can't be implemented
73  * without API backdoors into that DOM, and because some SAX parsers don't
74  * report all the information that DOM permits to be exposed.  The general
75  * problem areas involve information from the Document Type Declaration (DTD).
76  * DOM only represents a limited subset, but has some behaviors that depend
77  * on much deeper knowledge of a document's DTD.  You shouldn't have much to
78  * worry about unless you change handling of "noise" nodes from its default
79  * setting (which ignores them all); note if you use JAXP to populate your
80  * DOM trees, it wants to save "noise" nodes by default.  (Such nodes include
81  * ignorable whitespace, comments, entity references and CDATA boundaries.)
82  * Otherwise, your
83  * main worry will be if you use a SAX parser that doesn't flag ignorable
84  * whitespace unless it's validating (few don't).
85  *
86  * <p> The SAX2 events used as input must contain XML Names for elements
87  * and attributes, with original prefixes.  In SAX2,
88  * this is optional unless the "namespace-prefixes" parser feature is set.
89  * Moreover, many application components won't provide completely correct
90  * structures anyway.  <em>Before you convert a DOM to an output document,
91  * you should plan to postprocess it to create or repair such namespace
92  * information.</em> The {@link NSFilter} pipeline stage does such work.
93  *
94  * <p> <em>Note:  changes late in DOM L2 process made it impractical to
95  * attempt to create the DocumentType node in any implementation-neutral way,
96  * much less to populate it (L1 didn't support even creating such nodes).
97  * To create and populate such a node, subclass the inner
98  * {@link DomConsumer.Handler} class and teach it about the backdoors into
99  * whatever DOM implementation you want.  It's possible that some revised
100  * DOM API (L3?) will make this problem solvable again. </em>
101  *
102  * @see DomParser
103  *
104  * @author David Brownell
105  */
106 public class DomConsumer implements EventConsumer
107 {
108     private Class               domImpl;
109
110     private boolean             hidingCDATA = true;
111     private boolean             hidingComments = true;
112     private boolean             hidingWhitespace = true;
113     private boolean             hidingReferences = true;
114
115     private Handler             handler;
116     private ErrorHandler        errHandler;
117
118     private EventConsumer       next;
119
120     // FIXME:  this can't be a generic pipeline stage just now,
121     // since its input became a Class not a String (to be turned
122     // into a class, using the right class loader)
123
124
125     /**
126      * Configures this pipeline terminus to use the specified implementation
127      * of DOM when constructing its result value.
128      *
129      * @param impl class implementing {@link org.w3c.dom.Document Document}
130      *  which publicly exposes a default constructor
131      *
132      * @exception SAXException when there is a problem creating an
133      *  empty DOM document using the specified implementation
134      */
135     public DomConsumer (Class impl)
136     throws SAXException
137     {
138         domImpl = impl;
139         handler = new Handler (this);
140     }
141
142     /**
143      * This is the hook through which a subclass provides a handler
144      * which knows how to access DOM extensions, specific to some
145      * implementation, to record additional data in a DOM.
146      * Treat this as part of construction; don't call it except
147      * before (or between) parses.
148      */
149     protected void setHandler (Handler h)
150     {
151         handler = h;
152     }
153
154
155     private Document emptyDocument ()
156     throws SAXException
157     {
158         try {
159             return (Document) domImpl.newInstance ();
160         } catch (IllegalAccessException e) {
161             throw new SAXException ("can't access constructor: "
162                     + e.getMessage ());
163         } catch (InstantiationException e) {
164             throw new SAXException ("can't instantiate Document: "
165                     + e.getMessage ());
166         }
167     }
168
169
170     /**
171      * Configures this consumer as a buffer/filter, using the specified
172      * DOM implementation when constructing its result value.
173      *
174      * <p> This event consumer acts as a buffer and filter, in that it
175      * builds a DOM tree and then writes it out when <em>endDocument</em>
176      * is invoked.  Because of the limitations of DOM, much information
177      * will as a rule not be seen in that replay.  To get a full fidelity
178      * copy of the input event stream, use a {@link TeeConsumer}.
179      *
180      * @param impl class implementing {@link org.w3c.dom.Document Document}
181      *  which publicly exposes a default constructor
182      * @param next receives a "replayed" sequence of parse events when
183      *  the <em>endDocument</em> method is invoked.
184      *
185      * @exception SAXException when there is a problem creating an
186      *  empty DOM document using the specified DOM implementation
187      */
188     public DomConsumer (Class impl, EventConsumer n)
189     throws SAXException
190     {
191         this (impl);
192         next = n;
193     }
194
195
196     /**
197      * Returns the document constructed from the preceding
198      * sequence of events.  This method should not be
199      * used again until another sequence of events has been
200      * given to this EventConsumer.  
201      */
202     final public Document getDocument ()
203     {
204         return handler.clearDocument ();
205     }
206
207     public void setErrorHandler (ErrorHandler handler)
208     {
209         errHandler = handler;
210     }
211
212
213     /**
214      * Returns true if the consumer is hiding entity references nodes
215      * (the default), and false if EntityReference nodes should
216      * instead be created.  Such EntityReference nodes will normally be
217      * empty, unless an implementation arranges to populate them and then
218      * turn them back into readonly objects.
219      *
220      * @see #setHidingReferences
221      */
222     final public boolean        isHidingReferences ()
223         { return hidingReferences; }
224
225     /**
226      * Controls whether the consumer will hide entity expansions,
227      * or will instead mark them with entity reference nodes.
228      *
229      * @see #isHidingReferences
230      * @param flag False if entity reference nodes will appear
231      */
232     final public void           setHidingReferences (boolean flag)
233         { hidingReferences = flag; }
234     
235
236     /**
237      * Returns true if the consumer is hiding comments (the default),
238      * and false if they should be placed into the output document.
239      *
240      * @see #setHidingComments
241      */
242     public final boolean isHidingComments ()
243         { return hidingComments; }
244
245     /**
246      * Controls whether the consumer is hiding comments.
247      *
248      * @see #isHidingComments
249      */
250     public final void setHidingComments (boolean flag)
251         { hidingComments = flag; }
252
253
254     /**
255      * Returns true if the consumer is hiding ignorable whitespace
256      * (the default), and false if such whitespace should be placed
257      * into the output document as children of element nodes.
258      *
259      * @see #setHidingWhitespace
260      */
261     public final boolean isHidingWhitespace ()
262         { return hidingWhitespace; }
263
264     /**
265      * Controls whether the consumer hides ignorable whitespace
266      *
267      * @see #isHidingComments
268      */
269     public final void setHidingWhitespace (boolean flag)
270         { hidingWhitespace = flag; }
271
272
273     /**
274      * Returns true if the consumer is saving CDATA boundaries, or
275      * false (the default) otherwise.
276      *
277      * @see #setHidingCDATA
278      */
279     final public boolean        isHidingCDATA ()
280         { return hidingCDATA; }
281
282     /**
283      * Controls whether the consumer will save CDATA boundaries.
284      *
285      * @see #isHidingCDATA
286      * @param flag True to treat CDATA text differently from other
287      *  text nodes
288      */
289     final public void           setHidingCDATA (boolean flag)
290         { hidingCDATA = flag; }
291     
292
293
294     /** Returns the document handler being used. */
295     final public ContentHandler getContentHandler ()
296         { return handler; }
297
298     /** Returns the DTD handler being used. */
299     final public DTDHandler getDTDHandler ()
300         { return handler; }
301
302     /**
303      * Returns the lexical handler being used.
304      * (DOM construction can't really use declaration handlers.)
305      */
306     final public Object getProperty (String id)
307     throws SAXNotRecognizedException
308     {
309         if ("http://xml.org/sax/properties/lexical-handler".equals (id))
310             return handler;
311         if ("http://xml.org/sax/properties/declaration-handler".equals (id))
312             return handler;
313         throw new SAXNotRecognizedException (id);
314     }
315
316     EventConsumer getNext () { return next; }
317
318     ErrorHandler getErrorHandler () { return errHandler; }
319
320     /**
321      * Class used to intercept various parsing events and use them to
322      * populate a DOM document.  Subclasses would typically know and use
323      * backdoors into specific DOM implementations, used to implement 
324      * DTD-related functionality.
325      *
326      * <p> Note that if this ever throws a DOMException (runtime exception)
327      * that will indicate a bug in the DOM (e.g. doesn't support something
328      * per specification) or the parser (e.g. emitted an illegal name, or
329      * accepted illegal input data). </p>
330      */
331     public static class Handler
332         implements ContentHandler, LexicalHandler,
333             DTDHandler, DeclHandler
334     {
335         protected DomConsumer           consumer;
336
337         private DOMImplementation       impl;
338         private Document                document;
339         private boolean         isL2;
340
341         private Locator         locator;
342         private Node            top;
343         private boolean         inCDATA;
344         private boolean         mergeCDATA;
345         private boolean         inDTD;
346         private String          currentEntity;
347
348         private boolean         recreatedAttrs;
349         private AttributesImpl  attributes = new AttributesImpl ();
350
351         /**
352          * Subclasses may use SAX2 events to provide additional
353          * behaviors in the resulting DOM.
354          */
355         protected Handler (DomConsumer consumer)
356         throws SAXException
357         {
358             this.consumer = consumer;
359             document = consumer.emptyDocument ();
360             impl = document.getImplementation ();
361             isL2 = impl.hasFeature ("XML", "2.0");
362         }
363
364         private void fatal (String message, Exception x)
365         throws SAXException
366         {
367             SAXParseException   e;
368             ErrorHandler        errHandler = consumer.getErrorHandler ();
369
370             if (locator == null)
371                 e = new SAXParseException (message, null, null, -1, -1, x);
372             else
373                 e = new SAXParseException (message, locator, x);
374             if (errHandler != null)
375                 errHandler.fatalError (e);
376             throw e;
377         }
378
379         /**
380          * Returns and forgets the document produced.  If the handler is
381          * reused, a new document may be created.
382          */
383         Document clearDocument ()
384         {
385             Document retval = document;
386             document = null;
387             locator = null;
388             return retval;
389         }
390
391         /**
392          * Returns the document under construction.
393          */
394         protected Document getDocument ()
395             { return document; }
396         
397         /**
398          * Returns the current node being populated.  This is usually
399          * an Element or Document, but it might be an EntityReference
400          * node if some implementation-specific code knows how to put
401          * those into the result tree and later mark them as readonly.
402          */
403         protected Node getTop ()
404             { return top; }
405
406
407         // SAX1
408         public void setDocumentLocator (Locator locator)
409         {
410             this.locator = locator;
411         }
412
413         // SAX1
414         public void startDocument ()
415         throws SAXException
416         {
417             if (document == null)
418                 try {
419                     if (isL2) {
420                         // couple to original implementation
421                         document = impl.createDocument (null, "foo", null);
422                         document.removeChild (document.getFirstChild ());
423                     } else {
424                         document = consumer.emptyDocument ();
425                     }
426                 } catch (Exception e) {
427                     fatal ("DOM create document", e);
428                 }
429             top = document;
430         }
431
432         // SAX1
433         public void endDocument ()
434         throws SAXException
435         {
436             try {
437                 if (consumer.getNext () != null && document != null) {
438                     DomParser   parser = new DomParser (document);
439
440                     EventFilter.bind (parser, consumer.getNext ());
441                     parser.parse ("ignored");
442                 }
443             } finally {
444                 top = null;
445             }
446         }
447
448         // SAX1
449         public void processingInstruction (String target, String data)
450         throws SAXException
451         {
452             // we can't create populated entity ref nodes using
453             // only public DOM APIs (they've got to be readonly)
454             if (currentEntity != null)
455                 return;
456
457             ProcessingInstruction       pi;
458
459             if (isL2
460                     // && consumer.isUsingNamespaces ()
461                     && target.indexOf (':') != -1)
462                 namespaceError (
463                     "PI target name is namespace nonconformant: "
464                         + target);
465             if (inDTD)
466                 return;
467             pi = document.createProcessingInstruction (target, data);
468             top.appendChild (pi);
469         }
470
471         /**
472          * Subclasses may overrride this method to provide a more efficient
473          * way to construct text nodes.
474          * Typically, copying the text into a single character array will
475          * be more efficient than doing that as well as allocating other
476          * needed for a String, including an internal StringBuffer.
477          * Those additional memory and CPU costs can be incurred later,
478          * if ever needed.
479          * Unfortunately the standard DOM factory APIs encourage those costs
480          * to be incurred early.
481          */
482         protected Text createText (
483             boolean     isCDATA,
484             char        ch [],
485             int         start,
486             int         length
487         ) {
488             String      value = new String (ch, start, length);
489
490             if (isCDATA)
491                 return document.createCDATASection (value);
492             else
493                 return document.createTextNode (value);
494         }
495
496         // SAX1
497         public void characters (char ch [], int start, int length)
498         throws SAXException
499         {
500             // we can't create populated entity ref nodes using
501             // only public DOM APIs (they've got to be readonly
502             // at creation time)
503             if (currentEntity != null)
504                 return;
505
506             Node        lastChild = top.getLastChild ();
507
508             // merge consecutive text or CDATA nodes if appropriate.
509             if (lastChild instanceof Text) {
510                 if (consumer.isHidingCDATA ()
511                         // consecutive Text content ... always merge
512                         || (!inCDATA
513                             && !(lastChild instanceof CDATASection))
514                         // consecutive CDATASection content ... don't
515                         // merge between sections, only within them
516                         || (inCDATA && mergeCDATA
517                             && lastChild instanceof CDATASection)
518                             ) {
519                     CharacterData       last = (CharacterData) lastChild;
520                     String              value = new String (ch, start, length);
521                     
522                     last.appendData (value);
523                     return;
524                 }
525             }
526             if (inCDATA && !consumer.isHidingCDATA ()) {
527                 top.appendChild (createText (true, ch, start, length));
528                 mergeCDATA = true;
529             } else
530                 top.appendChild (createText (false, ch, start, length));
531         }
532
533         // SAX2
534         public void skippedEntity (String name)
535         throws SAXException
536         {
537             // this callback is useless except to report errors, since
538             // we can't know if the ref was in content, within an
539             // attribute, within a declaration ... only one of those
540             // cases supports more intelligent action than a panic.
541             fatal ("skipped entity: " + name, null);
542         }
543
544         // SAX2
545         public void startPrefixMapping (String prefix, String uri)
546         throws SAXException
547         {
548             // reconstruct "xmlns" attributes deleted by all
549             // SAX2 parsers without "namespace-prefixes" = true
550             if ("".equals (prefix))
551                 attributes.addAttribute ("", "", "xmlns",
552                         "CDATA", uri);
553             else
554                 attributes.addAttribute ("", "", "xmlns:" + prefix,
555                         "CDATA", uri);
556             recreatedAttrs = true;
557         }
558
559         // SAX2
560         public void endPrefixMapping (String prefix)
561         throws SAXException
562             { }
563
564         // SAX2
565         public void startElement (
566             String uri,
567             String localName,
568             String qName,
569             Attributes atts
570         ) throws SAXException
571         {
572             // we can't create populated entity ref nodes using
573             // only public DOM APIs (they've got to be readonly)
574             if (currentEntity != null)
575                 return;
576
577             // parser discarded basic information; DOM tree isn't writable
578             // without massaging to assign prefixes to all nodes.
579             // the "NSFilter" class does that massaging.
580             if (qName.length () == 0)
581                 qName = localName;
582
583
584             Element     element;
585             int         length = atts.getLength ();
586
587             if (!isL2) {
588                 element = document.createElement (qName);
589
590                 // first the explicit attributes ...
591                 length = atts.getLength ();
592                 for (int i = 0; i < length; i++)
593                     element.setAttribute (atts.getQName (i),
594                                             atts.getValue (i));
595                 // ... then any recreated ones (DOM deletes duplicates)
596                 if (recreatedAttrs) {
597                     recreatedAttrs = false;
598                     length = attributes.getLength ();
599                     for (int i = 0; i < length; i++)
600                         element.setAttribute (attributes.getQName (i),
601                                                 attributes.getValue (i));
602                     attributes.clear ();
603                 }
604
605                 top.appendChild (element);
606                 top = element;
607                 return;
608             }
609
610             // For an L2 DOM when namespace use is enabled, use
611             // createElementNS/createAttributeNS except when
612             // (a) it's an element in the default namespace, or
613             // (b) it's an attribute with no prefix
614             String      namespace;
615             
616             if (localName.length () != 0)
617                 namespace = (uri.length () == 0) ? null : uri;
618             else
619                 namespace = getNamespace (getPrefix (qName), atts);
620
621             if (namespace == null)
622                 element = document.createElement (qName);
623             else
624                 element = document.createElementNS (namespace, qName);
625
626             populateAttributes (element, atts);
627             if (recreatedAttrs) {
628                 recreatedAttrs = false;
629                 // ... DOM deletes any duplicates
630                 populateAttributes (element, attributes);
631                 attributes.clear ();
632             }
633
634             top.appendChild (element);
635             top = element;
636         }
637
638         final static String     xmlnsURI = "http://www.w3.org/2000/xmlns/";
639
640         private void populateAttributes (Element element, Attributes attrs)
641         throws SAXParseException
642         {
643             int         length = attrs.getLength ();
644
645             for (int i = 0; i < length; i++) {
646                 String  type = attrs.getType (i);
647                 String  value = attrs.getValue (i);
648                 String  name = attrs.getQName (i);
649                 String  local = attrs.getLocalName (i);
650                 String  uri = attrs.getURI (i);
651
652                 // parser discarded basic information, DOM tree isn't writable
653                 if (name.length () == 0)
654                     name = local;
655
656                 // all attribute types other than these three may not
657                 // contain scoped names... enumerated attributes get
658                 // reported as NMTOKEN, except for NOTATION values
659                 if (!("CDATA".equals (type)
660                         || "NMTOKEN".equals (type)
661                         || "NMTOKENS".equals (type))) {
662                     if (value.indexOf (':') != -1) {
663                         namespaceError (
664                                 "namespace nonconformant attribute value: "
665                                     + "<" + element.getNodeName ()
666                                     + " " + name + "='" + value + "' ...>");
667                     }
668                 }
669
670                 // xmlns="" is legal (undoes default NS)
671                 // xmlns:foo="" is illegal
672                 String prefix = getPrefix (name);
673                 String namespace;
674
675                 if ("xmlns".equals (prefix)) {
676                     if ("".equals (value))
677                         namespaceError ("illegal null namespace decl, " + name);
678                     namespace = xmlnsURI;
679                 } else if ("xmlns".equals (name))
680                     namespace = xmlnsURI;
681
682                 else if (prefix == null)
683                     namespace = null;
684                 else if (!"".equals(uri) && uri.length () != 0)
685                     namespace = uri;
686                 else
687                     namespace = getNamespace (prefix, attrs);
688
689                 if (namespace == null)
690                     element.setAttribute (name, value);
691                 else
692                     element.setAttributeNS (namespace, name, value);
693             }
694         }
695
696         private String getPrefix (String name)
697         {
698             int         temp;
699
700             if ((temp = name.indexOf (':')) > 0)
701                 return name.substring (0, temp);
702             return null;
703         }
704
705         // used with SAX1-level parser output 
706         private String getNamespace (String prefix, Attributes attrs)
707         throws SAXParseException
708         {
709             String namespace;
710             String decl;
711
712             // defaulting 
713             if (prefix == null) {
714                 decl = "xmlns";
715                 namespace = attrs.getValue (decl);
716                 if ("".equals (namespace))
717                     return null;
718                 else if (namespace != null)
719                     return namespace;
720
721             // "xmlns" is like a keyword
722             // ... according to the Namespace REC, but DOM L2 CR2+
723             // and Infoset violate that by assigning a namespace.
724             // that conflict is resolved elsewhere.
725             } else if ("xmlns".equals (prefix))
726                 return null;
727
728             // "xml" prefix is fixed
729             else if ("xml".equals (prefix))
730                 return "http://www.w3.org/XML/1998/namespace";
731
732             // otherwise, expect a declaration
733             else {
734                 decl = "xmlns:" + prefix;
735                 namespace = attrs.getValue (decl);
736             }
737             
738             // if we found a local declaration, great
739             if (namespace != null)
740                 return namespace;
741
742
743             // ELSE ... search up the tree we've been building
744             for (Node n = top;
745                     n != null && n.getNodeType () != Node.DOCUMENT_NODE;
746                     n = (Node) n.getParentNode ()) {
747                 if (n.getNodeType () == Node.ENTITY_REFERENCE_NODE)
748                     continue;
749                 Element e = (Element) n;
750                 Attr attr = e.getAttributeNode (decl);
751                 if (attr != null)
752                     return attr.getNodeValue ();
753             }
754             // see above re "xmlns" as keyword
755             if ("xmlns".equals (decl))
756                 return null;
757
758             namespaceError ("Undeclared namespace prefix: " + prefix);
759             return null;
760         }
761
762         // SAX2
763         public void endElement (String uri, String localName, String qName)
764         throws SAXException
765         {
766             // we can't create populated entity ref nodes using
767             // only public DOM APIs (they've got to be readonly)
768             if (currentEntity != null)
769                 return;
770
771             top = top.getParentNode ();
772         }
773
774         // SAX1 (mandatory reporting if validating)
775         public void ignorableWhitespace (char ch [], int start, int length)
776         throws SAXException
777         {
778             if (consumer.isHidingWhitespace ())
779                 return;
780             characters (ch, start, length);
781         }
782
783         // SAX2 lexical event
784         public void startCDATA ()
785         throws SAXException
786         {
787             inCDATA = true;
788             // true except for the first fragment of a cdata section
789             mergeCDATA = false;
790         }
791         
792         // SAX2 lexical event
793         public void endCDATA ()
794         throws SAXException
795         {
796             inCDATA = false;
797         }
798         
799         // SAX2 lexical event
800         //
801         // this SAX2 callback merges two unrelated things:
802         //      - Declaration of the root element type ... belongs with
803         //    the other DTD declaration methods, NOT HERE.
804         //      - IDs for the optional external subset ... belongs here
805         //    with other lexical information.
806         //
807         // ...and it doesn't include the internal DTD subset, desired
808         // both to support DOM L2 and to enable "pass through" processing
809         //
810         public void startDTD (String name, String publicId, String SystemId)
811         throws SAXException
812         {
813             // need to filter out comments and PIs within the DTD
814             inDTD = true;
815         }
816         
817         // SAX2 lexical event
818         public void endDTD ()
819         throws SAXException
820         {
821             inDTD = false;
822         }
823         
824         // SAX2 lexical event
825         public void comment (char ch [], int start, int length)
826         throws SAXException
827         {
828             Node        comment;
829
830             // we can't create populated entity ref nodes using
831             // only public DOM APIs (they've got to be readonly)
832             if (consumer.isHidingComments ()
833                     || inDTD
834                     || currentEntity != null)
835                 return;
836             comment = document.createComment (new String (ch, start, length));
837             top.appendChild (comment);
838         }
839
840         /**
841          * May be overridden by subclasses to return true, indicating
842          * that entity reference nodes can be populated and then made
843          * read-only.
844          */
845         public boolean canPopulateEntityRefs ()
846             { return false; }
847
848         // SAX2 lexical event
849         public void startEntity (String name)
850         throws SAXException
851         {
852             // are we ignoring what would be contents of an
853             // entity ref, since we can't populate it?
854             if (currentEntity != null)
855                 return;
856
857             // Are we hiding all entity boundaries?
858             if (consumer.isHidingReferences ())
859                 return;
860
861             // SAX2 shows parameter entities; DOM hides them
862             if (name.charAt (0) == '%' || "[dtd]".equals (name))
863                 return;
864
865             // Since we can't create a populated entity ref node in any
866             // standard way, we create an unpopulated one.
867             EntityReference ref = document.createEntityReference (name);
868             top.appendChild (ref);
869             top = ref;
870
871             // ... allowing subclasses to populate them
872             if (!canPopulateEntityRefs ())
873                 currentEntity = name;
874         }
875
876         // SAX2 lexical event
877         public void endEntity (String name)
878         throws SAXException
879         {
880             if (name.charAt (0) == '%' || "[dtd]".equals (name))
881                 return;
882             if (name.equals (currentEntity))
883                 currentEntity = null;
884             if (!consumer.isHidingReferences ())
885                 top = top.getParentNode ();
886         }
887
888
889         // SAX1 DTD event
890         public void notationDecl (
891             String name,
892             String publicId, String SystemId
893         ) throws SAXException
894         {
895             /* IGNORE -- no public DOM API lets us store these
896              * into the doctype node
897              */
898         }
899
900         // SAX1 DTD event
901         public void unparsedEntityDecl (
902             String name,
903             String publicId, String SystemId,
904             String notationName
905         ) throws SAXException
906         {
907             /* IGNORE -- no public DOM API lets us store these
908              * into the doctype node
909              */
910         }
911
912         // SAX2 declaration event
913         public void elementDecl (String name, String model)
914         throws SAXException
915         {
916             /* IGNORE -- no content model support in DOM L2 */
917         }
918
919         // SAX2 declaration event
920         public void attributeDecl (
921             String eName,
922             String aName,
923             String type,
924             String mode,
925             String value
926         ) throws SAXException
927         {
928             /* IGNORE -- no attribute model support in DOM L2 */
929         }
930
931         // SAX2 declaration event
932         public void internalEntityDecl (String name, String value)
933         throws SAXException
934         {
935             /* IGNORE -- no public DOM API lets us store these
936              * into the doctype node
937              */
938         }
939
940         // SAX2 declaration event
941         public void externalEntityDecl (
942             String name,
943             String publicId,
944             String SystemId
945         ) throws SAXException
946         {
947             /* IGNORE -- no public DOM API lets us store these
948              * into the doctype node
949              */
950         }
951
952         //
953         // These really should offer the option of nonfatal handling,
954         // like other validity errors, though that would cause major
955         // chaos in the DOM data structures.  DOM is already spec'd
956         // to treat many of these as fatal, so this is consistent.
957         //
958         private void namespaceError (String description)
959         throws SAXParseException
960         {
961             SAXParseException err;
962             
963             err = new SAXParseException (description, locator);
964             throw err;
965         }
966     }
967 }