1. /*
  2. * The Apache Software License, Version 1.1
  3. *
  4. *
  5. * Copyright (c) 1999-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.impl;
  58. import java.io.IOException;
  59. import java.io.InputStream;
  60. import java.io.InputStreamReader;
  61. import java.io.Reader;
  62. import java.io.StringReader;
  63. import java.net.HttpURLConnection;
  64. import java.net.URL;
  65. import java.net.URLConnection;
  66. import java.util.Hashtable;
  67. import java.util.Locale;
  68. import java.util.Stack;
  69. import java.util.Vector;
  70. import com.sun.org.apache.xerces.internal.impl.io.ASCIIReader;
  71. import com.sun.org.apache.xerces.internal.impl.io.UCSReader;
  72. import com.sun.org.apache.xerces.internal.impl.io.UTF8Reader;
  73. import com.sun.org.apache.xerces.internal.impl.msg.XMLMessageFormatter;
  74. import com.sun.org.apache.xerces.internal.impl.validation.ValidationManager;
  75. import com.sun.org.apache.xerces.internal.util.AugmentationsImpl;
  76. import com.sun.org.apache.xerces.internal.util.EncodingMap;
  77. import com.sun.org.apache.xerces.internal.util.SymbolTable;
  78. import com.sun.org.apache.xerces.internal.util.URI;
  79. import com.sun.org.apache.xerces.internal.util.XMLChar;
  80. import com.sun.org.apache.xerces.internal.util.XMLEntityDescriptionImpl;
  81. import com.sun.org.apache.xerces.internal.util.XMLResourceIdentifierImpl;
  82. import com.sun.org.apache.xerces.internal.util.SecurityManager;
  83. import com.sun.org.apache.xerces.internal.xni.Augmentations;
  84. import com.sun.org.apache.xerces.internal.xni.XMLResourceIdentifier;
  85. import com.sun.org.apache.xerces.internal.xni.XNIException;
  86. import com.sun.org.apache.xerces.internal.xni.parser.XMLComponent;
  87. import com.sun.org.apache.xerces.internal.xni.parser.XMLComponentManager;
  88. import com.sun.org.apache.xerces.internal.xni.parser.XMLConfigurationException;
  89. import com.sun.org.apache.xerces.internal.xni.parser.XMLEntityResolver;
  90. import com.sun.org.apache.xerces.internal.xni.parser.XMLInputSource;
  91. import com.sun.org.apache.xerces.internal.xinclude.XIncludeHandler;
  92. import com.sun.org.apache.xerces.internal.xinclude.XIncludeInputSource;
  93. /**
  94. * The entity manager handles the registration of general and parameter
  95. * entities; resolves entities; and starts entities. The entity manager
  96. * is a central component in a standard parser configuration and this
  97. * class works directly with the entity scanner to manage the underlying
  98. * xni.
  99. * <p>
  100. * This component requires the following features and properties from the
  101. * component manager that uses it:
  102. * <ul>
  103. * <li>http://xml.org/sax/features/validation</li>
  104. * <li>http://xml.org/sax/features/external-general-entities</li>
  105. * <li>http://xml.org/sax/features/external-parameter-entities</li>
  106. * <li>http://apache.org/xml/features/allow-java-encodings</li>
  107. * <li>http://apache.org/xml/properties/internal/symbol-table</li>
  108. * <li>http://apache.org/xml/properties/internal/error-reporter</li>
  109. * <li>http://apache.org/xml/properties/internal/entity-resolver</li>
  110. * <li>http://apache.org/xml/properties/security-manager</li>
  111. * </ul>
  112. *
  113. *
  114. * @author Andy Clark, IBM
  115. * @author Arnaud Le Hors, IBM
  116. *
  117. * @version $Id: XMLEntityManager.java,v 1.79 2004/03/16 22:03:22 mrglavas Exp $
  118. */
  119. public class XMLEntityManager
  120. implements XMLComponent, XMLEntityResolver {
  121. //
  122. // Constants
  123. //
  124. /** Default buffer size (2048). */
  125. public static final int DEFAULT_BUFFER_SIZE = 2048;
  126. /** Default buffer size before we've finished with the XMLDecl: */
  127. public static final int DEFAULT_XMLDECL_BUFFER_SIZE = 64;
  128. /** Default internal entity buffer size (1024). */
  129. public static final int DEFAULT_INTERNAL_BUFFER_SIZE = 1024;
  130. // feature identifiers
  131. /** Feature identifier: validation. */
  132. protected static final String VALIDATION =
  133. Constants.SAX_FEATURE_PREFIX + Constants.VALIDATION_FEATURE;
  134. /** Feature identifier: external general entities. */
  135. protected static final String EXTERNAL_GENERAL_ENTITIES =
  136. Constants.SAX_FEATURE_PREFIX + Constants.EXTERNAL_GENERAL_ENTITIES_FEATURE;
  137. /** Feature identifier: external parameter entities. */
  138. protected static final String EXTERNAL_PARAMETER_ENTITIES =
  139. Constants.SAX_FEATURE_PREFIX + Constants.EXTERNAL_PARAMETER_ENTITIES_FEATURE;
  140. /** Feature identifier: allow Java encodings. */
  141. protected static final String ALLOW_JAVA_ENCODINGS =
  142. Constants.XERCES_FEATURE_PREFIX + Constants.ALLOW_JAVA_ENCODINGS_FEATURE;
  143. /** Feature identifier: warn on duplicate EntityDef */
  144. protected static final String WARN_ON_DUPLICATE_ENTITYDEF =
  145. Constants.XERCES_FEATURE_PREFIX +Constants.WARN_ON_DUPLICATE_ENTITYDEF_FEATURE;
  146. /** Feature identifier: standard uri conformant */
  147. protected static final String STANDARD_URI_CONFORMANT =
  148. Constants.XERCES_FEATURE_PREFIX +Constants.STANDARD_URI_CONFORMANT_FEATURE;
  149. protected static final String PARSER_SETTINGS =
  150. Constants.XERCES_FEATURE_PREFIX + Constants.PARSER_SETTINGS;
  151. // property identifiers
  152. /** Property identifier: symbol table. */
  153. protected static final String SYMBOL_TABLE =
  154. Constants.XERCES_PROPERTY_PREFIX + Constants.SYMBOL_TABLE_PROPERTY;
  155. /** Property identifier: error reporter. */
  156. protected static final String ERROR_REPORTER =
  157. Constants.XERCES_PROPERTY_PREFIX + Constants.ERROR_REPORTER_PROPERTY;
  158. /** Property identifier: entity resolver. */
  159. protected static final String ENTITY_RESOLVER =
  160. Constants.XERCES_PROPERTY_PREFIX + Constants.ENTITY_RESOLVER_PROPERTY;
  161. // property identifier: ValidationManager
  162. protected static final String VALIDATION_MANAGER =
  163. Constants.XERCES_PROPERTY_PREFIX + Constants.VALIDATION_MANAGER_PROPERTY;
  164. /** property identifier: buffer size. */
  165. protected static final String BUFFER_SIZE =
  166. Constants.XERCES_PROPERTY_PREFIX + Constants.BUFFER_SIZE_PROPERTY;
  167. /** property identifier: security manager. */
  168. protected static final String SECURITY_MANAGER =
  169. Constants.XERCES_PROPERTY_PREFIX + Constants.SECURITY_MANAGER_PROPERTY;
  170. // recognized features and properties
  171. /** Recognized features. */
  172. private static final String[] RECOGNIZED_FEATURES = {
  173. VALIDATION,
  174. EXTERNAL_GENERAL_ENTITIES,
  175. EXTERNAL_PARAMETER_ENTITIES,
  176. ALLOW_JAVA_ENCODINGS,
  177. WARN_ON_DUPLICATE_ENTITYDEF,
  178. STANDARD_URI_CONFORMANT
  179. };
  180. /** Feature defaults. */
  181. private static final Boolean[] FEATURE_DEFAULTS = {
  182. null,
  183. Boolean.TRUE,
  184. Boolean.TRUE,
  185. Boolean.TRUE,
  186. Boolean.FALSE,
  187. Boolean.FALSE
  188. };
  189. /** Recognized properties. */
  190. private static final String[] RECOGNIZED_PROPERTIES = {
  191. SYMBOL_TABLE,
  192. ERROR_REPORTER,
  193. ENTITY_RESOLVER,
  194. VALIDATION_MANAGER,
  195. BUFFER_SIZE,
  196. SECURITY_MANAGER,
  197. };
  198. /** Property defaults. */
  199. private static final Object[] PROPERTY_DEFAULTS = {
  200. null,
  201. null,
  202. null,
  203. null,
  204. new Integer(DEFAULT_BUFFER_SIZE),
  205. null,
  206. };
  207. private static final String XMLEntity = "[xml]".intern();
  208. private static final String DTDEntity = "[dtd]".intern();
  209. // debugging
  210. /**
  211. * Debug printing of buffer. This debugging flag works best when you
  212. * resize the DEFAULT_BUFFER_SIZE down to something reasonable like
  213. * 64 characters.
  214. */
  215. private static final boolean DEBUG_BUFFER = false;
  216. /** Debug some basic entities. */
  217. private static final boolean DEBUG_ENTITIES = false;
  218. /** Debug switching readers for encodings. */
  219. private static final boolean DEBUG_ENCODINGS = false;
  220. // should be diplayed trace resolving messages
  221. private static final boolean DEBUG_RESOLVER = false;
  222. //
  223. // Data
  224. //
  225. // features
  226. /**
  227. * Validation. This feature identifier is:
  228. * http://xml.org/sax/features/validation
  229. */
  230. protected boolean fValidation;
  231. /**
  232. * External general entities. This feature identifier is:
  233. * http://xml.org/sax/features/external-general-entities
  234. */
  235. protected boolean fExternalGeneralEntities = true;
  236. /**
  237. * External parameter entities. This feature identifier is:
  238. * http://xml.org/sax/features/external-parameter-entities
  239. */
  240. protected boolean fExternalParameterEntities = true;
  241. /**
  242. * Allow Java encoding names. This feature identifier is:
  243. * http://apache.org/xml/features/allow-java-encodings
  244. */
  245. protected boolean fAllowJavaEncodings;
  246. /** warn on duplicate Entity declaration.
  247. * http://apache.org/xml/features/warn-on-duplicate-entitydef
  248. */
  249. protected boolean fWarnDuplicateEntityDef;
  250. /**
  251. * standard uri conformant (strict uri).
  252. * http://apache.org/xml/features/standard-uri-conformant
  253. */
  254. protected boolean fStrictURI;
  255. protected boolean fCharsetOverrideXmlEncoding;
  256. // properties
  257. /**
  258. * Symbol table. This property identifier is:
  259. * http://apache.org/xml/properties/internal/symbol-table
  260. */
  261. protected SymbolTable fSymbolTable;
  262. /**
  263. * Error reporter. This property identifier is:
  264. * http://apache.org/xml/properties/internal/error-reporter
  265. */
  266. protected XMLErrorReporter fErrorReporter;
  267. /**
  268. * Entity resolver. This property identifier is:
  269. * http://apache.org/xml/properties/internal/entity-resolver
  270. */
  271. protected XMLEntityResolver fEntityResolver;
  272. /**
  273. * Validation manager. This property identifier is:
  274. * http://apache.org/xml/properties/internal/validation-manager
  275. */
  276. protected ValidationManager fValidationManager;
  277. // settings
  278. /**
  279. * Buffer size. We get this value from a property. The default size
  280. * is used if the input buffer size property is not specified.
  281. * REVISIT: do we need a property for internal entity buffer size?
  282. */
  283. protected int fBufferSize = DEFAULT_BUFFER_SIZE;
  284. // stores defaults for entity expansion limit if it has
  285. // been set on the configuration.
  286. protected SecurityManager fSecurityManager = null;
  287. /**
  288. * True if the document entity is standalone. This should really
  289. * only be set by the document source (e.g. XMLDocumentScanner).
  290. */
  291. protected boolean fStandalone;
  292. // are the entities being parsed in the external subset?
  293. // NOTE: this *is not* the same as whether they're external entities!
  294. protected boolean fInExternalSubset = false;
  295. // handlers
  296. /** Entity handler. */
  297. protected XMLEntityHandler fEntityHandler;
  298. // scanner
  299. /** Current entity scanner. */
  300. protected XMLEntityScanner fEntityScanner;
  301. /** XML 1.0 entity scanner. */
  302. protected XMLEntityScanner fXML10EntityScanner;
  303. /** XML 1.1 entity scanner. */
  304. protected XMLEntityScanner fXML11EntityScanner;
  305. // entity expansion limit (contains useful data if and only if
  306. // fSecurityManager is non-null)
  307. protected int fEntityExpansionLimit = 0;
  308. // entity currently being expanded:
  309. protected int fEntityExpansionCount = 0;
  310. // entities
  311. /** Entities. */
  312. protected Hashtable fEntities = new Hashtable();
  313. /** Entity stack. */
  314. protected Stack fEntityStack = new Stack();
  315. /** Current entity. */
  316. protected ScannedEntity fCurrentEntity;
  317. // shared context
  318. /** Shared declared entities. */
  319. protected Hashtable fDeclaredEntities;
  320. // temp vars
  321. /** Resource identifer. */
  322. private final XMLResourceIdentifierImpl fResourceIdentifier = new XMLResourceIdentifierImpl();
  323. /** Augmentations for entities. */
  324. private final Augmentations fEntityAugs = new AugmentationsImpl();
  325. //
  326. // Constructors
  327. //
  328. /** Default constructor. */
  329. public XMLEntityManager() {
  330. this(null);
  331. } // <init>()
  332. /**
  333. * Constructs an entity manager that shares the specified entity
  334. * declarations during each parse.
  335. * <p>
  336. * <strong>REVISIT:</strong> We might want to think about the "right"
  337. * way to expose the list of declared entities. For now, the knowledge
  338. * how to access the entity declarations is implicit.
  339. */
  340. public XMLEntityManager(XMLEntityManager entityManager) {
  341. // save shared entity declarations
  342. fDeclaredEntities = entityManager != null
  343. ? entityManager.getDeclaredEntities() : null;
  344. setScannerVersion(Constants.XML_VERSION_1_0);
  345. } // <init>(XMLEntityManager)
  346. //
  347. // Public methods
  348. //
  349. /**
  350. * Sets whether the document entity is standalone.
  351. *
  352. * @param standalone True if document entity is standalone.
  353. */
  354. public void setStandalone(boolean standalone) {
  355. fStandalone = standalone;
  356. } // setStandalone(boolean)
  357. /** Returns true if the document entity is standalone. */
  358. public boolean isStandalone() {
  359. return fStandalone;
  360. } // isStandalone():boolean
  361. /**
  362. * Sets the entity handler. When an entity starts and ends, the
  363. * entity handler is notified of the change.
  364. *
  365. * @param entityHandler The new entity handler.
  366. */
  367. public void setEntityHandler(XMLEntityHandler entityHandler) {
  368. fEntityHandler = entityHandler;
  369. } // setEntityHandler(XMLEntityHandler)
  370. // this simply returns the fResourceIdentifier object;
  371. // this should only be used with caution by callers that
  372. // carefully manage the entity manager's behaviour, so that
  373. // this doesn't returning meaningless or misleading data.
  374. // @return a reference to the current fResourceIdentifier object
  375. public XMLResourceIdentifier getCurrentResourceIdentifier() {
  376. return fResourceIdentifier;
  377. }
  378. // this simply returns the fCurrentEntity object;
  379. // this should only be used with caution by callers that
  380. // carefully manage the entity manager's behaviour, so that
  381. // this doesn't returning meaningless or misleading data.
  382. // @return a reference to the current fCurrentEntity object
  383. public ScannedEntity getCurrentEntity() {
  384. return fCurrentEntity;
  385. }
  386. /**
  387. * Adds an internal entity declaration.
  388. * <p>
  389. * <strong>Note:</strong> This method ignores subsequent entity
  390. * declarations.
  391. * <p>
  392. * <strong>Note:</strong> The name should be a unique symbol. The
  393. * SymbolTable can be used for this purpose.
  394. *
  395. * @param name The name of the entity.
  396. * @param text The text of the entity.
  397. *
  398. * @see SymbolTable
  399. */
  400. public void addInternalEntity(String name, String text) {
  401. if (!fEntities.containsKey(name)) {
  402. Entity entity = new InternalEntity(name, text, fInExternalSubset);
  403. fEntities.put(name, entity);
  404. }
  405. else{
  406. if(fWarnDuplicateEntityDef){
  407. fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
  408. "MSG_DUPLICATE_ENTITY_DEFINITION",
  409. new Object[]{ name },
  410. XMLErrorReporter.SEVERITY_WARNING );
  411. }
  412. }
  413. } // addInternalEntity(String,String)
  414. /**
  415. * Adds an external entity declaration.
  416. * <p>
  417. * <strong>Note:</strong> This method ignores subsequent entity
  418. * declarations.
  419. * <p>
  420. * <strong>Note:</strong> The name should be a unique symbol. The
  421. * SymbolTable can be used for this purpose.
  422. *
  423. * @param name The name of the entity.
  424. * @param publicId The public identifier of the entity.
  425. * @param literalSystemId The system identifier of the entity.
  426. * @param baseSystemId The base system identifier of the entity.
  427. * This is the system identifier of the entity
  428. * where <em>the entity being added</em> and
  429. * is used to expand the system identifier when
  430. * the system identifier is a relative URI.
  431. * When null the system identifier of the first
  432. * external entity on the stack is used instead.
  433. *
  434. * @see SymbolTable
  435. */
  436. public void addExternalEntity(String name,
  437. String publicId, String literalSystemId,
  438. String baseSystemId) throws IOException {
  439. if (!fEntities.containsKey(name)) {
  440. if (baseSystemId == null) {
  441. // search for the first external entity on the stack
  442. int size = fEntityStack.size();
  443. if (size == 0 && fCurrentEntity != null && fCurrentEntity.entityLocation != null) {
  444. baseSystemId = fCurrentEntity.entityLocation.getExpandedSystemId();
  445. }
  446. for (int i = size - 1; i >= 0 ; i--) {
  447. ScannedEntity externalEntity =
  448. (ScannedEntity)fEntityStack.elementAt(i);
  449. if (externalEntity.entityLocation != null && externalEntity.entityLocation.getExpandedSystemId() != null) {
  450. baseSystemId = externalEntity.entityLocation.getExpandedSystemId();
  451. break;
  452. }
  453. }
  454. }
  455. Entity entity = new ExternalEntity(name,
  456. new XMLEntityDescriptionImpl(name, publicId, literalSystemId, baseSystemId,
  457. expandSystemId(literalSystemId, baseSystemId, false)), null, fInExternalSubset);
  458. fEntities.put(name, entity);
  459. }
  460. else{
  461. if(fWarnDuplicateEntityDef){
  462. fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
  463. "MSG_DUPLICATE_ENTITY_DEFINITION",
  464. new Object[]{ name },
  465. XMLErrorReporter.SEVERITY_WARNING );
  466. }
  467. }
  468. } // addExternalEntity(String,String,String,String)
  469. /**
  470. * Checks whether an entity given by name is external.
  471. *
  472. * @param entityName The name of the entity to check.
  473. * @return True if the entity is external, false otherwise
  474. * (including when the entity is not declared).
  475. */
  476. public boolean isExternalEntity(String entityName) {
  477. Entity entity = (Entity)fEntities.get(entityName);
  478. if (entity == null) {
  479. return false;
  480. }
  481. return entity.isExternal();
  482. }
  483. /**
  484. * Checks whether the declaration of an entity given by name is
  485. // in the external subset.
  486. *
  487. * @param entityName The name of the entity to check.
  488. * @return True if the entity was declared in the external subset, false otherwise
  489. * (including when the entity is not declared).
  490. */
  491. public boolean isEntityDeclInExternalSubset(String entityName) {
  492. Entity entity = (Entity)fEntities.get(entityName);
  493. if (entity == null) {
  494. return false;
  495. }
  496. return entity.isEntityDeclInExternalSubset();
  497. }
  498. /**
  499. * Adds an unparsed entity declaration.
  500. * <p>
  501. * <strong>Note:</strong> This method ignores subsequent entity
  502. * declarations.
  503. * <p>
  504. * <strong>Note:</strong> The name should be a unique symbol. The
  505. * SymbolTable can be used for this purpose.
  506. *
  507. * @param name The name of the entity.
  508. * @param publicId The public identifier of the entity.
  509. * @param systemId The system identifier of the entity.
  510. * @param notation The name of the notation.
  511. *
  512. * @see SymbolTable
  513. */
  514. public void addUnparsedEntity(String name,
  515. String publicId, String systemId,
  516. String baseSystemId, String notation) {
  517. if (!fEntities.containsKey(name)) {
  518. Entity entity = new ExternalEntity(name,
  519. new XMLEntityDescriptionImpl(name, publicId, systemId, baseSystemId, null),
  520. notation, fInExternalSubset);
  521. fEntities.put(name, entity);
  522. }
  523. else{
  524. if(fWarnDuplicateEntityDef){
  525. fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
  526. "MSG_DUPLICATE_ENTITY_DEFINITION",
  527. new Object[]{ name },
  528. XMLErrorReporter.SEVERITY_WARNING );
  529. }
  530. }
  531. } // addUnparsedEntity(String,String,String,String)
  532. /**
  533. * Checks whether an entity given by name is unparsed.
  534. *
  535. * @param entityName The name of the entity to check.
  536. * @return True if the entity is unparsed, false otherwise
  537. * (including when the entity is not declared).
  538. */
  539. public boolean isUnparsedEntity(String entityName) {
  540. Entity entity = (Entity)fEntities.get(entityName);
  541. if (entity == null) {
  542. return false;
  543. }
  544. return entity.isUnparsed();
  545. }
  546. /**
  547. * Checks whether an entity given by name is declared.
  548. *
  549. * @param entityName The name of the entity to check.
  550. * @return True if the entity is declared, false otherwise.
  551. */
  552. public boolean isDeclaredEntity(String entityName) {
  553. Entity entity = (Entity)fEntities.get(entityName);
  554. return entity != null;
  555. }
  556. /**
  557. * Resolves the specified public and system identifiers. This
  558. * method first attempts to resolve the entity based on the
  559. * EntityResolver registered by the application. If no entity
  560. * resolver is registered or if the registered entity handler
  561. * is unable to resolve the entity, then default entity
  562. * resolution will occur.
  563. *
  564. * @param publicId The public identifier of the entity.
  565. * @param systemId The system identifier of the entity.
  566. * @param baseSystemId The base system identifier of the entity.
  567. * This is the system identifier of the current
  568. * entity and is used to expand the system
  569. * identifier when the system identifier is a
  570. * relative URI.
  571. *
  572. * @return Returns an input source that wraps the resolved entity.
  573. * This method will never return null.
  574. *
  575. * @throws IOException Thrown on i/o error.
  576. * @throws XNIException Thrown by entity resolver to signal an error.
  577. */
  578. public XMLInputSource resolveEntity(XMLResourceIdentifier resourceIdentifier)
  579. throws IOException, XNIException {
  580. if(resourceIdentifier == null ) return null;
  581. String publicId = resourceIdentifier.getPublicId();
  582. String literalSystemId = resourceIdentifier.getLiteralSystemId();
  583. String baseSystemId = resourceIdentifier.getBaseSystemId();
  584. String expandedSystemId = resourceIdentifier.getExpandedSystemId();
  585. // if no base systemId given, assume that it's relative
  586. // to the systemId of the current scanned entity
  587. // Sometimes the system id is not (properly) expanded.
  588. // We need to expand the system id if:
  589. // a. the expanded one was null; or
  590. // b. the base system id was null, but becomes non-null from the current entity.
  591. boolean needExpand = (expandedSystemId == null);
  592. // REVISIT: why would the baseSystemId ever be null? if we
  593. // didn't have to make this check we wouldn't have to reuse the
  594. // fXMLResourceIdentifier object...
  595. if (baseSystemId == null && fCurrentEntity != null && fCurrentEntity.entityLocation != null) {
  596. baseSystemId = fCurrentEntity.entityLocation.getExpandedSystemId();
  597. if (baseSystemId != null)
  598. needExpand = true;
  599. }
  600. if (needExpand)
  601. expandedSystemId = expandSystemId(literalSystemId, baseSystemId, false);
  602. // give the entity resolver a chance
  603. XMLInputSource xmlInputSource = null;
  604. if (fEntityResolver != null) {
  605. resourceIdentifier.setBaseSystemId(baseSystemId);
  606. resourceIdentifier.setExpandedSystemId(expandedSystemId);
  607. xmlInputSource = fEntityResolver.resolveEntity(resourceIdentifier);
  608. }
  609. // do default resolution
  610. // REVISIT: what's the correct behavior if the user provided an entity
  611. // resolver (fEntityResolver != null), but resolveEntity doesn't return
  612. // an input source (xmlInputSource == null)?
  613. // do we do default resolution, or do we just return null? -SG
  614. if (xmlInputSource == null) {
  615. // REVISIT: when systemId is null, I think we should return null.
  616. // is this the right solution? -SG
  617. //if (systemId != null)
  618. xmlInputSource = new XMLInputSource(publicId, literalSystemId, baseSystemId);
  619. }
  620. if (DEBUG_RESOLVER) {
  621. System.err.println("XMLEntityManager.resolveEntity(" + publicId + ")");
  622. System.err.println(" = " + xmlInputSource);
  623. }
  624. return xmlInputSource;
  625. } // resolveEntity(XMLResourceIdentifier):XMLInputSource
  626. /**
  627. * Starts a named entity.
  628. *
  629. * @param entityName The name of the entity to start.
  630. * @param literal True if this entity is started within a literal
  631. * value.
  632. *
  633. * @throws IOException Thrown on i/o error.
  634. * @throws XNIException Thrown by entity handler to signal an error.
  635. */
  636. public void startEntity(String entityName, boolean literal)
  637. throws IOException, XNIException {
  638. // was entity declared?
  639. Entity entity = (Entity)fEntities.get(entityName);
  640. if (entity == null) {
  641. if (fEntityHandler != null) {
  642. String encoding = null;
  643. fResourceIdentifier.clear();
  644. fEntityAugs.removeAllItems();
  645. fEntityAugs.putItem(Constants.ENTITY_SKIPPED, Boolean.TRUE);
  646. fEntityHandler.startEntity(entityName, fResourceIdentifier, encoding, fEntityAugs);
  647. fEntityAugs.removeAllItems();
  648. fEntityAugs.putItem(Constants.ENTITY_SKIPPED, Boolean.TRUE);
  649. fEntityHandler.endEntity(entityName, fEntityAugs);
  650. }
  651. return;
  652. }
  653. // should we skip external entities?
  654. boolean external = entity.isExternal();
  655. if (external && (fValidationManager == null || !fValidationManager.isCachedDTD())) {
  656. boolean unparsed = entity.isUnparsed();
  657. boolean parameter = entityName.startsWith("%");
  658. boolean general = !parameter;
  659. if (unparsed || (general && !fExternalGeneralEntities) ||
  660. (parameter && !fExternalParameterEntities)) {
  661. if (fEntityHandler != null) {
  662. fResourceIdentifier.clear();
  663. final String encoding = null;
  664. ExternalEntity externalEntity = (ExternalEntity)entity;
  665. //REVISIT: since we're storing expandedSystemId in the
  666. // externalEntity, how could this have got here if it wasn't already
  667. // expanded??? - neilg
  668. String extLitSysId = (externalEntity.entityLocation != null ? externalEntity.entityLocation.getLiteralSystemId() : null);
  669. String extBaseSysId = (externalEntity.entityLocation != null ? externalEntity.entityLocation.getBaseSystemId() : null);
  670. String expandedSystemId = expandSystemId(extLitSysId, extBaseSysId, false);
  671. fResourceIdentifier.setValues(
  672. (externalEntity.entityLocation != null ? externalEntity.entityLocation.getPublicId() : null),
  673. extLitSysId, extBaseSysId, expandedSystemId);
  674. fEntityAugs.removeAllItems();
  675. fEntityAugs.putItem(Constants.ENTITY_SKIPPED, Boolean.TRUE);
  676. fEntityHandler.startEntity(entityName, fResourceIdentifier, encoding, fEntityAugs);
  677. fEntityAugs.removeAllItems();
  678. fEntityAugs.putItem(Constants.ENTITY_SKIPPED, Boolean.TRUE);
  679. fEntityHandler.endEntity(entityName, fEntityAugs);
  680. }
  681. return;
  682. }
  683. }
  684. // is entity recursive?
  685. int size = fEntityStack.size();
  686. for (int i = size; i >= 0; i--) {
  687. Entity activeEntity = i == size
  688. ? fCurrentEntity
  689. : (Entity)fEntityStack.elementAt(i);
  690. if (activeEntity.name == entityName) {
  691. String path = entityName;
  692. for (int j = i + 1; j < size; j++) {
  693. activeEntity = (Entity)fEntityStack.elementAt(j);
  694. path = path + " -> " + activeEntity.name;
  695. }
  696. path = path + " -> " + fCurrentEntity.name;
  697. path = path + " -> " + entityName;
  698. fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
  699. "RecursiveReference",
  700. new Object[] { entityName, path },
  701. XMLErrorReporter.SEVERITY_FATAL_ERROR);
  702. if (fEntityHandler != null) {
  703. fResourceIdentifier.clear();
  704. final String encoding = null;
  705. if (external) {
  706. ExternalEntity externalEntity = (ExternalEntity)entity;
  707. // REVISIT: for the same reason above...
  708. String extLitSysId = (externalEntity.entityLocation != null ? externalEntity.entityLocation.getLiteralSystemId() : null);
  709. String extBaseSysId = (externalEntity.entityLocation != null ? externalEntity.entityLocation.getBaseSystemId() : null);
  710. String expandedSystemId = expandSystemId(extLitSysId, extBaseSysId, false);
  711. fResourceIdentifier.setValues(
  712. (externalEntity.entityLocation != null ? externalEntity.entityLocation.getPublicId() : null),
  713. extLitSysId, extBaseSysId, expandedSystemId);
  714. }
  715. fEntityAugs.removeAllItems();
  716. fEntityAugs.putItem(Constants.ENTITY_SKIPPED, Boolean.TRUE);
  717. fEntityHandler.startEntity(entityName, fResourceIdentifier, encoding, fEntityAugs);
  718. fEntityAugs.removeAllItems();
  719. fEntityAugs.putItem(Constants.ENTITY_SKIPPED, Boolean.TRUE);
  720. fEntityHandler.endEntity(entityName, fEntityAugs);
  721. }
  722. return;
  723. }
  724. }
  725. // resolve external entity
  726. XMLInputSource xmlInputSource = null;
  727. if (external) {
  728. ExternalEntity externalEntity = (ExternalEntity)entity;
  729. xmlInputSource = resolveEntity(externalEntity.entityLocation);
  730. }
  731. // wrap internal entity
  732. else {
  733. InternalEntity internalEntity = (InternalEntity)entity;
  734. Reader reader = new StringReader(internalEntity.text);
  735. xmlInputSource = new XMLInputSource(null, null, null, reader, null);
  736. }
  737. // start the entity
  738. startEntity(entityName, xmlInputSource, literal, external);
  739. } // startEntity(String,boolean)
  740. /**
  741. * Starts the document entity. The document entity has the "[xml]"
  742. * pseudo-name.
  743. *
  744. * @param xmlInputSource The input source of the document entity.
  745. *
  746. * @throws IOException Thrown on i/o error.
  747. * @throws XNIException Thrown by entity handler to signal an error.
  748. */
  749. public void startDocumentEntity(XMLInputSource xmlInputSource)
  750. throws IOException, XNIException {
  751. startEntity(XMLEntity, xmlInputSource, false, true);
  752. } // startDocumentEntity(XMLInputSource)
  753. /**
  754. * Starts the DTD entity. The DTD entity has the "[dtd]"
  755. * pseudo-name.
  756. *
  757. * @param xmlInputSource The input source of the DTD entity.
  758. *
  759. * @throws IOException Thrown on i/o error.
  760. * @throws XNIException Thrown by entity handler to signal an error.
  761. */
  762. public void startDTDEntity(XMLInputSource xmlInputSource)
  763. throws IOException, XNIException {
  764. startEntity(DTDEntity, xmlInputSource, false, true);
  765. } // startDTDEntity(XMLInputSource)
  766. // indicate start of external subset so that
  767. // location of entity decls can be tracked
  768. public void startExternalSubset() {
  769. fInExternalSubset = true;
  770. }
  771. public void endExternalSubset() {
  772. fInExternalSubset = false;
  773. }
  774. /**
  775. * Starts an entity.
  776. * <p>
  777. * This method can be used to insert an application defined XML
  778. * entity stream into the parsing stream.
  779. *
  780. * @param name The name of the entity.
  781. * @param xmlInputSource The input source of the entity.
  782. * @param literal True if this entity is started within a
  783. * literal value.
  784. * @param isExternal whether this entity should be treated as an internal or external entity.
  785. *
  786. * @throws IOException Thrown on i/o error.
  787. * @throws XNIException Thrown by entity handler to signal an error.
  788. */
  789. public void startEntity(String name,
  790. XMLInputSource xmlInputSource,
  791. boolean literal, boolean isExternal)
  792. throws IOException, XNIException {
  793. String encoding = setupCurrentEntity(name, xmlInputSource, literal, isExternal);
  794. //when entity expansion limit is set by the Application, we need to
  795. //check for the entity expansion limit set by the parser, if number of entity
  796. //expansions exceeds the entity expansion limit, parser will throw fatal error.
  797. // Note that this represents the nesting level of open entities.
  798. fEntityExpansionCount++;
  799. if( fSecurityManager != null && fEntityExpansionCount > fEntityExpansionLimit ){
  800. fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
  801. "EntityExpansionLimitExceeded",
  802. new Object[]{new Integer(fEntityExpansionLimit) },
  803. XMLErrorReporter.SEVERITY_FATAL_ERROR );
  804. // is there anything better to do than reset the counter?
  805. // at least one can envision debugging applications where this might
  806. // be useful...
  807. fEntityExpansionCount = 0;
  808. }
  809. // call handler
  810. if (fEntityHandler != null) {
  811. fEntityHandler.startEntity(name, fResourceIdentifier, encoding, null);
  812. }
  813. } // startEntity(String,XMLInputSource)
  814. /**
  815. * This method uses the passed-in XMLInputSource to make
  816. * fCurrentEntity usable for reading.
  817. * @param name name of the entity (XML is it's the document entity)
  818. * @param xmlInputSource the input source, with sufficient information
  819. * to begin scanning characters.
  820. * @param literal True if this entity is started within a
  821. * literal value.
  822. * @param isExternal whether this entity should be treated as an internal or external entity.
  823. * @throws IOException if anything can't be read
  824. * XNIException If any parser-specific goes wrong.
  825. * @return the encoding of the new entity or null if a character stream was employed
  826. */
  827. public String setupCurrentEntity(String name, XMLInputSource xmlInputSource,
  828. boolean literal, boolean isExternal)
  829. throws IOException, XNIException {
  830. // get information
  831. final String publicId = xmlInputSource.getPublicId();
  832. String literalSystemId = xmlInputSource.getSystemId();
  833. String baseSystemId = xmlInputSource.getBaseSystemId();
  834. String encoding = xmlInputSource.getEncoding();
  835. Boolean isBigEndian = null;
  836. boolean declaredEncoding = false;
  837. // create reader
  838. InputStream stream = null;
  839. Reader reader = xmlInputSource.getCharacterStream();
  840. // First chance checking strict URI
  841. String expandedSystemId = expandSystemId(literalSystemId, baseSystemId, fStrictURI);
  842. if (baseSystemId == null) {
  843. baseSystemId = expandedSystemId;
  844. }
  845. if (reader == null) {
  846. stream = xmlInputSource.getByteStream();
  847. if(stream != null && encoding != null)
  848. declaredEncoding = true;
  849. if (stream == null) {
  850. URL location = new URL(expandedSystemId);
  851. URLConnection connect = location.openConnection();
  852. if (connect instanceof HttpURLConnection) {
  853. setHttpProperties(connect,xmlInputSource);
  854. }
  855. stream = connect.getInputStream();
  856. // REVISIT: If the URLConnection has external encoding
  857. // information, we should be reading it here. It's located
  858. // in the charset parameter of Content-Type. -- mrglavas
  859. if (connect instanceof HttpURLConnection) {
  860. String redirect = connect.getURL().toString();
  861. // E43: Check if the URL was redirected, and then
  862. // update literal and expanded system IDs if needed.
  863. if (!redirect.equals(expandedSystemId)) {
  864. literalSystemId = redirect;
  865. expandedSystemId = redirect;
  866. }
  867. }
  868. }
  869. // wrap this stream in RewindableInputStream
  870. stream = new RewindableInputStream(stream);
  871. // perform auto-detect of encoding if necessary
  872. if (encoding == null) {
  873. // read first four bytes and determine encoding
  874. final byte[] b4 = new byte[4];
  875. int count = 0;
  876. for (; count<4; count++ ) {
  877. b4[count] = (byte)stream.read();
  878. }
  879. if (count == 4) {
  880. Object [] encodingDesc = getEncodingName(b4, count);
  881. encoding = (String)(encodingDesc[0]);
  882. isBigEndian = (Boolean)(encodingDesc[1]);
  883. stream.reset();
  884. // Special case UTF-8 files with BOM created by Microsoft
  885. // tools. It's more efficient to consume the BOM than make
  886. // the reader perform extra checks. -Ac
  887. if (count > 2 && encoding.equals("UTF-8")) {
  888. int b0 = b4[0] & 0xFF;
  889. int b1 = b4[1] & 0xFF;
  890. int b2 = b4[2] & 0xFF;
  891. if (b0 == 0xEF && b1 == 0xBB && b2 == 0xBF) {
  892. // ignore first three bytes...
  893. stream.skip(3);
  894. }
  895. }
  896. reader = createReader(stream, encoding, isBigEndian);
  897. }
  898. else {
  899. reader = createReader(stream, encoding, isBigEndian);
  900. }
  901. }
  902. // use specified encoding
  903. else {
  904. encoding = encoding.toUpperCase(Locale.ENGLISH);
  905. // If encoding is UTF-8, consume BOM if one is present.
  906. if (encoding.equals("UTF-8")) {
  907. final int[] b3 = new int[3];
  908. int count = 0;
  909. for (; count < 3; ++count) {
  910. b3[count] = stream.read();
  911. if (b3[count] == -1)
  912. break;
  913. }
  914. if (count == 3) {
  915. if (b3[0] != 0xEF || b3[1] != 0xBB || b3[2] != 0xBF) {
  916. // First three bytes are not BOM, so reset.
  917. stream.reset();
  918. }
  919. }
  920. else {
  921. stream.reset();
  922. }
  923. }
  924. // If encoding is UCS-4, we still need to read the first four bytes
  925. // in order to discover the byte order.
  926. else if (encoding.equals("ISO-10646-UCS-4")) {
  927. final int[] b4 = new int[4];
  928. int count = 0;
  929. for (; count < 4; ++count) {
  930. b4[count] = stream.read();
  931. if (b4[count] == -1)
  932. break;
  933. }
  934. stream.reset();
  935. // Ignore unusual octet order for now.
  936. if (count == 4) {
  937. // UCS-4, big endian (1234)
  938. if (b4[0] == 0x00 && b4[1] == 0x00 && b4[2] == 0x00 && b4[3] == 0x3C) {
  939. isBigEndian = Boolean.TRUE;
  940. }
  941. // UCS-4, little endian (1234)
  942. else if (b4[0] == 0x3C && b4[1] == 0x00 && b4[2] == 0x00 && b4[3] == 0x00) {
  943. isBigEndian = Boolean.FALSE;
  944. }
  945. }
  946. }
  947. // If encoding is UCS-2, we still need to read the first four bytes
  948. // in order to discover the byte order.
  949. else if (encoding.equals("ISO-10646-UCS-2")) {
  950. final int[] b4 = new int[4];
  951. int count = 0;
  952. for (; count < 4; ++count) {
  953. b4[count] = stream.read();
  954. if (b4[count] == -1)
  955. break;
  956. }
  957. stream.reset();
  958. if (count == 4) {
  959. // UCS-2, big endian
  960. if (b4[0] == 0x00 && b4[1] == 0x3C && b4[2] == 0x00 && b4[3] == 0x3F) {
  961. isBigEndian = Boolean.TRUE;
  962. }
  963. // UCS-2, little endian
  964. else if (b4[0] == 0x3C && b4[1] == 0x00 && b4[2] == 0x3F && b4[3] == 0x00) {
  965. isBigEndian = Boolean.FALSE;
  966. }
  967. }
  968. }
  969. reader = createReader(stream, encoding, isBigEndian);
  970. }
  971. // read one character at a time so we don't jump too far
  972. // ahead, converting characters from the byte stream in
  973. // the wrong encoding
  974. if (DEBUG_ENCODINGS) {
  975. System.out.println("$$$ no longer wrapping reader in OneCharReader");
  976. }
  977. //reader = new OneCharReader(reader);
  978. }
  979. // We've seen a new Reader.
  980. // Push it on the stack so we can close it later.
  981. fReaderStack.push(reader);
  982. // push entity on stack
  983. if (fCurrentEntity != null) {
  984. fEntityStack.push(fCurrentEntity);
  985. }
  986. // create entity
  987. fCurrentEntity = new ScannedEntity(name,
  988. new XMLResourceIdentifierImpl(publicId, literalSystemId, baseSystemId, expandedSystemId),
  989. stream, reader, encoding, literal, false, isExternal);
  990. fCurrentEntity.setDeclaredEncoding(declaredEncoding);
  991. fEntityScanner.setCurrentEntity(fCurrentEntity);
  992. fResourceIdentifier.setValues(publicId, literalSystemId, baseSystemId, expandedSystemId);
  993. return encoding;
  994. } //setupCurrentEntity(String, XMLInputSource, boolean, boolean): String
  995. void setHttpProperties(URLConnection conn, XMLInputSource source ){
  996. if( source instanceof XIncludeInputSource ){
  997. String fAccept = null;
  998. String fAcceptCharset = null;
  999. String fAcceptLanguage = null;
  1000. XIncludeInputSource xiSrc= (XIncludeInputSource)source;
  1001. fAccept = (String)xiSrc.getProperty(XIncludeHandler.HTTP_ACCEPT);
  1002. fAcceptCharset = (String)xiSrc.getProperty(XIncludeHandler.HTTP_ACCEPT_CHARSET);
  1003. fAcceptLanguage = (String)xiSrc.getProperty(XIncludeHandler.HTTP_ACCEPT_LANGUAGE);
  1004. if( fAccept != null && fAccept.length() > 0) {
  1005. conn.setRequestProperty(XIncludeHandler.HTTP_ACCEPT, fAccept);
  1006. }
  1007. if( fAcceptCharset != null && fAcceptCharset.length() > 0) {
  1008. conn.setRequestProperty(XIncludeHandler.HTTP_ACCEPT_CHARSET, fAcceptCharset);
  1009. }
  1010. if( fAcceptLanguage != null && fAcceptLanguage.length() > 0) {
  1011. conn.setRequestProperty(XIncludeHandler.HTTP_ACCEPT_LANGUAGE, fAcceptLanguage);
  1012. }
  1013. }
  1014. }
  1015. // set version of scanner to use
  1016. public void setScannerVersion(short version) {
  1017. if(version == Constants.XML_VERSION_1_0) {
  1018. if(fXML10EntityScanner == null) {
  1019. fXML10EntityScanner = new XMLEntityScanner();
  1020. }
  1021. fXML10EntityScanner.reset(fSymbolTable, this, fErrorReporter);
  1022. fEntityScanner = fXML10EntityScanner;
  1023. fEntityScanner.setCurrentEntity(fCurrentEntity);
  1024. } else {
  1025. if(fXML11EntityScanner == null) {
  1026. fXML11EntityScanner = new XML11EntityScanner();
  1027. }
  1028. fXML11EntityScanner.reset(fSymbolTable, this, fErrorReporter);
  1029. fEntityScanner = fXML11EntityScanner;
  1030. fEntityScanner.setCurrentEntity(fCurrentEntity);
  1031. }
  1032. } // setScannerVersion(short)
  1033. /** Returns the entity scanner. */
  1034. public XMLEntityScanner getEntityScanner() {
  1035. if(fEntityScanner == null) {
  1036. // default to 1.0
  1037. if(fXML10EntityScanner == null) {
  1038. fXML10EntityScanner = new XMLEntityScanner();
  1039. }
  1040. fXML10EntityScanner.reset(fSymbolTable, this, fErrorReporter);
  1041. fEntityScanner = fXML10EntityScanner;
  1042. }
  1043. return fEntityScanner;
  1044. } // getEntityScanner():XMLEntityScanner
  1045. // A stack containing all the open readers
  1046. protected Stack fReaderStack = new Stack();
  1047. /**
  1048. * Close all opened InputStreams and Readers opened by this parser.
  1049. */
  1050. public void closeReaders() {
  1051. // close all readers
  1052. for (int i = fReaderStack.size()-1; i >= 0; i--) {
  1053. try {
  1054. ((Reader)fReaderStack.pop()).close();
  1055. } catch (IOException e) {
  1056. // ignore
  1057. }
  1058. }
  1059. }
  1060. //
  1061. // XMLComponent methods
  1062. //
  1063. /**
  1064. * Resets the component. The component can query the component manager
  1065. * about any features and properties that affect the operation of the
  1066. * component.
  1067. *
  1068. * @param componentManager The component manager.
  1069. *
  1070. * @throws SAXException Thrown by component on initialization error.
  1071. * For example, if a feature or property is
  1072. * required for the operation of the component, the
  1073. * component manager may throw a
  1074. * SAXNotRecognizedException or a
  1075. * SAXNotSupportedException.
  1076. */
  1077. public void reset(XMLComponentManager componentManager)
  1078. throws XMLConfigurationException {
  1079. boolean parser_settings;
  1080. try {
  1081. parser_settings = componentManager.getFeature(PARSER_SETTINGS);
  1082. } catch (XMLConfigurationException e) {
  1083. parser_settings = true;
  1084. }
  1085. if (!parser_settings) {
  1086. // parser settings have not been changed
  1087. reset();
  1088. return;
  1089. }
  1090. // sax features
  1091. try {
  1092. fValidation = componentManager.getFeature(VALIDATION);
  1093. }
  1094. catch (XMLConfigurationException e) {
  1095. fValidation = false;
  1096. }
  1097. try {
  1098. fExternalGeneralEntities = componentManager.getFeature(EXTERNAL_GENERAL_ENTITIES);
  1099. }
  1100. catch (XMLConfigurationException e) {
  1101. fExternalGeneralEntities = true;
  1102. }
  1103. try {
  1104. fExternalParameterEntities = componentManager.getFeature(EXTERNAL_PARAMETER_ENTITIES);
  1105. }
  1106. catch (XMLConfigurationException e) {
  1107. fExternalParameterEntities = true;
  1108. }
  1109. // xerces features
  1110. try {
  1111. fAllowJavaEncodings = componentManager.getFeature(ALLOW_JAVA_ENCODINGS);
  1112. }
  1113. catch (XMLConfigurationException e) {
  1114. fAllowJavaEncodings = false;
  1115. }
  1116. try {
  1117. fWarnDuplicateEntityDef = componentManager.getFeature(WARN_ON_DUPLICATE_ENTITYDEF);
  1118. }
  1119. catch (XMLConfigurationException e) {
  1120. fWarnDuplicateEntityDef = false;
  1121. }
  1122. try {
  1123. fStrictURI = componentManager.getFeature(STANDARD_URI_CONFORMANT);
  1124. }
  1125. catch (XMLConfigurationException e) {
  1126. fStrictURI = false;
  1127. }
  1128. try {
  1129. fCharsetOverrideXmlEncoding = componentManager.getFeature(Constants.DOM_CHARSET_OVERRIDES_XML_ENCODING);
  1130. }
  1131. catch (XMLConfigurationException e) {
  1132. fCharsetOverrideXmlEncoding = true;
  1133. }
  1134. // xerces properties
  1135. fSymbolTable = (SymbolTable)componentManager.getProperty(SYMBOL_TABLE);
  1136. fErrorReporter = (XMLErrorReporter)componentManager.getProperty(ERROR_REPORTER);
  1137. try {
  1138. fEntityResolver = (XMLEntityResolver)componentManager.getProperty(ENTITY_RESOLVER);
  1139. }
  1140. catch (XMLConfigurationException e) {
  1141. fEntityResolver = null;
  1142. }
  1143. try {
  1144. fValidationManager = (ValidationManager)componentManager.getProperty(VALIDATION_MANAGER);
  1145. }
  1146. catch (XMLConfigurationException e) {
  1147. fValidationManager = null;
  1148. }
  1149. try {
  1150. fSecurityManager = (SecurityManager)componentManager.getProperty(SECURITY_MANAGER);
  1151. }
  1152. catch (XMLConfigurationException e) {
  1153. fSecurityManager = null;
  1154. }
  1155. // reset general state
  1156. reset();
  1157. } // reset(XMLComponentManager)
  1158. // reset general state. Should not be called other than by
  1159. // a class acting as a component manager but not
  1160. // implementing that interface for whatever reason.
  1161. public void reset() {
  1162. fEntityExpansionLimit = (fSecurityManager != null)?fSecurityManager.getEntityExpansionLimit():0;
  1163. // initialize state
  1164. fStandalone = false;
  1165. fEntities.clear();
  1166. fEntityStack.removeAllElements();
  1167. fEntityExpansionCount = 0;
  1168. fCurrentEntity = null;
  1169. // reset scanner
  1170. if(fXML10EntityScanner != null){
  1171. fXML10EntityScanner.reset(fSymbolTable, this, fErrorReporter);
  1172. }
  1173. if(fXML11EntityScanner != null) {
  1174. fXML11EntityScanner.reset(fSymbolTable, this, fErrorReporter);
  1175. }
  1176. // DEBUG
  1177. if (DEBUG_ENTITIES) {
  1178. addInternalEntity("text", "Hello, World.");
  1179. addInternalEntity("empty-element", "<foo/>");
  1180. addInternalEntity("balanced-element", "<foo></foo>");
  1181. addInternalEntity("balanced-element-with-text", "<foo>Hello, World</foo>");
  1182. addInternalEntity("balanced-element-with-entity", "<foo>&text;</foo>");
  1183. addInternalEntity("unbalanced-entity", "<foo>");
  1184. addInternalEntity("recursive-entity", "<foo>&recursive-entity2;</foo>");
  1185. addInternalEntity("recursive-entity2", "<bar>&recursive-entity3;</bar>");
  1186. addInternalEntity("recursive-entity3", "<baz>&recursive-entity;</baz>");
  1187. try {
  1188. addExternalEntity("external-text", null, "external-text.ent", "test/external-text.xml");
  1189. addExternalEntity("external-balanced-element", null, "external-balanced-element.ent", "test/external-balanced-element.xml");
  1190. addExternalEntity("one", null, "ent/one.ent", "test/external-entity.xml");
  1191. addExternalEntity("two", null, "ent/two.ent", "test/ent/one.xml");
  1192. }
  1193. catch (IOException ex) {
  1194. // should never happen
  1195. }
  1196. }
  1197. // copy declared entities
  1198. if (fDeclaredEntities != null) {
  1199. java.util.Enumeration keys = fDeclaredEntities.keys();
  1200. while (keys.hasMoreElements()) {
  1201. Object key = keys.nextElement();
  1202. Object value = fDeclaredEntities.get(key);
  1203. fEntities.put(key, value);
  1204. }
  1205. }
  1206. fEntityHandler = null;
  1207. } // reset(XMLComponentManager)
  1208. /**
  1209. * Returns a list of feature identifiers that are recognized by
  1210. * this component. This method may return null if no features
  1211. * are recognized by this component.
  1212. */
  1213. public String[] getRecognizedFeatures() {
  1214. return (String[])(RECOGNIZED_FEATURES.clone());
  1215. } // getRecognizedFeatures():String[]
  1216. /**
  1217. * Sets the state of a feature. This method is called by the component
  1218. * manager any time after reset when a feature changes state.
  1219. * <p>
  1220. * <strong>Note:</strong> Components should silently ignore features
  1221. * that do not affect the operation of the component.
  1222. *
  1223. * @param featureId The feature identifier.
  1224. * @param state The state of the feature.
  1225. *
  1226. * @throws SAXNotRecognizedException The component should not throw
  1227. * this exception.
  1228. * @throws SAXNotSupportedException The component should not throw
  1229. * this exception.
  1230. */
  1231. public void setFeature(String featureId, boolean state)
  1232. throws XMLConfigurationException {
  1233. // xerces features
  1234. if (featureId.startsWith(Constants.XERCES_FEATURE_PREFIX)) {
  1235. final int suffixLength = featureId.length() - Constants.XERCES_FEATURE_PREFIX.length();
  1236. if (suffixLength == Constants.ALLOW_JAVA_ENCODINGS_FEATURE.length() &&
  1237. featureId.endsWith(Constants.ALLOW_JAVA_ENCODINGS_FEATURE)) {
  1238. fAllowJavaEncodings = state;
  1239. }
  1240. }
  1241. } // setFeature(String,boolean)
  1242. /**
  1243. * Returns a list of property identifiers that are recognized by
  1244. * this component. This method may return null if no properties
  1245. * are recognized by this component.
  1246. */
  1247. public String[] getRecognizedProperties() {
  1248. return (String[])(RECOGNIZED_PROPERTIES.clone());
  1249. } // getRecognizedProperties():String[]
  1250. /**
  1251. * Sets the value of a property. This method is called by the component
  1252. * manager any time after reset when a property changes value.
  1253. * <p>
  1254. * <strong>Note:</strong> Components should silently ignore properties
  1255. * that do not affect the operation of the component.
  1256. *
  1257. * @param propertyId The property identifier.
  1258. * @param value The value of the property.
  1259. *
  1260. * @throws SAXNotRecognizedException The component should not throw
  1261. * this exception.
  1262. * @throws SAXNotSupportedException The component should not throw
  1263. * this exception.
  1264. */
  1265. public void setProperty(String propertyId, Object value)
  1266. throws XMLConfigurationException {
  1267. // Xerces properties
  1268. if (propertyId.startsWith(Constants.XERCES_PROPERTY_PREFIX)) {
  1269. final int suffixLength = propertyId.length() - Constants.XERCES_PROPERTY_PREFIX.length();
  1270. if (suffixLength == Constants.SYMBOL_TABLE_PROPERTY.length() &&
  1271. propertyId.endsWith(Constants.SYMBOL_TABLE_PROPERTY)) {
  1272. fSymbolTable = (SymbolTable)value;
  1273. return;
  1274. }
  1275. if (suffixLength == Constants.ERROR_REPORTER_PROPERTY.length() &&
  1276. propertyId.endsWith(Constants.ERROR_REPORTER_PROPERTY)) {
  1277. fErrorReporter = (XMLErrorReporter)value;
  1278. return;
  1279. }
  1280. if (suffixLength == Constants.ENTITY_RESOLVER_PROPERTY.length() &&
  1281. propertyId.endsWith(Constants.ENTITY_RESOLVER_PROPERTY)) {
  1282. fEntityResolver = (XMLEntityResolver)value;
  1283. return;
  1284. }
  1285. if (suffixLength == Constants.BUFFER_SIZE_PROPERTY.length() &&
  1286. propertyId.endsWith(Constants.BUFFER_SIZE_PROPERTY)) {
  1287. Integer bufferSize = (Integer)value;
  1288. if (bufferSize != null &&
  1289. bufferSize.intValue() > DEFAULT_XMLDECL_BUFFER_SIZE) {
  1290. fBufferSize = bufferSize.intValue();
  1291. fEntityScanner.setBufferSize(fBufferSize);
  1292. }
  1293. }
  1294. if (suffixLength == Constants.SECURITY_MANAGER_PROPERTY.length() &&
  1295. propertyId.endsWith(Constants.SECURITY_MANAGER_PROPERTY)) {
  1296. fSecurityManager = (SecurityManager)value;
  1297. fEntityExpansionLimit = (fSecurityManager != null)?fSecurityManager.getEntityExpansionLimit():0;
  1298. }
  1299. }
  1300. } // setProperty(String,Object)
  1301. /**
  1302. * Returns the default state for a feature, or null if this
  1303. * component does not want to report a default value for this
  1304. * feature.
  1305. *
  1306. * @param featureId The feature identifier.
  1307. *
  1308. * @since Xerces 2.2.0
  1309. */
  1310. public Boolean getFeatureDefault(String featureId) {
  1311. for (int i = 0; i < RECOGNIZED_FEATURES.length; i++) {
  1312. if (RECOGNIZED_FEATURES[i].equals(featureId)) {
  1313. return FEATURE_DEFAULTS[i];
  1314. }
  1315. }
  1316. return null;
  1317. } // getFeatureDefault(String):Boolean
  1318. /**
  1319. * Returns the default state for a property, or null if this
  1320. * component does not want to report a default value for this
  1321. * property.
  1322. *
  1323. * @param propertyId The property identifier.
  1324. *
  1325. * @since Xerces 2.2.0
  1326. */
  1327. public Object getPropertyDefault(String propertyId) {
  1328. for (int i = 0; i < RECOGNIZED_PROPERTIES.length; i++) {
  1329. if (RECOGNIZED_PROPERTIES[i].equals(propertyId)) {
  1330. return PROPERTY_DEFAULTS[i];
  1331. }
  1332. }
  1333. return null;
  1334. } // getPropertyDefault(String):Object
  1335. //
  1336. // Public static methods
  1337. //
  1338. // current value of the "user.dir" property
  1339. private static String gUserDir;
  1340. // escaped value of the current "user.dir" property
  1341. private static String gEscapedUserDir;
  1342. // which ASCII characters need to be escaped
  1343. private static boolean gNeedEscaping[] = new boolean[128];
  1344. // the first hex character if a character needs to be escaped
  1345. private static char gAfterEscaping1[] = new char[128];
  1346. // the second hex character if a character needs to be escaped
  1347. private static char gAfterEscaping2[] = new char[128];
  1348. private static char[] gHexChs = {'0', '1', '2', '3', '4', '5', '6', '7',
  1349. '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
  1350. // initialize the above 3 arrays
  1351. static {
  1352. for (int i = 0; i <= 0x1f; i++) {
  1353. gNeedEscaping[i] = true;
  1354. gAfterEscaping1[i] = gHexChs[i >> 4];
  1355. gAfterEscaping2[i] = gHexChs[i & 0xf];
  1356. }
  1357. gNeedEscaping[0x7f] = true;
  1358. gAfterEscaping1[0x7f] = '7';
  1359. gAfterEscaping2[0x7f] = 'F';
  1360. char[] escChs = {' ', '<', '>', '#', '%', '"', '{', '}',
  1361. '|', '\\', '^', '~', '[', ']', '`'};
  1362. int len = escChs.length;
  1363. char ch;
  1364. for (int i = 0; i < len; i++) {
  1365. ch = escChs[i];
  1366. gNeedEscaping[ch] = true;
  1367. gAfterEscaping1[ch] = gHexChs[ch >> 4];
  1368. gAfterEscaping2[ch] = gHexChs[ch & 0xf];
  1369. }
  1370. }
  1371. // To escape the "user.dir" system property, by using %HH to represent
  1372. // special ASCII characters: 0x00~0x1F, 0x7F, ' ', '<', '>', '#', '%'
  1373. // and '"'. It's a static method, so needs to be synchronized.
  1374. // this method looks heavy, but since the system property isn't expected
  1375. // to change often, so in most cases, we only need to return the string
  1376. // that was escaped before.
  1377. // According to the URI spec, non-ASCII characters (whose value >= 128)
  1378. // need to be escaped too.
  1379. // REVISIT: don't know how to escape non-ASCII characters, especially
  1380. // which encoding to use. Leave them for now.
  1381. private static synchronized String getUserDir() {
  1382. // get the user.dir property
  1383. String userDir = "";
  1384. try {
  1385. userDir = System.getProperty("user.dir");
  1386. }
  1387. catch (SecurityException se) {
  1388. }
  1389. // return empty string if property value is empty string.
  1390. if (userDir.length() == 0)
  1391. return "";
  1392. // compute the new escaped value if the new property value doesn't
  1393. // match the previous one
  1394. if (userDir.equals(gUserDir)) {
  1395. return gEscapedUserDir;
  1396. }
  1397. // record the new value as the global property value
  1398. gUserDir = userDir;
  1399. char separator = java.io.File.separatorChar;
  1400. userDir = userDir.replace(separator, '/');
  1401. int len = userDir.length(), ch;
  1402. StringBuffer buffer = new StringBuffer(len*3);
  1403. // change C:/blah to /C:/blah
  1404. if (len >= 2 && userDir.charAt(1) == ':') {
  1405. ch = Character.toUpperCase(userDir.charAt(0));
  1406. if (ch >= 'A' && ch <= 'Z') {
  1407. buffer.append('/');
  1408. }
  1409. }
  1410. // for each character in the path
  1411. int i = 0;
  1412. for (; i < len; i++) {
  1413. ch = userDir.charAt(i);
  1414. // if it's not an ASCII character, break here, and use UTF-8 encoding
  1415. if (ch >= 128)
  1416. break;
  1417. if (gNeedEscaping[ch]) {
  1418. buffer.append('%');
  1419. buffer.append(gAfterEscaping1[ch]);
  1420. buffer.append(gAfterEscaping2[ch]);
  1421. // record the fact that it's escaped
  1422. }
  1423. else {
  1424. buffer.append((char)ch);
  1425. }
  1426. }
  1427. // we saw some non-ascii character
  1428. if (i < len) {
  1429. // get UTF-8 bytes for the remaining sub-string
  1430. byte[] bytes = null;
  1431. byte b;
  1432. try {
  1433. bytes = userDir.substring(i).getBytes("UTF-8");
  1434. } catch (java.io.UnsupportedEncodingException e) {
  1435. // should never happen
  1436. return userDir;
  1437. }
  1438. len = bytes.length;
  1439. // for each byte
  1440. for (i = 0; i < len; i++) {
  1441. b = bytes[i];
  1442. // for non-ascii character: make it positive, then escape
  1443. if (b < 0) {
  1444. ch = b + 256;
  1445. buffer.append('%');
  1446. buffer.append(gHexChs[ch >> 4]);
  1447. buffer.append(gHexChs[ch & 0xf]);
  1448. }
  1449. else if (gNeedEscaping[b]) {
  1450. buffer.append('%');
  1451. buffer.append(gAfterEscaping1[b]);
  1452. buffer.append(gAfterEscaping2[b]);
  1453. }
  1454. else {
  1455. buffer.append((char)b);
  1456. }
  1457. }
  1458. }
  1459. // change blah/blah to blah/blah/
  1460. if (!userDir.endsWith("/"))
  1461. buffer.append('/');
  1462. gEscapedUserDir = buffer.toString();
  1463. return gEscapedUserDir;
  1464. }
  1465. /**
  1466. * Expands a system id and returns the system id as a URI, if
  1467. * it can be expanded. A return value of null means that the
  1468. * identifier is already expanded. An exception thrown
  1469. * indicates a failure to expand the id.
  1470. *
  1471. * @param systemId The systemId to be expanded.
  1472. *
  1473. * @return Returns the URI string representing the expanded system
  1474. * identifier. A null value indicates that the given
  1475. * system identifier is already expanded.
  1476. *
  1477. */
  1478. public static String expandSystemId(String systemId, String baseSystemId,
  1479. boolean strict)
  1480. throws URI.MalformedURIException {
  1481. // system id has to be a valid URI
  1482. if (strict) {
  1483. // check if there is a system id before
  1484. // trying to expand it.
  1485. if (systemId == null) {
  1486. return null;
  1487. }
  1488. try {
  1489. // if it's already an absolute one, return it
  1490. new URI(systemId);
  1491. return systemId;
  1492. }
  1493. catch (URI.MalformedURIException ex) {
  1494. }
  1495. URI base = null;
  1496. // if there isn't a base uri, use the working directory
  1497. if (baseSystemId == null || baseSystemId.length() == 0) {
  1498. base = new URI("file", "", getUserDir(), null, null);
  1499. }
  1500. // otherwise, use the base uri
  1501. else {
  1502. try {
  1503. base = new URI(baseSystemId);
  1504. }
  1505. catch (URI.MalformedURIException e) {
  1506. // assume "base" is also a relative uri
  1507. String dir = getUserDir();
  1508. dir = dir + baseSystemId;
  1509. base = new URI("file", "", dir, null, null);
  1510. }
  1511. }
  1512. // absolutize the system id using the base
  1513. URI uri = new URI(base, systemId);
  1514. // return the string rep of the new uri (an absolute one)
  1515. return uri.toString();
  1516. // if any exception is thrown, it'll get thrown to the caller.
  1517. }
  1518. // check for bad parameters id
  1519. if (systemId == null || systemId.length() == 0) {
  1520. return systemId;
  1521. }
  1522. // if id already expanded, return
  1523. try {
  1524. URI uri = new URI(systemId.trim());
  1525. if (uri != null) {
  1526. return systemId;
  1527. }
  1528. }
  1529. catch (URI.MalformedURIException e) {
  1530. // continue on...
  1531. }
  1532. // normalize id
  1533. String id = fixURI(systemId);
  1534. // normalize base
  1535. URI base = null;
  1536. URI uri = null;
  1537. try {
  1538. if (baseSystemId == null || baseSystemId.length() == 0 ||
  1539. baseSystemId.equals(systemId)) {
  1540. String dir = getUserDir();
  1541. base = new URI("file", "", dir, null, null);
  1542. }
  1543. else {
  1544. try {
  1545. base = new URI(fixURI(baseSystemId).trim());
  1546. }
  1547. catch (URI.MalformedURIException e) {
  1548. if (baseSystemId.indexOf(':') != -1) {
  1549. // for xml schemas we might have baseURI with
  1550. // a specified drive
  1551. base = new URI("file", "", fixURI(baseSystemId).trim(), null, null);
  1552. }
  1553. else {
  1554. String dir = getUserDir();
  1555. dir = dir + fixURI(baseSystemId);
  1556. base = new URI("file", "", dir, null, null);
  1557. }
  1558. }
  1559. }
  1560. // expand id
  1561. uri = new URI(base, id.trim());
  1562. }
  1563. catch (Exception e) {
  1564. // let it go through
  1565. }
  1566. if (uri == null) {
  1567. return systemId;
  1568. }
  1569. return uri.toString();
  1570. } // expandSystemId(String,String):String
  1571. //
  1572. // Protected methods
  1573. //
  1574. /**
  1575. * Ends an entity.
  1576. *
  1577. * @throws XNIException Thrown by entity handler to signal an error.
  1578. */
  1579. void endEntity() throws XNIException {
  1580. // call handler
  1581. if (DEBUG_BUFFER) {
  1582. System.out.print("(endEntity: ");
  1583. print(fCurrentEntity);
  1584. System.out.println();
  1585. }
  1586. if (fEntityHandler != null) {
  1587. fEntityHandler.endEntity(fCurrentEntity.name, null);
  1588. }
  1589. // Close the reader for the current entity once we're
  1590. // done with it, and remove it from our stack. If parsing
  1591. // is halted at some point, the rest of the readers on
  1592. // the stack will be closed during cleanup.
  1593. try {
  1594. fCurrentEntity.reader.close();
  1595. }
  1596. catch (IOException e) {
  1597. // ignore
  1598. }
  1599. // REVISIT: We should never encounter underflow if the calls
  1600. // to startEntity and endEntity are balanced, but guard
  1601. // against the EmptyStackException for now. -- mrglavas
  1602. if(!fReaderStack.isEmpty()) {
  1603. fReaderStack.pop();
  1604. }
  1605. // Pop entity stack.
  1606. fCurrentEntity = fEntityStack.size() > 0
  1607. ? (ScannedEntity)fEntityStack.pop() : null;
  1608. fEntityScanner.setCurrentEntity(fCurrentEntity);
  1609. if (DEBUG_BUFFER) {
  1610. System.out.print(")endEntity: ");
  1611. print(fCurrentEntity);
  1612. System.out.println();
  1613. }
  1614. // as entity is no longer open, decrement open entity expansion count
  1615. } // endEntity()
  1616. /**
  1617. * Returns the IANA encoding name that is auto-detected from
  1618. * the bytes specified, with the endian-ness of that encoding where appropriate.
  1619. *
  1620. * @param b4 The first four bytes of the input.
  1621. * @param count The number of bytes actually read.
  1622. * @return a 2-element array: the first element, an IANA-encoding string,
  1623. * the second element a Boolean which is true iff the document is big endian, false
  1624. * if it's little-endian, and null if the distinction isn't relevant.
  1625. */
  1626. protected Object[] getEncodingName(byte[] b4, int count) {
  1627. if (count < 2) {
  1628. return new Object[]{"UTF-8", null};
  1629. }
  1630. // UTF-16, with BOM
  1631. int b0 = b4[0] & 0xFF;
  1632. int b1 = b4[1] & 0xFF;
  1633. if (b0 == 0xFE && b1 == 0xFF) {
  1634. // UTF-16, big-endian
  1635. return new Object [] {"UTF-16BE", new Boolean(true)};
  1636. }
  1637. if (b0 == 0xFF && b1 == 0xFE) {
  1638. // UTF-16, little-endian
  1639. return new Object [] {"UTF-16LE", new Boolean(false)};
  1640. }
  1641. // default to UTF-8 if we don't have enough bytes to make a
  1642. // good determination of the encoding
  1643. if (count < 3) {
  1644. return new Object [] {"UTF-8", null};
  1645. }
  1646. // UTF-8 with a BOM
  1647. int b2 = b4[2] & 0xFF;
  1648. if (b0 == 0xEF && b1 == 0xBB && b2 == 0xBF) {
  1649. return new Object [] {"UTF-8", null};
  1650. }
  1651. // default to UTF-8 if we don't have enough bytes to make a
  1652. // good determination of the encoding
  1653. if (count < 4) {
  1654. return new Object [] {"UTF-8", null};
  1655. }
  1656. // other encodings
  1657. int b3 = b4[3] & 0xFF;
  1658. if (b0 == 0x00 && b1 == 0x00 && b2 == 0x00 && b3 == 0x3C) {
  1659. // UCS-4, big endian (1234)
  1660. return new Object [] {"ISO-10646-UCS-4", new Boolean(true)};
  1661. }
  1662. if (b0 == 0x3C && b1 == 0x00 && b2 == 0x00 && b3 == 0x00) {
  1663. // UCS-4, little endian (4321)
  1664. return new Object [] {"ISO-10646-UCS-4", new Boolean(false)};
  1665. }
  1666. if (b0 == 0x00 && b1 == 0x00 && b2 == 0x3C && b3 == 0x00) {
  1667. // UCS-4, unusual octet order (2143)
  1668. // REVISIT: What should this be?
  1669. return new Object [] {"ISO-10646-UCS-4", null};
  1670. }
  1671. if (b0 == 0x00 && b1 == 0x3C && b2 == 0x00 && b3 == 0x00) {
  1672. // UCS-4, unusual octect order (3412)
  1673. // REVISIT: What should this be?
  1674. return new Object [] {"ISO-10646-UCS-4", null};
  1675. }
  1676. if (b0 == 0x00 && b1 == 0x3C && b2 == 0x00 && b3 == 0x3F) {
  1677. // UTF-16, big-endian, no BOM
  1678. // (or could turn out to be UCS-2...
  1679. // REVISIT: What should this be?
  1680. return new Object [] {"UTF-16BE", new Boolean(true)};
  1681. }
  1682. if (b0 == 0x3C && b1 == 0x00 && b2 == 0x3F && b3 == 0x00) {
  1683. // UTF-16, little-endian, no BOM
  1684. // (or could turn out to be UCS-2...
  1685. return new Object [] {"UTF-16LE", new Boolean(false)};
  1686. }
  1687. if (b0 == 0x4C && b1 == 0x6F && b2 == 0xA7 && b3 == 0x94) {
  1688. // EBCDIC
  1689. // a la xerces1, return CP037 instead of EBCDIC here
  1690. return new Object [] {"CP037", null};
  1691. }
  1692. // default encoding
  1693. return new Object [] {"UTF-8", null};
  1694. } // getEncodingName(byte[],int):Object[]
  1695. /**
  1696. * Creates a reader capable of reading the given input stream in
  1697. * the specified encoding.
  1698. *
  1699. * @param inputStream The input stream.
  1700. * @param encoding The encoding name that the input stream is
  1701. * encoded using. If the user has specified that
  1702. * Java encoding names are allowed, then the
  1703. * encoding name may be a Java encoding name;
  1704. * otherwise, it is an ianaEncoding name.
  1705. * @param isBigEndian For encodings (like uCS-4), whose names cannot
  1706. * specify a byte order, this tells whether the order is bigEndian. null menas
  1707. * unknown or not relevant.
  1708. *
  1709. * @return Returns a reader.
  1710. */
  1711. protected Reader createReader(InputStream inputStream, String encoding, Boolean isBigEndian)
  1712. throws IOException {
  1713. // normalize encoding name
  1714. if (encoding == null) {
  1715. encoding = "UTF-8";
  1716. }
  1717. // try to use an optimized reader
  1718. String ENCODING = encoding.toUpperCase(Locale.ENGLISH);
  1719. if (ENCODING.equals("UTF-8")) {
  1720. if (DEBUG_ENCODINGS) {
  1721. System.out.println("$$$ creating UTF8Reader");
  1722. }
  1723. return new UTF8Reader(inputStream, fBufferSize, fErrorReporter.getMessageFormatter(XMLMessageFormatter.XML_DOMAIN), fErrorReporter.getLocale() );
  1724. }
  1725. if(ENCODING.equals("ISO-10646-UCS-4")) {
  1726. if(isBigEndian != null) {
  1727. boolean isBE = isBigEndian.booleanValue();
  1728. if(isBE) {
  1729. return new UCSReader(inputStream, UCSReader.UCS4BE);
  1730. } else {
  1731. return new UCSReader(inputStream, UCSReader.UCS4LE);
  1732. }
  1733. } else {
  1734. fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
  1735. "EncodingByteOrderUnsupported",
  1736. new Object[] { encoding },
  1737. XMLErrorReporter.SEVERITY_FATAL_ERROR);
  1738. }
  1739. }
  1740. if(ENCODING.equals("ISO-10646-UCS-2")) {
  1741. if(isBigEndian != null) { // sould never happen with this encoding...
  1742. boolean isBE = isBigEndian.booleanValue();
  1743. if(isBE) {
  1744. return new UCSReader(inputStream, UCSReader.UCS2BE);
  1745. } else {
  1746. return new UCSReader(inputStream, UCSReader.UCS2LE);
  1747. }
  1748. } else {
  1749. fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
  1750. "EncodingByteOrderUnsupported",
  1751. new Object[] { encoding },
  1752. XMLErrorReporter.SEVERITY_FATAL_ERROR);
  1753. }
  1754. }
  1755. // check for valid name
  1756. boolean validIANA = XMLChar.isValidIANAEncoding(encoding);
  1757. boolean validJava = XMLChar.isValidJavaEncoding(encoding);
  1758. if (!validIANA || (fAllowJavaEncodings && !validJava)) {
  1759. fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
  1760. "EncodingDeclInvalid",
  1761. new Object[] { encoding },
  1762. XMLErrorReporter.SEVERITY_FATAL_ERROR);
  1763. // NOTE: AndyH suggested that, on failure, we use ISO Latin 1
  1764. // because every byte is a valid ISO Latin 1 character.
  1765. // It may not translate correctly but if we failed on
  1766. // the encoding anyway, then we're expecting the content
  1767. // of the document to be bad. This will just prevent an
  1768. // invalid UTF-8 sequence to be detected. This is only
  1769. // important when continue-after-fatal-error is turned
  1770. // on. -Ac
  1771. encoding = "ISO-8859-1";
  1772. }
  1773. // try to use a Java reader
  1774. String javaEncoding = EncodingMap.getIANA2JavaMapping(ENCODING);
  1775. if (javaEncoding == null) {
  1776. if(fAllowJavaEncodings) {
  1777. javaEncoding = encoding;
  1778. } else {
  1779. fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
  1780. "EncodingDeclInvalid",
  1781. new Object[] { encoding },
  1782. XMLErrorReporter.SEVERITY_FATAL_ERROR);
  1783. // see comment above.
  1784. javaEncoding = "ISO8859_1";
  1785. }
  1786. }
  1787. else if (javaEncoding.equals("ASCII")) {
  1788. if (DEBUG_ENCODINGS) {
  1789. System.out.println("$$$ creating ASCIIReader");
  1790. }
  1791. return new ASCIIReader(inputStream, fBufferSize, fErrorReporter.getMessageFormatter(XMLMessageFormatter.XML_DOMAIN), fErrorReporter.getLocale());
  1792. }
  1793. if (DEBUG_ENCODINGS) {
  1794. System.out.print("$$$ creating Java InputStreamReader: encoding="+javaEncoding);
  1795. if (javaEncoding == encoding) {
  1796. System.out.print(" (IANA encoding)");
  1797. }
  1798. System.out.println();
  1799. }
  1800. return new InputStreamReader(inputStream, javaEncoding);
  1801. } // createReader(InputStream,String, Boolean): Reader
  1802. //
  1803. // Protected static methods
  1804. //
  1805. /**
  1806. * Fixes a platform dependent filename to standard URI form.
  1807. *
  1808. * @param str The string to fix.
  1809. *
  1810. * @return Returns the fixed URI string.
  1811. */
  1812. protected static String fixURI(String str) {
  1813. // handle platform dependent strings
  1814. str = str.replace(java.io.File.separatorChar, '/');
  1815. StringBuffer sb = null;
  1816. // Windows fix
  1817. if (str.length() >= 2) {
  1818. char ch1 = str.charAt(1);
  1819. // change "C:blah" to "/C:blah"
  1820. if (ch1 == ':') {
  1821. char ch0 = Character.toUpperCase(str.charAt(0));
  1822. if (ch0 >= 'A' && ch0 <= 'Z') {
  1823. sb = new StringBuffer(str.length());
  1824. sb.append('/');
  1825. }
  1826. }
  1827. // change "//blah" to "file://blah"
  1828. else if (ch1 == '/' && str.charAt(0) == '/') {
  1829. sb = new StringBuffer(str.length());
  1830. sb.append("file:");
  1831. }
  1832. }
  1833. int pos = str.indexOf(' ');
  1834. // there is no space in the string
  1835. // we just append "str" to the end of sb
  1836. if (pos < 0) {
  1837. if (sb != null) {
  1838. sb.append(str);
  1839. str = sb.toString();
  1840. }
  1841. }
  1842. // otherwise, convert all ' ' to "%20".
  1843. // Note: the following algorithm might not be very performant,
  1844. // but people who want to use invalid URI's have to pay the price.
  1845. else {
  1846. if (sb == null)
  1847. sb = new StringBuffer(str.length());
  1848. // put characters before ' ' into the string buffer
  1849. for (int i = 0; i < pos; i++)
  1850. sb.append(str.charAt(i));
  1851. // and %20 for the space
  1852. sb.append("%20");
  1853. // for the remamining part, also convert ' ' to "%20".
  1854. for (int i = pos+1; i < str.length(); i++) {
  1855. if (str.charAt(i) == ' ')
  1856. sb.append("%20");
  1857. else
  1858. sb.append(str.charAt(i));
  1859. }
  1860. str = sb.toString();
  1861. }
  1862. // done
  1863. return str;
  1864. } // fixURI(String):String
  1865. //
  1866. // Package visible methods
  1867. //
  1868. /**
  1869. * Returns the hashtable of declared entities.
  1870. * <p>
  1871. * <strong>REVISIT:</strong>
  1872. * This should be done the "right" way by designing a better way to
  1873. * enumerate the declared entities. For now, this method is needed
  1874. * by the constructor that takes an XMLEntityManager parameter.
  1875. */
  1876. Hashtable getDeclaredEntities() {
  1877. return fEntities;
  1878. } // getDeclaredEntities():Hashtable
  1879. /** Prints the contents of the buffer. */
  1880. static final void print(ScannedEntity currentEntity) {
  1881. if (DEBUG_BUFFER) {
  1882. if (currentEntity != null) {
  1883. System.out.print('[');
  1884. System.out.print(currentEntity.count);
  1885. System.out.print(' ');
  1886. System.out.print(currentEntity.position);
  1887. if (currentEntity.count > 0) {
  1888. System.out.print(" \"");
  1889. for (int i = 0; i < currentEntity.count; i++) {
  1890. if (i == currentEntity.position) {
  1891. System.out.print('^');
  1892. }
  1893. char c = currentEntity.ch[i];
  1894. switch (c) {
  1895. case '\n': {
  1896. System.out.print("\\n");
  1897. break;
  1898. }
  1899. case '\r': {
  1900. System.out.print("\\r");
  1901. break;
  1902. }
  1903. case '\t': {
  1904. System.out.print("\\t");
  1905. break;
  1906. }
  1907. case '\\': {
  1908. System.out.print("\\\\");
  1909. break;
  1910. }
  1911. default: {
  1912. System.out.print(c);
  1913. }
  1914. }
  1915. }
  1916. if (currentEntity.position == currentEntity.count) {
  1917. System.out.print('^');
  1918. }
  1919. System.out.print('"');
  1920. }
  1921. System.out.print(']');
  1922. System.out.print(" @ ");
  1923. System.out.print(currentEntity.lineNumber);
  1924. System.out.print(',');
  1925. System.out.print(currentEntity.columnNumber);
  1926. }
  1927. else {
  1928. System.out.print("*NO CURRENT ENTITY*");
  1929. }
  1930. }
  1931. } // print(ScannedEntity)
  1932. //
  1933. // Classes
  1934. //
  1935. /**
  1936. * Entity information.
  1937. *
  1938. * @author Andy Clark, IBM
  1939. */
  1940. public static abstract class Entity {
  1941. //
  1942. // Data
  1943. //
  1944. /** Entity name. */
  1945. public String name;
  1946. // whether this entity's declaration was found in the internal
  1947. // or external subset
  1948. public boolean inExternalSubset;
  1949. //
  1950. // Constructors
  1951. //
  1952. /** Default constructor. */
  1953. public Entity() {
  1954. clear();
  1955. } // <init>()
  1956. /** Constructs an entity. */
  1957. public Entity(String name, boolean inExternalSubset) {
  1958. this.name = name;
  1959. this.inExternalSubset = inExternalSubset;
  1960. } // <init>(String)
  1961. //
  1962. // Public methods
  1963. //
  1964. /** Returns true if this entity was declared in the external subset. */
  1965. public boolean isEntityDeclInExternalSubset () {
  1966. return inExternalSubset;
  1967. }
  1968. /** Returns true if this is an external entity. */
  1969. public abstract boolean isExternal();
  1970. /** Returns true if this is an unparsed entity. */
  1971. public abstract boolean isUnparsed();
  1972. /** Clears the entity. */
  1973. public void clear() {
  1974. name = null;
  1975. inExternalSubset = false;
  1976. } // clear()
  1977. /** Sets the values of the entity. */
  1978. public void setValues(Entity entity) {
  1979. name = entity.name;
  1980. inExternalSubset = entity.inExternalSubset;
  1981. } // setValues(Entity)
  1982. } // class Entity
  1983. /**
  1984. * Internal entity.
  1985. *
  1986. * @author Andy Clark, IBM
  1987. */
  1988. protected static class InternalEntity
  1989. extends Entity {
  1990. //
  1991. // Data
  1992. //
  1993. /** Text value of entity. */
  1994. public String text;
  1995. //
  1996. // Constructors
  1997. //
  1998. /** Default constructor. */
  1999. public InternalEntity() {
  2000. clear();
  2001. } // <init>()
  2002. /** Constructs an internal entity. */
  2003. public InternalEntity(String name, String text, boolean inExternalSubset) {
  2004. super(name,inExternalSubset);
  2005. this.text = text;
  2006. } // <init>(String,String)
  2007. //
  2008. // Entity methods
  2009. //
  2010. /** Returns true if this is an external entity. */
  2011. public final boolean isExternal() {
  2012. return false;
  2013. } // isExternal():boolean
  2014. /** Returns true if this is an unparsed entity. */
  2015. public final boolean isUnparsed() {
  2016. return false;
  2017. } // isUnparsed():boolean
  2018. /** Clears the entity. */
  2019. public void clear() {
  2020. super.clear();
  2021. text = null;
  2022. } // clear()
  2023. /** Sets the values of the entity. */
  2024. public void setValues(Entity entity) {
  2025. super.setValues(entity);
  2026. text = null;
  2027. } // setValues(Entity)
  2028. /** Sets the values of the entity. */
  2029. public void setValues(InternalEntity entity) {
  2030. super.setValues(entity);
  2031. text = entity.text;
  2032. } // setValues(InternalEntity)
  2033. } // class InternalEntity
  2034. /**
  2035. * External entity.
  2036. *
  2037. * @author Andy Clark, IBM
  2038. */
  2039. protected static class ExternalEntity
  2040. extends Entity {
  2041. //
  2042. // Data
  2043. //
  2044. /** container for all relevant entity location information. */
  2045. public XMLResourceIdentifier entityLocation;
  2046. /** Notation name for unparsed entity. */
  2047. public String notation;
  2048. //
  2049. // Constructors
  2050. //
  2051. /** Default constructor. */
  2052. public ExternalEntity() {
  2053. clear();
  2054. } // <init>()
  2055. /** Constructs an internal entity. */
  2056. public ExternalEntity(String name, XMLResourceIdentifier entityLocation,
  2057. String notation, boolean inExternalSubset) {
  2058. super(name,inExternalSubset);
  2059. this.entityLocation = entityLocation;
  2060. this.notation = notation;
  2061. } // <init>(String,XMLResourceIdentifier, String)
  2062. //
  2063. // Entity methods
  2064. //
  2065. /** Returns true if this is an external entity. */
  2066. public final boolean isExternal() {
  2067. return true;
  2068. } // isExternal():boolean
  2069. /** Returns true if this is an unparsed entity. */
  2070. public final boolean isUnparsed() {
  2071. return notation != null;
  2072. } // isUnparsed():boolean
  2073. /** Clears the entity. */
  2074. public void clear() {
  2075. super.clear();
  2076. entityLocation = null;
  2077. notation = null;
  2078. } // clear()
  2079. /** Sets the values of the entity. */
  2080. public void setValues(Entity entity) {
  2081. super.setValues(entity);
  2082. entityLocation = null;
  2083. notation = null;
  2084. } // setValues(Entity)
  2085. /** Sets the values of the entity. */
  2086. public void setValues(ExternalEntity entity) {
  2087. super.setValues(entity);
  2088. entityLocation = entity.entityLocation;
  2089. notation = entity.notation;
  2090. } // setValues(ExternalEntity)
  2091. } // class ExternalEntity
  2092. /**
  2093. * Entity state.
  2094. *
  2095. * @author Andy Clark, IBM
  2096. */
  2097. public class ScannedEntity
  2098. extends Entity {
  2099. //
  2100. // Data
  2101. //
  2102. // i/o
  2103. /** Input stream. */
  2104. public InputStream stream;
  2105. /** Reader. */
  2106. public Reader reader;
  2107. // locator information
  2108. /** entity location information */
  2109. public XMLResourceIdentifier entityLocation;
  2110. /** Line number. */
  2111. public int lineNumber = 1;
  2112. /** Column number. */
  2113. public int columnNumber = 1;
  2114. // encoding
  2115. /** Auto-detected encoding. */
  2116. public String encoding;
  2117. /** Encoding has been set externally for eg: using DOMInput*/
  2118. boolean declaredEncoding = false;
  2119. // status
  2120. /** True if in a literal. */
  2121. public boolean literal;
  2122. // whether this is an external or internal scanned entity
  2123. public boolean isExternal;
  2124. // buffer
  2125. /** Character buffer. */
  2126. public char[] ch = null;
  2127. /** Position in character buffer. */
  2128. public int position;
  2129. /** Count of characters in buffer. */
  2130. public int count;
  2131. // to allow the reader/inputStream to behave efficiently:
  2132. public boolean mayReadChunks;
  2133. //
  2134. // Constructors
  2135. //
  2136. /** Constructs a scanned entity. */
  2137. public ScannedEntity(String name,
  2138. XMLResourceIdentifier entityLocation,
  2139. InputStream stream, Reader reader,
  2140. String encoding, boolean literal, boolean mayReadChunks, boolean isExternal) {
  2141. super(name,XMLEntityManager.this.fInExternalSubset);
  2142. this.entityLocation = entityLocation;
  2143. this.stream = stream;
  2144. this.reader = reader;
  2145. this.encoding = encoding;
  2146. this.literal = literal;
  2147. this.mayReadChunks = mayReadChunks;
  2148. this.isExternal = isExternal;
  2149. this.ch = new char[isExternal ? fBufferSize : DEFAULT_INTERNAL_BUFFER_SIZE];
  2150. } // <init>(StringXMLResourceIdentifier,InputStream,Reader,String,boolean, boolean)
  2151. //
  2152. // Entity methods
  2153. //
  2154. /** Returns true if this is an external entity. */
  2155. public final boolean isExternal() {
  2156. return isExternal;
  2157. } // isExternal():boolean
  2158. /** Returns true if this is an unparsed entity. */
  2159. public final boolean isUnparsed() {
  2160. return false;
  2161. } // isUnparsed():boolean
  2162. public void setReader(InputStream stream, String encoding, Boolean isBigEndian) throws IOException {
  2163. reader = createReader(stream, encoding, isBigEndian);
  2164. }
  2165. // return the expanded system ID of the
  2166. // first external entity on the stack, null
  2167. // otherwise.
  2168. public String getExpandedSystemId() {
  2169. // search for the first external entity on the stack
  2170. int size = fEntityStack.size();
  2171. for (int i = size - 1; i >= 0 ; i--) {
  2172. ScannedEntity externalEntity =
  2173. (ScannedEntity)fEntityStack.elementAt(i);
  2174. if (externalEntity.entityLocation != null &&
  2175. externalEntity.entityLocation.getExpandedSystemId() != null) {
  2176. return externalEntity.entityLocation.getExpandedSystemId();
  2177. }
  2178. }
  2179. return null;
  2180. }
  2181. // return literal systemId of
  2182. // nearest external entity
  2183. public String getLiteralSystemId() {
  2184. // search for the first external entity on the stack
  2185. int size = fEntityStack.size();
  2186. for (int i = size - 1; i >= 0 ; i--) {
  2187. ScannedEntity externalEntity =
  2188. (ScannedEntity)fEntityStack.elementAt(i);
  2189. if (externalEntity.entityLocation != null &&
  2190. externalEntity.entityLocation.getLiteralSystemId() != null) {
  2191. return externalEntity.entityLocation.getLiteralSystemId();
  2192. }
  2193. }
  2194. return null;
  2195. }
  2196. // return line number of position in most
  2197. // recent external entity
  2198. public int getLineNumber() {
  2199. // search for the first external entity on the stack
  2200. int size = fEntityStack.size();
  2201. for (int i=size-1; i>0 ; i--) {
  2202. ScannedEntity firstExternalEntity = (ScannedEntity)fEntityStack.elementAt(i);
  2203. if (firstExternalEntity.isExternal()) {
  2204. return firstExternalEntity.lineNumber;
  2205. }
  2206. }
  2207. return -1;
  2208. }
  2209. // return column number of position in most
  2210. // recent external entity
  2211. public int getColumnNumber() {
  2212. // search for the first external entity on the stack
  2213. int size = fEntityStack.size();
  2214. for (int i=size-1; i>0 ; i--) {
  2215. ScannedEntity firstExternalEntity = (ScannedEntity)fEntityStack.elementAt(i);
  2216. if (firstExternalEntity.isExternal()) {
  2217. return firstExternalEntity.columnNumber;
  2218. }
  2219. }
  2220. return -1;
  2221. }
  2222. // return encoding of most recent external entity
  2223. public String getEncoding() {
  2224. // search for the first external entity on the stack
  2225. int size = fEntityStack.size();
  2226. for (int i=size-1; i>0 ; i--) {
  2227. ScannedEntity firstExternalEntity = (ScannedEntity)fEntityStack.elementAt(i);
  2228. if (firstExternalEntity.isExternal()) {
  2229. return firstExternalEntity.encoding;
  2230. }
  2231. }
  2232. return null;
  2233. }
  2234. //
  2235. // Object methods
  2236. //
  2237. /** Returns a string representation of this object. */
  2238. public String toString() {
  2239. StringBuffer str = new StringBuffer();
  2240. str.append("name=\""+name+'"');
  2241. str.append(",ch=");
  2242. str.append(ch);
  2243. str.append(",position="+position);
  2244. str.append(",count="+count);
  2245. return str.toString();
  2246. } // toString():String
  2247. public boolean isDeclaredEncoding() {
  2248. return declaredEncoding;
  2249. }
  2250. public void setDeclaredEncoding(boolean value) {
  2251. declaredEncoding = value;
  2252. }
  2253. } // class ScannedEntity
  2254. // This class wraps the byte inputstreams we're presented with.
  2255. // We need it because java.io.InputStreams don't provide
  2256. // functionality to reread processed bytes, and they have a habit
  2257. // of reading more than one character when you call their read()
  2258. // methods. This means that, once we discover the true (declared)
  2259. // encoding of a document, we can neither backtrack to read the
  2260. // whole doc again nor start reading where we are with a new
  2261. // reader.
  2262. //
  2263. // This class allows rewinding an inputStream by allowing a mark
  2264. // to be set, and the stream reset to that position. <strong>The
  2265. // class assumes that it needs to read one character per
  2266. // invocation when it's read() method is inovked, but uses the
  2267. // underlying InputStream's read(char[], offset length) method--it
  2268. // won't buffer data read this way!</strong>
  2269. //
  2270. // @author Neil Graham, IBM
  2271. // @author Glenn Marcy, IBM
  2272. protected final class RewindableInputStream extends InputStream {
  2273. private InputStream fInputStream;
  2274. private byte[] fData;
  2275. private int fStartOffset;
  2276. private int fEndOffset;
  2277. private int fOffset;
  2278. private int fLength;
  2279. private int fMark;
  2280. public RewindableInputStream(InputStream is) {
  2281. fData = new byte[DEFAULT_XMLDECL_BUFFER_SIZE];
  2282. fInputStream = is;
  2283. fStartOffset = 0;
  2284. fEndOffset = -1;
  2285. fOffset = 0;
  2286. fLength = 0;
  2287. fMark = 0;
  2288. }
  2289. public void setStartOffset(int offset) {
  2290. fStartOffset = offset;
  2291. }
  2292. public void rewind() {
  2293. fOffset = fStartOffset;
  2294. }
  2295. public int read() throws IOException {
  2296. int b = 0;
  2297. if (fOffset < fLength) {
  2298. return fData[fOffset++] & 0xff;
  2299. }
  2300. if (fOffset == fEndOffset) {
  2301. return -1;
  2302. }
  2303. if (fOffset == fData.length) {
  2304. byte[] newData = new byte[fOffset << 1];
  2305. System.arraycopy(fData, 0, newData, 0, fOffset);
  2306. fData = newData;
  2307. }
  2308. b = fInputStream.read();
  2309. if (b == -1) {
  2310. fEndOffset = fOffset;
  2311. return -1;
  2312. }
  2313. fData[fLength++] = (byte)b;
  2314. fOffset++;
  2315. return b & 0xff;
  2316. }
  2317. public int read(byte[] b, int off, int len) throws IOException {
  2318. int bytesLeft = fLength - fOffset;
  2319. if (bytesLeft == 0) {
  2320. if (fOffset == fEndOffset) {
  2321. return -1;
  2322. }
  2323. // better get some more for the voracious reader...
  2324. if(fCurrentEntity.mayReadChunks) {
  2325. return fInputStream.read(b, off, len);
  2326. }
  2327. int returnedVal = read();
  2328. if(returnedVal == -1) {
  2329. fEndOffset = fOffset;
  2330. return -1;
  2331. }
  2332. b[off] = (byte)returnedVal;
  2333. return 1;
  2334. }
  2335. if (len < bytesLeft) {
  2336. if (len <= 0) {
  2337. return 0;
  2338. }
  2339. }
  2340. else {
  2341. len = bytesLeft;
  2342. }
  2343. if (b != null) {
  2344. System.arraycopy(fData, fOffset, b, off, len);
  2345. }
  2346. fOffset += len;
  2347. return len;
  2348. }
  2349. public long skip(long n)
  2350. throws IOException
  2351. {
  2352. int bytesLeft;
  2353. if (n <= 0) {
  2354. return 0;
  2355. }
  2356. bytesLeft = fLength - fOffset;
  2357. if (bytesLeft == 0) {
  2358. if (fOffset == fEndOffset) {
  2359. return 0;
  2360. }
  2361. return fInputStream.skip(n);
  2362. }
  2363. if (n <= bytesLeft) {
  2364. fOffset += n;
  2365. return n;
  2366. }
  2367. fOffset += bytesLeft;
  2368. if (fOffset == fEndOffset) {
  2369. return bytesLeft;
  2370. }
  2371. n -= bytesLeft;
  2372. /*
  2373. * In a manner of speaking, when this class isn't permitting more
  2374. * than one byte at a time to be read, it is "blocking". The
  2375. * available() method should indicate how much can be read without
  2376. * blocking, so while we're in this mode, it should only indicate
  2377. * that bytes in its buffer are available; otherwise, the result of
  2378. * available() on the underlying InputStream is appropriate.
  2379. */
  2380. return fInputStream.skip(n) + bytesLeft;
  2381. }
  2382. public int available() throws IOException {
  2383. int bytesLeft = fLength - fOffset;
  2384. if (bytesLeft == 0) {
  2385. if (fOffset == fEndOffset) {
  2386. return -1;
  2387. }
  2388. return fCurrentEntity.mayReadChunks ? fInputStream.available()
  2389. : 0;
  2390. }
  2391. return bytesLeft;
  2392. }
  2393. public void mark(int howMuch) {
  2394. fMark = fOffset;
  2395. }
  2396. public void reset() {
  2397. fOffset = fMark;
  2398. }
  2399. public boolean markSupported() {
  2400. return true;
  2401. }
  2402. public void close() throws IOException {
  2403. if (fInputStream != null) {
  2404. fInputStream.close();
  2405. fInputStream = null;
  2406. }
  2407. }
  2408. } // end of RewindableInputStream class
  2409. } // class XMLEntityManager