Merge from the pain train
[official-gcc.git] / libjava / gnu / xml / aelfred2 / SAXDriver.java
blob0e7b3c719a60c825ffc5b1f9b0e701fa6e200754
1 /* SAXDriver.java --
2 Copyright (C) 1999,2000,2001,2004 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
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.
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.
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., 59 Temple Place, Suite 330, Boston, MA
19 02111-1307 USA.
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.
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.
38 Portions derived from code which carried the following notice:
40 Copyright (c) 1997, 1998 by Microstar Software Ltd.
42 AElfred is free for both commercial and non-commercial use and
43 redistribution, provided that Microstar's copyright and disclaimer are
44 retained intact. You are free to modify AElfred for your own use and
45 to redistribute AElfred with your modifications, provided that the
46 modifications are clearly documented.
48 This program is distributed in the hope that it will be useful, but
49 WITHOUT ANY WARRANTY; without even the implied warranty of
50 merchantability or fitness for a particular purpose. Please use it AT
51 YOUR OWN RISK.
54 package gnu.xml.aelfred2;
56 import java.io.*;
58 import java.net.MalformedURLException;
59 import java.net.URL;
60 import java.util.Locale;
61 import java.util.Stack;
63 // maintaining 1.1 compatibility for now ... more portable, PJava, etc
64 // Iterator, Hashmap and ArrayList ought to be faster
65 import java.util.ArrayList;
66 import java.util.Collections;
67 import java.util.Enumeration;
68 import java.util.Hashtable;
69 import java.util.Iterator;
70 import java.util.List;
71 import java.util.Vector;
73 import org.xml.sax.*;
74 import org.xml.sax.ext.*;
75 import org.xml.sax.helpers.NamespaceSupport;
78 /**
79 * An enhanced SAX2 version of Microstar's Ælfred XML parser.
80 * The enhancements primarily relate to significant improvements in
81 * conformance to the XML specification, and SAX2 support. Performance
82 * has been improved. See the package level documentation for more
83 * information.
85 * <table border="1" width='100%' cellpadding='3' cellspacing='0'>
86 * <tr bgcolor='#ccccff'>
87 * <th><font size='+1'>Name</font></th>
88 * <th><font size='+1'>Notes</font></th></tr>
90 * <tr><td colspan=2><center><em>Features ... URL prefix is
91 * <b>http://xml.org/sax/features/</b></em></center></td></tr>
93 * <tr><td>(URL)/external-general-entities</td>
94 * <td>Value defaults to <em>true</em></td></tr>
95 * <tr><td>(URL)/external-parameter-entities</td>
96 * <td>Value defaults to <em>true</em></td></tr>
97 * <tr><td>(URL)/is-standalone</td>
98 * <td>(PRELIMINARY) Returns true iff the document's parsing
99 * has started (some non-error event after <em>startDocument()</em>
100 * was reported) and the document's standalone flag is set.</td></tr>
101 * <tr><td>(URL)/namespace-prefixes</td>
102 * <td>Value defaults to <em>false</em> (but XML 1.0 names are
103 * always reported)</td></tr>
104 * <tr><td>(URL)/lexical-handler/parameter-entities</td>
105 * <td>Value is fixed at <em>true</em></td></tr>
106 * <tr><td>(URL)/namespaces</td>
107 * <td>Value defaults to <em>true</em></td></tr>
108 * <tr><td>(URL)/resolve-dtd-uris</td>
109 * <td>(PRELIMINARY) Value defaults to <em>true</em></td></tr>
110 * <tr><td>(URL)/string-interning</td>
111 * <td>Value is fixed at <em>true</em></td></tr>
112 * <tr><td>(URL)/use-attributes2</td>
113 * <td>(PRELIMINARY) Value is fixed at <em>true</em></td></tr>
114 * <tr><td>(URL)/use-entity-resolver2</td>
115 * <td>(PRELIMINARY) Value defaults to <em>true</em></td></tr>
116 * <tr><td>(URL)/validation</td>
117 * <td>Value is fixed at <em>false</em></td></tr>
119 * <tr><td colspan=2><center><em>Handler Properties ... URL prefix is
120 * <b>http://xml.org/sax/properties/</b></em></center></td></tr>
122 * <tr><td>(URL)/declaration-handler</td>
123 * <td>A declaration handler may be provided. </td></tr>
124 * <tr><td>(URL)/lexical-handler</td>
125 * <td>A lexical handler may be provided. </td></tr>
126 * </table>
128 * <p>This parser currently implements the SAX1 Parser API, but
129 * it may not continue to do so in the future.
131 * @author Written by David Megginson (version 1.2a from Microstar)
132 * @author Updated by David Brownell &lt;dbrownell@users.sourceforge.net&gt;
133 * @see org.xml.sax.Parser
135 final public class SAXDriver
136 implements Locator, Attributes2, XMLReader, Parser, AttributeList
138 private final DefaultHandler2 base = new DefaultHandler2 ();
139 private XmlParser parser;
141 private EntityResolver entityResolver = base;
142 private EntityResolver2 resolver2 = null;
143 private ContentHandler contentHandler = base;
144 private DTDHandler dtdHandler = base;
145 private ErrorHandler errorHandler = base;
146 private DeclHandler declHandler = base;
147 private LexicalHandler lexicalHandler = base;
149 private String elementName;
150 private Stack entityStack;
152 // one vector (of object/struct): faster, smaller
153 private List attributesList;
155 private boolean namespaces = true;
156 private boolean xmlNames = false;
157 private boolean extGE = true;
158 private boolean extPE = true;
159 private boolean resolveAll = true;
160 private boolean useResolver2 = true;
161 private boolean stringInterning = true;
163 private int attributeCount;
164 private boolean attributes;
165 private String nsTemp [];
166 private NamespaceSupport prefixStack;
169 // Constructor.
172 /** Constructs a SAX Parser. */
173 public SAXDriver ()
175 reset ();
178 private void reset ()
180 elementName = null;
181 entityStack = new Stack ();
182 attributesList = Collections.synchronizedList(new ArrayList());
183 attributeCount = 0;
184 attributes = false;
185 nsTemp = new String[3];
186 prefixStack = null;
191 // Implementation of org.xml.sax.Parser.
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
199 public void setLocale (Locale locale)
200 throws SAXException
202 if ("en".equals (locale.getLanguage ()))
203 return ;
205 throw new SAXException ("AElfred2 only supports English locales.");
210 * <b>SAX2</b>: Returns the object used when resolving external
211 * entities during parsing (both general and parameter entities).
213 public EntityResolver getEntityResolver ()
215 return (entityResolver == base) ? null : entityResolver;
219 * <b>SAX1, SAX2</b>: Set the entity resolver for this parser.
220 * @param handler The object to receive entity events.
222 public void setEntityResolver (EntityResolver resolver)
224 if (resolver instanceof EntityResolver2)
225 resolver2 = (EntityResolver2) resolver;
226 else
227 resolver2 = null;
228 if (resolver == null)
229 resolver = base;
230 entityResolver = resolver;
235 * <b>SAX2</b>: Returns the object used to process declarations related
236 * to notations and unparsed entities.
238 public DTDHandler getDTDHandler ()
240 return (dtdHandler == base) ? null : dtdHandler;
244 * <b>SAX1, SAX2</b>: Set the DTD handler for this parser.
245 * @param handler The object to receive DTD events.
247 public void setDTDHandler (DTDHandler handler)
249 if (handler == null)
250 handler = base;
251 this.dtdHandler = handler;
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).
262 * @deprecated SAX2 programs should use the XMLReader interface
263 * and a ContentHandler.
265 * @param handler The object to receive document events.
267 public void setDocumentHandler (DocumentHandler handler)
269 contentHandler = new Adapter (handler);
270 xmlNames = true;
274 * <b>SAX2</b>: Returns the object used to report the logical
275 * content of an XML document.
277 public ContentHandler getContentHandler ()
279 return contentHandler == base ? null : contentHandler;
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).
288 public void setContentHandler (ContentHandler handler)
290 if (handler == null)
291 handler = base;
292 contentHandler = handler;
296 * <b>SAX1, SAX2</b>: Set the error handler for this parser.
297 * @param handler The object to receive error events.
299 public void setErrorHandler (ErrorHandler handler)
301 if (handler == null)
302 handler = base;
303 this.errorHandler = handler;
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;
310 public ErrorHandler getErrorHandler ()
311 { return errorHandler == base ? null : errorHandler; }
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.
330 public void parse (InputSource source)
331 throws SAXException, IOException
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);
341 try {
343 Reader r = source.getCharacterStream();
344 InputStream in = source.getByteStream();
347 parser.doParse (source.getSystemId (),
348 source.getPublicId (),
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();
369 * <b>SAX1, SAX2</b>: Preferred API to parse an XML document, using a
370 * system identifier (URI).
372 public void parse (String systemId)
373 throws SAXException, IOException
375 parse (new InputSource (systemId));
379 // Implementation of SAX2 "XMLReader" interface
381 static final String FEATURE = "http://xml.org/sax/features/";
382 static final String PROPERTY = "http://xml.org/sax/properties/";
385 * <b>SAX2</b>: Tells the value of the specified feature flag.
387 * @exception SAXNotRecognizedException thrown if the feature flag
388 * is neither built in, nor yet assigned.
390 public boolean getFeature (String featureId)
391 throws SAXNotRecognizedException, SAXNotSupportedException
393 if ((FEATURE + "validation").equals (featureId))
394 return false;
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;
402 // element/attribute names are as written in document; no mangling
403 if ((FEATURE + "namespace-prefixes").equals (featureId))
404 return xmlNames;
406 // report element/attribute namespaces?
407 if ((FEATURE + "namespaces").equals (featureId))
408 return namespaces;
410 // all PEs and GEs are reported
411 if ((FEATURE + "lexical-handler/parameter-entities").equals (featureId))
412 return true;
414 // default is true
415 if ((FEATURE + "string-interning").equals (featureId))
416 return stringInterning;
418 // EXTENSIONS 1.1
420 // always returns isSpecified info
421 if ((FEATURE + "use-attributes2").equals (featureId))
422 return true;
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 ();
431 // optionally don't absolutize URIs in declarations
432 if ((FEATURE + "resolve-dtd-uris").equals (featureId))
433 return resolveAll;
435 // optionally use resolver2 interface methods, if possible
436 if ((FEATURE + "use-entity-resolver2").equals (featureId))
437 return useResolver2;
439 throw new SAXNotRecognizedException (featureId);
442 // package private
443 DeclHandler getDeclHandler () { return declHandler; }
445 // package private
446 boolean resolveURIs () { return resolveAll; }
449 * <b>SAX2</b>: Returns the specified property.
451 * @exception SAXNotRecognizedException thrown if the property value
452 * is neither built in, nor yet stored.
454 public Object getProperty (String propertyId)
455 throws SAXNotRecognizedException
457 if ((PROPERTY + "declaration-handler").equals (propertyId))
458 return declHandler == base ? null : declHandler;
460 if ((PROPERTY + "lexical-handler").equals (propertyId))
461 return lexicalHandler == base ? null : lexicalHandler;
463 // unknown properties
464 throw new SAXNotRecognizedException (propertyId);
468 * <b>SAX2</b>: Sets the state of feature flags in this parser. Some
469 * built-in feature flags are mutable.
471 public void setFeature (String featureId, boolean value)
472 throws SAXNotRecognizedException, SAXNotSupportedException
474 boolean state;
476 // Features with a defined value, we just change it if we can.
477 state = getFeature (featureId);
479 if (state == value)
480 return;
481 if (parser != null)
482 throw new SAXNotSupportedException ("not while parsing");
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;
493 if ((FEATURE + "namespaces").equals (featureId)) {
494 namespaces = value;
495 // forcibly prevent illegal parser state
496 if (!namespaces)
497 xmlNames = true;
498 return;
501 if ((FEATURE + "external-general-entities").equals (featureId)) {
502 extGE = value;
503 return;
505 if ((FEATURE + "external-parameter-entities") .equals (featureId)) {
506 extPE = value;
507 return;
509 if ((FEATURE + "resolve-dtd-uris").equals (featureId)) {
510 resolveAll = value;
511 return;
514 if ((FEATURE + "use-entity-resolver2").equals (featureId)) {
515 useResolver2 = value;
516 return;
519 throw new SAXNotRecognizedException (featureId);
523 * <b>SAX2</b>: Assigns the specified property. Like SAX1 handlers,
524 * these may be changed at any time.
526 public void setProperty (String propertyId, Object value)
527 throws SAXNotRecognizedException, SAXNotSupportedException
529 // see if the property is recognized
530 getProperty (propertyId);
532 // Properties with a defined value, we just change it if we can.
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 ;
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 ;
554 throw new SAXNotSupportedException (propertyId);
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.
564 void startDocument ()
565 throws SAXException
567 contentHandler.setDocumentLocator (this);
568 contentHandler.startDocument ();
569 attributesList.clear ();
572 void xmlDecl(String version,
573 String encoding,
574 boolean standalone,
575 String inputEncoding)
576 throws SAXException
578 if (contentHandler instanceof ContentHandler2)
580 ((ContentHandler2) contentHandler).xmlDecl(version,
581 encoding,
582 standalone,
583 inputEncoding);
587 void skippedEntity (String name)
588 throws SAXException
589 { contentHandler.skippedEntity (name); }
591 InputSource getExternalSubset (String name, String baseURI)
592 throws SAXException, IOException
594 if (resolver2 == null || !useResolver2 || !extPE)
595 return null;
596 return resolver2.getExternalSubset (name, baseURI);
599 InputSource resolveEntity (boolean isPE, String name,
600 InputSource in, String baseURI)
601 throws SAXException, IOException
603 InputSource source;
605 // external entities might be skipped
606 if (isPE && !extPE)
607 return null;
608 if (!isPE && !extGE)
609 return null;
611 // ... or not
612 lexicalHandler.startEntity (name);
613 if (resolver2 != null && useResolver2) {
614 source = resolver2.resolveEntity (name, in.getPublicId (),
615 baseURI, in.getSystemId ());
616 if (source == null) {
617 in.setSystemId (absolutize (baseURI,
618 in.getSystemId (), false));
619 source = in;
621 } else {
622 in.setSystemId (absolutize (baseURI, in.getSystemId (), false));
623 source = entityResolver.resolveEntity (in.getPublicId (),
624 in.getSystemId ());
625 if (source == null)
626 source = in;
628 startExternalEntity (name, source.getSystemId (), true);
629 return source;
632 // absolutize a system ID relative to the specified base URI
633 // (temporarily) package-visible for external entity decls
634 String absolutize (String baseURI, String systemId, boolean nice)
635 throws MalformedURLException, SAXException
637 // FIXME normalize system IDs -- when?
638 // - Convert to UTF-8
639 // - Map reserved and non-ASCII characters to %HH
641 try {
642 if (baseURI == null) {
643 warn ("No base URI; hope this SYSTEM id is absolute: "
644 + systemId);
645 return new URL (systemId).toString ();
646 } else
647 return new URL (new URL (baseURI), systemId).toString ();
649 } catch (MalformedURLException e) {
651 // Let unknown URI schemes pass through unless we need
652 // the JVM to map them to i/o streams for us...
653 if (!nice)
654 throw e;
656 // sometimes sysids for notations or unparsed entities
657 // aren't really URIs...
658 warn ("Can't absolutize SYSTEM id: " + e.getMessage ());
659 return systemId;
663 void startExternalEntity (String name, String systemId,
664 boolean stackOnly)
665 throws SAXException
667 // The following warning was deleted because the application has the
668 // option of not setting systemId. Sun's JAXP or Xerces seems to
669 // ignore this case.
671 if (systemId == null)
672 warn ("URI was not reported to parser for entity " + name);
674 if (!stackOnly) // spliced [dtd] needs startEntity
675 lexicalHandler.startEntity (name);
676 entityStack.push (systemId);
679 void endExternalEntity (String name)
680 throws SAXException
682 if (!"[document]".equals (name))
683 lexicalHandler.endEntity (name);
684 entityStack.pop ();
687 void startInternalEntity (String name)
688 throws SAXException
690 lexicalHandler.startEntity (name);
693 void endInternalEntity (String name)
694 throws SAXException
696 lexicalHandler.endEntity (name);
699 void doctypeDecl (String name, String publicId, String systemId)
700 throws SAXException
702 lexicalHandler.startDTD (name, publicId, systemId);
704 // ... the "name" is a declaration and should be given
705 // to the DeclHandler (but sax2 doesn't).
707 // the IDs for the external subset are lexical details,
708 // as are the contents of the internal subset; but sax2
709 // doesn't provide the internal subset "pre-parse"
712 void notationDecl (String name, String ids [])
713 throws SAXException
715 try {
716 dtdHandler.notationDecl (name, ids [0],
717 (resolveAll && ids [1] != null)
718 ? absolutize (ids [2], ids [1], true)
719 : ids [1]);
720 } catch (IOException e) {
721 // "can't happen"
722 throw new SAXParseException (e.getMessage (), this, e);
726 void unparsedEntityDecl (String name, String ids [], String notation)
727 throws SAXException
729 try {
730 dtdHandler.unparsedEntityDecl (name, ids [0],
731 resolveAll
732 ? absolutize (ids [2], ids [1], true)
733 : ids [1],
734 notation);
735 } catch (IOException e) {
736 // "can't happen"
737 throw new SAXParseException (e.getMessage (), this, e);
741 void endDoctype ()
742 throws SAXException
744 lexicalHandler.endDTD ();
747 private void declarePrefix (String prefix, String uri)
748 throws SAXException
750 int index = uri.indexOf (':');
752 // many versions of nwalsh docbook stylesheets
753 // have bogus URLs; so this can't be an error...
754 if (index < 1 && uri.length () != 0)
755 warn ("relative URI for namespace: " + uri);
757 // FIXME: char [0] must be ascii alpha; chars [1..index]
758 // must be ascii alphanumeric or in "+-." [RFC 2396]
760 //Namespace Constraints
761 //name for xml prefix must be http://www.w3.org/XML/1998/namespace
762 boolean prefixEquality = prefix.equals("xml");
763 boolean uriEquality = uri.equals("http://www.w3.org/XML/1998/namespace");
764 if ((prefixEquality || uriEquality) && !(prefixEquality && uriEquality))
765 fatal ("xml is by definition bound to the namespace name " +
766 "http://www.w3.org/XML/1998/namespace");
768 //xmlns prefix declaration is illegal but xml prefix declaration is llegal...
769 if (prefixEquality && uriEquality)
770 return;
772 //name for xmlns prefix must be http://www.w3.org/2000/xmlns/
773 prefixEquality = prefix.equals("xmlns");
774 uriEquality = uri.equals("http://www.w3.org/2000/xmlns/");
775 if ((prefixEquality || uriEquality) && !(prefixEquality && uriEquality))
776 fatal("http://www.w3.org/2000/xmlns/ is by definition bound" +
777 " to prefix xmlns");
779 //even if the uri is http://www.w3.org/2000/xmlns/ it is illegal to declare it
780 if (prefixEquality && uriEquality)
781 fatal ("declaring the xmlns prefix is illegal");
783 uri = uri.intern ();
784 prefixStack.declarePrefix (prefix, uri);
785 contentHandler.startPrefixMapping (prefix, uri);
788 void attribute (String qname, String value, boolean isSpecified)
789 throws SAXException
791 if (!attributes) {
792 attributes = true;
793 if (namespaces)
794 prefixStack.pushContext ();
797 // process namespace decls immediately;
798 // then maybe forget this as an attribute
799 if (namespaces) {
800 int index;
802 // default NS declaration?
803 if (getFeature (FEATURE + "string-interning")) {
804 if ("xmlns" == qname) {
805 declarePrefix ("", value);
806 if (!xmlNames)
807 return;
809 // NS prefix declaration?
810 else if ((index = qname.indexOf (':')) == 5
811 && qname.startsWith ("xmlns")) {
812 String prefix = qname.substring (6);
814 if (prefix.equals(""))
815 fatal ("missing prefix in namespace declaration attribute");
816 if (value.length () == 0) {
817 verror ("missing URI in namespace declaration attribute: "
818 + qname);
819 } else
820 declarePrefix (prefix, value);
821 if (!xmlNames)
822 return;
824 } else {
825 if ("xmlns".equals(qname)) {
826 declarePrefix ("", value);
827 if (!xmlNames)
828 return;
830 // NS prefix declaration?
831 else if ((index = qname.indexOf (':')) == 5
832 && qname.startsWith ("xmlns")) {
833 String prefix = qname.substring (6);
835 if (value.length () == 0) {
836 verror ("missing URI in namespace decl attribute: "
837 + qname);
838 } else
839 declarePrefix (prefix, value);
840 if (!xmlNames)
841 return;
845 // remember this attribute ...
847 attributeCount++;
849 // attribute type comes from querying parser's DTD records
850 attributesList.add(new Attribute(qname, value, isSpecified));
854 void startElement (String elname)
855 throws SAXException
857 ContentHandler handler = contentHandler;
860 // NOTE: this implementation of namespace support adds something
861 // like six percent to parsing CPU time, in a large (~50 MB)
862 // document that doesn't use namespaces at all. (Measured by PC
863 // sampling, with a bug where endElement processing was omitted.)
864 // [Measurement referred to older implementation, older JVM ...]
866 // It ought to become notably faster in such cases. Most
867 // costs are the prefix stack calling Hashtable.get() (2%),
868 // String.hashCode() (1.5%) and about 1.3% each for pushing
869 // the context, and two chunks of name processing.
872 if (!attributes) {
873 if (namespaces)
874 prefixStack.pushContext ();
875 } else if (namespaces) {
877 // now we can patch up namespace refs; we saw all the
878 // declarations, so now we'll do the Right Thing
879 Iterator itt = attributesList.iterator ();
880 while(itt.hasNext())
882 Attribute attribute = (Attribute) itt.next();
883 String qname = attribute.name;
884 int index;
886 // default NS declaration?
887 if (getFeature (FEATURE + "string-interning")) {
888 if ("xmlns" == qname)
889 continue;
890 } else {
891 if ("xmlns".equals(qname))
892 continue;
894 //Illegal in the new Namespaces Draft
895 //should it be only in 1.1 docs??
896 if (qname.equals (":"))
897 fatal ("namespace names consisting of a single colon " +
898 "character are invalid");
899 index = qname.indexOf (':');
901 // NS prefix declaration?
902 if (index == 5 && qname.startsWith ("xmlns"))
903 continue;
905 // it's not a NS decl; patch namespace info items
906 if (prefixStack.processName (qname, nsTemp, true) == null)
907 fatal ("undeclared attribute prefix in: " + qname);
908 else {
909 attribute.nameSpace = nsTemp[0];
910 attribute.localName = nsTemp[1];
915 // save element name so attribute callbacks work
916 elementName = elname;
917 if (namespaces) {
918 if (prefixStack.processName (elname, nsTemp, false) == null) {
919 fatal ("undeclared element prefix in: " + elname);
920 nsTemp [0] = nsTemp [1] = "";
922 handler.startElement (nsTemp [0], nsTemp [1], elname, this);
923 } else
924 handler.startElement ("", "", elname, this);
925 // elementName = null;
927 // elements with no attributes are pretty common!
928 if (attributes) {
929 attributesList.clear();
930 attributeCount = 0;
931 attributes = false;
935 void endElement (String elname)
936 throws SAXException
938 ContentHandler handler = contentHandler;
940 if (!namespaces) {
941 handler.endElement ("", "", elname);
942 return;
944 prefixStack.processName (elname, nsTemp, false);
945 handler.endElement (nsTemp [0], nsTemp [1], elname);
947 Enumeration prefixes = prefixStack.getDeclaredPrefixes ();
949 while (prefixes.hasMoreElements ())
950 handler.endPrefixMapping ((String) prefixes.nextElement ());
951 prefixStack.popContext ();
954 void startCDATA ()
955 throws SAXException
957 lexicalHandler.startCDATA ();
960 void charData (char ch[], int start, int length)
961 throws SAXException
963 contentHandler.characters (ch, start, length);
966 void endCDATA ()
967 throws SAXException
969 lexicalHandler.endCDATA ();
972 void ignorableWhitespace (char ch[], int start, int length)
973 throws SAXException
975 contentHandler.ignorableWhitespace (ch, start, length);
978 void processingInstruction (String target, String data)
979 throws SAXException
981 contentHandler.processingInstruction (target, data);
984 void comment (char ch[], int start, int length)
985 throws SAXException
987 if (lexicalHandler != base)
988 lexicalHandler.comment (ch, start, length);
991 void fatal (String message)
992 throws SAXException
994 SAXParseException fatal;
996 fatal = new SAXParseException (message, this);
997 errorHandler.fatalError (fatal);
999 // Even if the application can continue ... we can't!
1000 throw fatal;
1003 // We can safely report a few validity errors that
1004 // make layered SAX2 DTD validation more conformant
1005 void verror (String message)
1006 throws SAXException
1008 SAXParseException err;
1010 err = new SAXParseException (message, this);
1011 errorHandler.error (err);
1014 void warn (String message)
1015 throws SAXException
1017 SAXParseException err;
1019 err = new SAXParseException (message, this);
1020 errorHandler.warning (err);
1025 // Implementation of org.xml.sax.Attributes.
1029 * <b>SAX1 AttributeList, SAX2 Attributes</b> method
1030 * (don't invoke on parser);
1032 public int getLength ()
1034 return attributesList.size();
1038 * <b>SAX2 Attributes</b> method (don't invoke on parser);
1040 public String getURI (int index)
1042 if (index < 0 || index >= attributesList.size())
1044 return null;
1046 return ((Attribute) attributesList.get(index)).nameSpace;
1050 * <b>SAX2 Attributes</b> method (don't invoke on parser);
1052 public String getLocalName (int index)
1054 if (index < 0 || index >= attributesList.size())
1056 return null;
1058 Attribute attr = (Attribute) attributesList.get(index);
1059 // FIXME attr.localName is sometimes null, why?
1060 if (namespaces && attr.localName == null)
1062 // XXX fix this here for now
1063 int ci = attr.name.indexOf(':');
1064 attr.localName = (ci == -1) ? attr.name :
1065 attr.name.substring(ci + 1);
1067 return (attr.localName == null) ? "" : attr.localName;
1071 * <b>SAX2 Attributes</b> method (don't invoke on parser);
1073 public String getQName (int index)
1075 if (index < 0 || index >= attributesList.size())
1077 return null;
1079 Attribute attr = (Attribute) attributesList.get(index);
1080 return (attr.name == null) ? "" : attr.name;
1084 * <b>SAX1 AttributeList</b> method (don't invoke on parser);
1086 public String getName (int index)
1088 return getQName(index);
1092 * <b>SAX1 AttributeList, SAX2 Attributes</b> method
1093 * (don't invoke on parser);
1095 public String getType (int index)
1097 if (index < 0 || index >= attributesList.size())
1099 return null;
1101 String type = parser.getAttributeType(elementName, getQName(index));
1102 if (type == null)
1104 return "CDATA";
1106 // ... use DeclHandler.attributeDecl to see enumerations
1107 if (type == "ENUMERATION")
1109 return "NMTOKEN";
1111 return type;
1116 * <b>SAX1 AttributeList, SAX2 Attributes</b> method
1117 * (don't invoke on parser);
1119 public String getValue (int index)
1121 if (index < 0 || index >= attributesList.size())
1123 return null;
1125 return ((Attribute) attributesList.get(index)).value;
1130 * <b>SAX2 Attributes</b> method (don't invoke on parser);
1132 public int getIndex (String uri, String local)
1134 int length = getLength();
1136 for (int i = 0; i < length; i++)
1138 if (!getURI(i).equals(uri))
1140 continue;
1142 if (getLocalName(i).equals(local))
1144 return i;
1147 return -1;
1152 * <b>SAX2 Attributes</b> method (don't invoke on parser);
1154 public int getIndex (String xmlName)
1156 int length = getLength();
1158 for (int i = 0; i < length; i++)
1160 if (getQName(i).equals(xmlName))
1162 return i;
1165 return -1;
1170 * <b>SAX2 Attributes</b> method (don't invoke on parser);
1172 public String getType (String uri, String local)
1174 int index = getIndex(uri, local);
1176 if (index < 0)
1178 return null;
1180 return getType(index);
1185 * <b>SAX1 AttributeList, SAX2 Attributes</b> method
1186 * (don't invoke on parser);
1188 public String getType (String xmlName)
1190 int index = getIndex(xmlName);
1192 if (index < 0)
1194 return null;
1196 return getType(index);
1201 * <b>SAX Attributes</b> method (don't invoke on parser);
1203 public String getValue (String uri, String local)
1205 int index = getIndex(uri, local);
1207 if (index < 0)
1209 return null;
1211 return getValue(index);
1216 * <b>SAX1 AttributeList, SAX2 Attributes</b> method
1217 * (don't invoke on parser);
1219 public String getValue (String xmlName)
1221 int index = getIndex(xmlName);
1223 if (index < 0)
1225 return null;
1227 return getValue(index);
1232 // Implementation of org.xml.sax.ext.Attributes2
1236 /** @return false unless the attribute was declared in the DTD.
1237 * @throws java.lang.ArrayIndexOutOfBoundsException
1238 * When the supplied index does not identify an attribute.
1240 public boolean isDeclared (int index)
1242 if (index < 0 || index >= attributeCount)
1243 throw new ArrayIndexOutOfBoundsException ();
1244 String type = parser.getAttributeType(elementName, getQName(index));
1245 return (type != null);
1248 /** @return false unless the attribute was declared in the DTD.
1249 * @throws java.lang.IllegalArgumentException
1250 * When the supplied names do not identify an attribute.
1252 public boolean isDeclared (String qName)
1254 int index = getIndex (qName);
1255 if (index < 0)
1256 throw new IllegalArgumentException ();
1257 String type = parser.getAttributeType(elementName, qName);
1258 return (type != null);
1261 /** @return false unless the attribute was declared in the DTD.
1262 * @throws java.lang.IllegalArgumentException
1263 * When the supplied names do not identify an attribute.
1265 public boolean isDeclared (String uri, String localName)
1267 int index = getIndex (uri, localName);
1268 return isDeclared(index);
1273 * <b>SAX-ext Attributes2</b> method (don't invoke on parser);
1275 public boolean isSpecified (int index)
1277 return ((Attribute) attributesList.get(index)).specified;
1281 * <b>SAX-ext Attributes2</b> method (don't invoke on parser);
1283 public boolean isSpecified (String uri, String local)
1285 int index = getIndex (uri, local);
1286 return isSpecified(index);
1290 * <b>SAX-ext Attributes2</b> method (don't invoke on parser);
1292 public boolean isSpecified (String xmlName)
1294 int index = getIndex (xmlName);
1295 return isSpecified(index);
1300 // Implementation of org.xml.sax.Locator.
1304 * <b>SAX Locator</b> method (don't invoke on parser);
1306 public String getPublicId ()
1308 return null; // FIXME track public IDs too
1312 * <b>SAX Locator</b> method (don't invoke on parser);
1314 public String getSystemId ()
1316 if (entityStack.empty ())
1317 return null;
1318 else
1319 return (String) entityStack.peek ();
1323 * <b>SAX Locator</b> method (don't invoke on parser);
1325 public int getLineNumber ()
1327 return parser.getLineNumber ();
1331 * <b>SAX Locator</b> method (don't invoke on parser);
1333 public int getColumnNumber ()
1335 return parser.getColumnNumber ();
1338 // adapter between SAX2 content handler and SAX1 document handler callbacks
1339 private static class Adapter implements ContentHandler
1341 private DocumentHandler docHandler;
1343 Adapter (DocumentHandler dh)
1344 { docHandler = dh; }
1347 public void setDocumentLocator (Locator l)
1348 { docHandler.setDocumentLocator (l); }
1350 public void startDocument () throws SAXException
1351 { docHandler.startDocument (); }
1353 public void processingInstruction (String target, String data)
1354 throws SAXException
1355 { docHandler.processingInstruction (target, data); }
1357 public void startPrefixMapping (String prefix, String uri)
1358 { /* ignored */ }
1360 public void startElement (
1361 String namespace,
1362 String local,
1363 String name,
1364 Attributes attrs
1365 ) throws SAXException
1366 { docHandler.startElement (name, (AttributeList) attrs); }
1368 public void characters (char buf [], int offset, int len)
1369 throws SAXException
1370 { docHandler.characters (buf, offset, len); }
1372 public void ignorableWhitespace (char buf [], int offset, int len)
1373 throws SAXException
1374 { docHandler.ignorableWhitespace (buf, offset, len); }
1376 public void skippedEntity (String name)
1377 { /* ignored */ }
1379 public void endElement (String u, String l, String name)
1380 throws SAXException
1381 { docHandler.endElement (name); }
1383 public void endPrefixMapping (String prefix)
1384 { /* ignored */ }
1386 public void endDocument () throws SAXException
1387 { docHandler.endDocument (); }
1391 class Attribute
1394 String name;
1395 String value;
1396 String nameSpace;
1397 String localName;
1398 boolean specified;
1400 Attribute(String name, String value, boolean specified)
1402 this.name = name;
1403 this.value = value;
1404 this.nameSpace = "";
1405 this.specified = specified;