1. /*
  2. * $Id: XMLReaderImpl.java,v 1.4 2002/04/23 00:09:22 edwingo Exp $
  3. *
  4. * The Apache Software License, Version 1.1
  5. *
  6. *
  7. * Copyright (c) 2000 The Apache Software Foundation. All rights
  8. * reserved.
  9. *
  10. * Redistribution and use in source and binary forms, with or without
  11. * modification, are permitted provided that the following conditions
  12. * are met:
  13. *
  14. * 1. Redistributions of source code must retain the above copyright
  15. * notice, this list of conditions and the following disclaimer.
  16. *
  17. * 2. Redistributions in binary form must reproduce the above copyright
  18. * notice, this list of conditions and the following disclaimer in
  19. * the documentation and/or other materials provided with the
  20. * distribution.
  21. *
  22. * 3. The end-user documentation included with the redistribution,
  23. * if any, must include the following acknowledgment:
  24. * "This product includes software developed by the
  25. * Apache Software Foundation (http://www.apache.org/)."
  26. * Alternately, this acknowledgment may appear in the software itself,
  27. * if and wherever such third-party acknowledgments normally appear.
  28. *
  29. * 4. The names "Crimson" and "Apache Software Foundation" must
  30. * not be used to endorse or promote products derived from this
  31. * software without prior written permission. For written
  32. * permission, please contact apache@apache.org.
  33. *
  34. * 5. Products derived from this software may not be called "Apache",
  35. * nor may "Apache" appear in their name, without prior written
  36. * permission of the Apache Software Foundation.
  37. *
  38. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  39. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  40. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  41. * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
  42. * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  43. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  44. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  45. * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  46. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  47. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  48. * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  49. * SUCH DAMAGE.
  50. * ====================================================================
  51. *
  52. * This software consists of voluntary contributions made by many
  53. * individuals on behalf of the Apache Software Foundation and was
  54. * originally based on software copyright (c) 1999, Sun Microsystems, Inc.,
  55. * http://www.sun.com. For more information on the Apache Software
  56. * Foundation, please see <http://www.apache.org/>.
  57. */
  58. package org.apache.crimson.parser;
  59. import java.io.IOException;
  60. import java.util.Enumeration;
  61. import java.util.StringTokenizer;
  62. import org.xml.sax.*;
  63. import org.xml.sax.ext.*;
  64. /**
  65. * This implements the SAX2 XMLReader.
  66. * @author Rajiv Mordani
  67. * @author Edwin Goei
  68. * @version $Revision: 1.4 $
  69. */
  70. public class XMLReaderImpl implements XMLReader {
  71. //
  72. // Internal constants for the sake of convenience: features
  73. //
  74. private final static String FEATURES = "http://xml.org/sax/features/";
  75. private final static String NAMESPACES = FEATURES + "namespaces";
  76. private final static String NAMESPACE_PREFIXES =
  77. FEATURES + "namespace-prefixes";
  78. private final static String STRING_INTERNING =
  79. FEATURES + "string-interning";
  80. private final static String VALIDATION = FEATURES + "validation";
  81. private final static String EXTERNAL_GENERAL =
  82. FEATURES + "external-general-entities";
  83. private final static String EXTERNAL_PARAMETER =
  84. FEATURES + "external-parameter-entities";
  85. // Features for org.sax.xml.ext.LexicalHandler
  86. private final static String LEXICAL_PARAMETER_ENTITIES = FEATURES +
  87. "lexical-handler/parameter-entities";
  88. // Properties
  89. private static final String PROPERTIES = "http://xml.org/sax/properties/";
  90. private final static String LEXICAL_HANDLER =
  91. PROPERTIES + "lexical-handler";
  92. private final static String DECLARATION_HANDLER =
  93. PROPERTIES + "declaration-handler";
  94. // Features with their default values
  95. private boolean namespaces = true;
  96. private boolean prefixes = false;
  97. private boolean validation = false;
  98. // Properties
  99. private LexicalHandler lexicalHandler;
  100. private DeclHandler declHandler;
  101. // SAX2 core event handlers
  102. private ContentHandler contentHandler;
  103. private DTDHandler dtdHandler;
  104. private ErrorHandler errorHandler;
  105. private EntityResolver entityResolver;
  106. private Parser2 parser;
  107. private boolean parsing; // true iff we are currently parsing
  108. ////////////////////////////////////////////////////////////////////
  109. // Constructors.
  110. ////////////////////////////////////////////////////////////////////
  111. /**
  112. * Default constructor is explicitly declared here
  113. */
  114. public XMLReaderImpl() {
  115. super();
  116. }
  117. ////////////////////////////////////////////////////////////////////
  118. // Implementation of org.xml.sax.XMLReader.
  119. ////////////////////////////////////////////////////////////////////
  120. /**
  121. * Check a parser feature.
  122. *
  123. * @param name The feature name, as a complete URI.
  124. * @return The current feature state.
  125. * @exception org.xml.sax.SAXNotRecognizedException If the feature
  126. * name is not known.
  127. * @exception org.xml.sax.SAXNotSupportedException If querying the
  128. * feature state is not supported.
  129. * @see org.xml.sax.XMLReader#setFeature
  130. */
  131. public boolean getFeature(String name)
  132. throws SAXNotRecognizedException, SAXNotSupportedException
  133. {
  134. if (name.equals(NAMESPACES)) {
  135. return namespaces;
  136. } else if (name.equals(NAMESPACE_PREFIXES)) {
  137. return prefixes;
  138. } else if (name.equals(VALIDATION)) {
  139. return validation;
  140. } else if (name.equals(STRING_INTERNING) ||
  141. name.equals(EXTERNAL_GENERAL) ||
  142. name.equals(EXTERNAL_PARAMETER)) {
  143. return true;
  144. } else if (name.equals(LEXICAL_PARAMETER_ENTITIES)) {
  145. return false;
  146. } else {
  147. throw new SAXNotRecognizedException("Feature: " + name);
  148. }
  149. }
  150. /**
  151. * Set a feature for the parser.
  152. *
  153. * @param name The feature name, as a complete URI.
  154. * @param state The requested feature state.
  155. * @exception org.xml.sax.SAXNotRecognizedException If the feature
  156. * name is not known.
  157. * @exception org.xml.sax.SAXNotSupportedException If the feature
  158. * state is not supported.
  159. * @see org.xml.sax.XMLReader#getFeature
  160. */
  161. public void setFeature(String name, boolean state)
  162. throws SAXNotRecognizedException, SAXNotSupportedException
  163. {
  164. if (name.equals(NAMESPACES)) {
  165. checkNotParsing("feature", name);
  166. namespaces = state;
  167. if (!namespaces && !prefixes) {
  168. prefixes = true;
  169. }
  170. } else if (name.equals(NAMESPACE_PREFIXES)) {
  171. checkNotParsing("feature", name);
  172. prefixes = state;
  173. if (!prefixes && !namespaces) {
  174. namespaces = true;
  175. }
  176. } else if (name.equals(VALIDATION)) {
  177. checkNotParsing("feature", name);
  178. if (validation != state) {
  179. parser = null;
  180. }
  181. validation = state;
  182. } else if (name.equals(STRING_INTERNING) ||
  183. name.equals(EXTERNAL_GENERAL) ||
  184. name.equals(EXTERNAL_PARAMETER)) {
  185. checkNotParsing("feature", name);
  186. if (state == false) {
  187. throw new SAXNotSupportedException("Feature: " + name
  188. + " State: false");
  189. }
  190. // else true is OK
  191. } else if (name.equals(LEXICAL_PARAMETER_ENTITIES)) {
  192. checkNotParsing("feature", name);
  193. if (state == true) {
  194. throw new SAXNotSupportedException("Feature: " + name
  195. + " State: true");
  196. }
  197. // else false is OK
  198. } else {
  199. throw new SAXNotRecognizedException("Feature: " + name);
  200. }
  201. }
  202. /**
  203. * Get a parser property.
  204. *
  205. * @param name The property name.
  206. * @return The property value.
  207. * @exception org.xml.sax.SAXNotRecognizedException If the feature
  208. * name is not known.
  209. * @exception org.xml.sax.SAXNotSupportedException If the feature
  210. * state is not supported.
  211. * @see org.xml.sax.XMLReader#getProperty
  212. */
  213. public Object getProperty(String name)
  214. throws SAXNotRecognizedException, SAXNotSupportedException
  215. {
  216. if (name.equals(LEXICAL_HANDLER)) {
  217. return lexicalHandler;
  218. } else if (name.equals(DECLARATION_HANDLER)) {
  219. return declHandler;
  220. } else {
  221. throw new SAXNotRecognizedException("Property: " + name);
  222. }
  223. }
  224. /**
  225. * Set a parser property.
  226. *
  227. * @param name The property name.
  228. * @param value The property value.
  229. * @exception org.xml.sax.SAXNotRecognizedException If the feature
  230. * name is not known.
  231. * @exception org.xml.sax.SAXNotSupportedException If the feature
  232. * state is not supported.
  233. * @see org.xml.sax.XMLReader#getProperty
  234. */
  235. public void setProperty(String name, Object value)
  236. throws SAXNotRecognizedException, SAXNotSupportedException
  237. {
  238. String detail = "Property: " + name;
  239. if (name.equals(LEXICAL_HANDLER)) {
  240. if (!(value instanceof LexicalHandler)) {
  241. throw new SAXNotSupportedException(detail);
  242. }
  243. lexicalHandler = (LexicalHandler)value;
  244. } else if (name.equals(DECLARATION_HANDLER)) {
  245. if (!(value instanceof DeclHandler)) {
  246. throw new SAXNotSupportedException(detail);
  247. }
  248. declHandler = (DeclHandler)value;
  249. } else {
  250. throw new SAXNotRecognizedException("Property: " + name);
  251. }
  252. }
  253. /**
  254. * Set the entity resolver.
  255. *
  256. * @param resolver The new entity resolver.
  257. * @exception java.lang.NullPointerException If the entity resolver
  258. * parameter is null.
  259. * @see org.xml.sax.XMLReader#setEntityResolver
  260. */
  261. public void setEntityResolver(EntityResolver resolver) {
  262. if (resolver == null) {
  263. throw new NullPointerException("Null entity resolver");
  264. }
  265. entityResolver = resolver;
  266. if (parser != null) {
  267. parser.setEntityResolver(resolver);
  268. }
  269. }
  270. /**
  271. * Return the current entity resolver.
  272. *
  273. * @return The current entity resolver, or null if none was supplied.
  274. * @see org.xml.sax.XMLReader#getEntityResolver
  275. */
  276. public EntityResolver getEntityResolver() {
  277. return entityResolver;
  278. }
  279. /**
  280. * Set the DTD handler.
  281. *
  282. * @param resolver The new DTD handler.
  283. * @exception java.lang.NullPointerException If the DTD handler
  284. * parameter is null.
  285. * @see org.xml.sax.XMLReader#setEntityResolver
  286. */
  287. public void setDTDHandler(DTDHandler handler) {
  288. if (handler == null) {
  289. throw new NullPointerException("Null DTD handler");
  290. }
  291. dtdHandler = handler;
  292. if (parser != null) {
  293. parser.setDTDHandler(dtdHandler);
  294. }
  295. }
  296. /**
  297. * Return the current DTD handler.
  298. *
  299. * @return The current DTD handler, or null if none was supplied.
  300. * @see org.xml.sax.XMLReader#getEntityResolver
  301. */
  302. public DTDHandler getDTDHandler() {
  303. return dtdHandler;
  304. }
  305. /**
  306. * Set the content handler.
  307. *
  308. * @param resolver The new content handler.
  309. * @exception java.lang.NullPointerException If the content handler
  310. * parameter is null.
  311. * @see org.xml.sax.XMLReader#setEntityResolver
  312. */
  313. public void setContentHandler(ContentHandler handler) {
  314. if (handler == null) {
  315. throw new NullPointerException("Null content handler");
  316. }
  317. contentHandler = handler;
  318. if (parser != null) {
  319. parser.setContentHandler(handler);
  320. }
  321. }
  322. /**
  323. * Return the current content handler.
  324. *
  325. * @return The current content handler, or null if none was supplied.
  326. * @see org.xml.sax.XMLReader#getEntityResolver
  327. */
  328. public ContentHandler getContentHandler() {
  329. return contentHandler;
  330. }
  331. /**
  332. * Set the error handler.
  333. *
  334. * @param resolver The new error handler.
  335. * @exception java.lang.NullPointerException If the error handler
  336. * parameter is null.
  337. * @see org.xml.sax.XMLReader#setEntityResolver
  338. */
  339. public void setErrorHandler(ErrorHandler handler) {
  340. if (handler == null) {
  341. throw new NullPointerException("Null error handler");
  342. }
  343. errorHandler = handler;
  344. if (parser != null) {
  345. parser.setErrorHandler(errorHandler);
  346. }
  347. }
  348. /**
  349. * Return the current error handler.
  350. *
  351. * @return The current error handler, or null if none was supplied.
  352. * @see org.xml.sax.XMLReader#getEntityResolver
  353. */
  354. public ErrorHandler getErrorHandler() {
  355. return errorHandler;
  356. }
  357. /**
  358. * Parse an XML document.
  359. *
  360. * @param systemId The absolute URL of the document.
  361. * @exception java.io.IOException If there is a problem reading
  362. * the raw content of the document.
  363. * @exception org.xml.sax.SAXException If there is a problem
  364. * processing the document.
  365. * @see #parse(org.xml.sax.InputSource)
  366. * @see org.xml.sax.Parser#parse(java.lang.String)
  367. */
  368. public void parse(String systemId)
  369. throws IOException, SAXException
  370. {
  371. parse(new InputSource(systemId));
  372. }
  373. /**
  374. * Parse an XML document.
  375. *
  376. * @param input An input source for the document.
  377. * @exception java.io.IOException If there is a problem reading
  378. * the raw content of the document.
  379. * @exception org.xml.sax.SAXException If there is a problem
  380. * processing the document.
  381. * @see #parse(java.lang.String)
  382. * @see org.xml.sax.Parser#parse(org.xml.sax.InputSource)
  383. */
  384. public void parse(InputSource input)
  385. throws IOException, SAXException
  386. {
  387. if (parsing) {
  388. throw new SAXException("Parser is already in use");
  389. }
  390. parsing = true;
  391. // Reuse existing parser if one already exists
  392. if (parser == null) {
  393. if (validation) {
  394. parser = new ValidatingParser();
  395. } else {
  396. parser = new Parser2();
  397. }
  398. }
  399. // Set up parser state. Note: we set the parser state independent
  400. // of new parser creation since the caller may set the validation
  401. // feature and the handlers in an arbritrary order. This allows us
  402. // to reuse existing parser instances when possible.
  403. parser.setNamespaceFeatures(namespaces, prefixes);
  404. parser.setContentHandler(contentHandler);
  405. parser.setDTDHandler(dtdHandler);
  406. parser.setErrorHandler(errorHandler);
  407. parser.setEntityResolver(entityResolver);
  408. // SAX2 ext handler
  409. parser.setLexicalHandler(lexicalHandler);
  410. // SAX2 ext handler
  411. parser.setDeclHandler(declHandler);
  412. try {
  413. parser.parse(input);
  414. } finally {
  415. parsing = false;
  416. }
  417. }
  418. ////////////////////////////////////////////////////////////////////
  419. // Internal utility methods.
  420. ////////////////////////////////////////////////////////////////////
  421. /**
  422. * Throw an exception if we are parsing.
  423. *
  424. * <p>Use this method to detect illegal feature or
  425. * property changes.</p>
  426. *
  427. * @param type The type of thing (feature or property).
  428. * @param name The feature or property name.
  429. * @exception org.xml.sax.SAXNotSupportedException If a
  430. * document is currently being parsed.
  431. */
  432. private void checkNotParsing(String type, String name)
  433. throws SAXNotSupportedException
  434. {
  435. if (parsing) {
  436. throw new SAXNotSupportedException("Cannot change " +
  437. type + ' ' +
  438. name + " while parsing");
  439. }
  440. }
  441. }