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