View Javadoc

1   /*
2    * Copyright (c) 1999-2000 by David Brownell.  All Rights Reserved.
3    *
4    * This program is open source software; you may use, copy, modify, and
5    * redistribute it under the terms of the LICENSE with which it was
6    * originally distributed.
7    *
8    * This program is distributed in the hope that it will be useful,
9    * but WITHOUT ANY WARRANTY; without even the implied warranty of
10   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11   * LICENSE for more details.
12   */
13  
14  //
15  // Copyright (c) 1998 by Microstar Software Ltd.
16  // From Microstar's README (the entire original license):
17  //
18  // AElfred is free for both commercial and non-commercial use and
19  // redistribution, provided that Microstar's copyright and disclaimer are
20  // retained intact.  You are free to modify AElfred for your own use and
21  // to redistribute AElfred with your modifications, provided that the
22  // modifications are clearly documented.
23  //
24  // This program is distributed in the hope that it will be useful, but
25  // WITHOUT ANY WARRANTY; without even the implied warranty of
26  // merchantability or fitness for a particular purpose.  Please use it AT
27  // YOUR OWN RISK.
28  //
29  
30  
31  package org.dom4j.io.aelfred;
32  
33  import java.io.IOException;
34  import java.io.InputStreamReader;
35  import java.util.ArrayList;
36  import java.util.Enumeration;
37  import java.util.HashMap;
38  import java.util.Iterator;
39  import java.util.Locale;
40  
41  import org.xml.sax.AttributeList;
42  import org.xml.sax.Attributes;
43  import org.xml.sax.ContentHandler;
44  import org.xml.sax.DTDHandler;
45  import org.xml.sax.DocumentHandler;
46  import org.xml.sax.EntityResolver;
47  import org.xml.sax.ErrorHandler;
48  import org.xml.sax.InputSource;
49  import org.xml.sax.Locator;
50  import org.xml.sax.Parser;
51  import org.xml.sax.SAXException;
52  import org.xml.sax.SAXNotRecognizedException;
53  import org.xml.sax.SAXNotSupportedException;
54  import org.xml.sax.SAXParseException;
55  import org.xml.sax.XMLReader;
56  import org.xml.sax.ext.DeclHandler;
57  import org.xml.sax.ext.LexicalHandler;
58  import org.xml.sax.helpers.NamespaceSupport;
59  
60  
61  // $Id: SAXDriver.java,v 1.6 2002/05/24 14:41:55 jstrachan dead $
62  
63  /***
64   * An enhanced SAX2 version of Microstar's Ælfred XML parser.
65   * The enhancements primarily relate to significant improvements in
66   * conformance to the XML specification, and SAX2 support.  Performance
67   * has been improved.  However, the Ælfred proprietary APIs are
68   * no longer public.  See the package level documentation for more
69   * information.
70   *
71   * <table border="1" width='100%' cellpadding='3' cellspacing='0'>
72   * <tr bgcolor='#ccccff'>
73   *  <th><font size='+1'>Name</font></th>
74   *  <th><font size='+1'>Notes</font></th></tr>
75   *
76   * <tr><td colspan=2><center><em>Features ... URL prefix is
77   * <b>http://xml.org/sax/features/</b></em></center></td></tr>
78   *
79   * <tr><td>(URL)/external-general-entities</td>
80   *  <td>Value is fixed at <em>true</em></td></tr>
81   * <tr><td>(URL)/external-parameter-entities</td>
82   *  <td>Value is fixed at <em>true</em></td></tr>
83   * <tr><td>(URL)/namespace-prefixes</td>
84   *  <td>Value defaults to <em>false</em> (but XML 1.0 names are
85   *      always reported)</td></tr>
86   * <tr><td>(URL)/namespaces</td>
87   *  <td>Value defaults to <em>true</em></td></tr>
88   * <tr><td>(URL)/string-interning</td>
89   *  <td>Value is fixed at <em>true</em></td></tr>
90   * <tr><td>(URL)/validation</td>
91   *  <td>Value is fixed at <em>false</em></td></tr>
92   *
93   * <tr><td colspan=2><center><em>Handler Properties ... URL prefix is
94   * <b>http://xml.org/sax/properties/</b></em></center></td></tr>
95   *
96   * <tr><td>(URL)/declaration-handler</td>
97   *  <td>A declaration handler may be provided.  Declaration of general
98   *  entities is exposed, but not parameter entities; none of the entity
99   *  names reported here will begin with "%". </td></tr>
100  * <tr><td>(URL)/lexical-handler</td>
101  *  <td>A lexical handler may be provided.  Entity boundaries and
102  *  comments are not exposed; only CDATA sections and the start/end of
103  *  the DTD (the internal subset is not detectible). </td></tr>
104  * </table>
105  *
106  * <p> Note that the declaration handler doesn't suffice for showing all
107  * the logical structure
108  * of the DTD; it doesn't expose the name of the root element, or the values
109  * that are permitted in a NOTATIONS attribute.  (The former is exposed as
110  * lexical data, and SAX2 beta doesn't expose the latter.)
111  *
112  * <p> Although support for several features and properties is "built in"
113  * to this parser, it support all others by storing the assigned values
114  * and returning them.
115  *
116  * <p>This parser currently implements the SAX1 Parser API, but
117  * it may not continue to do so in the future.
118  *
119  * @author Written by David Megginson &lt;dmeggins@microstar.com&gt;
120  *  (version 1.2a from Microstar)
121  * @author Updated by David Brownell &lt;david-b@pacbell.net&gt;
122  * @version $Date: 2002/05/24 14:41:55 $
123  * @see org.xml.sax.Parser
124  * @deprecated Use Aelfred2 instead! THIS CLASS WILL BE REMOVED IN dom4j-1.6 !!
125  */
126 final public class SAXDriver
127     implements Locator, Attributes, XMLReader, Parser, AttributeList
128 {
129     private final DefaultHandler    base = new DefaultHandler ();
130     private XmlParser           parser;
131 
132     private EntityResolver      entityResolver = base;
133     private ContentHandler      contentHandler = base;
134     private DTDHandler          dtdHandler = base;
135     private ErrorHandler        errorHandler = base;
136     private DeclHandler         declHandler = base;
137     private LexicalHandler      lexicalHandler = base;
138 
139     private String          elementName = null;
140     private ArrayList           entityStack = new ArrayList ();
141 
142     private ArrayList           attributeNames = new ArrayList ();
143     private ArrayList           attributeNamespaces = new ArrayList ();
144     private ArrayList           attributeLocalNames = new ArrayList ();
145     private ArrayList           attributeValues = new ArrayList ();
146 
147     private boolean         namespaces = true;
148     private boolean         xmlNames = false;
149     private boolean         nspending = false; // indicates an attribute was read before its
150                                                // namespace declaration
151 
152     private int             attributeCount = 0;
153     private String          nsTemp [] = new String [3];
154     private NamespaceSupport        prefixStack = new NamespaceSupport ();
155 
156     private HashMap             features;
157     private HashMap             properties;
158 
159 
160     //
161     // Constructor.
162     //
163 
164     /*** Constructs a SAX Parser.  */
165     public SAXDriver () {}
166 
167 
168     //
169     // Implementation of org.xml.sax.Parser.
170     //
171 
172     /***
173      * <b>SAX1</b>: Sets the locale used for diagnostics; currently,
174      * only locales using the English language are supported.
175      * @param locale The locale for which diagnostics will be generated
176      */
177     public void setLocale (Locale locale)
178     throws SAXException
179     {
180     if ("en".equals (locale.getLanguage ()))
181         return ;
182 
183     throw new SAXException ("AElfred only supports English locales.");
184     }
185 
186 
187     /***
188      * <b>SAX2</b>: Returns the object used when resolving external
189      * entities during parsing (both general and parameter entities).
190      */
191     public EntityResolver getEntityResolver ()
192     {
193     return entityResolver;
194     }
195 
196     /***
197      * <b>SAX1, SAX2</b>: Set the entity resolver for this parser.
198      * @param handler The object to receive entity events.
199      */
200     public void setEntityResolver (EntityResolver resolver)
201     {
202     if (resolver == null)
203         resolver = base;
204     this.entityResolver = resolver;
205     }
206 
207 
208     /***
209      * <b>SAX2</b>: Returns the object used to process declarations related
210      * to notations and unparsed entities.
211      */
212     public DTDHandler getDTDHandler ()
213     {
214     return dtdHandler;
215     }
216 
217     /***
218      * <b>SAX1, SAX2</b>: Set the DTD handler for this parser.
219      * @param handler The object to receive DTD events.
220      */
221     public void setDTDHandler (DTDHandler handler)
222     {
223     if (handler == null)
224         handler = base;
225     this.dtdHandler = handler;
226     }
227 
228 
229     /***
230      * <b>SAX1</b>: Set the document handler for this parser.  If a
231      * content handler was set, this document handler will supplant it.
232      * The parser is set to report all XML 1.0 names rather than to
233      * filter out "xmlns" attributes (the "namespace-prefixes" feature
234      * is set to true).
235      *
236      * @deprecated SAX2 programs should use the XMLReader interface
237      *  and a ContentHandler.
238      *
239      * @param handler The object to receive document events.
240      */
241     public void setDocumentHandler (DocumentHandler handler)
242     {
243     contentHandler = new Adapter (handler);
244     xmlNames = true;
245     }
246 
247     /***
248      * <b>SAX2</b>: Returns the object used to report the logical
249      * content of an XML document.
250      */
251     public ContentHandler getContentHandler ()
252     {
253     return contentHandler;
254     }
255 
256     /***
257      * <b>SAX2</b>: Assigns the object used to report the logical
258      * content of an XML document.  If a document handler was set,
259      * this content handler will supplant it (but XML 1.0 style name
260      * reporting may remain enabled).
261      */
262     public void setContentHandler (ContentHandler handler)
263     {
264         if (handler == null)
265             handler = base;
266         contentHandler = handler;
267     }
268 
269     /***
270      * <b>SAX1, SAX2</b>: Set the error handler for this parser.
271      * @param handler The object to receive error events.
272      */
273     public void setErrorHandler (ErrorHandler handler)
274     {
275         if (handler == null)
276             handler = base;
277         this.errorHandler = handler;
278     }
279 
280     /***
281      * <b>SAX2</b>: Returns the object used to receive callbacks for XML
282      * errors of all levels (fatal, nonfatal, warning); this is never null;
283      */
284     public ErrorHandler getErrorHandler ()
285     {
286         return errorHandler;
287     }
288 
289 
290     /***
291      * <b>SAX1, SAX2</b>: Auxiliary API to parse an XML document, used mostly
292      * when no URI is available.
293      * If you want anything useful to happen, you should set
294      * at least one type of handler.
295      * @param source The XML input source.  Don't set 'encoding' unless
296      *  you know for a fact that it's correct.
297      * @see #setEntityResolver
298      * @see #setDTDHandler
299      * @see #setContentHandler
300      * @see #setErrorHandler
301      * @exception SAXException The handlers may throw any SAXException,
302      *  and the parser normally throws SAXParseException objects.
303      * @exception IOException IOExceptions are normally through through
304      *  the parser if there are problems reading the source document.
305      */
306      
307     public void parse (InputSource source) throws SAXException, IOException
308     {
309         synchronized (base) {
310             parser = new XmlParser ();
311             parser.setHandler (this);
312 
313             try {
314                 String  systemId = source.getSystemId ();
315 
316                 // duplicate first entry, in case startDocument handler
317                 // needs to use Locator.getSystemId(), before entities
318                 // start to get reported by the parser
319 
320                 if (systemId != null)
321                     entityStack.add (systemId);
322                 else
323                     entityStack.add ("illegal:unknown system ID");
324 
325                 parser.doParse (systemId,
326                           source.getPublicId (),
327                           source.getCharacterStream (),
328                           source.getByteStream (),
329                           source.getEncoding ());
330             } catch (SAXException e) {
331                 throw e;
332             } catch (IOException e) {
333                 throw e;
334             } catch (RuntimeException e) {
335                 throw e;
336             } catch (Exception e) {
337                 throw new SAXException (e.getMessage (), e);
338             } finally {
339                 contentHandler.endDocument ();
340                 entityStack.clear ();
341             }
342         }
343     }
344 
345 
346     /***
347      * <b>SAX1, SAX2</b>: Preferred API to parse an XML document, using a
348      * system identifier (URI).
349      */
350      
351     public void parse (String systemId) throws SAXException, IOException
352     {
353         parse (new InputSource (systemId));
354     }
355 
356     //
357     // Implementation of SAX2 "XMLReader" interface
358     //
359     static final String FEATURE = "http://xml.org/sax/features/";
360     static final String HANDLER = "http://xml.org/sax/properties/";
361 
362     /***
363      * <b>SAX2</b>: Tells the value of the specified feature flag.
364      *
365      * @exception SAXNotRecognizedException thrown if the feature flag
366      *  is neither built in, nor yet assigned.
367      */
368     public boolean getFeature (String featureId)
369     throws SAXNotRecognizedException
370     {
371     if ((FEATURE + "validation").equals (featureId))
372         return false;
373 
374     // external entities (both types) are currently always included
375     if ((FEATURE + "external-general-entities").equals (featureId)
376         || (FEATURE + "external-parameter-entities")
377         .equals (featureId))
378         return true;
379 
380     // element/attribute names are as written in document; no mangling
381     if ((FEATURE + "namespace-prefixes").equals (featureId))
382         return xmlNames;
383 
384     // report element/attribute namespaces?
385     if ((FEATURE + "namespaces").equals (featureId))
386         return namespaces;
387 
388     // XXX always provides a locator ... removed in beta
389 
390     // always interns
391     if ((FEATURE + "string-interning").equals (featureId))
392         return true;
393 
394     if (features != null && features.containsKey (featureId))
395         return ((Boolean)features.get (featureId)).booleanValue ();
396 
397     throw new SAXNotRecognizedException (featureId);
398     }
399 
400     /***
401      * <b>SAX2</b>:  Returns the specified property.
402      *
403      * @exception SAXNotRecognizedException thrown if the property value
404      *  is neither built in, nor yet stored.
405      */
406     public Object getProperty (String propertyId)
407     throws SAXNotRecognizedException
408     {
409     if ((HANDLER + "declaration-handler").equals (propertyId))
410         return declHandler;
411 
412     if ((HANDLER + "lexical-handler").equals (propertyId))
413         return lexicalHandler;
414     
415     if (properties != null && properties.containsKey (propertyId))
416         return properties.get (propertyId);
417 
418     // unknown properties
419     throw new SAXNotRecognizedException (propertyId);
420     }
421 
422     /***
423      * <b>SAX2</b>:  Sets the state of feature flags in this parser.  Some
424      * built-in feature flags are mutable; all flags not built-in are
425      * motable.
426      */
427     public void setFeature (String featureId, boolean state)
428     throws SAXNotRecognizedException, SAXNotSupportedException
429     {
430     boolean value;
431     
432     try {
433         // Features with a defined value, we just change it if we can.
434         value = getFeature (featureId);
435 
436         if (state == value)
437         return;
438 
439         if ((FEATURE + "namespace-prefixes").equals (featureId)) {
440         // in this implementation, this only affects xmlns reporting
441         xmlNames = state;
442         return;
443         }
444 
445         if ((FEATURE + "namespaces").equals (featureId)) {
446         // XXX if not currently parsing ...
447         if (true) {
448             namespaces = state;
449             return;
450         }
451         // if in mid-parse, critical info hasn't been computed/saved
452         }
453 
454         // can't change builtins
455         if (features == null || !features.containsKey (featureId))
456         throw new SAXNotSupportedException (featureId);
457 
458     } catch (SAXNotRecognizedException e) {
459         // as-yet unknown features
460         if (features == null)
461         features = new HashMap (5);
462     }
463 
464     // record first value, or modify existing one
465     features.put (featureId, 
466         state
467         ? Boolean.TRUE
468         : Boolean.FALSE);
469     }
470 
471     /***
472      * <b>SAX2</b>:  Assigns the specified property.  Like SAX1 handlers,
473      * these may be changed at any time.
474      */
475     public void setProperty (String propertyId, Object property)
476     throws SAXNotRecognizedException, SAXNotSupportedException
477     {
478     Object  value;
479     
480     try {
481         // Properties with a defined value, we just change it if we can.
482         value = getProperty (propertyId);
483 
484         if ((HANDLER + "declaration-handler").equals (propertyId)) {
485             if (property == null)
486                 declHandler = base;
487             else if (! (property instanceof DeclHandler))
488                 throw new SAXNotSupportedException (propertyId);
489             else
490                 declHandler = (DeclHandler) property;
491             return ;
492         }
493 
494         if ((HANDLER + "lexical-handler").equals (propertyId)) {
495             if (property == null)
496                 lexicalHandler = base;
497             else if (! (property instanceof LexicalHandler))
498                 throw new SAXNotSupportedException (propertyId);
499             else
500                 lexicalHandler = (LexicalHandler) property;
501             return ;
502         }
503 
504         // can't change builtins
505         if (properties == null || !properties.containsKey (propertyId))
506         throw new SAXNotSupportedException (propertyId);
507 
508     } catch (SAXNotRecognizedException e) {
509         // as-yet unknown properties
510         if (properties == null)
511         properties = new HashMap (5);
512     }
513 
514     // record first value, or modify existing one
515     properties.put (propertyId, property);
516     }
517 
518 
519     //
520     // This is where the driver receives AElfred callbacks and translates
521     // them into SAX callbacks.  Some more callbacks have been added for
522     // SAX2 support.
523     //
524 
525     // NOTE:  in some cases, local copies of handlers are
526     // created and used, to work around codegen bugs in at
527     // least one snapshot version of GCJ.
528 
529     void startDocument () throws SAXException
530     {
531         contentHandler.setDocumentLocator (this);
532         contentHandler.startDocument ();
533         attributeNames.clear ();
534         attributeValues.clear ();
535     }
536 
537     void endDocument () throws SAXException
538     {
539         // SAX says endDocument _must_ be called (handy to close
540         // files etc) so it's in a "finally" clause
541     }
542 
543     Object resolveEntity (String publicId, String systemId)
544     throws SAXException, IOException
545     {
546         InputSource source = entityResolver.resolveEntity (publicId,
547                      systemId);
548 
549         if (source == null) {
550             return null;
551         } else if (source.getCharacterStream () != null) {
552             return source.getCharacterStream ();
553         } else if (source.getByteStream () != null) {
554             if (source.getEncoding () == null)
555             return source.getByteStream ();
556             else try {
557             return new InputStreamReader (
558                 source.getByteStream (),
559                 source.getEncoding ()
560                 );
561             } catch (IOException e) {
562             return source.getByteStream ();
563             }
564         } else {
565             return source.getSystemId ();
566         }
567         // XXX no way to tell AElfred about new public
568         // or system ids ... so relative URL resolution
569         // through that entity could be less than reliable.
570     }
571 
572 
573     void startExternalEntity (String systemId)
574     throws SAXException
575     {
576         entityStack.add (systemId);
577     }
578 
579     void endExternalEntity (String systemId)
580     throws SAXException
581     {
582         entityStack.remove ( entityStack.size() - 1 );
583     }
584 
585     void doctypeDecl (String name, String publicId, String systemId)
586     throws SAXException
587     {
588         lexicalHandler.startDTD (name, publicId, systemId);
589     
590         // ... the "name" is a declaration and should be given
591         // to the DeclHandler (but sax2 beta doesn't).
592 
593         // the IDs for the external subset are lexical details,
594         // as are the contents of the internal subset; but sax2
595         // beta only provides the external subset "pre-parse"
596     }
597 
598     void endDoctype () throws SAXException
599     {
600         // NOTE:  some apps may care that comments and PIs,
601         // are stripped from their DTD declaration context,
602         // and that those declarations are themselves quite
603         // thoroughly reordered here.
604 
605         deliverDTDEvents ();
606         lexicalHandler.endDTD ();
607     }
608 
609 
610     void attribute (String aname, String value, boolean isSpecified)
611     throws SAXException
612     {
613         if (attributeCount++ == 0) {
614             if (namespaces)
615                 prefixStack.pushContext ();
616         }
617 
618         // set nsTemp [0] == namespace URI (or empty)
619         // set nsTemp [1] == local name (or empty)
620         if (value != null) {
621             if (namespaces) {
622                 int index = aname.indexOf (':');
623 
624                 // prefixed name?
625                 if (index > 0) {
626                     // prefix declaration?
627                     if (index == 5 && aname.startsWith ("xmlns")) {
628                         String      prefix = aname.substring (index + 1);
629 
630                         if (value.length () == 0) {
631                             errorHandler.error (new SAXParseException (
632                                 "missing URI in namespace decl attribute: "
633                                 + aname,
634                                 this));
635                         } else {
636                             prefixStack.declarePrefix (prefix, value);
637                             contentHandler.startPrefixMapping (prefix, value);
638                         }
639                         if (!xmlNames)
640                             return;
641                         nsTemp [0] = "";
642                         nsTemp [1] = aname;
643 
644                     // prefix reference
645                     } else {
646                         if (prefixStack.processName (aname, nsTemp, true)
647                             == null) {
648                             // start of MHK code
649                                 nsTemp[0] = "";
650                                 nsTemp[1] = aname;    // defer checking till later
651                                 nspending = true;
652                             // end of MHK code
653                             // start of previous code    
654                             // errorHandler.error (new SAXParseException (
655                             //    "undeclared name prefix in: " + aname,
656                             //    this));
657                             // nsTemp [0] = nsTemp [1] = "";
658                             // end of previous code    
659                         } // else nsTemp [0, 1] received { uri, local }
660                     }
661 
662                 // no prefix
663                 } else {
664                     // default declaration?
665                     if ("xmlns".equals (aname)) {
666                     prefixStack.declarePrefix ("", value);
667                     contentHandler.startPrefixMapping ("", value);
668                     if (!xmlNames)
669                         return;
670                     }
671                     nsTemp [0] = "";
672                     nsTemp [1] = aname;
673                 }
674             } else
675                 nsTemp [0] = nsTemp [1] = "";
676 
677             attributeNamespaces.add (nsTemp [0]);
678             attributeLocalNames.add (nsTemp [1]);
679             attributeNames.add (aname);
680             // attribute type comes from querying parser's DTD records
681             attributeValues.add (value);
682         }
683     }
684 
685     void startElement (String elname)
686     throws SAXException
687     {
688         ContentHandler handler = contentHandler;
689 
690         //
691         // NOTE:  this implementation of namespace support adds something
692         // like six percent to parsing CPU time, in a large (~50 MB)
693         // document that doesn't use namespaces at all.  (Measured by PC
694         // sampling.)
695         //
696         // It ought to become notably faster in such cases.  Most
697         // costs are the prefix stack calling Hashtable.get() (2%),
698         // String.hashCode() (1.5%) and about 1.3% each for pushing
699         // the context, and two chunks of name processing.
700         //
701 
702         if (attributeCount == 0)
703             prefixStack.pushContext ();
704 
705         // save element name so attribute callbacks work
706         elementName = elname;
707         if (namespaces) {
708             // START MHK CODE
709             // Check that namespace prefixes for attributes are valid
710             if (attributeCount > 0 && nspending) {
711                 for (int i=0; i<attributeLocalNames.size(); i++) {
712                     String aname = (String)attributeLocalNames.get(i);
713                     if (aname.indexOf(':')>0) {
714 
715                         if (prefixStack.processName (aname, nsTemp, true) == null) {
716                             errorHandler.error (new SAXParseException (
717                                 "undeclared name prefix in: " + aname,
718                                 this));
719                         } else {
720                             attributeNamespaces.set(i, nsTemp[0]);
721                             attributeLocalNames.set(i, nsTemp[1]);
722                         }
723                     }
724                 }
725             }
726             // END MHK CODE
727 
728             if (prefixStack.processName (elname, nsTemp, false) == null) {
729             errorHandler.error (new SAXParseException (
730                 "undeclared name prefix in: " + elname,
731                 this));
732             nsTemp [0] = nsTemp [1] = "";
733             }
734 
735             handler.startElement (nsTemp [0], nsTemp [1], elname, this);
736         } else
737             handler.startElement ("", "", elname, this);
738         // elementName = null;
739 
740         // elements with no attributes are pretty common!
741         if (attributeCount != 0) {
742             attributeNames.clear ();
743             attributeNamespaces.clear ();
744             attributeLocalNames.clear ();
745             attributeValues.clear ();
746             attributeCount = 0;
747         }
748         nspending = false;
749     }
750 
751     void endElement (String elname)
752     throws SAXException
753     {
754         ContentHandler  handler = contentHandler;
755 
756         handler.endElement ("", "", elname);
757 
758         if (!namespaces)
759             return;
760 
761         Enumeration prefixes = prefixStack.getDeclaredPrefixes ();
762 
763         while (prefixes.hasMoreElements ())
764             handler.endPrefixMapping ((String) prefixes.nextElement ());
765         prefixStack.popContext ();
766     }
767 
768     void startCDATA ()
769     throws SAXException
770     {
771         lexicalHandler.startCDATA ();
772     }
773 
774     void charData (char ch[], int start, int length)
775     throws SAXException
776     {
777         contentHandler.characters (ch, start, length);
778     }
779 
780     void endCDATA ()
781     throws SAXException
782     {
783         lexicalHandler.endCDATA ();
784     }
785 
786     void ignorableWhitespace (char ch[], int start, int length)
787     throws SAXException
788     {
789         contentHandler.ignorableWhitespace (ch, start, length);
790     }
791 
792     void processingInstruction (String target, String data)
793     throws SAXException
794     {
795         // XXX if within DTD, perhaps it's best to discard
796         // PIs since the decls to which they (usually)
797         // apply get significantly rearranged
798 
799         contentHandler.processingInstruction (target, data);
800     }
801 
802     void comment (char ch[], int start, int length)
803     throws SAXException
804     {
805         // XXX if within DTD, perhaps it's best to discard
806         // comments since the decls to which they (usually)
807         // apply get significantly rearranged
808 
809         if (lexicalHandler != base)
810             lexicalHandler.comment (ch, start, length);
811     }
812 
813         // AElfred only has fatal errors
814         void error (String message, String url, int line, int column)
815         throws SAXException
816         {
817         SAXParseException fatal;
818     
819         fatal = new SAXParseException (message, null, url, line, column);
820         errorHandler.fatalError (fatal);
821 
822         // Even if the application can continue ... we can't!
823         throw fatal;
824     }
825 
826 
827     //
828     // Before the endDtd event, deliver all non-PE declarations.
829     //
830     private void deliverDTDEvents ()
831     throws SAXException
832     {
833     String  ename;
834     String  nname;
835     String publicId;
836     String  systemId;
837 
838     // First, report all notations.
839     if (dtdHandler != base) {
840         Iterator    notationNames = parser.declaredNotations ();
841 
842         while (notationNames.hasNext ()) {
843         nname = (String) notationNames.next ();
844         publicId = parser.getNotationPublicId (nname);
845         systemId = parser.getNotationSystemId (nname);
846         dtdHandler.notationDecl (nname, publicId, systemId);
847         }
848     }
849 
850     // Next, report all entities.
851     if (dtdHandler != base || declHandler != base) {
852         Iterator    entityNames = parser.declaredEntities ();
853         int type;
854 
855         while (entityNames.hasNext ()) {
856         ename = (String) entityNames.next ();
857         type = parser.getEntityType (ename);
858 
859         if (ename.charAt (0) == '%')
860             continue;
861 
862         // unparsed
863         if (type == XmlParser.ENTITY_NDATA) {
864             publicId = parser.getEntityPublicId (ename);
865             systemId = parser.getEntitySystemId (ename);
866             nname = parser.getEntityNotationName (ename);
867             dtdHandler.unparsedEntityDecl (ename,
868                             publicId, systemId, nname);
869 
870             // external parsed
871         }
872         else if (type == XmlParser.ENTITY_TEXT) {
873             publicId = parser.getEntityPublicId (ename);
874             systemId = parser.getEntitySystemId (ename);
875             declHandler.externalEntityDecl (ename,
876                              publicId, systemId);
877 
878             // internal parsed
879         }
880         else if (type == XmlParser.ENTITY_INTERNAL) {
881             // filter out the built-ins; even if they were
882             // declared, they didn't need to be.
883             if ("lt".equals (ename) || "gt".equals (ename)
884                 || "quot".equals (ename)
885                 || "apos".equals (ename)
886                 || "amp".equals (ename))
887             continue;
888             declHandler.internalEntityDecl (ename,
889                  parser.getEntityValue (ename));
890         }
891         }
892     }
893 
894     // elements, attributes
895     if (declHandler != base) {
896         Iterator    elementNames = parser.declaredElements ();
897         Iterator    attNames;
898 
899         while (elementNames.hasNext ()) {
900         String model = null;
901 
902         ename = (String) elementNames.next ();
903         switch (parser.getElementContentType (ename)) {
904             case XmlParser.CONTENT_ANY:
905             model = "ANY";
906             break;
907             case XmlParser.CONTENT_EMPTY:
908             model = "EMPTY";
909             break;
910             case XmlParser.CONTENT_MIXED:
911             case XmlParser.CONTENT_ELEMENTS:
912             model = parser.getElementContentModel (ename);
913             break;
914             case XmlParser.CONTENT_UNDECLARED:
915             default:
916             model = null;
917             break;
918         }
919         if (model != null)
920             declHandler.elementDecl (ename, model);
921 
922         attNames = parser.declaredAttributes (ename);
923         while (attNames != null && attNames.hasNext ()) {
924             String aname = (String) attNames.next ();
925             String type;
926             String valueDefault;
927             String value;
928 
929             switch (parser.getAttributeType (ename, aname)) {
930             case XmlParser.ATTRIBUTE_CDATA:
931             type = "CDATA";
932             break;
933             case XmlParser.ATTRIBUTE_ENTITY:
934             type = "ENTITY";
935             break;
936             case XmlParser.ATTRIBUTE_ENTITIES:
937             type = "ENTITIES";
938             break;
939             case XmlParser.ATTRIBUTE_ENUMERATED:
940             type = parser.getAttributeIterator (ename, aname);
941             break;
942             case XmlParser.ATTRIBUTE_ID:
943             type = "ID";
944             break;
945             case XmlParser.ATTRIBUTE_IDREF:
946             type = "IDREF";
947             break;
948             case XmlParser.ATTRIBUTE_IDREFS:
949             type = "IDREFS";
950             break;
951             case XmlParser.ATTRIBUTE_NMTOKEN:
952             type = "NMTOKEN";
953             break;
954             case XmlParser.ATTRIBUTE_NMTOKENS:
955             type = "NMTOKENS";
956             break;
957 
958             // XXX SAX2 beta doesn't have a way to return the
959             // enumerated list of permitted notations ... SAX1
960             // kluged it as NMTOKEN, but that won't work for
961             // the sort of apps that really use the DTD info
962             case XmlParser.ATTRIBUTE_NOTATION:
963             type = "NOTATION";
964             break;
965 
966             default:
967             errorHandler.fatalError (new SAXParseException (
968                   "internal error, att type", this));
969             type = null;
970             }
971 
972             switch (parser.getAttributeDefaultValueType (
973                  ename, aname)) {
974             case XmlParser.ATTRIBUTE_DEFAULT_IMPLIED:
975             valueDefault = "#IMPLIED";
976             break;
977             case XmlParser.ATTRIBUTE_DEFAULT_REQUIRED:
978             valueDefault = "#REQUIRED";
979             break;
980             case XmlParser.ATTRIBUTE_DEFAULT_FIXED:
981             valueDefault = "#FIXED";
982             break;
983             case XmlParser.ATTRIBUTE_DEFAULT_SPECIFIED:
984             valueDefault = null;
985             break;
986 
987             default:
988             errorHandler.fatalError (new SAXParseException (
989                     "internal error, att default", this));
990             valueDefault = null;
991             }
992 
993             value = parser.getAttributeDefaultValue (ename, aname);
994 
995             declHandler.attributeDecl (ename, aname,
996                         type, valueDefault, value);
997         }
998         }
999     }
1000     }
1001 
1002 
1003     //
1004     // Implementation of org.xml.sax.Attributes.
1005     //
1006 
1007     /***
1008      * <b>SAX1 AttributeList, SAX2 Attributes</b> method
1009      * (don't invoke on parser);
1010      */
1011     public int getLength ()
1012     {
1013         return attributeNames.size ();
1014     }
1015 
1016     /***
1017      * <b>SAX2 Attributes</b> method (don't invoke on parser);
1018      */
1019     public String getURI (int index)
1020     {
1021         return (String) (attributeNamespaces.get (index));
1022     }
1023 
1024     /***
1025      * <b>SAX2 Attributes</b> method (don't invoke on parser);
1026      */
1027     public String getLocalName (int index)
1028     {
1029         return (String) (attributeLocalNames.get (index));
1030     }
1031 
1032     /***
1033      * <b>SAX2 Attributes</b> method (don't invoke on parser);
1034      */
1035     public String getQName (int i)
1036     {
1037         return (String) (attributeNames.get (i));
1038     }
1039 
1040     /***
1041      * <b>SAX1 AttributeList</b> method (don't invoke on parser);
1042      */
1043     public String getName (int i)
1044     {
1045         return (String) (attributeNames.get (i));
1046     }
1047 
1048     /***
1049      * <b>SAX1 AttributeList, SAX2 Attributes</b> method
1050      * (don't invoke on parser);
1051      */
1052     public String getType (int i)
1053     {
1054     switch (parser.getAttributeType (elementName, getQName (i))) {
1055 
1056     case XmlParser.ATTRIBUTE_UNDECLARED:
1057     case XmlParser.ATTRIBUTE_CDATA:
1058         return "CDATA";
1059     case XmlParser.ATTRIBUTE_ID:
1060         return "ID";
1061     case XmlParser.ATTRIBUTE_IDREF:
1062         return "IDREF";
1063     case XmlParser.ATTRIBUTE_IDREFS:
1064         return "IDREFS";
1065     case XmlParser.ATTRIBUTE_ENTITY:
1066         return "ENTITY";
1067     case XmlParser.ATTRIBUTE_ENTITIES:
1068         return "ENTITIES";
1069     case XmlParser.ATTRIBUTE_ENUMERATED:
1070         // XXX doesn't have a way to return permitted enum values,
1071         // though they must each be a NMTOKEN 
1072     case XmlParser.ATTRIBUTE_NMTOKEN:
1073         return "NMTOKEN";
1074     case XmlParser.ATTRIBUTE_NMTOKENS:
1075         return "NMTOKENS";
1076     case XmlParser.ATTRIBUTE_NOTATION:
1077         // XXX doesn't have a way to return the permitted values,
1078         // each of which must be name a declared notation
1079         return "NOTATION";
1080 
1081     }
1082 
1083     return null;
1084     }
1085 
1086 
1087     /***
1088      * <b>SAX1 AttributeList, SAX2 Attributes</b> method
1089      * (don't invoke on parser);
1090      */
1091     public String getValue (int i)
1092     {
1093         return (String) (attributeValues.get (i));
1094     }
1095 
1096 
1097     /***
1098      * <b>SAX2 Attributes</b> method (don't invoke on parser);
1099      */
1100     public int getIndex (String uri, String local)
1101     {
1102         int length = getLength ();
1103 
1104         for (int i = 0; i < length; i++) {
1105             if (!getURI (i).equals (uri))
1106             continue;
1107             if (getLocalName (i).equals (local))
1108             return i;
1109         }
1110         return -1;
1111     }
1112 
1113 
1114     /***
1115      * <b>SAX2 Attributes</b> method (don't invoke on parser);
1116      */
1117     public int getIndex (String xmlName)
1118     {
1119         int length = getLength ();
1120 
1121         for (int i = 0; i < length; i++) {
1122             if (getQName (i).equals (xmlName))
1123             return i;
1124         }
1125         return -1;
1126     }
1127 
1128 
1129     /***
1130      * <b>SAX2 Attributes</b> method (don't invoke on parser);
1131      */
1132     public String getType (String uri, String local)
1133     {
1134         int index = getIndex (uri, local);
1135 
1136         if (index < 0)
1137             return null;
1138         return getType (index);
1139     }
1140 
1141 
1142     /***
1143      * <b>SAX1 AttributeList, SAX2 Attributes</b> method
1144      * (don't invoke on parser);
1145      */
1146     public String getType (String xmlName)
1147     {
1148         int index = getIndex (xmlName);
1149 
1150         if (index < 0)
1151             return null;
1152         return getType (index);
1153     }
1154 
1155 
1156     /***
1157      * <b>SAX Attributes</b> method (don't invoke on parser);
1158      */
1159     public String getValue (String uri, String local)
1160     {
1161         int index = getIndex (uri, local);
1162 
1163         if (index < 0)
1164             return null;
1165         return getValue (index);
1166     }
1167 
1168 
1169     /***
1170      * <b>SAX1 AttributeList, SAX2 Attributes</b> method
1171      * (don't invoke on parser);
1172      */
1173     public String getValue (String xmlName)
1174     {
1175         int index = getIndex (xmlName);
1176 
1177         if (index < 0)
1178             return null;
1179         return getValue (index);
1180     }
1181 
1182 
1183     //
1184     // Implementation of org.xml.sax.Locator.
1185     //
1186 
1187     /***
1188      * <b>SAX Locator</b> method (don't invoke on parser);
1189      */
1190     public String getPublicId ()
1191     {
1192         return null;        // XXX track public IDs too
1193     }
1194 
1195     /***
1196      * <b>SAX Locator</b> method (don't invoke on parser);
1197      */
1198     public String getSystemId ()
1199     {
1200         return (String) entityStack.get ( entityStack.size() - 1 );
1201     }
1202 
1203     /***
1204      * <b>SAX Locator</b> method (don't invoke on parser);
1205      */
1206     public int getLineNumber ()
1207     {
1208         return parser.getLineNumber ();
1209     }
1210 
1211     /***
1212      * <b>SAX Locator</b> method (don't invoke on parser);
1213      */
1214     public int getColumnNumber ()
1215     {
1216         return parser.getColumnNumber ();
1217     }
1218 
1219     // adapter between content handler and document handler callbacks
1220     
1221     private static class Adapter implements ContentHandler
1222     {
1223         private DocumentHandler     docHandler;
1224 
1225         Adapter (DocumentHandler dh)
1226             { docHandler = dh; }
1227 
1228 
1229         public void setDocumentLocator (Locator l)
1230             { docHandler.setDocumentLocator (l); }
1231     
1232         public void startDocument () throws SAXException
1233             { docHandler.startDocument (); }
1234     
1235         public void processingInstruction (String target, String data)
1236         throws SAXException
1237             { docHandler.processingInstruction (target, data); }
1238     
1239         public void startPrefixMapping (String prefix, String uri)
1240             { /* ignored */ }
1241 
1242         public void startElement (
1243             String  namespace,
1244             String  local,
1245             String  name,
1246             Attributes  attrs ) throws SAXException
1247         {
1248             docHandler.startElement (name, (AttributeList) attrs);
1249         }
1250 
1251         public void characters (char buf [], int offset, int len)
1252         throws SAXException
1253         {
1254             docHandler.characters (buf, offset, len);
1255         }
1256 
1257         public void ignorableWhitespace (char buf [], int offset, int len)
1258         throws SAXException
1259         {
1260             docHandler.ignorableWhitespace (buf, offset, len);
1261         }
1262 
1263         public void skippedEntity (String name)
1264             { /* ignored */ }
1265 
1266         public void endElement (String u, String l, String name)
1267         throws SAXException
1268             { docHandler.endElement (name); }
1269 
1270         public void endPrefixMapping (String prefix)
1271             { /* ignored */ }
1272 
1273         public void endDocument () throws SAXException
1274             { docHandler.endDocument (); }
1275     }
1276 }
1277