001// ParserAdapter.java - adapt a SAX1 Parser to a SAX2 XMLReader.
002// Written by David Megginson, sax@megginson.com
003// NO WARRANTY!  This class is in the public domain.
004
005// $Id: ParserAdapter.java,v 1.1 2001/03/05 21:40:06 jstrachan Exp $
006
007package org.xml.sax.helpers;
008
009import java.io.IOException;
010import java.util.Enumeration;
011
012import org.xml.sax.Parser;      // deprecated
013import org.xml.sax.InputSource;
014import org.xml.sax.Locator;
015import org.xml.sax.AttributeList; // deprecated
016import org.xml.sax.EntityResolver;
017import org.xml.sax.DTDHandler;
018import org.xml.sax.DocumentHandler; // deprecated
019import org.xml.sax.ErrorHandler;
020import org.xml.sax.SAXException;
021import org.xml.sax.SAXParseException;
022
023import org.xml.sax.XMLReader;
024import org.xml.sax.Attributes;
025import org.xml.sax.ContentHandler;
026import org.xml.sax.SAXNotRecognizedException;
027import org.xml.sax.SAXNotSupportedException;
028
029
030/**
031 * Adapt a SAX1 Parser as a SAX2 XMLReader.
032 *
033 * <blockquote>
034 * <em>This module, both source code and documentation, is in the
035 * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em>
036 * </blockquote>
037 *
038 * <p>This class wraps a SAX1 {@link org.xml.sax.Parser Parser}
039 * and makes it act as a SAX2 {@link org.xml.sax.XMLReader XMLReader},
040 * with feature, property, and Namespace support.  Note
041 * that it is not possible to report {@link org.xml.sax.ContentHandler#skippedEntity
042 * skippedEntity} events, since SAX1 does not make that information available.</p>
043 *
044 * <p>This adapter does not test for duplicate Namespace-qualified
045 * attribute names.</p>
046 *
047 * @since SAX 2.0
048 * @author David Megginson, 
049 *         <a href="mailto:sax@megginson.com">sax@megginson.com</a>
050 * @version 2.0
051 * @see org.xml.sax.helpers.XMLReaderAdapter
052 * @see org.xml.sax.XMLReader
053 * @see org.xml.sax.Parser
054 */
055public class ParserAdapter implements XMLReader, DocumentHandler
056{
057
058
059    ////////////////////////////////////////////////////////////////////
060    // Constructors.
061    ////////////////////////////////////////////////////////////////////
062
063
064    /**
065     * Construct a new parser adapter.
066     *
067     * <p>Use the "org.xml.sax.parser" property to locate the
068     * embedded SAX1 driver.</p>
069     *
070     * @exception org.xml.sax.SAXException If the embedded driver
071     *            cannot be instantiated or if the
072     *            org.xml.sax.parser property is not specified.
073     */
074    public ParserAdapter ()
075      throws SAXException
076    {
077        super();
078
079        String driver = System.getProperty("org.xml.sax.parser");
080
081        try {
082            setup(ParserFactory.makeParser());
083        } catch (ClassNotFoundException e1) {
084            throw new
085                SAXException("Cannot find SAX1 driver class " +
086                             driver, e1);
087        } catch (IllegalAccessException e2) {
088            throw new
089                SAXException("SAX1 driver class " +
090                             driver +
091                             " found but cannot be loaded", e2);
092        } catch (InstantiationException e3) {
093            throw new
094                SAXException("SAX1 driver class " +
095                             driver +
096                             " loaded but cannot be instantiated", e3);
097        } catch (ClassCastException e4) {
098            throw new
099                SAXException("SAX1 driver class " +
100                             driver +
101                             " does not implement org.xml.sax.Parser");
102        } catch (NullPointerException e5) {
103            throw new 
104                SAXException("System property org.xml.sax.parser not specified");
105        }
106    }
107
108
109    /**
110     * Construct a new parser adapter.
111     *
112     * <p>Note that the embedded parser cannot be changed once the
113     * adapter is created; to embed a different parser, allocate
114     * a new ParserAdapter.</p>
115     *
116     * @param parser The SAX1 parser to embed.
117     * @exception java.lang.NullPointerException If the parser parameter
118     *            is null.
119     */
120    public ParserAdapter (Parser parser)
121    {
122        super();
123        setup(parser);
124    }
125
126
127    /**
128     * Internal setup method.
129     *
130     * @param parser The embedded parser.
131     * @exception java.lang.NullPointerException If the parser parameter
132     *            is null.
133     */
134    private void setup (Parser parser)
135    {
136        if (parser == null) {
137            throw new
138                NullPointerException("Parser argument must not be null");
139        }
140        this.parser = parser;
141        atts = new AttributesImpl();
142        nsSupport = new NamespaceSupport();
143        attAdapter = new AttributeListAdapter();
144    }
145
146
147
148    ////////////////////////////////////////////////////////////////////
149    // Implementation of org.xml.sax.XMLReader.
150    ////////////////////////////////////////////////////////////////////
151
152
153    //
154    // Internal constants for the sake of convenience.
155    //
156    private final static String FEATURES = "http://xml.org/sax/features/";
157    private final static String NAMESPACES = FEATURES + "namespaces";
158    private final static String NAMESPACE_PREFIXES = FEATURES + "namespace-prefixes";
159    private final static String VALIDATION = FEATURES + "validation";
160    private final static String EXTERNAL_GENERAL =
161        FEATURES + "external-general-entities";
162    private final static String EXTERNAL_PARAMETER =
163        FEATURES + "external-parameter-entities";
164
165
166    /**
167     * Set a feature for the parser.
168     *
169     * <p>The only features supported are namespaces and 
170     * namespace-prefixes.</p>
171     *
172     * @param name The feature name, as a complete URI.
173     * @param state The requested feature state.
174     * @exception org.xml.sax.SAXNotRecognizedException If the feature
175     *            name is not known.
176     * @exception org.xml.sax.SAXNotSupportedException If the feature
177     *            state is not supported.
178     * @see org.xml.sax.XMLReader#setFeature
179     */
180    public void setFeature (String name, boolean state)
181        throws SAXNotRecognizedException, SAXNotSupportedException
182    {
183        if (name.equals(NAMESPACES)) {
184            checkNotParsing("feature", name);
185            namespaces = state;
186            if (!namespaces && !prefixes) {
187                prefixes = true;
188            }
189        } else if (name.equals(NAMESPACE_PREFIXES)) {
190            checkNotParsing("feature", name);
191            prefixes = state;
192            if (!prefixes && !namespaces) {
193                namespaces = true;
194            }
195        } else if (name.equals(VALIDATION) ||
196                   name.equals(EXTERNAL_GENERAL) ||
197                   name.equals(EXTERNAL_PARAMETER)) {
198            throw new SAXNotSupportedException("Feature: " + name);
199        } else {
200            throw new SAXNotRecognizedException("Feature: " + name);
201        }
202    }
203
204
205    /**
206     * Check a parser feature.
207     *
208     * <p>The only features supported are namespaces and 
209     * namespace-prefixes.</p>
210     *
211     * @param name The feature name, as a complete URI.
212     * @return The current feature state.
213     * @exception org.xml.sax.SAXNotRecognizedException If the feature
214     *            name is not known.
215     * @exception org.xml.sax.SAXNotSupportedException If querying the
216     *            feature state is not supported.
217     * @see org.xml.sax.XMLReader#setFeature
218     */
219    public boolean getFeature (String name)
220        throws SAXNotRecognizedException, SAXNotSupportedException
221    {
222        if (name.equals(NAMESPACES)) {
223            return namespaces;
224        } else if (name.equals(NAMESPACE_PREFIXES)) {
225            return prefixes;
226        } else if (name.equals(VALIDATION) ||
227                   name.equals(EXTERNAL_GENERAL) ||
228                   name.equals(EXTERNAL_PARAMETER)) {
229            throw new SAXNotSupportedException("Feature: " + name);
230        } else {
231            throw new SAXNotRecognizedException("Feature: " + name);
232        }
233    }
234
235
236    /**
237     * Set a parser property.
238     *
239     * <p>No special properties are currently supported.</p>
240     *
241     * @param name The property name.
242     * @param value The property value.
243     * @exception org.xml.sax.SAXNotRecognizedException If the feature
244     *            name is not known.
245     * @exception org.xml.sax.SAXNotSupportedException If the feature
246     *            state is not supported.
247     * @see org.xml.sax.XMLReader#setProperty
248     */
249    public void setProperty (String name, Object value)
250        throws SAXNotRecognizedException, SAXNotSupportedException
251    {
252        throw new SAXNotRecognizedException("Property: " + name);
253    }
254
255
256    /**
257     * Get a parser property.
258     *
259     * <p>No special properties are currently supported.</p>
260     *
261     * @param name The property name.
262     * @return The property value.
263     * @exception org.xml.sax.SAXNotRecognizedException If the feature
264     *            name is not known.
265     * @exception org.xml.sax.SAXNotSupportedException If the feature
266     *            state is not supported.
267     * @see org.xml.sax.XMLReader#getProperty
268     */
269    public Object getProperty (String name)
270        throws SAXNotRecognizedException, SAXNotSupportedException
271    {
272        throw new SAXNotRecognizedException("Property: " + name);
273    }
274
275
276    /**
277     * Set the entity resolver.
278     *
279     * @param resolver The new entity resolver.
280     * @exception java.lang.NullPointerException If the entity resolver
281     *            parameter is null.
282     * @see org.xml.sax.XMLReader#setEntityResolver
283     */
284    public void setEntityResolver (EntityResolver resolver)
285    {
286        if (resolver == null) {
287            throw new NullPointerException("Null entity resolver");
288        }
289        entityResolver = resolver;
290    }
291
292
293    /**
294     * Return the current entity resolver.
295     *
296     * @return The current entity resolver, or null if none was supplied.
297     * @see org.xml.sax.XMLReader#getEntityResolver
298     */
299    public EntityResolver getEntityResolver ()
300    {
301        return entityResolver;
302    }
303
304
305    /**
306     * Set the DTD handler.
307     *
308     * @param resolver The new DTD handler.
309     * @exception java.lang.NullPointerException If the DTD handler
310     *            parameter is null.
311     * @see org.xml.sax.XMLReader#setEntityResolver
312     */
313    public void setDTDHandler (DTDHandler handler)
314    {
315        if (handler == null) {
316            throw new NullPointerException("Null DTD handler");
317        }
318        dtdHandler = handler;
319    }
320
321
322    /**
323     * Return the current DTD handler.
324     *
325     * @return The current DTD handler, or null if none was supplied.
326     * @see org.xml.sax.XMLReader#getEntityResolver
327     */
328    public DTDHandler getDTDHandler ()
329    {
330        return dtdHandler;
331    }
332
333
334    /**
335     * Set the content handler.
336     *
337     * @param resolver The new content handler.
338     * @exception java.lang.NullPointerException If the content handler
339     *            parameter is null.
340     * @see org.xml.sax.XMLReader#setEntityResolver
341     */
342    public void setContentHandler (ContentHandler handler)
343    {
344        if (handler == null) {
345            throw new NullPointerException("Null content handler");
346        }
347        contentHandler = handler;
348    }
349
350
351    /**
352     * Return the current content handler.
353     *
354     * @return The current content handler, or null if none was supplied.
355     * @see org.xml.sax.XMLReader#getEntityResolver
356     */
357    public ContentHandler getContentHandler ()
358    {
359        return contentHandler;
360    }
361
362
363    /**
364     * Set the error handler.
365     *
366     * @param resolver The new error handler.
367     * @exception java.lang.NullPointerException If the error handler
368     *            parameter is null.
369     * @see org.xml.sax.XMLReader#setEntityResolver
370     */
371    public void setErrorHandler (ErrorHandler handler)
372    {
373        if (handler == null) {
374            throw new NullPointerException("Null error handler");
375        }
376        errorHandler = handler;
377    }
378
379
380    /**
381     * Return the current error handler.
382     *
383     * @return The current error handler, or null if none was supplied.
384     * @see org.xml.sax.XMLReader#getEntityResolver
385     */
386    public ErrorHandler getErrorHandler ()
387    {
388        return errorHandler;
389    }
390
391
392    /**
393     * Parse an XML document.
394     *
395     * @param systemId The absolute URL of the document.
396     * @exception java.io.IOException If there is a problem reading
397     *            the raw content of the document.
398     * @exception org.xml.sax.SAXException If there is a problem
399     *            processing the document.
400     * @see #parse(org.xml.sax.InputSource)
401     * @see org.xml.sax.Parser#parse(java.lang.String)
402     */
403    public void parse (String systemId)
404        throws IOException, SAXException
405    {
406        parse(new InputSource(systemId));
407    }
408
409
410    /**
411     * Parse an XML document.
412     *
413     * @param input An input source for the document.
414     * @exception java.io.IOException If there is a problem reading
415     *            the raw content of the document.
416     * @exception org.xml.sax.SAXException If there is a problem
417     *            processing the document.
418     * @see #parse(java.lang.String)
419     * @see org.xml.sax.Parser#parse(org.xml.sax.InputSource)
420     */
421    public void parse (InputSource input)
422        throws IOException, SAXException
423    {
424        if (parsing) {
425            throw new SAXException("Parser is already in use");
426        }
427        setupParser();
428        parsing = true;
429        try {
430            parser.parse(input);
431        } finally {
432            parsing = false;
433        }
434        parsing = false;
435    }
436
437
438
439    ////////////////////////////////////////////////////////////////////
440    // Implementation of org.xml.sax.DocumentHandler.
441    ////////////////////////////////////////////////////////////////////
442
443
444    /**
445     * Adapt a SAX1 document locator event.
446     *
447     * @param locator A document locator.
448     * @see org.xml.sax.ContentHandler#setDocumentLocator
449     */
450    public void setDocumentLocator (Locator locator)
451    {
452        this.locator = locator;
453        if (contentHandler != null) {
454            contentHandler.setDocumentLocator(locator);
455        }
456    }
457
458
459    /**
460     * Adapt a SAX1 start document event.
461     *
462     * @exception org.xml.sax.SAXException The client may raise a
463     *            processing exception.
464     * @see org.xml.sax.DocumentHandler#startDocument
465     */
466    public void startDocument ()
467        throws SAXException
468    {
469        if (contentHandler != null) {
470            contentHandler.startDocument();
471        }
472    }
473
474
475    /**
476     * Adapt a SAX1 end document event.
477     *
478     * @exception org.xml.sax.SAXException The client may raise a
479     *            processing exception.
480     * @see org.xml.sax.DocumentHandler#endDocument
481     */
482    public void endDocument ()
483        throws SAXException
484    {
485        if (contentHandler != null) {
486            contentHandler.endDocument();
487        }
488    }
489
490
491    /**
492     * Adapt a SAX1 startElement event.
493     *
494     * <p>If necessary, perform Namespace processing.</p>
495     *
496     * @param qName The qualified (prefixed) name.
497     * @param qAtts The XML 1.0 attribute list (with qnames).
498     */
499    public void startElement (String qName, AttributeList qAtts)
500        throws SAXException
501    {
502                                // If we're not doing Namespace
503                                // processing, dispatch this quickly.
504        if (!namespaces) {
505            if (contentHandler != null) {
506                attAdapter.setAttributeList(qAtts);
507                contentHandler.startElement("", "", qName.intern(),
508                                            attAdapter);
509            }
510            return;
511        }
512
513
514                                // OK, we're doing Namespace processing.
515        nsSupport.pushContext();
516        boolean seenDecl = false;
517        atts.clear();
518        
519                                // Take a first pass and copy all
520                                // attributes into the SAX2 attribute
521                                // list, noting any Namespace 
522                                // declarations.
523        int length = qAtts.getLength();
524        for (int i = 0; i < length; i++) {
525            String attQName = qAtts.getName(i);
526            String type = qAtts.getType(i);
527            String value = qAtts.getValue(i);
528
529                                // Found a declaration...
530            if (attQName.startsWith("xmlns")) {
531                String prefix;
532                int n = attQName.indexOf(':');
533                if (n == -1) {
534                    prefix = "";
535                } else {
536                    prefix = attQName.substring(n+1);
537                }
538                if (!nsSupport.declarePrefix(prefix, value)) {
539                    reportError("Illegal Namespace prefix: " + prefix);
540                }
541                if (contentHandler != null) {
542                    contentHandler.startPrefixMapping(prefix, value);
543                }
544                                // We may still have to add this to
545                                // the list.
546                if (prefixes) {
547                    atts.addAttribute("", "", attQName.intern(),
548                                      type, value);
549                }
550                seenDecl = true;
551
552                                // This isn't a declaration.
553            } else {
554                String attName[] = processName(attQName, true);
555                atts.addAttribute(attName[0], attName[1], attName[2],
556                                  type, value);
557            }
558        }
559        
560                                // If there was a Namespace declaration,
561                                // we have to make a second pass just
562                                // to be safe -- this will happen very
563                                // rarely, possibly only once for each
564                                // document.
565        if (seenDecl) {
566            length = atts.getLength();
567            for (int i = 0; i < length; i++) {
568                String attQName = atts.getQName(i);
569                if (!attQName.startsWith("xmlns")) {
570                    String attName[] = processName(attQName, true);
571                    atts.setURI(i, attName[0]);
572                    atts.setLocalName(i, attName[1]);
573                }
574            }
575        }
576
577                                // OK, finally report the event.
578        if (contentHandler != null) {
579            String name[] = processName(qName, false);
580            contentHandler.startElement(name[0], name[1], name[2], atts);
581        }
582    }
583
584
585    /**
586     * Adapt a SAX1 end element event.
587     *
588     * @param qName The qualified (prefixed) name.
589     * @exception org.xml.sax.SAXException The client may raise a
590     *            processing exception.
591     * @see org.xml.sax.DocumentHandler#endElement
592     */
593    public void endElement (String qName)
594        throws SAXException
595    {
596                                // If we're not doing Namespace
597                                // processing, dispatch this quickly.
598        if (!namespaces) {
599            if (contentHandler != null) {
600                contentHandler.endElement("", "", qName.intern());
601            }
602            return;
603        }
604
605                                // Split the name.
606        String names[] = processName(qName, false);
607        if (contentHandler != null) {
608            contentHandler.endElement(names[0], names[1], names[2]);
609            Enumeration prefixes = nsSupport.getDeclaredPrefixes();
610            while (prefixes.hasMoreElements()) {
611                String prefix = (String)prefixes.nextElement();
612                contentHandler.endPrefixMapping(prefix);
613            }
614        }
615        nsSupport.popContext();
616    }
617
618
619    /**
620     * Adapt a SAX1 characters event.
621     *
622     * @param ch An array of characters.
623     * @param start The starting position in the array.
624     * @param length The number of characters to use.
625     * @exception org.xml.sax.SAXException The client may raise a
626     *            processing exception.
627     * @see org.xml.sax.DocumentHandler#characters
628     */
629    public void characters (char ch[], int start, int length)
630        throws SAXException
631    {
632        if (contentHandler != null) {
633            contentHandler.characters(ch, start, length);
634        }
635    }
636
637
638    /**
639     * Adapt a SAX1 ignorable whitespace 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 org.xml.sax.SAXException The client may raise a
645     *            processing exception.
646     * @see org.xml.sax.DocumentHandler#ignorableWhitespace
647     */
648    public void ignorableWhitespace (char ch[], int start, int length)
649        throws SAXException
650    {
651        if (contentHandler != null) {
652            contentHandler.ignorableWhitespace(ch, start, length);
653        }
654    }
655
656
657    /**
658     * Adapt a SAX1 processing instruction event.
659     *
660     * @param target The processing instruction target.
661     * @param data The remainder of the processing instruction
662     * @exception org.xml.sax.SAXException The client may raise a
663     *            processing exception.
664     * @see org.xml.sax.DocumentHandler#processingInstruction
665     */
666    public void processingInstruction (String target, String data)
667        throws SAXException
668    {
669        if (contentHandler != null) {
670            contentHandler.processingInstruction(target, data);
671        }
672    }
673
674
675
676    ////////////////////////////////////////////////////////////////////
677    // Internal utility methods.
678    ////////////////////////////////////////////////////////////////////
679
680
681    /**
682     * Initialize the parser before each run.
683     */
684    private void setupParser ()
685    {
686        nsSupport.reset();
687
688        if (entityResolver != null) {
689            parser.setEntityResolver(entityResolver);
690        }
691        if (dtdHandler != null) {
692            parser.setDTDHandler(dtdHandler);
693        }
694        if (errorHandler != null) {
695            parser.setErrorHandler(errorHandler);
696        }
697        parser.setDocumentHandler(this);
698        locator = null;
699    }
700
701
702    /**
703     * Process a qualified (prefixed) name.
704     *
705     * <p>If the name has an undeclared prefix, use only the qname
706     * and make an ErrorHandler.error callback in case the app is
707     * interested.</p>
708     *
709     * @param qName The qualified (prefixed) name.
710     * @param isAttribute true if this is an attribute name.
711     * @return The name split into three parts.
712     * @exception org.xml.sax.SAXException The client may throw
713     *            an exception if there is an error callback.
714     */
715    private String [] processName (String qName, boolean isAttribute)
716        throws SAXException
717    {
718        String parts[] = nsSupport.processName(qName, nameParts,
719                                               isAttribute);
720        if (parts == null) {
721            parts = new String[3];
722            parts[2] = qName.intern();
723            reportError("Undeclared prefix: " + qName);
724        }
725        return parts;
726    }
727
728
729    /**
730     * Report a non-fatal error.
731     *
732     * @param message The error message.
733     * @exception org.xml.sax.SAXException The client may throw
734     *            an exception.
735     */
736    void reportError (String message)
737        throws SAXException
738    {
739        if (errorHandler == null) {
740            return;
741        }
742
743        SAXParseException e;
744        if (locator != null) {
745            e = new SAXParseException(message, locator);
746        } else {
747            e = new SAXParseException(message, null, null, -1, -1);
748        }
749        errorHandler.error(e);
750    }
751
752
753    /**
754     * Throw an exception if we are parsing.
755     *
756     * <p>Use this method to detect illegal feature or
757     * property changes.</p>
758     *
759     * @param type The type of thing (feature or property).
760     * @param name The feature or property name.
761     * @exception org.xml.sax.SAXNotSupportedException If a
762     *            document is currently being parsed.
763     */
764    private void checkNotParsing (String type, String name)
765        throws SAXNotSupportedException
766    {
767        if (parsing) {
768            throw new SAXNotSupportedException("Cannot change " +
769                                               type + ' ' +
770                                               name + " while parsing");
771                                               
772        }
773    }
774
775
776
777    ////////////////////////////////////////////////////////////////////
778    // Internal state.
779    ////////////////////////////////////////////////////////////////////
780
781    private NamespaceSupport nsSupport;
782    private AttributeListAdapter attAdapter;
783
784    private boolean parsing = false;
785    private String nameParts[] = new String[3];
786
787    private Parser parser = null;
788
789    private AttributesImpl atts = null;
790
791                                // Features
792    private boolean namespaces = true;
793    private boolean prefixes = false;
794
795                                // Properties
796
797                                // Handlers
798    Locator locator;
799
800    EntityResolver entityResolver = null;
801    DTDHandler dtdHandler = null;
802    ContentHandler contentHandler = null;
803    ErrorHandler errorHandler = null;
804
805
806
807    ////////////////////////////////////////////////////////////////////
808    // Inner class to wrap an AttributeList when not doing NS proc.
809    ////////////////////////////////////////////////////////////////////
810
811
812    /**
813     * Adapt a SAX1 AttributeList as a SAX2 Attributes object.
814     *
815     * <p>This class is in the Public Domain, and comes with NO
816     * WARRANTY of any kind.</p>
817     *
818     * <p>This wrapper class is used only when Namespace support
819     * is disabled -- it provides pretty much a direct mapping
820     * from SAX1 to SAX2, except that names and types are 
821     * interned whenever requested.</p>
822     */
823    final class AttributeListAdapter implements Attributes
824    {
825
826        /**
827         * Construct a new adapter.
828         */
829        AttributeListAdapter ()
830        {
831        }
832
833
834        /**
835         * Set the embedded AttributeList.
836         *
837         * <p>This method must be invoked before any of the others
838         * can be used.</p>
839         *
840         * @param The SAX1 attribute list (with qnames).
841         */
842        void setAttributeList (AttributeList qAtts)
843        {
844            this.qAtts = qAtts;
845        }
846
847
848        /**
849         * Return the length of the attribute list.
850         *
851         * @return The number of attributes in the list.
852         * @see org.xml.sax.Attributes#getLength
853         */
854        public int getLength ()
855        {
856            return qAtts.getLength();
857        }
858
859
860        /**
861         * Return the Namespace URI of the specified attribute.
862         *
863         * @param The attribute's index.
864         * @return Always the empty string.
865         * @see org.xml.sax.Attributes#getURI
866         */
867        public String getURI (int i)
868        {
869            return "";
870        }
871
872
873        /**
874         * Return the local name of the specified attribute.
875         *
876         * @param The attribute's index.
877         * @return Always the empty string.
878         * @see org.xml.sax.Attributes#getLocalName
879         */
880        public String getLocalName (int i)
881        {
882            return "";
883        }
884
885
886        /**
887         * Return the qualified (prefixed) name of the specified attribute.
888         *
889         * @param The attribute's index.
890         * @return The attribute's qualified name, internalized.
891         */
892        public String getQName (int i)
893        {
894            return qAtts.getName(i).intern();
895        }
896
897
898        /**
899         * Return the type of the specified attribute.
900         *
901         * @param The attribute's index.
902         * @return The attribute's type as an internalized string.
903         */
904        public String getType (int i)
905        {
906            return qAtts.getType(i).intern();
907        }
908
909
910        /**
911         * Return the value of the specified attribute.
912         *
913         * @param The attribute's index.
914         * @return The attribute's value.
915         */
916        public String getValue (int i)
917        {
918            return qAtts.getValue(i);
919        }
920
921
922        /**
923         * Look up an attribute index by Namespace name.
924         *
925         * @param uri The Namespace URI or the empty string.
926         * @param localName The local name.
927         * @return The attributes index, or -1 if none was found.
928         * @see org.xml.sax.Attributes#getIndex(java.lang.String,java.lang.String)
929         */
930        public int getIndex (String uri, String localName)
931        {
932            return -1;
933        }
934
935
936        /**
937         * Look up an attribute index by qualified (prefixed) name.
938         *
939         * @param qName The qualified name.
940         * @return The attributes index, or -1 if none was found.
941         * @see org.xml.sax.Attributes#getIndex(java.lang.String)
942         */
943        public int getIndex (String qName)
944        {
945            int max = atts.getLength();
946            for (int i = 0; i < max; i++) {
947                if (qAtts.getName(i).equals(qName)) {
948                    return i;
949                }
950            }
951            return -1;
952        }
953
954
955        /**
956         * Look up the type of an attribute by Namespace name.
957         *
958         * @param uri The Namespace URI
959         * @param localName The local name.
960         * @return The attribute's type as an internalized string.
961         */
962        public String getType (String uri, String localName)
963        {
964            return null;
965        }
966
967
968        /**
969         * Look up the type of an attribute by qualified (prefixed) name.
970         *
971         * @param qName The qualified name.
972         * @return The attribute's type as an internalized string.
973         */
974        public String getType (String qName)
975        {
976            return qAtts.getType(qName).intern();
977        }
978
979
980        /**
981         * Look up the value of an attribute by Namespace name.
982         *
983         * @param uri The Namespace URI
984         * @param localName The local name.
985         * @return The attribute's value.
986         */
987        public String getValue (String uri, String localName)
988        {
989            return null;
990        }
991
992
993        /**
994         * Look up the value of an attribute by qualified (prefixed) name.
995         *
996         * @param qName The qualified name.
997         * @return The attribute's value.
998         */
999        public String getValue (String qName)
1000        {
1001            return qAtts.getValue(qName);
1002        }
1003
1004        private AttributeList qAtts;
1005    }
1006}
1007
1008// end of ParserAdapter.java