View Javadoc

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.
5   // $Id: ParserAdapter.java,v 1.5 2004/03/19 20:17:55 maartenc Exp $
6   
7   package org.xml.sax.helpers;
8   
9   import java.io.IOException;
10  import java.util.Enumeration;
11  import java.util.Vector;
12  
13  import org.xml.sax.Parser;	// deprecated
14  import org.xml.sax.InputSource;
15  import org.xml.sax.Locator;
16  import org.xml.sax.AttributeList; // deprecated
17  import org.xml.sax.EntityResolver;
18  import org.xml.sax.DTDHandler;
19  import org.xml.sax.DocumentHandler; // deprecated
20  import org.xml.sax.ErrorHandler;
21  import org.xml.sax.SAXException;
22  import org.xml.sax.SAXParseException;
23  
24  import org.xml.sax.XMLReader;
25  import org.xml.sax.Attributes;
26  import org.xml.sax.ContentHandler;
27  import org.xml.sax.SAXNotRecognizedException;
28  import org.xml.sax.SAXNotSupportedException;
29  
30  
31  /***
32   * Adapt a SAX1 Parser as a SAX2 XMLReader.
33   *
34   * <blockquote>
35   * <em>This module, both source code and documentation, is in the
36   * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em>
37   * See <a href='http://www.saxproject.org'>http://www.saxproject.org</a>
38   * for further information.
39   * </blockquote>
40   *
41   * <p>This class wraps a SAX1 {@link org.xml.sax.Parser Parser}
42   * and makes it act as a SAX2 {@link org.xml.sax.XMLReader XMLReader},
43   * with feature, property, and Namespace support.  Note
44   * that it is not possible to report {@link org.xml.sax.ContentHandler#skippedEntity
45   * skippedEntity} events, since SAX1 does not make that information available.</p>
46   *
47   * <p>This adapter does not test for duplicate Namespace-qualified
48   * attribute names.</p>
49   *
50   * @since SAX 2.0
51   * @author David Megginson
52   * @version 2.0.1 (sax2r2)
53   * @see org.xml.sax.helpers.XMLReaderAdapter
54   * @see org.xml.sax.XMLReader
55   * @see org.xml.sax.Parser
56   */
57  public class ParserAdapter implements XMLReader, DocumentHandler
58  {
59  
60  
61      ////////////////////////////////////////////////////////////////////
62      // Constructors.
63      ////////////////////////////////////////////////////////////////////
64  
65  
66      /***
67       * Construct a new parser adapter.
68       *
69       * <p>Use the "org.xml.sax.parser" property to locate the
70       * embedded SAX1 driver.</p>
71       *
72       * @exception SAXException If the embedded driver
73       *            cannot be instantiated or if the
74       *            org.xml.sax.parser property is not specified.
75       */
76      public ParserAdapter ()
77        throws SAXException
78      {
79  	super();
80  
81  	String driver = System.getProperty("org.xml.sax.parser");
82  
83  	try {
84  	    setup(ParserFactory.makeParser());
85  	} catch (ClassNotFoundException e1) {
86  	    throw new
87  		SAXException("Cannot find SAX1 driver class " +
88  			     driver, e1);
89  	} catch (IllegalAccessException e2) {
90  	    throw new
91  		SAXException("SAX1 driver class " +
92  			     driver +
93  			     " found but cannot be loaded", e2);
94  	} catch (InstantiationException e3) {
95  	    throw new
96  		SAXException("SAX1 driver class " +
97  			     driver +
98  			     " loaded but cannot be instantiated", e3);
99  	} catch (ClassCastException e4) {
100 	    throw new
101 		SAXException("SAX1 driver class " +
102 			     driver +
103 			     " does not implement org.xml.sax.Parser");
104 	} catch (NullPointerException e5) {
105 	    throw new 
106 		SAXException("System property org.xml.sax.parser not specified");
107 	}
108     }
109 
110 
111     /***
112      * Construct a new parser adapter.
113      *
114      * <p>Note that the embedded parser cannot be changed once the
115      * adapter is created; to embed a different parser, allocate
116      * a new ParserAdapter.</p>
117      *
118      * @param parser The SAX1 parser to embed.
119      * @exception java.lang.NullPointerException If the parser parameter
120      *            is null.
121      */
122     public ParserAdapter (Parser parser)
123     {
124 	super();
125 	setup(parser);
126     }
127 
128 
129     /***
130      * Internal setup method.
131      *
132      * @param parser The embedded parser.
133      * @exception java.lang.NullPointerException If the parser parameter
134      *            is null.
135      */
136     private void setup (Parser parser)
137     {
138 	if (parser == null) {
139 	    throw new
140 		NullPointerException("Parser argument must not be null");
141 	}
142 	this.parser = parser;
143 	atts = new AttributesImpl();
144 	nsSupport = new NamespaceSupport();
145 	attAdapter = new AttributeListAdapter();
146     }
147 
148 
149 
150     ////////////////////////////////////////////////////////////////////
151     // Implementation of org.xml.sax.XMLReader.
152     ////////////////////////////////////////////////////////////////////
153 
154 
155     //
156     // Internal constants for the sake of convenience.
157     //
158     private final static String FEATURES = "http://xml.org/sax/features/";
159     private final static String NAMESPACES = FEATURES + "namespaces";
160     private final static String NAMESPACE_PREFIXES = FEATURES + "namespace-prefixes";
161     private final static String XMLNS_URIs = FEATURES + "xmlns-uris";
162 
163 
164     /***
165      * Set a feature flag for the parser.
166      *
167      * <p>The only features recognized are namespaces and 
168      * namespace-prefixes.</p>
169      *
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
177      */
178     public void setFeature (String name, boolean value)
179 	throws SAXNotRecognizedException, SAXNotSupportedException
180     {
181 	if (name.equals(NAMESPACES)) {
182 	    checkNotParsing("feature", name);
183 	    namespaces = value;
184 	    if (!namespaces && !prefixes) {
185 		prefixes = true;
186 	    }
187 	} else if (name.equals(NAMESPACE_PREFIXES)) {
188 	    checkNotParsing("feature", name);
189 	    prefixes = value;
190 	    if (!prefixes && !namespaces) {
191 		namespaces = true;
192 	    }
193 	} else if (name.equals(XMLNS_URIs)) {
194 	    checkNotParsing("feature", name);
195 	    uris = value;
196 	} else {
197 	    throw new SAXNotRecognizedException("Feature: " + name);
198 	}
199     }
200 
201 
202     /***
203      * Check a parser feature flag.
204      *
205      * <p>The only features recognized are namespaces and 
206      * namespace-prefixes.</p>
207      *
208      * @param name The feature name, as a complete URI.
209      * @return The current feature value.
210      * @exception SAXNotRecognizedException If the feature
211      *            value can't be assigned or retrieved.
212      * @exception SAXNotSupportedException If the
213      *            feature is not currently readable.
214      * @see org.xml.sax.XMLReader#setFeature
215      */
216     public boolean getFeature (String name)
217 	throws SAXNotRecognizedException, SAXNotSupportedException
218     {
219 	if (name.equals(NAMESPACES)) {
220 	    return namespaces;
221 	} else if (name.equals(NAMESPACE_PREFIXES)) {
222 	    return prefixes;
223 	} else if (name.equals(XMLNS_URIs)) {
224 	    return uris;
225 	} else {
226 	    throw new SAXNotRecognizedException("Feature: " + name);
227 	}
228     }
229 
230 
231     /***
232      * Set a parser property.
233      *
234      * <p>No properties are currently recognized.</p>
235      *
236      * @param name The property name.
237      * @param value The property value.
238      * @exception SAXNotRecognizedException If the property
239      *            value can't be assigned or retrieved.
240      * @exception SAXNotSupportedException If the property
241      *            can't be assigned that value.
242      * @see org.xml.sax.XMLReader#setProperty
243      */
244     public void setProperty (String name, Object value)
245 	throws SAXNotRecognizedException, SAXNotSupportedException
246     {
247 	throw new SAXNotRecognizedException("Property: " + name);
248     }
249 
250 
251     /***
252      * Get a parser property.
253      *
254      * <p>No properties are currently recognized.</p>
255      *
256      * @param name The property name.
257      * @return The property value.
258      * @exception SAXNotRecognizedException If the property
259      *            value can't be assigned or retrieved.
260      * @exception SAXNotSupportedException If the property
261      *            value is not currently readable.
262      * @see org.xml.sax.XMLReader#getProperty
263      */
264     public Object getProperty (String name)
265 	throws SAXNotRecognizedException, SAXNotSupportedException
266     {
267 	throw new SAXNotRecognizedException("Property: " + name);
268     }
269 
270 
271     /***
272      * Set the entity resolver.
273      *
274      * @param resolver The new entity resolver.
275      * @see org.xml.sax.XMLReader#setEntityResolver
276      */
277     public void setEntityResolver (EntityResolver resolver)
278     {
279 	entityResolver = resolver;
280     }
281 
282 
283     /***
284      * Return the current entity resolver.
285      *
286      * @return The current entity resolver, or null if none was supplied.
287      * @see org.xml.sax.XMLReader#getEntityResolver
288      */
289     public EntityResolver getEntityResolver ()
290     {
291 	return entityResolver;
292     }
293 
294 
295     /***
296      * Set the DTD handler.
297      *
298      * @param resolver The new DTD handler.
299      * @see org.xml.sax.XMLReader#setEntityResolver
300      */
301     public void setDTDHandler (DTDHandler handler)
302     {
303 	dtdHandler = handler;
304     }
305 
306 
307     /***
308      * Return the current DTD handler.
309      *
310      * @return The current DTD handler, or null if none was supplied.
311      * @see org.xml.sax.XMLReader#getEntityResolver
312      */
313     public DTDHandler getDTDHandler ()
314     {
315 	return dtdHandler;
316     }
317 
318 
319     /***
320      * Set the content handler.
321      *
322      * @param resolver The new content handler.
323      * @see org.xml.sax.XMLReader#setEntityResolver
324      */
325     public void setContentHandler (ContentHandler handler)
326     {
327 	contentHandler = handler;
328     }
329 
330 
331     /***
332      * Return the current content handler.
333      *
334      * @return The current content handler, or null if none was supplied.
335      * @see org.xml.sax.XMLReader#getEntityResolver
336      */
337     public ContentHandler getContentHandler ()
338     {
339 	return contentHandler;
340     }
341 
342 
343     /***
344      * Set the error handler.
345      *
346      * @param resolver The new error handler.
347      * @see org.xml.sax.XMLReader#setEntityResolver
348      */
349     public void setErrorHandler (ErrorHandler handler)
350     {
351 	errorHandler = handler;
352     }
353 
354 
355     /***
356      * Return the current error handler.
357      *
358      * @return The current error handler, or null if none was supplied.
359      * @see org.xml.sax.XMLReader#getEntityResolver
360      */
361     public ErrorHandler getErrorHandler ()
362     {
363 	return errorHandler;
364     }
365 
366 
367     /***
368      * Parse an XML document.
369      *
370      * @param systemId The absolute URL of the document.
371      * @exception java.io.IOException If there is a problem reading
372      *            the raw content of the document.
373      * @exception SAXException If there is a problem
374      *            processing the document.
375      * @see #parse(org.xml.sax.InputSource)
376      * @see org.xml.sax.Parser#parse(java.lang.String)
377      */
378     public void parse (String systemId)
379 	throws IOException, SAXException
380     {
381 	parse(new InputSource(systemId));
382     }
383 
384 
385     /***
386      * Parse an XML document.
387      *
388      * @param input An input source for the document.
389      * @exception java.io.IOException If there is a problem reading
390      *            the raw content of the document.
391      * @exception SAXException If there is a problem
392      *            processing the document.
393      * @see #parse(java.lang.String)
394      * @see org.xml.sax.Parser#parse(org.xml.sax.InputSource)
395      */
396     public void parse (InputSource input)
397 	throws IOException, SAXException
398     {
399 	if (parsing) {
400 	    throw new SAXException("Parser is already in use");
401 	}
402 	setupParser();
403 	parsing = true;
404 	try {
405 	    parser.parse(input);
406 	} finally {
407 	    parsing = false;
408 	}
409 	parsing = false;
410     }
411 
412 
413 
414     ////////////////////////////////////////////////////////////////////
415     // Implementation of org.xml.sax.DocumentHandler.
416     ////////////////////////////////////////////////////////////////////
417 
418 
419     /***
420      * Adapter implementation method; do not call.
421      * Adapt a SAX1 document locator event.
422      *
423      * @param locator A document locator.
424      * @see org.xml.sax.ContentHandler#setDocumentLocator
425      */
426     public void setDocumentLocator (Locator locator)
427     {
428 	this.locator = locator;
429 	if (contentHandler != null) {
430 	    contentHandler.setDocumentLocator(locator);
431 	}
432     }
433 
434 
435     /***
436      * Adapter implementation method; do not call.
437      * Adapt a SAX1 start document event.
438      *
439      * @exception SAXException The client may raise a
440      *            processing exception.
441      * @see org.xml.sax.DocumentHandler#startDocument
442      */
443     public void startDocument ()
444 	throws SAXException
445     {
446 	if (contentHandler != null) {
447 	    contentHandler.startDocument();
448 	}
449     }
450 
451 
452     /***
453      * Adapter implementation method; do not call.
454      * Adapt a SAX1 end document event.
455      *
456      * @exception SAXException The client may raise a
457      *            processing exception.
458      * @see org.xml.sax.DocumentHandler#endDocument
459      */
460     public void endDocument ()
461 	throws SAXException
462     {
463 	if (contentHandler != null) {
464 	    contentHandler.endDocument();
465 	}
466     }
467 
468 
469     /***
470      * Adapter implementation method; do not call.
471      * Adapt a SAX1 startElement event.
472      *
473      * <p>If necessary, perform Namespace processing.</p>
474      *
475      * @param qName The qualified (prefixed) name.
476      * @param qAtts The XML attribute list (with qnames).
477      * @exception SAXException The client may raise a
478      *            processing exception.
479      */
480     public void startElement (String qName, AttributeList qAtts)
481 	throws SAXException
482     {
483 				// These are exceptions from the
484 				// first pass; they should be
485 				// ignored if there's a second pass,
486 				// but reported otherwise.
487 	Vector exceptions = null;
488 
489 				// If we're not doing Namespace
490 				// processing, dispatch this quickly.
491 	if (!namespaces) {
492 	    if (contentHandler != null) {
493 		attAdapter.setAttributeList(qAtts);
494 		contentHandler.startElement("", "", qName.intern(),
495 					    attAdapter);
496 	    }
497 	    return;
498 	}
499 
500 
501 				// OK, we're doing Namespace processing.
502 	nsSupport.pushContext();
503 	int length = qAtts.getLength();
504 	
505 				// First pass:  handle NS decls
506 	for (int i = 0; i < length; i++) {
507 	    String attQName = qAtts.getName(i);
508 
509 	    if (!attQName.startsWith("xmlns"))
510 		continue;
511 				// Could be a declaration...
512 	    String prefix;
513 	    int n = attQName.indexOf(':');
514 
515 	    			// xmlns=...
516 	    if (n == -1 && attQName.length () == 5) {
517 		prefix = "";
518 	    } else if (n != 5) {
519 		// XML namespaces spec doesn't discuss "xmlnsf:oo"
520 		// (and similarly named) attributes ... at most, warn
521 		continue;
522 	    } else 		// xmlns:foo=...
523 		prefix = attQName.substring(n+1);
524 
525 	    String value = qAtts.getValue(i);
526 	    if (!nsSupport.declarePrefix(prefix, value)) {
527 		reportError("Illegal Namespace prefix: " + prefix);
528 		continue;
529 	    }
530 	    if (contentHandler != null)
531 		contentHandler.startPrefixMapping(prefix, value);
532 	}
533 	
534 				// Second pass: copy all relevant
535 				// attributes into the SAX2 AttributeList
536 				// using updated prefix bindings
537 	atts.clear();
538 	for (int i = 0; i < length; i++) {
539 	    String attQName = qAtts.getName(i);
540 	    String type = qAtts.getType(i);
541 	    String value = qAtts.getValue(i);
542 
543 				// Declaration?
544 	    if (attQName.startsWith("xmlns")) {
545 		String prefix;
546 		int n = attQName.indexOf(':');
547 
548 		if (n == -1 && attQName.length () == 5) {
549 		    prefix = "";
550 		} else if (n != 5) {
551 		    // XML namespaces spec doesn't discuss "xmlnsf:oo"
552 		    // (and similarly named) attributes ... ignore
553 		    prefix = null;
554 		} else {
555 		    prefix = attQName.substring(6);
556 		}
557 				// Yes, decl:  report or prune
558 		if (prefix != null) {
559 		    if (prefixes) {
560 			if (uris)
561 			    // note funky case:  localname can be null
562 			    // when declaring the default prefix, and
563 			    // yet the uri isn't null.
564 			    atts.addAttribute (nsSupport.XMLNS, prefix,
565 				    attQName.intern(), type, value);
566 			else
567 			    atts.addAttribute ("", "",
568 				    attQName.intern(), type, value);
569 		    }
570 		    continue;
571 		}
572 	    } 
573 
574 				// Not a declaration -- report
575 	    try {
576 		String attName[] = processName(attQName, true, true);
577 		atts.addAttribute(attName[0], attName[1], attName[2],
578 				  type, value);
579 	    } catch (SAXException e) {
580 		if (exceptions == null)
581 		    exceptions = new Vector();
582 		exceptions.addElement(e);
583 		atts.addAttribute("", attQName, attQName, type, value);
584 	    }
585 	}
586 	
587 	// now handle the deferred exception reports
588 	if (exceptions != null && errorHandler != null) {
589 	    for (int i = 0; i < exceptions.size(); i++)
590 		errorHandler.error((SAXParseException)
591 				(exceptions.elementAt(i)));
592 	}
593 
594 				// OK, finally report the event.
595 	if (contentHandler != null) {
596 	    String name[] = processName(qName, false, false);
597 	    contentHandler.startElement(name[0], name[1], name[2], atts);
598 	}
599     }
600 
601 
602     /***
603      * Adapter implementation method; do not call.
604      * Adapt a SAX1 end element event.
605      *
606      * @param qName The qualified (prefixed) name.
607      * @exception SAXException The client may raise a
608      *            processing exception.
609      * @see org.xml.sax.DocumentHandler#endElement
610      */
611     public void endElement (String qName)
612 	throws SAXException
613     {
614 				// If we're not doing Namespace
615 				// processing, dispatch this quickly.
616 	if (!namespaces) {
617 	    if (contentHandler != null) {
618 		contentHandler.endElement("", "", qName.intern());
619 	    }
620 	    return;
621 	}
622 
623 				// Split the name.
624 	String names[] = processName(qName, false, false);
625 	if (contentHandler != null) {
626 	    contentHandler.endElement(names[0], names[1], names[2]);
627 	    Enumeration prefixes = nsSupport.getDeclaredPrefixes();
628 	    while (prefixes.hasMoreElements()) {
629 		String prefix = (String)prefixes.nextElement();
630 		contentHandler.endPrefixMapping(prefix);
631 	    }
632 	}
633 	nsSupport.popContext();
634     }
635 
636 
637     /***
638      * Adapter implementation method; do not call.
639      * Adapt a SAX1 characters event.
640      *
641      * @param ch An array of characters.
642      * @param start The starting position in the array.
643      * @param length The number of characters to use.
644      * @exception SAXException The client may raise a
645      *            processing exception.
646      * @see org.xml.sax.DocumentHandler#characters
647      */
648     public void characters (char ch[], int start, int length)
649 	throws SAXException
650     {
651 	if (contentHandler != null) {
652 	    contentHandler.characters(ch, start, length);
653 	}
654     }
655 
656 
657     /***
658      * Adapter implementation method; do not call.
659      * Adapt a SAX1 ignorable whitespace event.
660      *
661      * @param ch An array of characters.
662      * @param start The starting position in the array.
663      * @param length The number of characters to use.
664      * @exception SAXException The client may raise a
665      *            processing exception.
666      * @see org.xml.sax.DocumentHandler#ignorableWhitespace
667      */
668     public void ignorableWhitespace (char ch[], int start, int length)
669 	throws SAXException
670     {
671 	if (contentHandler != null) {
672 	    contentHandler.ignorableWhitespace(ch, start, length);
673 	}
674     }
675 
676 
677     /***
678      * Adapter implementation method; do not call.
679      * Adapt a SAX1 processing instruction event.
680      *
681      * @param target The processing instruction target.
682      * @param data The remainder of the processing instruction
683      * @exception SAXException The client may raise a
684      *            processing exception.
685      * @see org.xml.sax.DocumentHandler#processingInstruction
686      */
687     public void processingInstruction (String target, String data)
688 	throws SAXException
689     {
690 	if (contentHandler != null) {
691 	    contentHandler.processingInstruction(target, data);
692 	}
693     }
694 
695 
696 
697     ////////////////////////////////////////////////////////////////////
698     // Internal utility methods.
699     ////////////////////////////////////////////////////////////////////
700 
701 
702     /***
703      * Initialize the parser before each run.
704      */
705     private void setupParser ()
706     {
707 	// catch an illegal "nonsense" state.
708 	if (!prefixes && !namespaces)
709 	    throw new IllegalStateException ();
710 
711 	nsSupport.reset();
712 	if (uris)
713 	    nsSupport.setNamespaceDeclUris (true);
714 
715 	if (entityResolver != null) {
716 	    parser.setEntityResolver(entityResolver);
717 	}
718 	if (dtdHandler != null) {
719 	    parser.setDTDHandler(dtdHandler);
720 	}
721 	if (errorHandler != null) {
722 	    parser.setErrorHandler(errorHandler);
723 	}
724 	parser.setDocumentHandler(this);
725 	locator = null;
726     }
727 
728 
729     /***
730      * Process a qualified (prefixed) name.
731      *
732      * <p>If the name has an undeclared prefix, use only the qname
733      * and make an ErrorHandler.error callback in case the app is
734      * interested.</p>
735      *
736      * @param qName The qualified (prefixed) name.
737      * @param isAttribute true if this is an attribute name.
738      * @return The name split into three parts.
739      * @exception SAXException The client may throw
740      *            an exception if there is an error callback.
741      */
742     private String [] processName (String qName, boolean isAttribute,
743 				   boolean useException)
744 	throws SAXException
745     {
746 	String parts[] = nsSupport.processName(qName, nameParts,
747 					       isAttribute);
748 	if (parts == null) {
749 	    if (useException)
750 		throw makeException("Undeclared prefix: " + qName);
751 	    reportError("Undeclared prefix: " + qName);
752 	    parts = new String[3];
753 	    parts[0] = parts[1] = "";
754 	    parts[2] = qName.intern();
755 	}
756 	return parts;
757     }
758 
759 
760     /***
761      * Report a non-fatal error.
762      *
763      * @param message The error message.
764      * @exception SAXException The client may throw
765      *            an exception.
766      */
767     void reportError (String message)
768 	throws SAXException
769     {
770 	if (errorHandler != null)
771 	    errorHandler.error(makeException(message));
772     }
773 
774     
775     /***
776      * Construct an exception for the current context.
777      *
778      * @param message The error message.
779      */
780     private SAXParseException makeException (String message)
781     {
782 	if (locator != null) {
783 	    return new SAXParseException(message, locator);
784 	} else {
785 	    return new SAXParseException(message, null, null, -1, -1);
786 	}
787     }
788 
789 
790     /***
791      * Throw an exception if we are parsing.
792      *
793      * <p>Use this method to detect illegal feature or
794      * property changes.</p>
795      *
796      * @param type The type of thing (feature or property).
797      * @param name The feature or property name.
798      * @exception SAXNotSupportedException If a
799      *            document is currently being parsed.
800      */
801     private void checkNotParsing (String type, String name)
802 	throws SAXNotSupportedException
803     {
804 	if (parsing) {
805 	    throw new SAXNotSupportedException("Cannot change " +
806 					       type + ' ' +
807 					       name + " while parsing");
808 					       
809 	}
810     }
811 
812 
813 
814     ////////////////////////////////////////////////////////////////////
815     // Internal state.
816     ////////////////////////////////////////////////////////////////////
817 
818     private NamespaceSupport nsSupport;
819     private AttributeListAdapter attAdapter;
820 
821     private boolean parsing = false;
822     private String nameParts[] = new String[3];
823 
824     private Parser parser = null;
825 
826     private AttributesImpl atts = null;
827 
828 				// Features
829     private boolean namespaces = true;
830     private boolean prefixes = false;
831     private boolean uris = false;
832 
833 				// Properties
834 
835 				// Handlers
836     Locator locator;
837 
838     EntityResolver entityResolver = null;
839     DTDHandler dtdHandler = null;
840     ContentHandler contentHandler = null;
841     ErrorHandler errorHandler = null;
842 
843 
844 
845     ////////////////////////////////////////////////////////////////////
846     // Inner class to wrap an AttributeList when not doing NS proc.
847     ////////////////////////////////////////////////////////////////////
848 
849 
850     /***
851      * Adapt a SAX1 AttributeList as a SAX2 Attributes object.
852      *
853      * <p>This class is in the Public Domain, and comes with NO
854      * WARRANTY of any kind.</p>
855      *
856      * <p>This wrapper class is used only when Namespace support
857      * is disabled -- it provides pretty much a direct mapping
858      * from SAX1 to SAX2, except that names and types are 
859      * interned whenever requested.</p>
860      */
861     final class AttributeListAdapter implements Attributes
862     {
863 
864 	/***
865 	 * Construct a new adapter.
866 	 */
867 	AttributeListAdapter ()
868 	{
869 	}
870 
871 
872 	/***
873 	 * Set the embedded AttributeList.
874 	 *
875 	 * <p>This method must be invoked before any of the others
876 	 * can be used.</p>
877 	 *
878 	 * @param The SAX1 attribute list (with qnames).
879 	 */
880 	void setAttributeList (AttributeList qAtts)
881 	{
882 	    this.qAtts = qAtts;
883 	}
884 
885 
886 	/***
887 	 * Return the length of the attribute list.
888 	 *
889 	 * @return The number of attributes in the list.
890 	 * @see org.xml.sax.Attributes#getLength
891 	 */
892 	public int getLength ()
893 	{
894 	    return qAtts.getLength();
895 	}
896 
897 
898 	/***
899 	 * Return the Namespace URI of the specified attribute.
900 	 *
901 	 * @param The attribute's index.
902 	 * @return Always the empty string.
903 	 * @see org.xml.sax.Attributes#getURI
904 	 */
905 	public String getURI (int i)
906 	{
907 	    return "";
908 	}
909 
910 
911 	/***
912 	 * Return the local name of the specified attribute.
913 	 *
914 	 * @param The attribute's index.
915 	 * @return Always the empty string.
916 	 * @see org.xml.sax.Attributes#getLocalName
917 	 */
918 	public String getLocalName (int i)
919 	{
920 	    return "";
921 	}
922 
923 
924 	/***
925 	 * Return the qualified (prefixed) name of the specified attribute.
926 	 *
927 	 * @param The attribute's index.
928 	 * @return The attribute's qualified name, internalized.
929 	 */
930 	public String getQName (int i)
931 	{
932 	    return qAtts.getName(i).intern();
933 	}
934 
935 
936 	/***
937 	 * Return the type of the specified attribute.
938 	 *
939 	 * @param The attribute's index.
940 	 * @return The attribute's type as an internalized string.
941 	 */
942 	public String getType (int i)
943 	{
944 	    return qAtts.getType(i).intern();
945 	}
946 
947 
948 	/***
949 	 * Return the value of the specified attribute.
950 	 *
951 	 * @param The attribute's index.
952 	 * @return The attribute's value.
953 	 */
954 	public String getValue (int i)
955 	{
956 	    return qAtts.getValue(i);
957 	}
958 
959 
960 	/***
961 	 * Look up an attribute index by Namespace name.
962 	 *
963 	 * @param uri The Namespace URI or the empty string.
964 	 * @param localName The local name.
965 	 * @return The attributes index, or -1 if none was found.
966 	 * @see org.xml.sax.Attributes#getIndex(java.lang.String,java.lang.String)
967 	 */
968 	public int getIndex (String uri, String localName)
969 	{
970 	    return -1;
971 	}
972 
973 
974 	/***
975 	 * Look up an attribute index by qualified (prefixed) name.
976 	 *
977 	 * @param qName The qualified name.
978 	 * @return The attributes index, or -1 if none was found.
979 	 * @see org.xml.sax.Attributes#getIndex(java.lang.String)
980 	 */
981 	public int getIndex (String qName)
982 	{
983 	    int max = atts.getLength();
984 	    for (int i = 0; i < max; i++) {
985 		if (qAtts.getName(i).equals(qName)) {
986 		    return i;
987 		}
988 	    }
989 	    return -1;
990 	}
991 
992 
993 	/***
994 	 * Look up the type of an attribute by Namespace name.
995 	 *
996 	 * @param uri The Namespace URI
997 	 * @param localName The local name.
998 	 * @return The attribute's type as an internalized string.
999 	 */
1000 	public String getType (String uri, String localName)
1001 	{
1002 	    return null;
1003 	}
1004 
1005 
1006 	/***
1007 	 * Look up the type of an attribute by qualified (prefixed) name.
1008 	 *
1009 	 * @param qName The qualified name.
1010 	 * @return The attribute's type as an internalized string.
1011 	 */
1012 	public String getType (String qName)
1013 	{
1014 	    return qAtts.getType(qName).intern();
1015 	}
1016 
1017 
1018 	/***
1019 	 * Look up the value of an attribute by Namespace name.
1020 	 *
1021 	 * @param uri The Namespace URI
1022 	 * @param localName The local name.
1023 	 * @return The attribute's value.
1024 	 */
1025 	public String getValue (String uri, String localName)
1026 	{
1027 	    return null;
1028 	}
1029 
1030 
1031 	/***
1032 	 * Look up the value of an attribute by qualified (prefixed) name.
1033 	 *
1034 	 * @param qName The qualified name.
1035 	 * @return The attribute's value.
1036 	 */
1037 	public String getValue (String qName)
1038 	{
1039 	    return qAtts.getValue(qName);
1040 	}
1041 
1042 	private AttributeList qAtts;
1043     }
1044 }
1045 
1046 // end of ParserAdapter.java