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