1. /*
  2. * The Apache Software License, Version 1.1
  3. *
  4. *
  5. * Copyright (c) 2001-2004 The Apache Software Foundation.
  6. * All rights reserved.
  7. *
  8. * Redistribution and use in source and binary forms, with or without
  9. * modification, are permitted provided that the following conditions
  10. * are met:
  11. *
  12. * 1. Redistributions of source code must retain the above copyright
  13. * notice, this list of conditions and the following disclaimer.
  14. *
  15. * 2. Redistributions in binary form must reproduce the above copyright
  16. * notice, this list of conditions and the following disclaimer in
  17. * the documentation and/or other materials provided with the
  18. * distribution.
  19. *
  20. * 3. The end-user documentation included with the redistribution,
  21. * if any, must include the following acknowledgment:
  22. * "This product includes software developed by the
  23. * Apache Software Foundation (http://www.apache.org/)."
  24. * Alternately, this acknowledgment may appear in the software itself,
  25. * if and wherever such third-party acknowledgments normally appear.
  26. *
  27. * 4. The names "Xerces" and "Apache Software Foundation" must
  28. * not be used to endorse or promote products derived from this
  29. * software without prior written permission. For written
  30. * permission, please contact apache@apache.org.
  31. *
  32. * 5. Products derived from this software may not be called "Apache",
  33. * nor may "Apache" appear in their name, without prior written
  34. * permission of the Apache Software Foundation.
  35. *
  36. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  37. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  38. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  39. * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
  40. * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  41. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  42. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  43. * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  44. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  45. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  46. * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  47. * SUCH DAMAGE.
  48. * ====================================================================
  49. *
  50. * This software consists of voluntary contributions made by many
  51. * individuals on behalf of the Apache Software Foundation and was
  52. * originally based on software copyright (c) 1999, International
  53. * Business Machines, Inc., http://www.apache.org. For more
  54. * information on the Apache Software Foundation, please see
  55. * <http://www.apache.org/>.
  56. */
  57. package com.sun.org.apache.xerces.internal.parsers;
  58. import java.io.IOException;
  59. import java.util.Locale;
  60. import com.sun.org.apache.xerces.internal.impl.Constants;
  61. import com.sun.org.apache.xerces.internal.xs.PSVIProvider;
  62. import com.sun.org.apache.xerces.internal.util.EntityResolverWrapper;
  63. import com.sun.org.apache.xerces.internal.util.EntityResolver2Wrapper;
  64. import com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper;
  65. import com.sun.org.apache.xerces.internal.util.SAXMessageFormatter;
  66. import com.sun.org.apache.xerces.internal.util.SymbolHash;
  67. import com.sun.org.apache.xerces.internal.util.XMLSymbols;
  68. import com.sun.org.apache.xerces.internal.xni.Augmentations;
  69. import com.sun.org.apache.xerces.internal.xni.NamespaceContext;
  70. import com.sun.org.apache.xerces.internal.xni.QName;
  71. import com.sun.org.apache.xerces.internal.xni.XMLAttributes;
  72. import com.sun.org.apache.xerces.internal.xni.XMLLocator;
  73. import com.sun.org.apache.xerces.internal.xni.XMLResourceIdentifier;
  74. import com.sun.org.apache.xerces.internal.xni.XMLString;
  75. import com.sun.org.apache.xerces.internal.xni.XNIException;
  76. import com.sun.org.apache.xerces.internal.xni.parser.XMLConfigurationException;
  77. import com.sun.org.apache.xerces.internal.xni.parser.XMLEntityResolver;
  78. import com.sun.org.apache.xerces.internal.xni.parser.XMLErrorHandler;
  79. import com.sun.org.apache.xerces.internal.xni.parser.XMLInputSource;
  80. import com.sun.org.apache.xerces.internal.xni.parser.XMLParseException;
  81. import com.sun.org.apache.xerces.internal.xni.parser.XMLParserConfiguration;
  82. import com.sun.org.apache.xerces.internal.xs.AttributePSVI;
  83. import com.sun.org.apache.xerces.internal.xs.ElementPSVI;
  84. import org.xml.sax.AttributeList;
  85. import org.xml.sax.Attributes;
  86. import org.xml.sax.ext.Attributes2;
  87. import org.xml.sax.ContentHandler;
  88. import org.xml.sax.DTDHandler;
  89. import org.xml.sax.DocumentHandler;
  90. import org.xml.sax.EntityResolver;
  91. import org.xml.sax.ErrorHandler;
  92. import org.xml.sax.InputSource;
  93. import org.xml.sax.Locator;
  94. import org.xml.sax.ext.Locator2Impl;
  95. import org.xml.sax.ext.Locator2;
  96. import org.xml.sax.Parser;
  97. import org.xml.sax.SAXException;
  98. import org.xml.sax.SAXNotRecognizedException;
  99. import org.xml.sax.SAXNotSupportedException;
  100. import org.xml.sax.SAXParseException;
  101. import org.xml.sax.XMLReader;
  102. import org.xml.sax.ext.DeclHandler;
  103. import org.xml.sax.ext.EntityResolver2;
  104. import org.xml.sax.ext.LexicalHandler;
  105. import org.xml.sax.helpers.LocatorImpl;
  106. /**
  107. * This is the base class of all SAX parsers. It implements both the
  108. * SAX1 and SAX2 parser functionality, while the actual pipeline is
  109. * defined in the parser configuration.
  110. *
  111. * @author Arnaud Le Hors, IBM
  112. * @author Andy Clark, IBM
  113. *
  114. * @version $Id: AbstractSAXParser.java,v 1.54 2004/04/07 15:42:05 mrglavas Exp $
  115. */
  116. public abstract class AbstractSAXParser
  117. extends AbstractXMLDocumentParser
  118. implements PSVIProvider, // PSVI
  119. Parser, XMLReader // SAX1, SAX2
  120. {
  121. //
  122. // Constants
  123. //
  124. // features
  125. /** Feature identifier: namespaces. */
  126. protected static final String NAMESPACES =
  127. Constants.SAX_FEATURE_PREFIX + Constants.NAMESPACES_FEATURE;
  128. /** Feature identifier: namespace prefixes. */
  129. protected static final String NAMESPACE_PREFIXES =
  130. Constants.SAX_FEATURE_PREFIX + Constants.NAMESPACE_PREFIXES_FEATURE;
  131. /** Feature id: string interning. */
  132. protected static final String STRING_INTERNING =
  133. Constants.SAX_FEATURE_PREFIX + Constants.STRING_INTERNING_FEATURE;
  134. /** Feature identifier: allow notation and unparsed entity events to be sent out of order. */
  135. // this is not meant to be a recognized feature, but we need it here to use
  136. // if it is already a recognized feature for the pipeline
  137. protected static final String ALLOW_UE_AND_NOTATION_EVENTS =
  138. Constants.SAX_FEATURE_PREFIX + Constants.ALLOW_DTD_EVENTS_AFTER_ENDDTD_FEATURE;
  139. /** Recognized features. */
  140. private static final String[] RECOGNIZED_FEATURES = {
  141. NAMESPACES,
  142. NAMESPACE_PREFIXES,
  143. STRING_INTERNING,
  144. };
  145. // properties
  146. /** Property id: lexical handler. */
  147. protected static final String LEXICAL_HANDLER =
  148. Constants.SAX_PROPERTY_PREFIX + Constants.LEXICAL_HANDLER_PROPERTY;
  149. /** Property id: declaration handler. */
  150. protected static final String DECLARATION_HANDLER =
  151. Constants.SAX_PROPERTY_PREFIX + Constants.DECLARATION_HANDLER_PROPERTY;
  152. /** Property id: DOM node. */
  153. protected static final String DOM_NODE =
  154. Constants.SAX_PROPERTY_PREFIX + Constants.DOM_NODE_PROPERTY;
  155. /** Recognized properties. */
  156. private static final String[] RECOGNIZED_PROPERTIES = {
  157. LEXICAL_HANDLER,
  158. DECLARATION_HANDLER,
  159. DOM_NODE,
  160. };
  161. //
  162. // Data
  163. //
  164. // features
  165. /** Namespaces. */
  166. protected boolean fNamespaces;
  167. /** Namespace prefixes. */
  168. protected boolean fNamespacePrefixes = false;
  169. /** Lexical handler parameter entities. */
  170. protected boolean fLexicalHandlerParameterEntities = true;
  171. // parser handlers
  172. /** Content handler. */
  173. protected ContentHandler fContentHandler;
  174. /** Document handler. */
  175. protected DocumentHandler fDocumentHandler;
  176. /** Namespace context */
  177. protected NamespaceContext fNamespaceContext;
  178. /** DTD handler. */
  179. protected org.xml.sax.DTDHandler fDTDHandler;
  180. /** Decl handler. */
  181. protected DeclHandler fDeclHandler;
  182. /** Lexical handler. */
  183. protected LexicalHandler fLexicalHandler;
  184. protected QName fQName = new QName();
  185. protected boolean resolve_dtd_uris = true;
  186. protected boolean startDocumentCalled = false;
  187. protected boolean resolverType = true;
  188. protected boolean locatorType = false;
  189. protected boolean attributeType = true;
  190. protected boolean isStandalone = false;
  191. // state
  192. /**
  193. * True if a parse is in progress. This state is needed because
  194. * some features/properties cannot be set while parsing (e.g.
  195. * validation and namespaces).
  196. */
  197. protected boolean fParseInProgress = false;
  198. // track the version of the document being parsed
  199. protected String fVersion;
  200. // temp vars
  201. private final AttributesProxy fAttributesProxy = new AttributesProxy();
  202. private Augmentations fAugmentations = null;
  203. // temporary buffer for sending normalized values
  204. // REVISIT: what should be the size of the buffer?
  205. private static final int BUFFER_SIZE = 20;
  206. private char[] fCharBuffer = new char[BUFFER_SIZE];
  207. // allows us to keep track of whether an attribute has
  208. // been declared twice, so that we can avoid exposing the
  209. // second declaration to any registered DeclHandler
  210. protected SymbolHash fDeclaredAttrs = null;
  211. //
  212. // Constructors
  213. //
  214. /** Default constructor. */
  215. protected AbstractSAXParser(XMLParserConfiguration config) {
  216. super(config);
  217. config.addRecognizedFeatures(RECOGNIZED_FEATURES);
  218. config.addRecognizedProperties(RECOGNIZED_PROPERTIES);
  219. try {
  220. config.setFeature(ALLOW_UE_AND_NOTATION_EVENTS, false);
  221. }
  222. catch (XMLConfigurationException e) {
  223. // it wasn't a recognized feature, so we don't worry about it
  224. }
  225. } // <init>(XMLParserConfiguration)
  226. //
  227. // XMLDocumentHandler methods
  228. //
  229. /**
  230. * The start of the document.
  231. *
  232. * @param locator The document locator, or null if the document
  233. * location cannot be reported during the parsing
  234. * of this document. However, it is <em>strongly</em>
  235. * recommended that a locator be supplied that can
  236. * at least report the system identifier of the
  237. * document.
  238. * @param encoding The auto-detected IANA encoding name of the entity
  239. * stream. This value will be null in those situations
  240. * where the entity encoding is not auto-detected (e.g.
  241. * internal entities or a document entity that is
  242. * parsed from a java.io.Reader).
  243. * @param namespaceContext
  244. * The namespace context in effect at the
  245. * start of this document.
  246. * This object represents the current context.
  247. * Implementors of this class are responsible
  248. * for copying the namespace bindings from the
  249. * the current context (and its parent contexts)
  250. * if that information is important.
  251. * @param augs Additional information that may include infoset augmentations
  252. *
  253. * @throws XNIException Thrown by handler to signal an error.
  254. */
  255. public void startDocument(XMLLocator locator, String encoding,
  256. NamespaceContext namespaceContext, Augmentations augs)
  257. throws XNIException {
  258. fNamespaceContext = namespaceContext;
  259. startDocumentCalled = true;
  260. try {
  261. // SAX1
  262. if (fDocumentHandler != null) {
  263. if (locator != null) {
  264. fDocumentHandler.setDocumentLocator(new LocatorProxy(locator));
  265. }
  266. fDocumentHandler.startDocument();
  267. }
  268. // SAX2
  269. if (fContentHandler != null) {
  270. if (locator != null) {
  271. locatorType = true;
  272. fContentHandler.setDocumentLocator(new LocatorProxy(locator));
  273. }
  274. fContentHandler.startDocument();
  275. }
  276. }
  277. catch (SAXException e) {
  278. throw new XNIException(e);
  279. }
  280. } // startDocument(locator,encoding,augs)
  281. /**
  282. * Notifies of the presence of an XMLDecl line in the document. If
  283. * present, this method will be called immediately following the
  284. * startDocument call.
  285. *
  286. * @param version The XML version.
  287. * @param encoding The IANA encoding name of the document, or null if
  288. * not specified.
  289. * @param standalone The standalone value, or null if not specified.
  290. * @param augs Additional information that may include infoset augmentations
  291. *
  292. * @throws XNIException Thrown by handler to signal an error.
  293. */
  294. public void xmlDecl(String version, String encoding, String standalone, Augmentations augs)
  295. throws XNIException {
  296. // the version need only be set once; if
  297. // document's XML 1.0|1.1, that's how it'll stay
  298. fVersion = version;
  299. if(standalone != null && standalone.equalsIgnoreCase("yes"))
  300. isStandalone = true;
  301. else
  302. isStandalone = false;
  303. } // xmlDecl(String,String,String)
  304. /**
  305. * Notifies of the presence of the DOCTYPE line in the document.
  306. *
  307. * @param rootElement The name of the root element.
  308. * @param publicId The public identifier if an external DTD or null
  309. * if the external DTD is specified using SYSTEM.
  310. * @param systemId The system identifier if an external DTD, null
  311. * otherwise.
  312. * @param augs Additional information that may include infoset augmentations
  313. *
  314. * @throws XNIException Thrown by handler to signal an error.
  315. */
  316. public void doctypeDecl(String rootElement,
  317. String publicId, String systemId, Augmentations augs)
  318. throws XNIException {
  319. fInDTD = true;
  320. try {
  321. // SAX2 extension
  322. if (fLexicalHandler != null) {
  323. fLexicalHandler.startDTD(rootElement, publicId, systemId);
  324. }
  325. }
  326. catch (SAXException e) {
  327. throw new XNIException(e);
  328. }
  329. // is there a DeclHandler?
  330. if(fDeclHandler != null) {
  331. fDeclaredAttrs = new SymbolHash();
  332. }
  333. } // doctypeDecl(String,String,String)
  334. /**
  335. * This method notifies of the start of an entity. The DTD has the
  336. * pseudo-name of "[dtd]" parameter entity names start with '%'; and
  337. * general entity names are just the entity name.
  338. * <p>
  339. * <strong>Note:</strong> Since the document is an entity, the handler
  340. * will be notified of the start of the document entity by calling the
  341. * startEntity method with the entity name "[xml]" <em>before</em> calling
  342. * the startDocument method. When exposing entity boundaries through the
  343. * SAX API, the document entity is never reported, however.
  344. * <p>
  345. * <strong>Note:</strong> This method is not called for entity references
  346. * appearing as part of attribute values.
  347. *
  348. * @param name The name of the entity.
  349. * @param identifier The resource identifier.
  350. * @param encoding The auto-detected IANA encoding name of the entity
  351. * stream. This value will be null in those situations
  352. * where the entity encoding is not auto-detected (e.g.
  353. * internal parameter entities).
  354. * @param augs Additional information that may include infoset augmentations
  355. *
  356. * @throws XNIException Thrown by handler to signal an error.
  357. */
  358. public void startGeneralEntity(String name, XMLResourceIdentifier identifier,
  359. String encoding, Augmentations augs)
  360. throws XNIException {
  361. try {
  362. // Only report startEntity if this entity was actually read.
  363. if (augs != null && Boolean.TRUE.equals(augs.getItem(Constants.ENTITY_SKIPPED))) {
  364. // report skipped entity to content handler
  365. if (fContentHandler != null) {
  366. fContentHandler.skippedEntity(name);
  367. }
  368. }
  369. else {
  370. // SAX2 extension
  371. if (fLexicalHandler != null) {
  372. fLexicalHandler.startEntity(name);
  373. }
  374. }
  375. }
  376. catch (SAXException e) {
  377. throw new XNIException(e);
  378. }
  379. } // startGeneralEntity(String,String,String,String,String)
  380. /**
  381. * This method notifies the end of an entity. The DTD has the pseudo-name
  382. * of "[dtd]" parameter entity names start with '%'; and general entity
  383. * names are just the entity name.
  384. * <p>
  385. * <strong>Note:</strong> Since the document is an entity, the handler
  386. * will be notified of the end of the document entity by calling the
  387. * endEntity method with the entity name "[xml]" <em>after</em> calling
  388. * the endDocument method. When exposing entity boundaries through the
  389. * SAX API, the document entity is never reported, however.
  390. * <p>
  391. * <strong>Note:</strong> This method is not called for entity references
  392. * appearing as part of attribute values.
  393. *
  394. * @param name The name of the entity.
  395. * @param augs Additional information that may include infoset augmentations
  396. *
  397. * @throws XNIException Thrown by handler to signal an error.
  398. */
  399. public void endGeneralEntity(String name, Augmentations augs) throws XNIException {
  400. try {
  401. // Only report endEntity if this entity was actually read.
  402. if (augs == null || !Boolean.TRUE.equals(augs.getItem(Constants.ENTITY_SKIPPED))) {
  403. // SAX2 extension
  404. if (fLexicalHandler != null) {
  405. fLexicalHandler.endEntity(name);
  406. }
  407. }
  408. }
  409. catch (SAXException e) {
  410. throw new XNIException(e);
  411. }
  412. } // endEntity(String)
  413. /**
  414. * The start of an element. If the document specifies the start element
  415. * by using an empty tag, then the startElement method will immediately
  416. * be followed by the endElement method, with no intervening methods.
  417. *
  418. * @param element The name of the element.
  419. * @param attributes The element attributes.
  420. * @param augs Additional information that may include infoset augmentations
  421. *
  422. * @throws XNIException Thrown by handler to signal an error.
  423. */
  424. public void startElement(QName element, XMLAttributes attributes, Augmentations augs)
  425. throws XNIException {
  426. try {
  427. // SAX1
  428. if (fDocumentHandler != null) {
  429. // REVISIT: should we support schema-normalized-value for SAX1 events
  430. //
  431. fAttributesProxy.setAttributes(attributes);
  432. fDocumentHandler.startElement(element.rawname, fAttributesProxy);
  433. }
  434. // SAX2
  435. if (fContentHandler != null) {
  436. if (fNamespaces) {
  437. // send prefix mapping events
  438. startNamespaceMapping();
  439. // REVISIT: It should not be necessary to iterate over the attribute
  440. // list when the set of [namespace attributes] is empty for this
  441. // element. This should be computable from the NamespaceContext, but
  442. // since we currently don't report the mappings for the xml prefix
  443. // we cannot use the declared prefix count for the current context
  444. // to skip this section. -- mrglavas
  445. int len = attributes.getLength();
  446. for (int i = len - 1; i >= 0; --i) {
  447. attributes.getName(i, fQName);
  448. if ((fQName.prefix == XMLSymbols.PREFIX_XMLNS) ||
  449. (fQName.rawname == XMLSymbols.PREFIX_XMLNS)) {
  450. if (!fNamespacePrefixes) {
  451. // remove namespace declaration attributes
  452. attributes.removeAttributeAt(i);
  453. }
  454. else {
  455. // localpart should be empty string as per SAX documentation:
  456. // http://www.saxproject.org/?selected=namespaces
  457. fQName.prefix = "";
  458. fQName.uri = "";
  459. fQName.localpart = "";
  460. attributes.setName(i, fQName);
  461. }
  462. }
  463. }
  464. }
  465. fAugmentations = augs;
  466. String uri = element.uri != null ? element.uri : "";
  467. String localpart = fNamespaces ? element.localpart : "";
  468. fAttributesProxy.setAttributes(attributes);
  469. fContentHandler.startElement(uri, localpart, element.rawname,
  470. fAttributesProxy);
  471. }
  472. }
  473. catch (SAXException e) {
  474. throw new XNIException(e);
  475. }
  476. } // startElement(QName,XMLAttributes)
  477. /**
  478. * Character content.
  479. *
  480. * @param text The content.
  481. * @param augs Additional information that may include infoset augmentations
  482. *
  483. * @throws XNIException Thrown by handler to signal an error.
  484. */
  485. public void characters(XMLString text, Augmentations augs) throws XNIException {
  486. // if type is union (XML Schema) it is possible that we receive
  487. // character call with empty data
  488. if (text.length == 0) {
  489. return;
  490. }
  491. try {
  492. // SAX1
  493. if (fDocumentHandler != null) {
  494. // REVISIT: should we support schema-normalized-value for SAX1 events
  495. //
  496. fDocumentHandler.characters(text.ch, text.offset, text.length);
  497. }
  498. // SAX2
  499. if (fContentHandler != null) {
  500. fContentHandler.characters(text.ch, text.offset, text.length);
  501. }
  502. }
  503. catch (SAXException e) {
  504. throw new XNIException(e);
  505. }
  506. } // characters(XMLString)
  507. /**
  508. * Ignorable whitespace. For this method to be called, the document
  509. * source must have some way of determining that the text containing
  510. * only whitespace characters should be considered ignorable. For
  511. * example, the validator can determine if a length of whitespace
  512. * characters in the document are ignorable based on the element
  513. * content model.
  514. *
  515. * @param text The ignorable whitespace.
  516. * @param augs Additional information that may include infoset augmentations
  517. *
  518. * @throws XNIException Thrown by handler to signal an error.
  519. */
  520. public void ignorableWhitespace(XMLString text, Augmentations augs) throws XNIException {
  521. try {
  522. // SAX1
  523. if (fDocumentHandler != null) {
  524. fDocumentHandler.ignorableWhitespace(text.ch, text.offset, text.length);
  525. }
  526. // SAX2
  527. if (fContentHandler != null) {
  528. fContentHandler.ignorableWhitespace(text.ch, text.offset, text.length);
  529. }
  530. }
  531. catch (SAXException e) {
  532. throw new XNIException(e);
  533. }
  534. } // ignorableWhitespace(XMLString)
  535. /**
  536. * The end of an element.
  537. *
  538. * @param element The name of the element.
  539. * @param augs Additional information that may include infoset augmentations
  540. *
  541. * @throws XNIException Thrown by handler to signal an error.
  542. */
  543. public void endElement(QName element, Augmentations augs) throws XNIException {
  544. try {
  545. // SAX1
  546. if (fDocumentHandler != null) {
  547. fDocumentHandler.endElement(element.rawname);
  548. }
  549. // SAX2
  550. if (fContentHandler != null) {
  551. fAugmentations = augs;
  552. String uri = element.uri != null ? element.uri : "";
  553. String localpart = fNamespaces ? element.localpart : "";
  554. fContentHandler.endElement(uri, localpart,
  555. element.rawname);
  556. if (fNamespaces) {
  557. endNamespaceMapping();
  558. }
  559. }
  560. }
  561. catch (SAXException e) {
  562. throw new XNIException(e);
  563. }
  564. } // endElement(QName)
  565. /**
  566. * The start of a CDATA section.
  567. * @param augs Additional information that may include infoset augmentations
  568. *
  569. * @throws XNIException Thrown by handler to signal an error.
  570. */
  571. public void startCDATA(Augmentations augs) throws XNIException {
  572. try {
  573. // SAX2 extension
  574. if (fLexicalHandler != null) {
  575. fLexicalHandler.startCDATA();
  576. }
  577. }
  578. catch (SAXException e) {
  579. throw new XNIException(e);
  580. }
  581. } // startCDATA()
  582. /**
  583. * The end of a CDATA section.
  584. * @param augs Additional information that may include infoset augmentations
  585. *
  586. * @throws XNIException Thrown by handler to signal an error.
  587. */
  588. public void endCDATA(Augmentations augs) throws XNIException {
  589. try {
  590. // SAX2 extension
  591. if (fLexicalHandler != null) {
  592. fLexicalHandler.endCDATA();
  593. }
  594. }
  595. catch (SAXException e) {
  596. throw new XNIException(e);
  597. }
  598. } // endCDATA()
  599. /**
  600. * A comment.
  601. *
  602. * @param text The text in the comment.
  603. * @param augs Additional information that may include infoset augmentations
  604. *
  605. * @throws XNIException Thrown by application to signal an error.
  606. */
  607. public void comment(XMLString text, Augmentations augs) throws XNIException {
  608. try {
  609. // SAX2 extension
  610. if (fLexicalHandler != null) {
  611. fLexicalHandler.comment(text.ch, 0, text.length);
  612. }
  613. }
  614. catch (SAXException e) {
  615. throw new XNIException(e);
  616. }
  617. } // comment(XMLString)
  618. /**
  619. * A processing instruction. Processing instructions consist of a
  620. * target name and, optionally, text data. The data is only meaningful
  621. * to the application.
  622. * <p>
  623. * Typically, a processing instruction's data will contain a series
  624. * of pseudo-attributes. These pseudo-attributes follow the form of
  625. * element attributes but are <strong>not</strong> parsed or presented
  626. * to the application as anything other than text. The application is
  627. * responsible for parsing the data.
  628. *
  629. * @param target The target.
  630. * @param data The data or null if none specified.
  631. * @param augs Additional information that may include infoset augmentations
  632. *
  633. * @throws XNIException Thrown by handler to signal an error.
  634. */
  635. public void processingInstruction(String target, XMLString data, Augmentations augs)
  636. throws XNIException {
  637. //
  638. // REVISIT - I keep running into SAX apps that expect
  639. // null data to be an empty string, which is contrary
  640. // to the comment for this method in the SAX API.
  641. //
  642. try {
  643. // SAX1
  644. if (fDocumentHandler != null) {
  645. fDocumentHandler.processingInstruction(target,
  646. data.toString());
  647. }
  648. // SAX2
  649. if (fContentHandler != null) {
  650. fContentHandler.processingInstruction(target, data.toString());
  651. }
  652. }
  653. catch (SAXException e) {
  654. throw new XNIException(e);
  655. }
  656. } // processingInstruction(String,XMLString)
  657. /**
  658. * The end of the document.
  659. * @param augs Additional information that may include infoset augmentations
  660. *
  661. * @throws XNIException Thrown by handler to signal an error.
  662. */
  663. public void endDocument(Augmentations augs) throws XNIException {
  664. try {
  665. // SAX1
  666. if (fDocumentHandler != null) {
  667. fDocumentHandler.endDocument();
  668. }
  669. // SAX2
  670. if (fContentHandler != null) {
  671. fContentHandler.endDocument();
  672. }
  673. }
  674. catch (SAXException e) {
  675. throw new XNIException(e);
  676. }
  677. } // endDocument()
  678. //
  679. // XMLDTDHandler methods
  680. //
  681. /**
  682. * The start of the DTD external subset.
  683. *
  684. * @param augs Additional information that may include infoset
  685. * augmentations.
  686. *
  687. * @throws XNIException Thrown by handler to signal an error.
  688. */
  689. public void startExternalSubset(XMLResourceIdentifier identifier,
  690. Augmentations augs) throws XNIException {
  691. startParameterEntity("[dtd]", null, null, augs);
  692. }
  693. /**
  694. * The end of the DTD external subset.
  695. *
  696. * @param augs Additional information that may include infoset
  697. * augmentations.
  698. *
  699. * @throws XNIException Thrown by handler to signal an error.
  700. */
  701. public void endExternalSubset(Augmentations augs) throws XNIException {
  702. endParameterEntity("[dtd]", augs);
  703. }
  704. /**
  705. * This method notifies of the start of parameter entity. The DTD has the
  706. * pseudo-name of "[dtd]" parameter entity names start with '%'; and
  707. * general entity names are just the entity name.
  708. * <p>
  709. * <strong>Note:</strong> Since the document is an entity, the handler
  710. * will be notified of the start of the document entity by calling the
  711. * startEntity method with the entity name "[xml]" <em>before</em> calling
  712. * the startDocument method. When exposing entity boundaries through the
  713. * SAX API, the document entity is never reported, however.
  714. * <p>
  715. * <strong>Note:</strong> This method is not called for entity references
  716. * appearing as part of attribute values.
  717. *
  718. * @param name The name of the parameter entity.
  719. * @param identifier The resource identifier.
  720. * @param encoding The auto-detected IANA encoding name of the entity
  721. * stream. This value will be null in those situations
  722. * where the entity encoding is not auto-detected (e.g.
  723. * internal parameter entities).
  724. * @param augs Additional information that may include infoset
  725. * augmentations.
  726. *
  727. * @throws XNIException Thrown by handler to signal an error.
  728. */
  729. public void startParameterEntity(String name,
  730. XMLResourceIdentifier identifier,
  731. String encoding, Augmentations augs)
  732. throws XNIException {
  733. try {
  734. // Only report startEntity if this entity was actually read.
  735. if (augs != null && Boolean.TRUE.equals(augs.getItem(Constants.ENTITY_SKIPPED))) {
  736. // report skipped entity to content handler
  737. if (fContentHandler != null) {
  738. fContentHandler.skippedEntity(name);
  739. }
  740. }
  741. else {
  742. // SAX2 extension
  743. if (fLexicalHandler != null && fLexicalHandlerParameterEntities) {
  744. fLexicalHandler.startEntity(name);
  745. }
  746. }
  747. }
  748. catch (SAXException e) {
  749. throw new XNIException(e);
  750. }
  751. } // startParameterEntity(String,identifier,String,Augmentation)
  752. /**
  753. * This method notifies the end of an entity. The DTD has the pseudo-name
  754. * of "[dtd]" parameter entity names start with '%'; and general entity
  755. * names are just the entity name.
  756. * <p>
  757. * <strong>Note:</strong> Since the document is an entity, the handler
  758. * will be notified of the end of the document entity by calling the
  759. * endEntity method with the entity name "[xml]" <em>after</em> calling
  760. * the endDocument method. When exposing entity boundaries through the
  761. * SAX API, the document entity is never reported, however.
  762. * <p>
  763. * <strong>Note:</strong> This method is not called for entity references
  764. * appearing as part of attribute values.
  765. *
  766. * @param name The name of the parameter entity.
  767. * @param augs Additional information that may include infoset
  768. * augmentations.
  769. *
  770. * @throws XNIException Thrown by handler to signal an error.
  771. */
  772. public void endParameterEntity(String name, Augmentations augs) throws XNIException {
  773. try {
  774. // Only report endEntity if this entity was actually read.
  775. if (augs == null || !Boolean.TRUE.equals(augs.getItem(Constants.ENTITY_SKIPPED))) {
  776. // SAX2 extension
  777. if (fLexicalHandler != null && fLexicalHandlerParameterEntities) {
  778. fLexicalHandler.endEntity(name);
  779. }
  780. }
  781. }
  782. catch (SAXException e) {
  783. throw new XNIException(e);
  784. }
  785. } // endEntity(String)
  786. /**
  787. * An element declaration.
  788. *
  789. * @param name The name of the element.
  790. * @param contentModel The element content model.
  791. *
  792. * @param augs Additional information that may include infoset
  793. * augmentations.
  794. *
  795. * @throws XNIException Thrown by handler to signal an error.
  796. */
  797. public void elementDecl(String name, String contentModel, Augmentations augs)
  798. throws XNIException {
  799. try {
  800. // SAX2 extension
  801. if (fDeclHandler != null) {
  802. fDeclHandler.elementDecl(name, contentModel);
  803. }
  804. }
  805. catch (SAXException e) {
  806. throw new XNIException(e);
  807. }
  808. } // elementDecl(String,String, Augmentations)
  809. /**
  810. * An attribute declaration.
  811. *
  812. * @param elementName The name of the element that this attribute
  813. * is associated with.
  814. * @param attributeName The name of the attribute.
  815. * @param type The attribute type. This value will be one of
  816. * the following: "CDATA", "ENTITY", "ENTITIES",
  817. * "ENUMERATION", "ID", "IDREF", "IDREFS",
  818. * "NMTOKEN", "NMTOKENS", or "NOTATION".
  819. * @param enumeration If the type has the value "ENUMERATION" or
  820. * "NOTATION", this array holds the allowed attribute
  821. * values; otherwise, this array is null.
  822. * @param defaultType The attribute default type. This value will be
  823. * one of the following: "#FIXED", "#IMPLIED",
  824. * "#REQUIRED", or null.
  825. * @param defaultValue The attribute default value, or null if no
  826. * default value is specified.
  827. *
  828. * @param nonNormalizedDefaultValue The attribute default value with no normalization
  829. * performed, or null if no default value is specified.
  830. * @param augs Additional information that may include infoset
  831. * augmentations.
  832. *
  833. * @throws XNIException Thrown by handler to signal an error.
  834. */
  835. public void attributeDecl(String elementName, String attributeName,
  836. String type, String[] enumeration,
  837. String defaultType, XMLString defaultValue,
  838. XMLString nonNormalizedDefaultValue, Augmentations augs) throws XNIException {
  839. try {
  840. // SAX2 extension
  841. if (fDeclHandler != null) {
  842. // used as a key to detect duplicate attribute definitions.
  843. String elemAttr = new StringBuffer(elementName).append("<").append(attributeName).toString();
  844. if(fDeclaredAttrs.get(elemAttr) != null) {
  845. // we aren't permitted to return duplicate attribute definitions
  846. return;
  847. }
  848. fDeclaredAttrs.put(elemAttr, Boolean.TRUE);
  849. if (type.equals("NOTATION") ||
  850. type.equals("ENUMERATION")) {
  851. StringBuffer str = new StringBuffer();
  852. if (type.equals("NOTATION")) {
  853. str.append(type);
  854. str.append(" (");
  855. }
  856. else {
  857. str.append("(");
  858. }
  859. for (int i = 0; i < enumeration.length; i++) {
  860. str.append(enumeration[i]);
  861. if (i < enumeration.length - 1) {
  862. str.append('|');
  863. }
  864. }
  865. str.append(')');
  866. type = str.toString();
  867. }
  868. String value = (defaultValue==null) ? null : defaultValue.toString();
  869. fDeclHandler.attributeDecl(elementName, attributeName,
  870. type, defaultType, value);
  871. }
  872. }
  873. catch (SAXException e) {
  874. throw new XNIException(e);
  875. }
  876. } // attributeDecl(String,String,String,String[],String,XMLString, XMLString, Augmentations)
  877. /**
  878. * An internal entity declaration.
  879. *
  880. * @param name The name of the entity. Parameter entity names start with
  881. * '%', whereas the name of a general entity is just the
  882. * entity name.
  883. * @param text The value of the entity.
  884. * @param nonNormalizedText The non-normalized value of the entity. This
  885. * value contains the same sequence of characters that was in
  886. * the internal entity declaration, without any entity
  887. * references expanded.
  888. *
  889. * @param augs Additional information that may include infoset
  890. * augmentations.
  891. *
  892. * @throws XNIException Thrown by handler to signal an error.
  893. */
  894. public void internalEntityDecl(String name, XMLString text,
  895. XMLString nonNormalizedText,
  896. Augmentations augs) throws XNIException {
  897. try {
  898. // SAX2 extensions
  899. if (fDeclHandler != null) {
  900. fDeclHandler.internalEntityDecl(name, text.toString());
  901. }
  902. }
  903. catch (SAXException e) {
  904. throw new XNIException(e);
  905. }
  906. } // internalEntityDecl(String,XMLString,XMLString)
  907. /**
  908. * An external entity declaration.
  909. *
  910. * @param name The name of the entity. Parameter entity names start
  911. * with '%', whereas the name of a general entity is just
  912. * the entity name.
  913. * @param identifier An object containing all location information
  914. * pertinent to this entity.
  915. * @param augs Additional information that may include infoset
  916. * augmentations.
  917. *
  918. * @throws XNIException Thrown by handler to signal an error.
  919. */
  920. public void externalEntityDecl(String name, XMLResourceIdentifier identifier,
  921. Augmentations augs) throws XNIException {
  922. String publicId = identifier.getPublicId();
  923. String literalSystemId = identifier.getLiteralSystemId();
  924. String expandedSystemId = identifier.getExpandedSystemId();
  925. try {
  926. // SAX2 extension
  927. if (fDeclHandler != null) {
  928. fDeclHandler.externalEntityDecl(name, publicId, ((resolve_dtd_uris) ? expandedSystemId : literalSystemId));
  929. }
  930. }
  931. catch (SAXException e) {
  932. throw new XNIException(e);
  933. }
  934. } // externalEntityDecl(String,,XMLResourceIdentifier, Augmentations)
  935. /**
  936. * An unparsed entity declaration.
  937. *
  938. * @param name The name of the entity.
  939. * @param identifier An object containing all location information
  940. * pertinent to this entity.
  941. * @param notation The name of the notation.
  942. *
  943. * @param augs Additional information that may include infoset
  944. * augmentations.
  945. *
  946. * @throws XNIException Thrown by handler to signal an error.
  947. */
  948. public void unparsedEntityDecl(String name, XMLResourceIdentifier identifier,
  949. String notation,
  950. Augmentations augs) throws XNIException {
  951. String publicId = identifier.getPublicId();
  952. String expandedSystemId = identifier.getExpandedSystemId();
  953. String literalSystemId = identifier.getLiteralSystemId();
  954. try {
  955. // SAX2 extension
  956. if (fDTDHandler != null) {
  957. fDTDHandler.unparsedEntityDecl(name, publicId,
  958. ((resolve_dtd_uris) ? expandedSystemId : literalSystemId), notation);
  959. }
  960. }
  961. catch (SAXException e) {
  962. throw new XNIException(e);
  963. }
  964. } // unparsedEntityDecl(String,XMLResourceIdentifier, String, Augmentations)
  965. /**
  966. * A notation declaration
  967. *
  968. * @param name The name of the notation.
  969. * @param identifier An object containing all location information
  970. * pertinent to this notation.
  971. * @param augs Additional information that may include infoset
  972. * augmentations.
  973. *
  974. * @throws XNIException Thrown by handler to signal an error.
  975. */
  976. public void notationDecl(String name, XMLResourceIdentifier identifier,
  977. Augmentations augs) throws XNIException {
  978. String publicId = identifier.getPublicId();
  979. String expandedSystemId = identifier.getExpandedSystemId();
  980. String literalSystemId = identifier.getLiteralSystemId();
  981. try {
  982. // SAX1 and SAX2
  983. if (fDTDHandler != null) {
  984. fDTDHandler.notationDecl(name, publicId, ((resolve_dtd_uris) ? expandedSystemId : literalSystemId));
  985. }
  986. }
  987. catch (SAXException e) {
  988. throw new XNIException(e);
  989. }
  990. } // notationDecl(String,XMLResourceIdentifier, Augmentations)
  991. /**
  992. * The end of the DTD.
  993. *
  994. * @param augs Additional information that may include infoset
  995. * augmentations.
  996. *
  997. * @throws XNIException Thrown by handler to signal an error.
  998. */
  999. public void endDTD(Augmentations augs) throws XNIException {
  1000. fInDTD = false;
  1001. try {
  1002. // SAX2 extension
  1003. if (fLexicalHandler != null) {
  1004. fLexicalHandler.endDTD();
  1005. }
  1006. }
  1007. catch (SAXException e) {
  1008. throw new XNIException(e);
  1009. }
  1010. if(fDeclaredAttrs != null) {
  1011. // help out the GC
  1012. fDeclaredAttrs.clear();
  1013. }
  1014. } // endDTD()
  1015. //
  1016. // Parser and XMLReader methods
  1017. //
  1018. /**
  1019. * Parses the input source specified by the given system identifier.
  1020. * <p>
  1021. * This method is equivalent to the following:
  1022. * <pre>
  1023. * parse(new InputSource(systemId));
  1024. * </pre>
  1025. *
  1026. * @param systemId The system identifier (URI).
  1027. *
  1028. * @exception org.xml.sax.SAXException Throws exception on SAX error.
  1029. * @exception java.io.IOException Throws exception on i/o error.
  1030. */
  1031. public void parse(String systemId) throws SAXException, IOException {
  1032. // parse document
  1033. XMLInputSource source = new XMLInputSource(null, systemId, null);
  1034. try {
  1035. parse(source);
  1036. }
  1037. // wrap XNI exceptions as SAX exceptions
  1038. catch (XMLParseException e) {
  1039. Exception ex = e.getException();
  1040. if (ex == null) {
  1041. // must be a parser exception; mine it for locator info and throw
  1042. // a SAXParseException
  1043. locatorType = true;
  1044. LocatorImpl locatorImpl = new LocatorImpl(){
  1045. public String getXMLVersion() {
  1046. return fVersion;
  1047. }
  1048. // since XMLParseExceptions know nothing about encoding,
  1049. // we cannot return anything meaningful in this context.
  1050. // We *could* consult the LocatorProxy, but the
  1051. // application can do this itself if it wishes to possibly
  1052. // be mislead.
  1053. public String getEncoding() {
  1054. return null;
  1055. }
  1056. };
  1057. locatorImpl.setPublicId(e.getPublicId());
  1058. locatorImpl.setSystemId(e.getExpandedSystemId());
  1059. locatorImpl.setLineNumber(e.getLineNumber());
  1060. locatorImpl.setColumnNumber(e.getColumnNumber());
  1061. throw new SAXParseException(e.getMessage(), locatorImpl);
  1062. }
  1063. if (ex instanceof SAXException) {
  1064. // why did we create an XMLParseException?
  1065. throw (SAXException)ex;
  1066. }
  1067. if (ex instanceof IOException) {
  1068. throw (IOException)ex;
  1069. }
  1070. throw new SAXException(ex);
  1071. }
  1072. catch (XNIException e) {
  1073. Exception ex = e.getException();
  1074. if (ex == null) {
  1075. throw new SAXException(e.getMessage());
  1076. }
  1077. if (ex instanceof SAXException) {
  1078. throw (SAXException)ex;
  1079. }
  1080. if (ex instanceof IOException) {
  1081. throw (IOException)ex;
  1082. }
  1083. throw new SAXException(ex);
  1084. }
  1085. } // parse(String)
  1086. /**
  1087. * parse
  1088. *
  1089. * @param inputSource
  1090. *
  1091. * @exception org.xml.sax.SAXException
  1092. * @exception java.io.IOException
  1093. */
  1094. public void parse(InputSource inputSource)
  1095. throws SAXException, IOException {
  1096. // parse document
  1097. try {
  1098. XMLInputSource xmlInputSource =
  1099. new XMLInputSource(inputSource.getPublicId(),
  1100. inputSource.getSystemId(),
  1101. null);
  1102. xmlInputSource.setByteStream(inputSource.getByteStream());
  1103. xmlInputSource.setCharacterStream(inputSource.getCharacterStream());
  1104. xmlInputSource.setEncoding(inputSource.getEncoding());
  1105. parse(xmlInputSource);
  1106. }
  1107. // wrap XNI exceptions as SAX exceptions
  1108. catch (XMLParseException e) {
  1109. Exception ex = e.getException();
  1110. if (ex == null) {
  1111. // must be a parser exception; mine it for locator info and throw
  1112. // a SAXParseException
  1113. locatorType = true;
  1114. LocatorImpl locatorImpl = new LocatorImpl() {
  1115. public String getXMLVersion() {
  1116. return fVersion;
  1117. }
  1118. // since XMLParseExceptions know nothing about encoding,
  1119. // we cannot return anything meaningful in this context.
  1120. // We *could* consult the LocatorProxy, but the
  1121. // application can do this itself if it wishes to possibly
  1122. // be mislead.
  1123. public String getEncoding() {
  1124. return null;
  1125. }
  1126. };
  1127. locatorImpl.setPublicId(e.getPublicId());
  1128. locatorImpl.setSystemId(e.getExpandedSystemId());
  1129. locatorImpl.setLineNumber(e.getLineNumber());
  1130. locatorImpl.setColumnNumber(e.getColumnNumber());
  1131. throw new SAXParseException(e.getMessage(), l