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