FSF GCC merge 02/23/03
[official-gcc.git] / libjava / org / xml / sax / helpers / ParserAdapter.java
blobca66a9e2dc77ff665794f65732b899b468d6e7e2
1 // ParserAdapter.java - adapt a SAX1 Parser to a SAX2 XMLReader.
2 // http://www.saxproject.org
3 // Written by David Megginson
4 // NO WARRANTY! This class is in the public domain.
6 // $Id: ParserAdapter.java,v 1.8.2.4 2002/01/29 21:34:14 dbrownell Exp $
8 package org.xml.sax.helpers;
10 import java.io.IOException;
11 import java.util.Enumeration;
12 import java.util.Vector;
14 import org.xml.sax.Parser; // deprecated
15 import org.xml.sax.InputSource;
16 import org.xml.sax.Locator;
17 import org.xml.sax.AttributeList; // deprecated
18 import org.xml.sax.EntityResolver;
19 import org.xml.sax.DTDHandler;
20 import org.xml.sax.DocumentHandler; // deprecated
21 import org.xml.sax.ErrorHandler;
22 import org.xml.sax.SAXException;
23 import org.xml.sax.SAXParseException;
25 import org.xml.sax.XMLReader;
26 import org.xml.sax.Attributes;
27 import org.xml.sax.ContentHandler;
28 import org.xml.sax.SAXNotRecognizedException;
29 import org.xml.sax.SAXNotSupportedException;
32 /**
33 * Adapt a SAX1 Parser as a SAX2 XMLReader.
35 * <blockquote>
36 * <em>This module, both source code and documentation, is in the
37 * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em>
38 * See <a href='http://www.saxproject.org'>http://www.saxproject.org</a>
39 * for further information.
40 * </blockquote>
42 * <p>This class wraps a SAX1 {@link org.xml.sax.Parser Parser}
43 * and makes it act as a SAX2 {@link org.xml.sax.XMLReader XMLReader},
44 * with feature, property, and Namespace support. Note
45 * that it is not possible to report {@link org.xml.sax.ContentHandler#skippedEntity
46 * skippedEntity} events, since SAX1 does not make that information available.</p>
48 * <p>This adapter does not test for duplicate Namespace-qualified
49 * attribute names.</p>
51 * @since SAX 2.0
52 * @author David Megginson
53 * @version 2.0.1 (sax2r2)
54 * @see org.xml.sax.helpers.XMLReaderAdapter
55 * @see org.xml.sax.XMLReader
56 * @see org.xml.sax.Parser
58 public class ParserAdapter implements XMLReader, DocumentHandler
62 ////////////////////////////////////////////////////////////////////
63 // Constructors.
64 ////////////////////////////////////////////////////////////////////
67 /**
68 * Construct a new parser adapter.
70 * <p>Use the "org.xml.sax.parser" property to locate the
71 * embedded SAX1 driver.</p>
73 * @exception SAXException If the embedded driver
74 * cannot be instantiated or if the
75 * org.xml.sax.parser property is not specified.
77 public ParserAdapter ()
78 throws SAXException
80 super();
82 String driver = System.getProperty("org.xml.sax.parser");
84 try {
85 setup(ParserFactory.makeParser());
86 } catch (ClassNotFoundException e1) {
87 throw new
88 SAXException("Cannot find SAX1 driver class " +
89 driver, e1);
90 } catch (IllegalAccessException e2) {
91 throw new
92 SAXException("SAX1 driver class " +
93 driver +
94 " found but cannot be loaded", e2);
95 } catch (InstantiationException e3) {
96 throw new
97 SAXException("SAX1 driver class " +
98 driver +
99 " loaded but cannot be instantiated", e3);
100 } catch (ClassCastException e4) {
101 throw new
102 SAXException("SAX1 driver class " +
103 driver +
104 " does not implement org.xml.sax.Parser");
105 } catch (NullPointerException e5) {
106 throw new
107 SAXException("System property org.xml.sax.parser not specified");
113 * Construct a new parser adapter.
115 * <p>Note that the embedded parser cannot be changed once the
116 * adapter is created; to embed a different parser, allocate
117 * a new ParserAdapter.</p>
119 * @param parser The SAX1 parser to embed.
120 * @exception java.lang.NullPointerException If the parser parameter
121 * is null.
123 public ParserAdapter (Parser parser)
125 super();
126 setup(parser);
131 * Internal setup method.
133 * @param parser The embedded parser.
134 * @exception java.lang.NullPointerException If the parser parameter
135 * is null.
137 private void setup (Parser parser)
139 if (parser == null) {
140 throw new
141 NullPointerException("Parser argument must not be null");
143 this.parser = parser;
144 atts = new AttributesImpl();
145 nsSupport = new NamespaceSupport();
146 attAdapter = new AttributeListAdapter();
151 ////////////////////////////////////////////////////////////////////
152 // Implementation of org.xml.sax.XMLReader.
153 ////////////////////////////////////////////////////////////////////
157 // Internal constants for the sake of convenience.
159 private final static String FEATURES = "http://xml.org/sax/features/";
160 private final static String NAMESPACES = FEATURES + "namespaces";
161 private final static String NAMESPACE_PREFIXES = FEATURES + "namespace-prefixes";
165 * Set a feature flag for the parser.
167 * <p>The only features recognized are namespaces and
168 * namespace-prefixes.</p>
170 * @param name The feature name, as a complete URI.
171 * @param value The requested feature value.
172 * @exception SAXNotRecognizedException If the feature
173 * can't be assigned or retrieved.
174 * @exception SAXNotSupportedException If the feature
175 * can't be assigned that value.
176 * @see org.xml.sax.XMLReader#setFeature
178 public void setFeature (String name, boolean value)
179 throws SAXNotRecognizedException, SAXNotSupportedException
181 if (name.equals(NAMESPACES)) {
182 checkNotParsing("feature", name);
183 namespaces = value;
184 if (!namespaces && !prefixes) {
185 prefixes = true;
187 } else if (name.equals(NAMESPACE_PREFIXES)) {
188 checkNotParsing("feature", name);
189 prefixes = value;
190 if (!prefixes && !namespaces) {
191 namespaces = true;
193 } else {
194 throw new SAXNotRecognizedException("Feature: " + name);
200 * Check a parser feature flag.
202 * <p>The only features recognized are namespaces and
203 * namespace-prefixes.</p>
205 * @param name The feature name, as a complete URI.
206 * @return The current feature value.
207 * @exception SAXNotRecognizedException If the feature
208 * value can't be assigned or retrieved.
209 * @exception SAXNotSupportedException If the
210 * feature is not currently readable.
211 * @see org.xml.sax.XMLReader#setFeature
213 public boolean getFeature (String name)
214 throws SAXNotRecognizedException, SAXNotSupportedException
216 if (name.equals(NAMESPACES)) {
217 return namespaces;
218 } else if (name.equals(NAMESPACE_PREFIXES)) {
219 return prefixes;
220 } else {
221 throw new SAXNotRecognizedException("Feature: " + name);
227 * Set a parser property.
229 * <p>No properties are currently recognized.</p>
231 * @param name The property name.
232 * @param value The property value.
233 * @exception SAXNotRecognizedException If the property
234 * value can't be assigned or retrieved.
235 * @exception SAXNotSupportedException If the property
236 * can't be assigned that value.
237 * @see org.xml.sax.XMLReader#setProperty
239 public void setProperty (String name, Object value)
240 throws SAXNotRecognizedException, SAXNotSupportedException
242 throw new SAXNotRecognizedException("Property: " + name);
247 * Get a parser property.
249 * <p>No properties are currently recognized.</p>
251 * @param name The property name.
252 * @return The property value.
253 * @exception SAXNotRecognizedException If the property
254 * value can't be assigned or retrieved.
255 * @exception SAXNotSupportedException If the property
256 * value is not currently readable.
257 * @see org.xml.sax.XMLReader#getProperty
259 public Object getProperty (String name)
260 throws SAXNotRecognizedException, SAXNotSupportedException
262 throw new SAXNotRecognizedException("Property: " + name);
267 * Set the entity resolver.
269 * @param resolver The new entity resolver.
270 * @see org.xml.sax.XMLReader#setEntityResolver
272 public void setEntityResolver (EntityResolver resolver)
274 entityResolver = resolver;
279 * Return the current entity resolver.
281 * @return The current entity resolver, or null if none was supplied.
282 * @see org.xml.sax.XMLReader#getEntityResolver
284 public EntityResolver getEntityResolver ()
286 return entityResolver;
291 * Set the DTD handler.
293 * @param resolver The new DTD handler.
294 * @see org.xml.sax.XMLReader#setEntityResolver
296 public void setDTDHandler (DTDHandler handler)
298 dtdHandler = handler;
303 * Return the current DTD handler.
305 * @return The current DTD handler, or null if none was supplied.
306 * @see org.xml.sax.XMLReader#getEntityResolver
308 public DTDHandler getDTDHandler ()
310 return dtdHandler;
315 * Set the content handler.
317 * @param resolver The new content handler.
318 * @see org.xml.sax.XMLReader#setEntityResolver
320 public void setContentHandler (ContentHandler handler)
322 contentHandler = handler;
327 * Return the current content handler.
329 * @return The current content handler, or null if none was supplied.
330 * @see org.xml.sax.XMLReader#getEntityResolver
332 public ContentHandler getContentHandler ()
334 return contentHandler;
339 * Set the error handler.
341 * @param resolver The new error handler.
342 * @see org.xml.sax.XMLReader#setEntityResolver
344 public void setErrorHandler (ErrorHandler handler)
346 errorHandler = handler;
351 * Return the current error handler.
353 * @return The current error handler, or null if none was supplied.
354 * @see org.xml.sax.XMLReader#getEntityResolver
356 public ErrorHandler getErrorHandler ()
358 return errorHandler;
363 * Parse an XML document.
365 * @param systemId The absolute URL of the document.
366 * @exception java.io.IOException If there is a problem reading
367 * the raw content of the document.
368 * @exception SAXException If there is a problem
369 * processing the document.
370 * @see #parse(org.xml.sax.InputSource)
371 * @see org.xml.sax.Parser#parse(java.lang.String)
373 public void parse (String systemId)
374 throws IOException, SAXException
376 parse(new InputSource(systemId));
381 * Parse an XML document.
383 * @param input An input source for the document.
384 * @exception java.io.IOException If there is a problem reading
385 * the raw content of the document.
386 * @exception SAXException If there is a problem
387 * processing the document.
388 * @see #parse(java.lang.String)
389 * @see org.xml.sax.Parser#parse(org.xml.sax.InputSource)
391 public void parse (InputSource input)
392 throws IOException, SAXException
394 if (parsing) {
395 throw new SAXException("Parser is already in use");
397 setupParser();
398 parsing = true;
399 try {
400 parser.parse(input);
401 } finally {
402 parsing = false;
404 parsing = false;
409 ////////////////////////////////////////////////////////////////////
410 // Implementation of org.xml.sax.DocumentHandler.
411 ////////////////////////////////////////////////////////////////////
415 * Adapter implementation method; do not call.
416 * Adapt a SAX1 document locator event.
418 * @param locator A document locator.
419 * @see org.xml.sax.ContentHandler#setDocumentLocator
421 public void setDocumentLocator (Locator locator)
423 this.locator = locator;
424 if (contentHandler != null) {
425 contentHandler.setDocumentLocator(locator);
431 * Adapter implementation method; do not call.
432 * Adapt a SAX1 start document event.
434 * @exception SAXException The client may raise a
435 * processing exception.
436 * @see org.xml.sax.DocumentHandler#startDocument
438 public void startDocument ()
439 throws SAXException
441 if (contentHandler != null) {
442 contentHandler.startDocument();
448 * Adapter implementation method; do not call.
449 * Adapt a SAX1 end document event.
451 * @exception SAXException The client may raise a
452 * processing exception.
453 * @see org.xml.sax.DocumentHandler#endDocument
455 public void endDocument ()
456 throws SAXException
458 if (contentHandler != null) {
459 contentHandler.endDocument();
465 * Adapter implementation method; do not call.
466 * Adapt a SAX1 startElement event.
468 * <p>If necessary, perform Namespace processing.</p>
470 * @param qName The qualified (prefixed) name.
471 * @param qAtts The XML 1.0 attribute list (with qnames).
472 * @exception SAXException The client may raise a
473 * processing exception.
475 public void startElement (String qName, AttributeList qAtts)
476 throws SAXException
478 // These are exceptions from the
479 // first pass; they should be
480 // ignored if there's a second pass,
481 // but reported otherwise.
482 Vector exceptions = null;
484 // If we're not doing Namespace
485 // processing, dispatch this quickly.
486 if (!namespaces) {
487 if (contentHandler != null) {
488 attAdapter.setAttributeList(qAtts);
489 contentHandler.startElement("", "", qName.intern(),
490 attAdapter);
492 return;
496 // OK, we're doing Namespace processing.
497 nsSupport.pushContext();
498 int length = qAtts.getLength();
500 // First pass: handle NS decls
501 for (int i = 0; i < length; i++) {
502 String attQName = qAtts.getName(i);
504 if (!attQName.startsWith("xmlns"))
505 continue;
506 // Could be a declaration...
507 String prefix;
508 int n = attQName.indexOf(':');
510 // xmlns=...
511 if (n == -1 && attQName.length () == 5) {
512 prefix = "";
513 } else if (n != 5) {
514 // XML namespaces spec doesn't discuss "xmlnsf:oo"
515 // (and similarly named) attributes ... at most, warn
516 continue;
517 } else // xmlns:foo=...
518 prefix = attQName.substring(n+1);
520 String value = qAtts.getValue(i);
521 if (!nsSupport.declarePrefix(prefix, value)) {
522 reportError("Illegal Namespace prefix: " + prefix);
523 continue;
525 if (contentHandler != null)
526 contentHandler.startPrefixMapping(prefix, value);
529 // Second pass: copy all relevant
530 // attributes into the SAX2 AttributeList
531 // using updated prefix bindings
532 atts.clear();
533 for (int i = 0; i < length; i++) {
534 String attQName = qAtts.getName(i);
535 String type = qAtts.getType(i);
536 String value = qAtts.getValue(i);
538 // Declaration?
539 if (attQName.startsWith("xmlns")) {
540 String prefix;
541 int n = attQName.indexOf(':');
543 if (n == -1 && attQName.length () == 5) {
544 prefix = "";
545 } else if (n != 5) {
546 // XML namespaces spec doesn't discuss "xmlnsf:oo"
547 // (and similarly named) attributes ... ignore
548 prefix = null;
549 } else {
550 prefix = attQName.substring(n+1);
552 // Yes, decl: report or prune
553 if (prefix != null) {
554 if (prefixes)
555 atts.addAttribute("", "", attQName.intern(),
556 type, value);
557 continue;
561 // Not a declaration -- report
562 try {
563 String attName[] = processName(attQName, true, true);
564 atts.addAttribute(attName[0], attName[1], attName[2],
565 type, value);
566 } catch (SAXException e) {
567 if (exceptions == null)
568 exceptions = new Vector();
569 exceptions.addElement(e);
570 atts.addAttribute("", attQName, attQName, type, value);
574 // now handle the deferred exception reports
575 if (exceptions != null && errorHandler != null) {
576 for (int i = 0; i < exceptions.size(); i++)
577 errorHandler.error((SAXParseException)
578 (exceptions.elementAt(i)));
581 // OK, finally report the event.
582 if (contentHandler != null) {
583 String name[] = processName(qName, false, false);
584 contentHandler.startElement(name[0], name[1], name[2], atts);
590 * Adapter implementation method; do not call.
591 * Adapt a SAX1 end element event.
593 * @param qName The qualified (prefixed) name.
594 * @exception SAXException The client may raise a
595 * processing exception.
596 * @see org.xml.sax.DocumentHandler#endElement
598 public void endElement (String qName)
599 throws SAXException
601 // If we're not doing Namespace
602 // processing, dispatch this quickly.
603 if (!namespaces) {
604 if (contentHandler != null) {
605 contentHandler.endElement("", "", qName.intern());
607 return;
610 // Split the name.
611 String names[] = processName(qName, false, false);
612 if (contentHandler != null) {
613 contentHandler.endElement(names[0], names[1], names[2]);
614 Enumeration prefixes = nsSupport.getDeclaredPrefixes();
615 while (prefixes.hasMoreElements()) {
616 String prefix = (String)prefixes.nextElement();
617 contentHandler.endPrefixMapping(prefix);
620 nsSupport.popContext();
625 * Adapter implementation method; do not call.
626 * Adapt a SAX1 characters event.
628 * @param ch An array of characters.
629 * @param start The starting position in the array.
630 * @param length The number of characters to use.
631 * @exception SAXException The client may raise a
632 * processing exception.
633 * @see org.xml.sax.DocumentHandler#characters
635 public void characters (char ch[], int start, int length)
636 throws SAXException
638 if (contentHandler != null) {
639 contentHandler.characters(ch, start, length);
645 * Adapter implementation method; do not call.
646 * Adapt a SAX1 ignorable whitespace event.
648 * @param ch An array of characters.
649 * @param start The starting position in the array.
650 * @param length The number of characters to use.
651 * @exception SAXException The client may raise a
652 * processing exception.
653 * @see org.xml.sax.DocumentHandler#ignorableWhitespace
655 public void ignorableWhitespace (char ch[], int start, int length)
656 throws SAXException
658 if (contentHandler != null) {
659 contentHandler.ignorableWhitespace(ch, start, length);
665 * Adapter implementation method; do not call.
666 * Adapt a SAX1 processing instruction event.
668 * @param target The processing instruction target.
669 * @param data The remainder of the processing instruction
670 * @exception SAXException The client may raise a
671 * processing exception.
672 * @see org.xml.sax.DocumentHandler#processingInstruction
674 public void processingInstruction (String target, String data)
675 throws SAXException
677 if (contentHandler != null) {
678 contentHandler.processingInstruction(target, data);
684 ////////////////////////////////////////////////////////////////////
685 // Internal utility methods.
686 ////////////////////////////////////////////////////////////////////
690 * Initialize the parser before each run.
692 private void setupParser ()
694 nsSupport.reset();
696 if (entityResolver != null) {
697 parser.setEntityResolver(entityResolver);
699 if (dtdHandler != null) {
700 parser.setDTDHandler(dtdHandler);
702 if (errorHandler != null) {
703 parser.setErrorHandler(errorHandler);
705 parser.setDocumentHandler(this);
706 locator = null;
711 * Process a qualified (prefixed) name.
713 * <p>If the name has an undeclared prefix, use only the qname
714 * and make an ErrorHandler.error callback in case the app is
715 * interested.</p>
717 * @param qName The qualified (prefixed) name.
718 * @param isAttribute true if this is an attribute name.
719 * @return The name split into three parts.
720 * @exception SAXException The client may throw
721 * an exception if there is an error callback.
723 private String [] processName (String qName, boolean isAttribute,
724 boolean useException)
725 throws SAXException
727 String parts[] = nsSupport.processName(qName, nameParts,
728 isAttribute);
729 if (parts == null) {
730 if (useException)
731 throw makeException("Undeclared prefix: " + qName);
732 reportError("Undeclared prefix: " + qName);
733 parts = new String[3];
734 parts[0] = parts[1] = "";
735 parts[2] = qName.intern();
737 return parts;
742 * Report a non-fatal error.
744 * @param message The error message.
745 * @exception SAXException The client may throw
746 * an exception.
748 void reportError (String message)
749 throws SAXException
751 if (errorHandler != null)
752 errorHandler.error(makeException(message));
757 * Construct an exception for the current context.
759 * @param message The error message.
761 private SAXParseException makeException (String message)
763 if (locator != null) {
764 return new SAXParseException(message, locator);
765 } else {
766 return new SAXParseException(message, null, null, -1, -1);
772 * Throw an exception if we are parsing.
774 * <p>Use this method to detect illegal feature or
775 * property changes.</p>
777 * @param type The type of thing (feature or property).
778 * @param name The feature or property name.
779 * @exception SAXNotSupportedException If a
780 * document is currently being parsed.
782 private void checkNotParsing (String type, String name)
783 throws SAXNotSupportedException
785 if (parsing) {
786 throw new SAXNotSupportedException("Cannot change " +
787 type + ' ' +
788 name + " while parsing");
795 ////////////////////////////////////////////////////////////////////
796 // Internal state.
797 ////////////////////////////////////////////////////////////////////
799 private NamespaceSupport nsSupport;
800 private AttributeListAdapter attAdapter;
802 private boolean parsing = false;
803 private String nameParts[] = new String[3];
805 private Parser parser = null;
807 private AttributesImpl atts = null;
809 // Features
810 private boolean namespaces = true;
811 private boolean prefixes = false;
813 // Properties
815 // Handlers
816 Locator locator;
818 EntityResolver entityResolver = null;
819 DTDHandler dtdHandler = null;
820 ContentHandler contentHandler = null;
821 ErrorHandler errorHandler = null;
825 ////////////////////////////////////////////////////////////////////
826 // Inner class to wrap an AttributeList when not doing NS proc.
827 ////////////////////////////////////////////////////////////////////
831 * Adapt a SAX1 AttributeList as a SAX2 Attributes object.
833 * <p>This class is in the Public Domain, and comes with NO
834 * WARRANTY of any kind.</p>
836 * <p>This wrapper class is used only when Namespace support
837 * is disabled -- it provides pretty much a direct mapping
838 * from SAX1 to SAX2, except that names and types are
839 * interned whenever requested.</p>
841 final class AttributeListAdapter implements Attributes
845 * Construct a new adapter.
847 AttributeListAdapter ()
853 * Set the embedded AttributeList.
855 * <p>This method must be invoked before any of the others
856 * can be used.</p>
858 * @param The SAX1 attribute list (with qnames).
860 void setAttributeList (AttributeList qAtts)
862 this.qAtts = qAtts;
867 * Return the length of the attribute list.
869 * @return The number of attributes in the list.
870 * @see org.xml.sax.Attributes#getLength
872 public int getLength ()
874 return qAtts.getLength();
879 * Return the Namespace URI of the specified attribute.
881 * @param The attribute's index.
882 * @return Always the empty string.
883 * @see org.xml.sax.Attributes#getURI
885 public String getURI (int i)
887 return "";
892 * Return the local name of the specified attribute.
894 * @param The attribute's index.
895 * @return Always the empty string.
896 * @see org.xml.sax.Attributes#getLocalName
898 public String getLocalName (int i)
900 return "";
905 * Return the qualified (prefixed) name of the specified attribute.
907 * @param The attribute's index.
908 * @return The attribute's qualified name, internalized.
910 public String getQName (int i)
912 return qAtts.getName(i).intern();
917 * Return the type of the specified attribute.
919 * @param The attribute's index.
920 * @return The attribute's type as an internalized string.
922 public String getType (int i)
924 return qAtts.getType(i).intern();
929 * Return the value of the specified attribute.
931 * @param The attribute's index.
932 * @return The attribute's value.
934 public String getValue (int i)
936 return qAtts.getValue(i);
941 * Look up an attribute index by Namespace name.
943 * @param uri The Namespace URI or the empty string.
944 * @param localName The local name.
945 * @return The attributes index, or -1 if none was found.
946 * @see org.xml.sax.Attributes#getIndex(java.lang.String,java.lang.String)
948 public int getIndex (String uri, String localName)
950 return -1;
955 * Look up an attribute index by qualified (prefixed) name.
957 * @param qName The qualified name.
958 * @return The attributes index, or -1 if none was found.
959 * @see org.xml.sax.Attributes#getIndex(java.lang.String)
961 public int getIndex (String qName)
963 int max = atts.getLength();
964 for (int i = 0; i < max; i++) {
965 if (qAtts.getName(i).equals(qName)) {
966 return i;
969 return -1;
974 * Look up the type of an attribute by Namespace name.
976 * @param uri The Namespace URI
977 * @param localName The local name.
978 * @return The attribute's type as an internalized string.
980 public String getType (String uri, String localName)
982 return null;
987 * Look up the type of an attribute by qualified (prefixed) name.
989 * @param qName The qualified name.
990 * @return The attribute's type as an internalized string.
992 public String getType (String qName)
994 return qAtts.getType(qName).intern();
999 * Look up the value of an attribute by Namespace name.
1001 * @param uri The Namespace URI
1002 * @param localName The local name.
1003 * @return The attribute's value.
1005 public String getValue (String uri, String localName)
1007 return null;
1012 * Look up the value of an attribute by qualified (prefixed) name.
1014 * @param qName The qualified name.
1015 * @return The attribute's value.
1017 public String getValue (String qName)
1019 return qAtts.getValue(qName);
1022 private AttributeList qAtts;
1026 // end of ParserAdapter.java