View Javadoc

1   /*
2    * SAXDriver.java
3    * Copyright (C) 1999,2000,2001 The Free Software Foundation
4    * 
5    * This file is part of GNU JAXP, a library.
6    *
7    * GNU JAXP is free software; you can redistribute it and/or modify
8    * it under the terms of the GNU General Public License as published by
9    * the Free Software Foundation; either version 2 of the License, or
10   * (at your option) any later version.
11   * 
12   * GNU JAXP is distributed in the hope that it will be useful,
13   * but WITHOUT ANY WARRANTY; without even the implied warranty of
14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   * GNU General Public License for more details.
16   * 
17   * You should have received a copy of the GNU General Public License
18   * along with this program; if not, write to the Free Software
19   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20   *
21   * As a special exception, if you link this library with other files to
22   * produce an executable, this library does not by itself cause the
23   * resulting executable to be covered by the GNU General Public License.
24   * This exception does not however invalidate any other reasons why the
25   * executable file might be covered by the GNU General Public License. 
26   */
27  
28  //
29  // Copyright (c) 1998 by Microstar Software Ltd.
30  // From Microstar's README (the entire original license):
31  //
32  //	Separate statements also said it's in the public domain.
33  //	All modifications are distributed under the license
34  //	above (GPL with library exception).
35  //
36  // AElfred is free for both commercial and non-commercial use and
37  // redistribution, provided that Microstar's copyright and disclaimer are
38  // retained intact.  You are free to modify AElfred for your own use and
39  // to redistribute AElfred with your modifications, provided that the
40  // modifications are clearly documented.
41  //
42  // This program is distributed in the hope that it will be useful, but
43  // WITHOUT ANY WARRANTY; without even the implied warranty of
44  // merchantability or fitness for a particular purpose.  Please use it AT
45  // YOUR OWN RISK.
46  //
47  
48  
49  package org.dom4j.io.aelfred2;
50  
51  import java.io.*;
52  
53  import java.net.MalformedURLException;
54  import java.net.URL;
55  import java.util.Locale;
56  import java.util.Stack;
57  
58  // maintaining 1.1 compatibility for now ... more portable, PJava, etc
59  // Iterator, Hashmap and ArrayList ought to be faster
60  import java.util.ArrayList;
61  import java.util.Collections;
62  import java.util.Enumeration;
63  import java.util.Hashtable;
64  import java.util.Iterator;
65  import java.util.List;
66  import java.util.Vector;
67  
68  import org.xml.sax.*;
69  import org.xml.sax.ext.*;
70  import org.xml.sax.helpers.NamespaceSupport;
71  
72  
73  /***
74   * An enhanced SAX2 version of Microstar's Ælfred XML parser.
75   * The enhancements primarily relate to significant improvements in
76   * conformance to the XML specification, and SAX2 support.  Performance
77   * has been improved.  See the package level documentation for more
78   * information.
79   *
80   * <table border="1" width='100%' cellpadding='3' cellspacing='0'>
81   * <tr bgcolor='#ccccff'>
82   *	<th><font size='+1'>Name</font></th>
83   *	<th><font size='+1'>Notes</font></th></tr>
84   *
85   * <tr><td colspan=2><center><em>Features ... URL prefix is
86   * <b>http://xml.org/sax/features/</b></em></center></td></tr>
87   *
88   * <tr><td>(URL)/external-general-entities</td>
89   *	<td>Value defaults to <em>true</em></td></tr>
90   * <tr><td>(URL)/external-parameter-entities</td>
91   *	<td>Value defaults to <em>true</em></td></tr>
92   * <tr><td>(URL)/is-standalone</td>
93   *	<td>(PRELIMINARY) Returns true iff the document's parsing
94   *	has started (some non-error event after <em>startDocument()</em>
95   *	was reported) and the document's standalone flag is set.</td></tr>
96   * <tr><td>(URL)/namespace-prefixes</td>
97   *	<td>Value defaults to <em>false</em> (but XML 1.0 names are
98   *		always reported)</td></tr>
99   * <tr><td>(URL)/lexical-handler/parameter-entities</td>
100  *	<td>Value is fixed at <em>true</em></td></tr>
101  * <tr><td>(URL)/namespaces</td>
102  *	<td>Value defaults to <em>true</em></td></tr>
103  * <tr><td>(URL)/resolve-dtd-uris</td>
104  *	<td>(PRELIMINARY) Value defaults to <em>true</em></td></tr>
105  * <tr><td>(URL)/string-interning</td>
106  *	<td>Value is fixed at <em>true</em></td></tr>
107  * <tr><td>(URL)/use-attributes2</td>
108  *	<td>(PRELIMINARY) Value is fixed at <em>true</em></td></tr>
109  * <tr><td>(URL)/use-entity-resolver2</td>
110  *	<td>(PRELIMINARY) Value defaults to <em>true</em></td></tr>
111  * <tr><td>(URL)/validation</td>
112  *	<td>Value is fixed at <em>false</em></td></tr>
113  *
114  * <tr><td colspan=2><center><em>Handler Properties ... URL prefix is
115  * <b>http://xml.org/sax/properties/</b></em></center></td></tr>
116  *
117  * <tr><td>(URL)/declaration-handler</td>
118  *	<td>A declaration handler may be provided.  </td></tr>
119  * <tr><td>(URL)/lexical-handler</td>
120  *	<td>A lexical handler may be provided.  </td></tr>
121  * </table>
122  *
123  * <p>This parser currently implements the SAX1 Parser API, but
124  * it may not continue to do so in the future.
125  *
126  * @author Written by David Megginson (version 1.2a from Microstar)
127  * @author Updated by David Brownell &lt;dbrownell@users.sourceforge.net&gt;
128  * @see org.xml.sax.Parser
129  */
130 final public class SAXDriver
131     implements Locator, Attributes2, XMLReader, Parser, AttributeList
132 {
133     private final DefaultHandler2	base = new DefaultHandler2 ();
134     private XmlParser			parser;
135 
136     private EntityResolver		entityResolver = base;
137     private EntityResolver2		resolver2 = null;
138     private ContentHandler		contentHandler = base;
139     private DTDHandler			dtdHandler = base;
140     private ErrorHandler 		errorHandler = base;
141     private DeclHandler			declHandler = base;
142     private LexicalHandler		lexicalHandler = base;
143 
144     private String			elementName;
145     private Stack			entityStack;
146 
147     // one vector (of object/struct): faster, smaller
148     private List			attributesList = Collections.synchronizedList(new ArrayList());
149   
150     private boolean			attributeSpecified [] = new boolean[10];
151     private boolean			attributeDeclared [] = new boolean[10];
152 
153     private boolean			namespaces = true;
154     private boolean			xmlNames = false;
155     private boolean			extGE = true;
156     private boolean			extPE = true;
157     private boolean			resolveAll = true;
158     private boolean			useResolver2 = true;
159     private boolean                     stringInterning = true;
160 
161     private int				attributeCount;
162     private boolean			attributes;
163     private String			nsTemp [];
164     private NamespaceSupport		prefixStack;
165 
166     //
167     // Constructor.
168     //
169 
170     /*** Constructs a SAX Parser.  */
171     public SAXDriver ()
172     {
173       reset ();
174     }
175 
176     private void reset ()
177     {
178       elementName = null;
179       entityStack = new Stack ();
180       attributesList = Collections.synchronizedList(new ArrayList());
181       attributeSpecified = new boolean[10];
182       attributeDeclared = new boolean[10];
183       attributeCount = 0;
184       attributes = false;
185       nsTemp = new String[3];
186       prefixStack = null;
187     }
188 
189 
190     //
191     // Implementation of org.xml.sax.Parser.
192     //
193 
194     /***
195      * <b>SAX1</b>: Sets the locale used for diagnostics; currently,
196      * only locales using the English language are supported.
197      * @param locale The locale for which diagnostics will be generated
198      */
199     public void setLocale (Locale locale)
200     throws SAXException
201     {
202 	if ("en".equals (locale.getLanguage ()))
203 	    return ;
204 
205 	throw new SAXException ("AElfred2 only supports English locales.");
206     }
207 
208 
209     /***
210      * <b>SAX2</b>: Returns the object used when resolving external
211      * entities during parsing (both general and parameter entities).
212      */
213     public EntityResolver getEntityResolver ()
214     {
215 	return (entityResolver == base) ? null : entityResolver;
216     }
217 
218     /***
219      * <b>SAX1, SAX2</b>: Set the entity resolver for this parser.
220      * @param handler The object to receive entity events.
221      */
222     public void setEntityResolver (EntityResolver resolver)
223     {
224 	if (resolver instanceof EntityResolver2)
225 	    resolver2 = (EntityResolver2) resolver;
226 	else
227 	    resolver2 = null;
228 	if (resolver == null)
229 	    resolver = base;
230 	entityResolver = resolver;
231     }
232 
233 
234     /***
235      * <b>SAX2</b>: Returns the object used to process declarations related
236      * to notations and unparsed entities.
237      */
238     public DTDHandler getDTDHandler ()
239     {
240 	return (dtdHandler == base) ? null : dtdHandler;
241     }
242 
243     /***
244      * <b>SAX1, SAX2</b>: Set the DTD handler for this parser.
245      * @param handler The object to receive DTD events.
246      */
247     public void setDTDHandler (DTDHandler handler)
248     {
249 	if (handler == null)
250 	    handler = base;
251 	this.dtdHandler = handler;
252     }
253 
254 
255     /***
256      * <b>SAX1</b>: Set the document handler for this parser.  If a
257      * content handler was set, this document handler will supplant it.
258      * The parser is set to report all XML 1.0 names rather than to
259      * filter out "xmlns" attributes (the "namespace-prefixes" feature
260      * is set to true).
261      *
262      * @deprecated SAX2 programs should use the XMLReader interface
263      *	and a ContentHandler.
264      *
265      * @param handler The object to receive document events.
266      */
267     public void setDocumentHandler (DocumentHandler handler)
268     {
269 	contentHandler = new Adapter (handler);
270 	xmlNames = true;
271     }
272 
273     /***
274      * <b>SAX2</b>: Returns the object used to report the logical
275      * content of an XML document.
276      */
277     public ContentHandler getContentHandler ()
278     {
279 	return contentHandler == base ? null : contentHandler;
280     }
281 
282     /***
283      * <b>SAX2</b>: Assigns the object used to report the logical
284      * content of an XML document.  If a document handler was set,
285      * this content handler will supplant it (but XML 1.0 style name
286      * reporting may remain enabled).
287      */
288     public void setContentHandler (ContentHandler handler)
289     {
290 	if (handler == null)
291 	    handler = base;
292 	contentHandler = handler;
293     }
294 
295     /***
296      * <b>SAX1, SAX2</b>: Set the error handler for this parser.
297      * @param handler The object to receive error events.
298      */
299     public void setErrorHandler (ErrorHandler handler)
300     {
301 	if (handler == null)
302 	    handler = base;
303 	this.errorHandler = handler;
304     }
305 
306     /***
307      * <b>SAX2</b>: Returns the object used to receive callbacks for XML
308      * errors of all levels (fatal, nonfatal, warning); this is never null;
309      */
310     public ErrorHandler getErrorHandler ()
311 	{ return errorHandler == base ? null : errorHandler; }
312 
313 
314     /***
315      * <b>SAX1, SAX2</b>: Auxiliary API to parse an XML document, used mostly
316      * when no URI is available.
317      * If you want anything useful to happen, you should set
318      * at least one type of handler.
319      * @param source The XML input source.  Don't set 'encoding' unless
320      *	you know for a fact that it's correct.
321      * @see #setEntityResolver
322      * @see #setDTDHandler
323      * @see #setContentHandler
324      * @see #setErrorHandler
325      * @exception SAXException The handlers may throw any SAXException,
326      *	and the parser normally throws SAXParseException objects.
327      * @exception IOException IOExceptions are normally through through
328      *	the parser if there are problems reading the source document.
329      */
330     public void parse (InputSource source)
331     throws SAXException, IOException
332     {
333 	synchronized (base) {
334 	    parser = new XmlParser ();
335 	    if (namespaces)
336 		prefixStack = new NamespaceSupport ();
337 	    else if (!xmlNames)
338 		throw new IllegalStateException ();
339 	    parser.setHandler (this);
340 
341 	    try {
342 
343 	      Reader r = source.getCharacterStream();
344 	      InputStream in = source.getByteStream();
345 
346 	      
347 		parser.doParse (source.getSystemId (),
348 			      source.getPublicId (),
349 			      r,
350 			      in,
351 			      source.getEncoding ());
352 	    } catch (SAXException e) {
353 		throw e;
354 	    } catch (IOException e) {
355 		throw e;
356 	    } catch (RuntimeException e) {
357 		throw e;
358 	    } catch (Exception e) {
359 		throw new SAXParseException (e.getMessage (), this, e);
360 	    } finally {
361 		contentHandler.endDocument ();
362                 reset();
363 	    }
364 	}
365     }
366 
367 
368     /***
369      * <b>SAX1, SAX2</b>: Preferred API to parse an XML document, using a
370      * system identifier (URI).
371      */
372     public void parse (String systemId)
373     throws SAXException, IOException
374     {
375 	parse (new InputSource (systemId));
376     }
377 
378     //
379     // Implementation of SAX2 "XMLReader" interface
380     //
381     static final String	FEATURE = "http://xml.org/sax/features/";
382     static final String	PROPERTY = "http://xml.org/sax/properties/";
383 
384     /***
385      * <b>SAX2</b>: Tells the value of the specified feature flag.
386      *
387      * @exception SAXNotRecognizedException thrown if the feature flag
388      *	is neither built in, nor yet assigned.
389      */
390     public boolean getFeature (String featureId)
391     throws SAXNotRecognizedException, SAXNotSupportedException
392     {
393 	if ((FEATURE + "validation").equals (featureId))
394 	    return false;
395 
396 	// external entities (both types) are optionally included
397 	if ((FEATURE + "external-general-entities").equals (featureId))
398 	    return extGE;
399 	if ((FEATURE + "external-parameter-entities") .equals (featureId))
400 	    return extPE;
401 
402 	// element/attribute names are as written in document; no mangling
403 	if ((FEATURE + "namespace-prefixes").equals (featureId))
404 	    return xmlNames;
405 
406 	// report element/attribute namespaces?
407 	if ((FEATURE + "namespaces").equals (featureId))
408 	    return namespaces;
409 
410 	// all PEs and GEs are reported
411 	if ((FEATURE + "lexical-handler/parameter-entities").equals (featureId))
412 	    return true;
413 
414 	// default is true
415 	if ((FEATURE + "string-interning").equals (featureId))
416 	    return stringInterning;
417 	
418 	// EXTENSIONS 1.1
419 
420 	// always returns isSpecified info
421 	if ((FEATURE + "use-attributes2").equals (featureId))
422 	    return true;
423 	
424 	// meaningful between startDocument/endDocument
425 	if ((FEATURE + "is-standalone").equals (featureId)) {
426 	    if (parser == null)
427 		throw new SAXNotSupportedException (featureId);
428 	    return parser.isStandalone ();
429 	}
430 
431 	// optionally don't absolutize URIs in declarations
432 	if ((FEATURE + "resolve-dtd-uris").equals (featureId))
433 	    return resolveAll;
434 
435 	// optionally use resolver2 interface methods, if possible
436 	if ((FEATURE + "use-entity-resolver2").equals (featureId))
437 	    return useResolver2;
438 	
439 	throw new SAXNotRecognizedException (featureId);
440     }
441 
442     // package private
443     DeclHandler getDeclHandler () { return declHandler; }
444 
445     // package private
446     boolean resolveURIs () { return resolveAll; }
447 
448     /***
449      * <b>SAX2</b>:  Returns the specified property.
450      *
451      * @exception SAXNotRecognizedException thrown if the property value
452      *	is neither built in, nor yet stored.
453      */
454     public Object getProperty (String propertyId)
455     throws SAXNotRecognizedException
456     {
457 	if ((PROPERTY + "declaration-handler").equals (propertyId))
458 	    return declHandler == base ? null : declHandler;
459 
460 	if ((PROPERTY + "lexical-handler").equals (propertyId))
461 	    return lexicalHandler == base ? null : lexicalHandler;
462 	
463 	// unknown properties
464 	throw new SAXNotRecognizedException (propertyId);
465     }
466 
467     /***
468      * <b>SAX2</b>:  Sets the state of feature flags in this parser.  Some
469      * built-in feature flags are mutable.
470      */
471     public void setFeature (String featureId, boolean value)
472     throws SAXNotRecognizedException, SAXNotSupportedException
473     {
474 	boolean	state;
475 	
476 	// Features with a defined value, we just change it if we can.
477 	state = getFeature (featureId);
478 
479 	if (state == value)
480 	    return;
481 	if (parser != null)
482 	    throw new SAXNotSupportedException ("not while parsing");
483 
484 	if ((FEATURE + "namespace-prefixes").equals (featureId)) {
485 	    // in this implementation, this only affects xmlns reporting
486 	    xmlNames = value;
487 	    // forcibly prevent illegal parser state
488 	    if (!xmlNames)
489 		namespaces = true;
490 	    return;
491 	}
492 
493 	if ((FEATURE + "namespaces").equals (featureId)) {
494 	    namespaces = value;
495 	    // forcibly prevent illegal parser state
496 	    if (!namespaces)
497 		xmlNames = true;
498 	    return;
499 	}
500 
501 	if ((FEATURE + "external-general-entities").equals (featureId)) {
502 	    extGE = value;
503 	    return;
504 	}
505 	if ((FEATURE + "external-parameter-entities") .equals (featureId)) {
506 	    extPE = value;
507 	    return;
508 	}
509 	if ((FEATURE + "resolve-dtd-uris").equals (featureId)) {
510 	    resolveAll = value;
511 	    return;
512 	}
513 
514 	if ((FEATURE + "use-entity-resolver2").equals (featureId)) {
515 	    useResolver2 = value;
516 	    return;
517 	}
518 
519 	throw new SAXNotRecognizedException (featureId);
520     }
521 
522     /***
523      * <b>SAX2</b>:  Assigns the specified property.  Like SAX1 handlers,
524      * these may be changed at any time.
525      */
526     public void setProperty (String propertyId, Object value)
527     throws SAXNotRecognizedException, SAXNotSupportedException
528     {
529 	// see if the property is recognized
530 	getProperty (propertyId);
531 
532 	// Properties with a defined value, we just change it if we can.
533 
534 	if ((PROPERTY + "declaration-handler").equals (propertyId)) {
535 	    if (value == null)
536 		declHandler = base;
537 	    else if (! (value instanceof DeclHandler))
538 		throw new SAXNotSupportedException (propertyId);
539 	    else
540 		declHandler = (DeclHandler) value;
541 	    return ;
542 	}
543 
544 	if ((PROPERTY + "lexical-handler").equals (propertyId)) {
545 	    if (value == null)
546 		lexicalHandler = base;
547 	    else if (! (value instanceof LexicalHandler))
548 		throw new SAXNotSupportedException (propertyId);
549 	    else
550 		lexicalHandler = (LexicalHandler) value;
551 	    return ;
552 	}
553 
554 	throw new SAXNotSupportedException (propertyId);
555     }
556 
557 
558     //
559     // This is where the driver receives XmlParser callbacks and translates
560     // them into SAX callbacks.  Some more callbacks have been added for
561     // SAX2 support.
562     //
563 
564     void startDocument ()
565     throws SAXException
566     {
567 	contentHandler.setDocumentLocator (this);
568 	contentHandler.startDocument ();
569 	attributesList.clear ();
570     }
571 
572     void skippedEntity (String name)
573     throws SAXException
574 	{ contentHandler.skippedEntity (name); }
575 
576     InputSource getExternalSubset (String name, String baseURI)
577     throws SAXException, IOException
578     {
579 	if (resolver2 == null || !useResolver2 || !extPE)
580 	    return null;
581 	return resolver2.getExternalSubset (name, baseURI);
582     }
583 
584     InputSource resolveEntity (boolean isPE, String name,
585     	InputSource in, String baseURI)
586     throws SAXException, IOException
587     {
588 	InputSource	source;
589 
590 	// external entities might be skipped
591 	if (isPE && !extPE)
592 	    return null;
593 	if (!isPE && !extGE)
594 	    return null;
595 
596 	// ... or not
597 	lexicalHandler.startEntity (name);
598 	if (resolver2 != null && useResolver2) {
599 	    source = resolver2.resolveEntity (name, in.getPublicId (),
600 			baseURI, in.getSystemId ());
601 	    if (source == null) {
602 		in.setSystemId (absolutize (baseURI,
603 				in.getSystemId (), false));
604 		source = in;
605 	    }
606 	} else {
607 	    in.setSystemId (absolutize (baseURI, in.getSystemId (), false));
608 	    source = entityResolver.resolveEntity (in.getPublicId (),
609 			in.getSystemId ());
610 	    if (source == null)
611 		source = in;
612 	}
613 	startExternalEntity (name, source.getSystemId (), true);
614 	return source;
615     }
616 
617     // absolutize a system ID relative to the specified base URI
618     // (temporarily) package-visible for external entity decls
619     String absolutize (String baseURI, String systemId, boolean nice)
620     throws MalformedURLException, SAXException
621     {
622 	// FIXME normalize system IDs -- when?
623 	// - Convert to UTF-8
624 	// - Map reserved and non-ASCII characters to %HH
625 
626 	try {
627 	    if (baseURI == null) {
628 		warn ("No base URI; hope this SYSTEM id is absolute: "
629 			+ systemId);
630 		return new URL (systemId).toString ();
631 	    } else
632 		return new URL (new URL (baseURI), systemId).toString ();
633 
634 	} catch (MalformedURLException e) {
635 
636 	    // Let unknown URI schemes pass through unless we need
637 	    // the JVM to map them to i/o streams for us...
638 	    if (!nice)
639 		throw e;
640 
641 	    // sometimes sysids for notations or unparsed entities
642 	    // aren't really URIs...
643 	    warn ("Can't absolutize SYSTEM id: " + e.getMessage ());
644 	    return systemId;
645 	}
646     }
647 
648     void startExternalEntity (String name, String systemId,
649     	boolean stackOnly)
650     throws SAXException
651     {
652 	// The following warning was deleted because the application has the
653 	// option of not setting systemId. Sun's JAXP or Xerces seems to
654 	// ignore this case.
655 	/*
656 	if (systemId == null)
657 	    warn ("URI was not reported to parser for entity " + name);
658 	*/
659 	if (!stackOnly)		// spliced [dtd] needs startEntity
660 	    lexicalHandler.startEntity (name);
661 	entityStack.push (systemId);
662     }
663 
664     void endExternalEntity (String name)
665     throws SAXException
666     {
667 	if (!"[document]".equals (name))
668 	    lexicalHandler.endEntity (name);
669 	entityStack.pop ();
670     }
671 
672     void startInternalEntity (String name)
673     throws SAXException
674     {
675 	lexicalHandler.startEntity (name);
676     }
677 
678     void endInternalEntity (String name)
679     throws SAXException
680     {
681 	lexicalHandler.endEntity (name);
682     }
683 
684     void doctypeDecl (String name, String publicId, String systemId)
685     throws SAXException
686     {
687 	lexicalHandler.startDTD (name, publicId, systemId);
688 	
689 	// ... the "name" is a declaration and should be given
690 	// to the DeclHandler (but sax2 doesn't).
691 
692 	// the IDs for the external subset are lexical details,
693 	// as are the contents of the internal subset; but sax2
694 	// doesn't provide the internal subset "pre-parse"
695     }
696 
697     void notationDecl (String name, String ids [])
698     throws SAXException
699     {
700 	try {
701 	    dtdHandler.notationDecl (name, ids [0],
702 		(resolveAll && ids [1] != null)
703 			? absolutize (ids [2], ids [1], true)
704 			: ids [1]);
705 	} catch (IOException e) {
706 	    // "can't happen"
707 	    throw new SAXParseException (e.getMessage (), this, e);
708 	}
709     }
710 
711     void unparsedEntityDecl (String name, String ids [], String notation)
712     throws SAXException
713     {
714 	try {
715 	    dtdHandler.unparsedEntityDecl (name, ids [0],
716 		    resolveAll
717 			? absolutize (ids [2], ids [1], true)
718 			: ids [1],
719 		    notation);
720 	} catch (IOException e) {
721 	    // "can't happen"
722 	    throw new SAXParseException (e.getMessage (), this, e);
723 	}
724     }
725 
726     void endDoctype ()
727     throws SAXException
728     {
729 	lexicalHandler.endDTD ();
730     }
731 
732     private void declarePrefix (String prefix, String uri)
733     throws SAXException
734     {
735 	int index = uri.indexOf (':');
736 
737 	// many versions of nwalsh docbook stylesheets 
738 	// have bogus URLs; so this can't be an error...
739 	if (index < 1 && uri.length () != 0)
740 	    warn ("relative URI for namespace: " + uri);
741 
742 	// FIXME:  char [0] must be ascii alpha; chars [1..index]
743 	// must be ascii alphanumeric or in "+-." [RFC 2396]
744 	
745 	//Namespace Constraints
746 	//name for xml prefix must be http://www.w3.org/XML/1998/namespace
747 	boolean prefixEquality = prefix.equals("xml");
748 	boolean uriEquality = uri.equals("http://www.w3.org/XML/1998/namespace");
749 	if ((prefixEquality || uriEquality) && !(prefixEquality && uriEquality))
750 	   fatal ("xml is by definition bound to the namespace name " +
751 	   		"http://www.w3.org/XML/1998/namespace");
752 	
753         //xmlns prefix declaration is illegal but xml prefix declaration is llegal...
754 	if (prefixEquality && uriEquality)
755 	   return;
756 	
757         //name for xmlns prefix must be http://www.w3.org/2000/xmlns/
758 	prefixEquality = prefix.equals("xmlns");
759 	uriEquality = uri.equals("http://www.w3.org/2000/xmlns/");
760 	if ((prefixEquality || uriEquality) && !(prefixEquality && uriEquality))
761 	   fatal("http://www.w3.org/2000/xmlns/ is by definition bound" +
762 	   		" to prefix xmlns");
763 	
764 	//even if the uri is http://www.w3.org/2000/xmlns/ it is illegal to declare it
765 	if (prefixEquality && uriEquality)
766 	   fatal ("declaring the xmlns prefix is illegal");
767 		
768 	uri = uri.intern ();
769 	prefixStack.declarePrefix (prefix, uri);
770 	contentHandler.startPrefixMapping (prefix, uri);
771     }
772 
773     void attribute (String qname, String value, boolean isSpecified)
774     throws SAXException
775     {
776 	if (!attributes) {
777 	    attributes = true;
778 	    if (namespaces)
779 		prefixStack.pushContext ();
780 	}
781 
782 	// process namespace decls immediately;
783 	// then maybe forget this as an attribute
784 	if (namespaces) {
785 	    int	index;
786 
787       // default NS declaration?
788       if (getFeature (FEATURE + "string-interning")) {
789         if ("xmlns" == qname) {
790           declarePrefix ("", value);
791           if (!xmlNames)
792             return;
793         }
794         // NS prefix declaration?
795         else if ((index = qname.indexOf (':')) == 5
796                  && qname.startsWith ("xmlns")) {
797           String		prefix = qname.substring (6);
798           
799           if (prefix.equals(""))
800           	fatal ("missing prefix in namespace declaration attribute");	
801           if (value.length () == 0) {
802             verror ("missing URI in namespace declaration attribute: "
803                     + qname);
804           } else
805             declarePrefix (prefix, value);
806           if (!xmlNames)
807             return;
808         }
809       } else {
810         if ("xmlns".equals(qname)) {
811           declarePrefix ("", value);
812           if (!xmlNames)
813             return;
814         }
815         // NS prefix declaration?
816         else if ((index = qname.indexOf (':')) == 5
817                  && qname.startsWith ("xmlns")) {
818           String		prefix = qname.substring (6);
819           
820           if (value.length () == 0) {
821             verror ("missing URI in namespace decl attribute: "
822                     + qname);
823           } else
824             declarePrefix (prefix, value);
825           if (!xmlNames)
826             return;
827         }
828       }
829   }
830 	// remember this attribute ...
831 
832 	if (attributeCount == attributeSpecified.length) { 	// grow array?
833 	    boolean temp [] = new boolean [attributeSpecified.length + 5];
834 	    System.arraycopy (attributeSpecified, 0, temp, 0, attributeCount);
835 	    attributeSpecified = temp;
836 	}
837 	attributeSpecified [attributeCount] = isSpecified;
838 
839 	attributeCount++;
840 	
841 	// attribute type comes from querying parser's DTD records
842 	attributesList.add(new Attribute(qname, value));
843 
844     }
845 
846     void startElement (String elname)
847     throws SAXException
848     {
849 	ContentHandler handler = contentHandler;
850 
851 	//
852 	// NOTE:  this implementation of namespace support adds something
853 	// like six percent to parsing CPU time, in a large (~50 MB)
854 	// document that doesn't use namespaces at all.  (Measured by PC
855 	// sampling, with a bug where endElement processing was omitted.)
856 	// [Measurement referred to older implementation, older JVM ...]
857 	//
858 	// It ought to become notably faster in such cases.  Most
859 	// costs are the prefix stack calling Hashtable.get() (2%),
860 	// String.hashCode() (1.5%) and about 1.3% each for pushing
861 	// the context, and two chunks of name processing.
862 	//
863 
864 	if (!attributes) {
865 	    if (namespaces)
866 		prefixStack.pushContext ();
867 	} else if (namespaces) {
868 
869 	    // now we can patch up namespace refs; we saw all the
870 	    // declarations, so now we'll do the Right Thing
871 	    Iterator itt = attributesList.iterator ();
872 	    while(itt.hasNext())
873 	    {
874 	    	Attribute attribute = (Attribute) itt.next();
875 	    	String	qname = attribute.name;
876 		int	index;
877 
878     // default NS declaration?
879     if (getFeature (FEATURE + "string-interning")) {
880       if ("xmlns" == qname)
881 		    continue;
882     } else {
883       if ("xmlns".equals(qname))
884 		    continue;
885     }
886                //Illegal in the new Namespaces Draft
887                //should it be only in 1.1 docs??
888                if (qname.equals (":"))
889                    fatal ("namespace names consisting of a single colon " +
890                    		"character are invalid");
891 		index = qname.indexOf (':');
892 
893 		// NS prefix declaration?
894 		if (index == 5 && qname.startsWith ("xmlns"))
895 		    continue;
896 
897 		// it's not a NS decl; patch namespace info items
898 		if (prefixStack.processName (qname, nsTemp, true) == null)
899 		    fatal ("undeclared attribute prefix in: " + qname);
900 		else {
901 		    attribute.nameSpace = nsTemp[0];
902 		    attribute.localName = nsTemp[1];
903 		}
904 	    }
905 	}
906 
907 	// save element name so attribute callbacks work
908 	elementName = elname;
909 	if (namespaces) {
910 	    if (prefixStack.processName (elname, nsTemp, false) == null) {
911 		fatal ("undeclared element prefix in: " + elname);
912 		nsTemp [0] = nsTemp [1] = "";
913 	    }
914 	    handler.startElement (nsTemp [0], nsTemp [1], elname, this);
915 	} else
916 	    handler.startElement ("", "", elname, this);
917 	// elementName = null;
918 
919 	// elements with no attributes are pretty common!
920 	if (attributes) {
921 	    attributesList.clear();
922 	    attributeCount = 0;
923 	    attributes = false;
924 	}
925     }
926 
927     void endElement (String elname)
928     throws SAXException
929     {
930 	ContentHandler	handler = contentHandler;
931 
932 	if (!namespaces) {
933 	    handler.endElement ("", "", elname);
934 	    return;
935 	}
936 	prefixStack.processName (elname, nsTemp, false);
937 	handler.endElement (nsTemp [0], nsTemp [1], elname);
938 
939 	Enumeration	prefixes = prefixStack.getDeclaredPrefixes ();
940 
941 	while (prefixes.hasMoreElements ())
942 	    handler.endPrefixMapping ((String) prefixes.nextElement ());
943 	prefixStack.popContext ();
944     }
945 
946     void startCDATA ()
947     throws SAXException
948     {
949 	lexicalHandler.startCDATA ();
950     }
951 
952     void charData (char ch[], int start, int length)
953     throws SAXException
954     {
955 	contentHandler.characters (ch, start, length);
956     }
957 
958     void endCDATA ()
959     throws SAXException
960     {
961 	lexicalHandler.endCDATA ();
962     }
963 
964     void ignorableWhitespace (char ch[], int start, int length)
965     throws SAXException
966     {
967 	contentHandler.ignorableWhitespace (ch, start, length);
968     }
969 
970     void processingInstruction (String target, String data)
971     throws SAXException
972     {
973 	contentHandler.processingInstruction (target, data);
974     }
975 
976     void comment (char ch[], int start, int length)
977     throws SAXException
978     {
979 	if (lexicalHandler != base)
980 	    lexicalHandler.comment (ch, start, length);
981     }
982 
983     void fatal (String message)
984     throws SAXException
985     {
986 	SAXParseException fatal;
987 	
988 	fatal = new SAXParseException (message, this);
989 	errorHandler.fatalError (fatal);
990 
991 	// Even if the application can continue ... we can't!
992 	throw fatal;
993     }
994 
995     // We can safely report a few validity errors that
996     // make layered SAX2 DTD validation more conformant
997     void verror (String message)
998     throws SAXException
999     {
1000 	SAXParseException err;
1001 	
1002 	err = new SAXParseException (message, this);
1003 	errorHandler.error (err);
1004     }
1005 
1006     void warn (String message)
1007     throws SAXException
1008     {
1009 	SAXParseException err;
1010 	
1011 	err = new SAXParseException (message, this);
1012 	errorHandler.warning (err);
1013     }
1014 
1015 
1016     //
1017     // Implementation of org.xml.sax.Attributes.
1018     //
1019 
1020     /***
1021      * <b>SAX1 AttributeList, SAX2 Attributes</b> method
1022      * (don't invoke on parser);
1023      */
1024     public int getLength ()
1025     {
1026 	return attributesList.size ();
1027     }
1028 
1029     /***
1030      * <b>SAX2 Attributes</b> method (don't invoke on parser);
1031      */
1032     public String getURI (int index)
1033     {
1034 	return ((Attribute) attributesList.get (index)).nameSpace;
1035     }
1036 
1037     /***
1038      * <b>SAX2 Attributes</b> method (don't invoke on parser);
1039      */
1040     public String getLocalName (int index)
1041     {
1042         return ((Attribute) attributesList.get (index)).localName;
1043     }
1044 
1045     /***
1046      * <b>SAX2 Attributes</b> method (don't invoke on parser);
1047      */
1048     public String getQName (int i)
1049     {
1050     	return ((Attribute) attributesList.get (i)).name;
1051     }
1052 
1053     /***
1054      * <b>SAX1 AttributeList</b> method (don't invoke on parser);
1055      */
1056     public String getName (int i)
1057     {
1058     	return ((Attribute) attributesList.get (i)).name;
1059     }
1060 
1061     /***
1062      * <b>SAX1 AttributeList, SAX2 Attributes</b> method
1063      * (don't invoke on parser);
1064      */
1065     public String getType (int i)
1066     {
1067 	String	type = parser.getAttributeType (elementName, getQName (i));
1068 	if (type == null)
1069 	    return "CDATA";
1070 	// ... use DeclHandler.attributeDecl to see enumerations
1071       if (type == "ENUMERATION")
1072         return "NMTOKEN";
1073 	return type;
1074     }
1075 
1076 
1077     /***
1078      * <b>SAX1 AttributeList, SAX2 Attributes</b> method
1079      * (don't invoke on parser);
1080      */
1081     public String getValue (int i)
1082     {
1083     	return ((Attribute) attributesList.get (i)).value;
1084     }
1085 
1086 
1087     /***
1088      * <b>SAX2 Attributes</b> method (don't invoke on parser);
1089      */
1090     public int getIndex (String uri, String local)
1091     {
1092 	int length = getLength ();
1093 
1094 	for (int i = 0; i < length; i++) {
1095 	    if (!getURI (i).equals (uri))
1096 		continue;
1097 	    if (getLocalName (i).equals (local))
1098 		return i;
1099 	}
1100 	return -1;
1101     }
1102 
1103 
1104     /***
1105      * <b>SAX2 Attributes</b> method (don't invoke on parser);
1106      */
1107     public int getIndex (String xmlName)
1108     {
1109 	int length = getLength ();
1110 
1111 	for (int i = 0; i < length; i++) {
1112 	    if (getQName (i).equals (xmlName))
1113 		return i;
1114 	}
1115 	return -1;
1116     }
1117 
1118 
1119     /***
1120      * <b>SAX2 Attributes</b> method (don't invoke on parser);
1121      */
1122     public String getType (String uri, String local)
1123     {
1124 	int index = getIndex (uri, local);
1125 
1126 	if (index < 0)
1127 	    return null;
1128 	return getType (index);
1129     }
1130 
1131 
1132     /***
1133      * <b>SAX1 AttributeList, SAX2 Attributes</b> method
1134      * (don't invoke on parser);
1135      */
1136     public String getType (String xmlName)
1137     {
1138 	int index = getIndex (xmlName);
1139 
1140 	if (index < 0)
1141 	    return null;
1142 	return getType (index);
1143     }
1144 
1145 
1146     /***
1147      * <b>SAX Attributes</b> method (don't invoke on parser);
1148      */
1149     public String getValue (String uri, String local)
1150     {
1151 	int index = getIndex (uri, local);
1152 
1153 	if (index < 0)
1154 	    return null;
1155 	return getValue (index);
1156     }
1157 
1158 
1159     /***
1160      * <b>SAX1 AttributeList, SAX2 Attributes</b> method
1161      * (don't invoke on parser);
1162      */
1163     public String getValue (String xmlName)
1164     {
1165 	int index = getIndex (xmlName);
1166 
1167 	if (index < 0)
1168 	    return null;
1169 	return getValue (index);
1170     }
1171 
1172 
1173     //
1174     // Implementation of org.xml.sax.ext.Attributes2
1175     //
1176 
1177 
1178     /*** @return false unless the attribute was declared in the DTD.
1179      * @throws java.lang.ArrayIndexOutOfBoundsException
1180      *   When the supplied index does not identify an attribute.
1181      */    
1182     public boolean isDeclared (int index)
1183     {
1184 	if (index < 0 || index >= attributeCount) 
1185 	    throw new ArrayIndexOutOfBoundsException ();
1186 	return attributeDeclared [index];
1187     }
1188 
1189     /*** @return false unless the attribute was declared in the DTD.
1190      * @throws java.lang.IllegalArgumentException
1191      *   When the supplied names do not identify an attribute.
1192      */
1193     public boolean isDeclared (java.lang.String qName)
1194     {
1195 	int index = getIndex (qName);
1196 	if (index < 0)
1197 	    throw new IllegalArgumentException ();
1198 	return attributeDeclared [index];
1199     }
1200 
1201     /*** @return false unless the attribute was declared in the DTD.
1202      * @throws java.lang.IllegalArgumentException
1203      *   When the supplied names do not identify an attribute.
1204      */
1205     public boolean isDeclared (java.lang.String uri, java.lang.String localName)
1206     {
1207 	int index = getIndex (uri, localName);
1208 	if (index < 0)
1209 	    throw new IllegalArgumentException ();
1210 	return attributeDeclared [index];
1211     }
1212 
1213 
1214     /***
1215      * <b>SAX-ext Attributes2</b> method (don't invoke on parser);
1216      */
1217     public boolean isSpecified (int index)
1218     {
1219 	if (index < 0 || index >= attributeCount) 
1220 	    throw new ArrayIndexOutOfBoundsException ();
1221 	return attributeSpecified [index];
1222     }
1223 
1224     /***
1225      * <b>SAX-ext Attributes2</b> method (don't invoke on parser);
1226      */
1227     public boolean isSpecified (String uri, String local)
1228     {
1229 	int index = getIndex (uri, local);
1230 
1231 	if (index < 0)
1232 	    throw new IllegalArgumentException ();
1233 	return attributeSpecified [index];
1234     }
1235 
1236     /***
1237      * <b>SAX-ext Attributes2</b> method (don't invoke on parser);
1238      */
1239     public boolean isSpecified (String xmlName)
1240     {
1241 	int index = getIndex (xmlName);
1242 
1243 	if (index < 0)
1244 	    throw new IllegalArgumentException ();
1245 	return attributeSpecified [index];
1246     }
1247 
1248 
1249     //
1250     // Implementation of org.xml.sax.Locator.
1251     //
1252 
1253     /***
1254      * <b>SAX Locator</b> method (don't invoke on parser);
1255      */
1256     public String getPublicId ()
1257     {
1258 	return null; 		// FIXME track public IDs too
1259     }
1260 
1261     /***
1262      * <b>SAX Locator</b> method (don't invoke on parser);
1263      */
1264     public String getSystemId ()
1265     {
1266 	if (entityStack.empty ())
1267 	    return null;
1268 	else
1269 	    return (String) entityStack.peek ();
1270     }
1271 
1272     /***
1273      * <b>SAX Locator</b> method (don't invoke on parser);
1274      */
1275     public int getLineNumber ()
1276     {
1277 	return parser.getLineNumber ();
1278     }
1279 
1280     /***
1281      * <b>SAX Locator</b> method (don't invoke on parser);
1282      */
1283     public int getColumnNumber ()
1284     {
1285 	return parser.getColumnNumber ();
1286     }
1287 
1288     // adapter between SAX2 content handler and SAX1 document handler callbacks
1289     private static class Adapter implements ContentHandler
1290     {
1291 	private DocumentHandler		docHandler;
1292 
1293 	Adapter (DocumentHandler dh)
1294 	    { docHandler = dh; }
1295 
1296 
1297 	public void setDocumentLocator (Locator l)
1298 	    { docHandler.setDocumentLocator (l); }
1299 	
1300 	public void startDocument () throws SAXException
1301 	    { docHandler.startDocument (); }
1302 	
1303 	public void processingInstruction (String target, String data)
1304 	throws SAXException
1305 	    { docHandler.processingInstruction (target, data); }
1306 	
1307 	public void startPrefixMapping (String prefix, String uri)
1308 	    { /* ignored */ }
1309 
1310 	public void startElement (
1311 	    String	namespace,
1312 	    String	local,
1313 	    String	name,
1314 	    Attributes	attrs
1315 	) throws SAXException
1316 	    { docHandler.startElement (name, (AttributeList) attrs); }
1317 
1318 	public void characters (char buf [], int offset, int len)
1319 	throws SAXException
1320 	    { docHandler.characters (buf, offset, len); }
1321 
1322 	public void ignorableWhitespace (char buf [], int offset, int len)
1323 	throws SAXException
1324 	    { docHandler.ignorableWhitespace (buf, offset, len); }
1325 
1326 	public void skippedEntity (String name)
1327 	    { /* ignored */ }
1328 
1329 	public void endElement (String u, String l, String name)
1330 	throws SAXException
1331 	    { docHandler.endElement (name); }
1332 
1333 	public void endPrefixMapping (String prefix)
1334 	    { /* ignored */ }
1335 
1336 	public void endDocument () throws SAXException
1337 	    { docHandler.endDocument (); }
1338     }
1339 }
1340 
1341 class Attribute
1342 {
1343 
1344     String name;
1345     String value;
1346     String nameSpace;
1347     String localName;
1348 
1349     Attribute(String name, String value)
1350     {
1351         this.name = name;
1352         this.value = value;
1353     }
1354 }