1. /*
  2. * $Id: Parser2.java,v 1.16 2001/09/29 04:17:47 edwingo Exp $
  3. *
  4. * The Apache Software License, Version 1.1
  5. *
  6. *
  7. * Copyright (c) 2000 The Apache Software Foundation. All rights
  8. * reserved.
  9. *
  10. * Redistribution and use in source and binary forms, with or without
  11. * modification, are permitted provided that the following conditions
  12. * are met:
  13. *
  14. * 1. Redistributions of source code must retain the above copyright
  15. * notice, this list of conditions and the following disclaimer.
  16. *
  17. * 2. Redistributions in binary form must reproduce the above copyright
  18. * notice, this list of conditions and the following disclaimer in
  19. * the documentation and/or other materials provided with the
  20. * distribution.
  21. *
  22. * 3. The end-user documentation included with the redistribution,
  23. * if any, must include the following acknowledgment:
  24. * "This product includes software developed by the
  25. * Apache Software Foundation (http://www.apache.org/)."
  26. * Alternately, this acknowledgment may appear in the software itself,
  27. * if and wherever such third-party acknowledgments normally appear.
  28. *
  29. * 4. The names "Crimson" and "Apache Software Foundation" must
  30. * not be used to endorse or promote products derived from this
  31. * software without prior written permission. For written
  32. * permission, please contact apache@apache.org.
  33. *
  34. * 5. Products derived from this software may not be called "Apache",
  35. * nor may "Apache" appear in their name, without prior written
  36. * permission of the Apache Software Foundation.
  37. *
  38. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  39. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  40. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  41. * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
  42. * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  43. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  44. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  45. * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  46. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  47. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  48. * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  49. * SUCH DAMAGE.
  50. * ====================================================================
  51. *
  52. * This software consists of voluntary contributions made by many
  53. * individuals on behalf of the Apache Software Foundation and was
  54. * originally based on software copyright (c) 1999, Sun Microsystems, Inc.,
  55. * http://www.sun.com. For more information on the Apache Software
  56. * Foundation, please see <http://www.apache.org/>.
  57. */
  58. package org.apache.crimson.parser;
  59. import java.io.InputStream;
  60. import java.io.File;
  61. import java.io.FileInputStream;
  62. import java.io.IOException;
  63. import java.io.Reader;
  64. import java.io.FileNotFoundException;
  65. import java.util.Enumeration;
  66. import java.util.Hashtable;
  67. import java.util.Locale;
  68. import java.util.Vector;
  69. import java.util.Properties;
  70. import java.security.AccessController;
  71. import java.security.PrivilegedAction;
  72. import java.security.PrivilegedExceptionAction;
  73. import org.xml.sax.*;
  74. import org.xml.sax.helpers.*;
  75. import org.xml.sax.ext.*;
  76. import org.apache.crimson.util.MessageCatalog;
  77. import org.apache.crimson.util.XmlChars;
  78. import org.apache.crimson.util.XmlNames;
  79. //
  80. // NOTE: when maintaining this code, take care to keep the message
  81. // catalogue(s) up to date!! It's important that the diagnostics
  82. // be informative.
  83. //
  84. /**
  85. * This implements a fast non-validating SAX2 parser. This one always
  86. * processes external parsed entities, strictly adheres to the XML 1.0
  87. * specification, and provides useful diagnostics. It supports an
  88. * optimization allowing faster processing of valid standalone XML
  89. * documents. For multi-language applications (such as web servers using
  90. * XML processing to create dynamic content), a method supports choosing a
  91. * locale for parser diagnostics which is both understood by the message
  92. * recipient and supported by the parser.
  93. *
  94. * <P> This conforms to the XML 1.0 specification. To configure an XML
  95. * processor which tests document conformance against XML Namespaces,
  96. * provide a <em>DtdEventListener</em> which examines declarations of
  97. * entities and notations, and have your document listener check other
  98. * constraints such as ensuring <em>xmlns*</em> attribute values properly
  99. * declare all namespace prefixes. (Only element and attribute names may
  100. * contain colons, and even then the name prefix before the colon must be
  101. * properly declared.)
  102. *
  103. * <P> SAX parsers produce a stream of parse events, which applications
  104. * process to create an object model which is specific to their tasks.
  105. * Applications which do not want to process event streams in that way
  106. * should use an API producing a standardized object model, such as the
  107. * W3C's <em>Document Object Model</em> (DOM). This parser supports
  108. * building fully conformant DOM <em>Document</em> objects, through
  109. * use of DtdEventListener extensions to SAX in conjunction with an
  110. * appropriate implementation of a SAX <em>DocumentHandler</em>. In
  111. * addition, it supports some features (exposing comments, CDATA sections,
  112. * and entity references) which are allowed by DOM but not required to
  113. * be reported by conformant XML processors. (As usual, the default
  114. * handler for parsing events other than fatal errors ignores them.)
  115. *
  116. * @see ValidatingParser
  117. *
  118. * @author David Brownell
  119. * @author Rajiv Mordani
  120. * @author Edwin Goei
  121. * @version $Revision: 1.16 $
  122. */
  123. public class Parser2
  124. {
  125. // stack of input entities being merged
  126. private InputEntity in;
  127. // temporaries reused during parsing
  128. private AttributesExImpl attTmp;
  129. private StringBuffer strTmp;
  130. private char nameTmp [];
  131. private NameCache nameCache;
  132. private char charTmp [] = new char [2];
  133. private String[] namePartsTmp = new String[3];
  134. // temporaries local to namespace attribute processing in elements
  135. private boolean seenNSDecl;
  136. private NamespaceSupport nsSupport;
  137. /**
  138. * nsAttTmp holds a list of namespace attributes used to check for
  139. * #REQUIRED when validating and (namespaces == true && prefixes ==
  140. * false)
  141. */
  142. private Vector nsAttTmp;
  143. // NOTE: odd heap behavior, at least with classic VM: if "strTmp" is
  144. // reused, LOTS of extra memory is consumed in some simple situations.
  145. // JVM bug filed; it's no longer a win to reuse it as much, in any case.
  146. // parsing modes
  147. private boolean isValidating = false;
  148. private boolean fastStandalone = false;
  149. private boolean isInAttribute = false;
  150. private boolean namespaces; // new in SAX2
  151. private boolean prefixes; // new in SAX2
  152. // temporary DTD parsing state
  153. private boolean inExternalPE;
  154. private boolean doLexicalPE;
  155. private boolean donePrologue;
  156. // info about the document
  157. private boolean isStandalone;
  158. private String rootElementName;
  159. // DTD state, used during parsing
  160. private boolean ignoreDeclarations;
  161. private SimpleHashtable elements = new SimpleHashtable (47);
  162. private SimpleHashtable params = new SimpleHashtable (7);
  163. // exposed to package-private subclass
  164. Hashtable notations = new Hashtable (7);
  165. SimpleHashtable entities = new SimpleHashtable (17);
  166. // stuff associated with SAX
  167. private ContentHandler contentHandler;
  168. private DTDHandler dtdHandler;
  169. private EntityResolver resolver;
  170. private ErrorHandler errHandler;
  171. private Locale locale;
  172. private Locator locator;
  173. // SAX2 extension API support
  174. private DeclHandler declHandler;
  175. private LexicalHandler lexicalHandler;
  176. private boolean disallowDoctypeDecl = false ;
  177. private String propertyEntityExpansionLimit = null;
  178. private String propertyDisallowDoctypeDecl = null ;
  179. private String propertyElementAttributeLimit = null;
  180. //restricting entity expansions
  181. //set this value to zero, initially no entity is expanded
  182. private int entityExpansionCount = 0 ;
  183. //this can be set to any arbitrary value, it would be reset by the value obtained from system property.
  184. private int entityExpansionLimit = -1 ;
  185. private int elementAttributeLimit = -1;
  186. private static final int DEFAULT_ENTITY_EXPANSION_LIMIT = 64000 ;
  187. private static final int DEFAULT_ELEMENT_ATTRIBUTE_LIMIT = 10000;
  188. // Compile time option: disable validation support for a better
  189. // fit in memory-critical environments (P-Java etc). Doing that
  190. // and removing the validating parser support saves (at this time)
  191. // about 15% in size.
  192. private static final boolean supportValidation = true;
  193. // string constants -- use these copies so "==" works
  194. // package private
  195. static final String strANY = "ANY";
  196. static final String strEMPTY = "EMPTY";
  197. // system properties
  198. static final String SYSTEM_PROPERTY_ENTITY_EXPANSION_LIMIT = "entityExpansionLimit" ;
  199. static final String SYSTEM_PROPERTY_DISALLOW_DOCTYPE_DECL = "disallowDoctypeDecl" ;
  200. static final String SYSTEM_PROPERTY_ELEMENT_ATTRIBUTE_LIMIT = "elementAttributeLimit";
  201. static final boolean SECURITY_DEBUG = false ;
  202. ////////////////////////////////////////////////////////////////
  203. //
  204. // PARSER methods
  205. //
  206. ////////////////////////////////////////////////////////////////
  207. /**
  208. * Construct a SAX2 parser object
  209. */
  210. public Parser2 ()
  211. {
  212. locator = new DocLocator ();
  213. setHandlers ();
  214. //setSecuirtyConstraintValues()
  215. setSecurityConstraintValues() ;
  216. }
  217. /**
  218. * Set up the namespace related features for this parser. SAX2 specifies
  219. * these are read-only during a parse, read-write otherwise.
  220. */
  221. void setNamespaceFeatures(boolean namespaces, boolean prefixes) {
  222. this.namespaces = namespaces;
  223. this.prefixes = prefixes;
  224. }
  225. void setEntityResolver(EntityResolver resolver) {
  226. this.resolver = resolver;
  227. }
  228. public void setDTDHandler(DTDHandler handler) {
  229. dtdHandler = handler;
  230. }
  231. void setContentHandler(ContentHandler handler) {
  232. contentHandler = handler;
  233. }
  234. void setErrorHandler (ErrorHandler handler) {
  235. errHandler = handler;
  236. }
  237. void setLexicalHandler (LexicalHandler handler) {
  238. lexicalHandler = handler;
  239. }
  240. void setDeclHandler (DeclHandler handler) {
  241. declHandler = handler;
  242. }
  243. // XXX Maybe we can remove some of these old locale methods
  244. /**
  245. * <b>SAX:</b> Used by applications to request locale for diagnostics.
  246. *
  247. * @param l The locale to use, or null to use system defaults
  248. * (which may include only message IDs).
  249. * @throws SAXException If no diagnostic messages are available
  250. * in that locale.
  251. */
  252. public void setLocale (Locale l)
  253. throws SAXException
  254. {
  255. if (l != null && !messages.isLocaleSupported (l.toString ()))
  256. throw new SAXException (messages.getMessage (locale,
  257. "P-078", new Object [] { l }));
  258. locale = l;
  259. }
  260. /** Returns the diagnostic locale. */
  261. public Locale getLocale ()
  262. { return locale; }
  263. /**
  264. * Chooses a client locale to use for diagnostics, using the first
  265. * language specified in the list that is supported by this parser.
  266. * That locale is then set using <a href="#setLocale(java.util.Locale)">
  267. * setLocale()</a>. Such a list could be provided by a variety of user
  268. * preference mechanisms, including the HTTP <em>Accept-Language</em>
  269. * header field.
  270. *
  271. * @see org.apache.crimson.util.MessageCatalog
  272. *
  273. * @param languages Array of language specifiers, ordered with the most
  274. * preferable one at the front. For example, "en-ca" then "fr-ca",
  275. * followed by "zh_CN". Both RFC 1766 and Java styles are supported.
  276. * @return The chosen locale, or null.
  277. */
  278. public Locale chooseLocale (String languages [])
  279. throws SAXException
  280. {
  281. Locale l = messages.chooseLocale (languages);
  282. if (l != null)
  283. setLocale (l);
  284. return l;
  285. }
  286. /** <b>SAX:</b> Parse a document. */
  287. public void parse (InputSource in)
  288. throws SAXException, IOException
  289. {
  290. init ();
  291. parseInternal (in);
  292. }
  293. /**
  294. * Setting this flag enables faster processing of valid standalone
  295. * documents: external DTD information is not processed, and no
  296. * attribute normalization or defaulting is done. This optimization
  297. * is only permitted in non-validating parsers; for validating
  298. * parsers, this mode is silently disabled.
  299. *
  300. * <P> For documents which are declared as standalone, but which are
  301. * not valid, a fatal error may be reported for references to externally
  302. * defined entities. That could happen in any nonvalidating parser which
  303. * did not read externally defined entities. Also, if any attribute
  304. * values need normalization or defaulting, it will not be done.
  305. */
  306. public void setFastStandalone (boolean value)
  307. { fastStandalone = value && !isValidating; }
  308. /**
  309. * Returns true if standalone documents skip processing of
  310. * all external DTD information.
  311. */
  312. public boolean isFastStandalone ()
  313. { return fastStandalone; }
  314. /**
  315. * In support of the HTML DOM model of client side
  316. * <em><xhtml:script></em> tag processing, this method permits
  317. * data to be spliced into the input stream. This method would
  318. * normally be called from an <em>endElement</em> callback to put the
  319. * buffered result of calls such as DOM <em>HTMLDocument.write</em>
  320. * into the input stream.
  321. */
  322. public void pushInputBuffer (char buf [], int offset, int len)
  323. throws SAXException
  324. {
  325. if (len <= 0)
  326. return;
  327. // arraycopy is inelegant, but that's the worst penalty for now
  328. if (offset != 0 || len != buf.length) {
  329. char tmp [] = new char [len];
  330. System.arraycopy (buf, offset, tmp, 0, len);
  331. buf = tmp;
  332. }
  333. pushReader (buf, null, false);
  334. }
  335. // package private
  336. void setIsValidating (boolean value)
  337. {
  338. if (supportValidation)
  339. isValidating = value;
  340. else
  341. throw new RuntimeException (messages.getMessage (locale, "V-000"));
  342. if (value)
  343. fastStandalone = false;
  344. }
  345. // makes sure the parser's reset to "before a document"
  346. private void init ()
  347. {
  348. in = null;
  349. // alloc temporary data used in parsing
  350. attTmp = new AttributesExImpl ();
  351. strTmp = new StringBuffer ();
  352. nameTmp = new char [20];
  353. nameCache = new NameCache ();
  354. if (namespaces) {
  355. nsSupport = new NamespaceSupport();
  356. if (supportValidation && isValidating && !prefixes) {
  357. nsAttTmp = new Vector();
  358. }
  359. }
  360. // reset doc info
  361. isStandalone = false;
  362. rootElementName = null;
  363. isInAttribute = false;
  364. inExternalPE = false;
  365. doLexicalPE = false;
  366. donePrologue = false;
  367. entities.clear ();
  368. notations.clear ();
  369. params.clear ();
  370. elements.clear ();
  371. ignoreDeclarations = false;
  372. // initialize predefined references ... re-interpreted later
  373. builtin ("amp", "&");
  374. builtin ("lt", "<");
  375. builtin ("gt", ">");
  376. builtin ("quot", "\"");
  377. builtin ("apos", "'");
  378. if (locale == null)
  379. locale = Locale.getDefault ();
  380. if (resolver == null)
  381. resolver = new Resolver ();
  382. setHandlers ();
  383. //only for SECURITY_DEBUG
  384. if(SECURITY_DEBUG)System.out.println(" Last Entity expansion count = " + entityExpansionCount );
  385. //reset it to zero before next parse
  386. entityExpansionCount = 0;
  387. //if there is no limit set by the application.. set the default entity expansion limit to DEFAULT_ENTITY_EXPANSION_LIMIT
  388. if(entityExpansionLimit < 0){
  389. entityExpansionLimit = DEFAULT_ENTITY_EXPANSION_LIMIT ;
  390. }
  391. //if there is no limit set by the application.. set the default element attribute limit to DEFAULT_ELEMENT_ATTRIBUTE_LIMIT
  392. if(elementAttributeLimit < 0){
  393. elementAttributeLimit = DEFAULT_ELEMENT_ATTRIBUTE_LIMIT;
  394. }
  395. if(SECURITY_DEBUG){
  396. System.out.println(" Entity expansion limit in effect = " + entityExpansionLimit );
  397. System.out.println(" DisallowDoctypeDecl in effect = " + disallowDoctypeDecl );
  398. System.out.println(" Element Attribute limit in effect = " + elementAttributeLimit );
  399. }
  400. }//init()
  401. void setSecurityConstraintValues(){
  402. //SYSTEM PROPERTY ENTITY EXPANSION LIMIT
  403. //get the value of entityExpansionLimit from SYSTEM PROPERTY
  404. //put this code in doPriviliged block so it can still be executed if the caller have less privileges
  405. try {
  406. propertyEntityExpansionLimit = (String)AccessController.doPrivileged(new PrivilegedAction(){
  407. public Object run(){
  408. return System.getProperty(SYSTEM_PROPERTY_ENTITY_EXPANSION_LIMIT);
  409. }
  410. });
  411. } catch ( SecurityException se ) {
  412. //This exception can happen in case we are running as an applet
  413. }
  414. //SYSTEM PROPERTY DISALLOW DOCTYPE DECL
  415. //get the value of disallowDoctypeDecl from system property
  416. //put this code in doPriviliged block so it can still be executed if the caller have less privileges
  417. try {
  418. propertyDisallowDoctypeDecl = (String) AccessController.doPrivileged( new PrivilegedAction(){
  419. public Object run(){
  420. return System.getProperty(SYSTEM_PROPERTY_DISALLOW_DOCTYPE_DECL);
  421. }
  422. });
  423. } catch ( SecurityException se ) {
  424. //This exception can happen in case we are running as an applet
  425. }
  426. //SYSTEM PROPERTY ELEMENT ATTRIBUTE LIMIT
  427. //get the value of elementAttributeLimit from system property
  428. //put this code in doPriviliged block so it can still be executed if the caller have less privileges
  429. try {
  430. propertyElementAttributeLimit = (String) AccessController.doPrivileged( new PrivilegedAction(){
  431. public Object run(){
  432. return System.getProperty(SYSTEM_PROPERTY_ELEMENT_ATTRIBUTE_LIMIT);
  433. }
  434. });
  435. } catch ( SecurityException se ) {
  436. //This exception can happen in case we are running as an applet
  437. }
  438. if(SECURITY_DEBUG){
  439. System.out.println(" ENTITY_EXPANSION_LIMIT SET FROM SYSTEM PROPERTY = " + propertyEntityExpansionLimit );
  440. System.out.println(" DISALLOW_DOCTYPE_DECL SET FROM SYSTEM PROPERTY = " + propertyDisallowDoctypeDecl );
  441. System.out.println(" ELEMENT_ATTRIBUT_LIMIT SET FROM SYSTEM PROPERTY = " + propertyElementAttributeLimit );
  442. }
  443. //if either of the value is not set.. try to get the value from jaxp.properties file..
  444. if( propertyEntityExpansionLimit == null || propertyDisallowDoctypeDecl == null ){
  445. //for performance reasons don't read jaxp.properties file again and again..
  446. // try to read from $java.home/lib/jaxp.properties
  447. try {
  448. //put this code in doPriviliged block so it can still be executed if the caller have less privileges
  449. FileInputStream fis = (FileInputStream) AccessController.doPrivileged(new PrivilegedExceptionAction() {
  450. public Object run() throws FileNotFoundException {
  451. String javah = System.getProperty( "java.home" );
  452. String configFile = javah + File.separator +
  453. "lib" + File.separator + "jaxp.properties";
  454. File f = new File( configFile );
  455. if(f.exists()){
  456. return new FileInputStream(f);
  457. }
  458. else{
  459. return null ;
  460. }
  461. }
  462. });
  463. if(fis != null){
  464. Properties props = new Properties();
  465. props.load( fis );
  466. //we dont know which one was null..
  467. if(propertyEntityExpansionLimit == null){
  468. propertyEntityExpansionLimit = props.getProperty(SYSTEM_PROPERTY_ENTITY_EXPANSION_LIMIT);
  469. if(SECURITY_DEBUG)System.out.println("Value from jaxp.properites file, propertyEntityExpansionLimit = " + propertyEntityExpansionLimit );
  470. }
  471. //we dont know which one was null..
  472. if(propertyDisallowDoctypeDecl == null){
  473. propertyDisallowDoctypeDecl = props.getProperty(SYSTEM_PROPERTY_DISALLOW_DOCTYPE_DECL);
  474. if(SECURITY_DEBUG)System.out.println("Value from jaxp.properites file, propertyDisallowDoctypeDecl = " + propertyDisallowDoctypeDecl );
  475. }
  476. }
  477. } catch(Exception ex ) {
  478. //ignore the exception
  479. }
  480. }
  481. //get the value of entityExpansionLimit
  482. try{
  483. if(propertyEntityExpansionLimit != null){
  484. entityExpansionLimit = Integer.parseInt(propertyEntityExpansionLimit);
  485. }
  486. }catch(NumberFormatException nfe){
  487. //ignore the exception.. or
  488. }
  489. //get the value of disallowDoctypeDecl
  490. if(propertyDisallowDoctypeDecl != null && (propertyDisallowDoctypeDecl.equals("true") || propertyDisallowDoctypeDecl.equals("TRUE"))){
  491. disallowDoctypeDecl = true;
  492. }
  493. //get the value of elementAttributeLimit
  494. try{
  495. if(propertyElementAttributeLimit != null){
  496. elementAttributeLimit = Integer.parseInt(propertyElementAttributeLimit);
  497. }
  498. }catch(NumberFormatException nfe){
  499. //ignore the exception..
  500. }
  501. }//getSecurityConstraintValues()
  502. static private final NullHandler nullHandler = new NullHandler();
  503. private void setHandlers ()
  504. {
  505. if (contentHandler == null) {
  506. contentHandler = nullHandler;
  507. }
  508. if (errHandler == null) {
  509. errHandler = nullHandler;
  510. }
  511. if (dtdHandler == null) {
  512. dtdHandler = nullHandler;
  513. }
  514. if (lexicalHandler == null) {
  515. lexicalHandler = nullHandler;
  516. }
  517. if (declHandler == null) {
  518. declHandler = nullHandler;
  519. }
  520. }
  521. private void builtin (String entityName, String entityValue)
  522. {
  523. InternalEntity entity;
  524. entity = new InternalEntity (entityName, entityValue.toCharArray ());
  525. entities.put (entityName, entity);
  526. }
  527. ////////////////////////////////////////////////////////////////
  528. //
  529. // parsing is by recursive descent, code roughly
  530. // following the BNF rules except tweaked for simple
  531. // lookahead. rules are more or less in numeric order,
  532. // except where code sharing suggests other structures.
  533. //
  534. // a classic benefit of recursive descent parsers: it's
  535. // relatively easy to get diagnostics that make sense.
  536. //
  537. ////////////////////////////////////////////////////////////////
  538. //
  539. // CHAPTER 2: Documents
  540. //
  541. private void parseInternal (InputSource input)
  542. throws SAXException, IOException
  543. {
  544. if (input == null)
  545. fatal ("P-000");
  546. try {
  547. in = InputEntity.getInputEntity (errHandler, locale);
  548. in.init (input, null, null, false);
  549. //
  550. // doc handler sees the locator, lots of PIs, DTD info
  551. // about external entities and notations, then the body.
  552. //Need to initialize this after InputEntity cos locator uses
  553. //InputEntity's systemid, publicid, line no. etc
  554. contentHandler.setDocumentLocator (locator);
  555. contentHandler.startDocument ();
  556. // [1] document ::= prolog element Misc*
  557. // [22] prolog ::= XMLDecl? Misc* (DoctypeDecl Misc *)?
  558. maybeXmlDecl ();
  559. maybeMisc (false);
  560. if (!maybeDoctypeDecl ()) {
  561. if (supportValidation && isValidating)
  562. warning ("V-001", null);
  563. }
  564. maybeMisc (false);
  565. donePrologue = true;
  566. //
  567. // One root element ... then basically PIs before EOF.
  568. //
  569. if (!in.peekc ('<') || !maybeElement (null))
  570. fatal ("P-067");
  571. //Check subclass. Used for validation of id refs.
  572. afterRoot ();
  573. maybeMisc (true);
  574. if (!in.isEOF ())
  575. fatal ("P-001", new Object []
  576. { Integer.toHexString (((int)getc ())) } );
  577. contentHandler.endDocument ();
  578. } catch (EndOfInputException e) {
  579. if (!in.isDocument ()) {
  580. String name = in.getName ();
  581. do { // force a relevant URI and line number
  582. in = in.pop ();
  583. } while (in.isInternal ());
  584. fatal ("P-002", new Object []
  585. { name },
  586. e);
  587. } else
  588. fatal ("P-003", null, e);
  589. } catch (RuntimeException e) {
  590. // Don't discard location that triggered the exception
  591. throw new SAXParseException (
  592. e.getMessage () != null
  593. ? e.getMessage ()
  594. : e.getClass ().getName (),
  595. locator.getPublicId (), locator.getSystemId (),
  596. locator.getLineNumber (), locator.getColumnNumber (),
  597. e);
  598. } finally {
  599. // recycle temporary data used during parsing
  600. strTmp = null;
  601. attTmp = null;
  602. nameTmp = null;
  603. nameCache = null;
  604. nsAttTmp = null;
  605. // ditto input sources etc
  606. if (in != null) {
  607. in.close ();
  608. in = null;
  609. }
  610. // get rid of all DTD info ... some of it would be
  611. // useful for editors etc, investigate later.
  612. params.clear ();
  613. entities.clear ();
  614. notations.clear ();
  615. elements.clear ();
  616. afterDocument ();
  617. }
  618. }
  619. // package private -- for subclass
  620. void afterRoot () throws SAXException { }
  621. // package private -- for subclass
  622. void afterDocument () { }
  623. // role is for diagnostics
  624. private void whitespace (String roleId) throws IOException, SAXException
  625. // [3] S ::= (#x20 | #x9 | #xd | #xa)+
  626. {
  627. if (!maybeWhitespace ())
  628. fatal ("P-004", new Object []
  629. { messages.getMessage (locale, roleId) });
  630. }
  631. // S?
  632. private boolean maybeWhitespace () throws IOException, SAXException
  633. {
  634. if (!(inExternalPE && doLexicalPE))
  635. return in.maybeWhitespace ();
  636. // see getc() for the PE logic -- this lets us splice
  637. // expansions of PEs in "anywhere". getc() has smarts,
  638. // so for external PEs we don't bypass it.
  639. // XXX we can marginally speed PE handling, and certainly
  640. // be cleaner (hence potentially more correct), by using
  641. // the observations that expanded PEs only start and stop
  642. // where whitespace is allowed. getc wouldn't need any
  643. // "lexical" PE expansion logic, and no other method needs
  644. // to handle termination of PEs. (parsing of literals would
  645. // still need to pop entities, but not parsing of references
  646. // in content.)
  647. char c = getc();
  648. boolean saw = false;
  649. while (c == ' ' || c == '\t' || c == '\n' || c == '\r') {
  650. saw = true;
  651. // this gracefully ends things when we stop playing
  652. // with internal parameters. caller should have a
  653. // grammar rule allowing whitespace at end of entity.
  654. if (in.isEOF () && !in.isInternal ())
  655. return saw;
  656. c = getc ();
  657. }
  658. ungetc ();
  659. return saw;
  660. }
  661. private String maybeGetName ()
  662. throws IOException, SAXException
  663. {
  664. NameCacheEntry entry = maybeGetNameCacheEntry ();
  665. return (entry == null) ? null : entry.name;
  666. }
  667. private NameCacheEntry maybeGetNameCacheEntry ()
  668. throws IOException, SAXException
  669. {
  670. // [5] Name ::= (Letter|'_'|':') (Namechar)*
  671. char c = getc ();
  672. if (!XmlChars.isLetter (c) && c != ':' && c != '_') {
  673. ungetc ();
  674. return null;
  675. }
  676. return nameCharString (c);
  677. }
  678. // Used when parsing enumerations
  679. private String getNmtoken ()
  680. throws SAXException, IOException
  681. {
  682. // [7] Nmtoken ::= (Namechar)+
  683. char c = getc ();
  684. if (!XmlChars.isNameChar (c))
  685. fatal ("P-006", new Object [] { new Character (c) });
  686. return nameCharString (c).name;
  687. }
  688. // n.b. this gets used when parsing attribute values (for
  689. // internal references) so we can't use strTmp; it's also
  690. // a hotspot for CPU and memory in the parser (called at least
  691. // once for each element) so this has been optimized a bit.
  692. private NameCacheEntry nameCharString (char c)
  693. throws IOException, SAXException
  694. {
  695. int i = 1;
  696. nameTmp [0] = c;
  697. for (;;) {
  698. if ((c = in.getNameChar ()) == 0)
  699. break;
  700. if (i >= nameTmp.length) {
  701. char tmp [] = new char [nameTmp.length + 10];
  702. System.arraycopy (nameTmp, 0, tmp, 0, nameTmp.length);
  703. nameTmp = tmp;
  704. }
  705. nameTmp [i++] = c;
  706. }
  707. return nameCache.lookupEntry (nameTmp, i);
  708. }
  709. //
  710. // much similarity between parsing entity values in DTD
  711. // and attribute values (in DTD or content) ... both follow
  712. // literal parsing rules, newline canonicalization, etc
  713. //
  714. // leaves value in 'strTmp' ... either a "replacement text" (4.5),
  715. // or else partially normalized attribute value (the first bit
  716. // of 3.3.3's spec, without the "if not CDATA" bits).
  717. //
  718. private void parseLiteral (boolean isEntityValue)
  719. throws IOException, SAXException
  720. {
  721. // [9] EntityValue ::=
  722. // '"' ([^"&%] | Reference | PEReference)* '"'
  723. // | "'" ([^'&%] | Reference | PEReference)* "'"
  724. // [10] AttValue ::=
  725. // '"' ([^"&] | Reference )* '"'
  726. // | "'" ([^'&] | Reference )* "'"
  727. // Only expand PEs in getc() when processing entity value literals
  728. // and do not expand when processing AttValue. Save state of
  729. // doLexicalPE and restore it before returning.
  730. boolean savedLexicalPE = doLexicalPE;
  731. // doLexicalPE = isEntityValue;
  732. char quote = getc ();
  733. char c;
  734. InputEntity source = in;
  735. if (quote != '\'' && quote != '"')
  736. fatal ("P-007");
  737. // don't report entity expansions within attributes,
  738. // they're reported "fully expanded" via SAX
  739. isInAttribute = !isEntityValue;
  740. // get value into strTmp
  741. strTmp = new StringBuffer ();
  742. // scan, allowing entity push/pop wherever ...
  743. // expanded entities can't terminate the literal!
  744. for (;;) {
  745. if (in != source && in.isEOF ()) {
  746. // we don't report end of parsed entities
  747. // within attributes (no SAX hooks)
  748. in = in.pop ();
  749. continue;
  750. }
  751. if ((c = getc ()) == quote && in == source)
  752. break;
  753. //
  754. // Basically the "reference in attribute value"
  755. // row of the chart in section 4.4 of the spec
  756. //
  757. if (c == '&') {
  758. String entityName = maybeGetName ();
  759. if (entityName != null) {
  760. nextChar (';', "F-020", entityName);
  761. // 4.4 says: bypass these here ... we'll catch
  762. // forbidden refs to unparsed entities on use
  763. if (isEntityValue) {
  764. strTmp.append ('&');
  765. strTmp.append (entityName);
  766. strTmp.append (';');
  767. continue;
  768. }
  769. expandEntityInLiteral (entityName, entities, isEntityValue);
  770. // character references are always included immediately
  771. } else if ((c = getc ()) == '#') {
  772. int tmp = parseCharNumber ();
  773. if (tmp > 0xffff) {
  774. tmp = surrogatesToCharTmp (tmp);
  775. strTmp.append (charTmp [0]);
  776. if (tmp == 2)
  777. strTmp.append (charTmp [1]);
  778. } else
  779. strTmp.append ((char) tmp);
  780. } else
  781. fatal ("P-009");
  782. continue;
  783. }
  784. // expand parameter entities only within entity value literals
  785. if (c == '%' && isEntityValue) {
  786. String entityName = maybeGetName ();
  787. if (entityName != null) {
  788. nextChar (';', "F-021", entityName);
  789. if (inExternalPE)
  790. expandEntityInLiteral (entityName,
  791. params, isEntityValue);
  792. else
  793. fatal ("P-010", new Object [] { entityName });
  794. continue;
  795. } else
  796. fatal ("P-011");
  797. }
  798. // For attribute values ...
  799. if (!isEntityValue) {
  800. // 3.3.3 says whitespace normalizes to space...
  801. if (c == ' ' || c == '\t' || c == '\n' || c == '\r') {
  802. strTmp.append (' ');
  803. continue;
  804. }
  805. // "<" not legal in parsed literals ...
  806. if (c == '<')
  807. fatal ("P-012");
  808. }
  809. strTmp.append (c);
  810. }
  811. isInAttribute = false;
  812. // doLexicalPE = savedLexicalPE;
  813. }
  814. // does a SINGLE expansion of the entity (often reparsed later)
  815. private void expandEntityInLiteral (
  816. String name,
  817. SimpleHashtable table,
  818. boolean isEntityValue
  819. ) throws SAXException, IOException
  820. {
  821. Object entity = table.get (name);
  822. //throw fatal error when entity expansion count reaches the limit set by application
  823. //if we don't want to have any costraint on number of entity that can be expanaded
  824. //set the DEFAULT_ENTITY_EXPANSION_LIMIT to -1.
  825. if( entityExpansionLimit != -1 && entityExpansionCount++ >= entityExpansionLimit){
  826. fatal ("P-086", new Object[] {new Integer(entityExpansionLimit)});
  827. };
  828. //
  829. // Note: if entity is a PE (value.isPE) there is an XML
  830. // requirement that the content be "markkupdecl", but that error
  831. // is ignored here (as permitted by the XML spec).
  832. //
  833. if (entity instanceof InternalEntity) {
  834. InternalEntity value = (InternalEntity) entity;
  835. if (supportValidation && isValidating
  836. && isStandalone
  837. && !value.isFromInternalSubset)
  838. error ("V-002", new Object [] { name });
  839. pushReader (value.buf, name, !value.isPE);
  840. } else if (entity instanceof ExternalEntity) {
  841. if (!isEntityValue) // must be a PE ...
  842. fatal ("P-013", new Object [] { name });
  843. // XXX if this returns false ...
  844. pushReader ((ExternalEntity) entity);
  845. } else if (entity == null) {
  846. //
  847. // Note: much confusion about whether spec requires such
  848. // errors to be fatal in many cases, but none about whether
  849. // it allows "normal" errors to be unrecoverable!
  850. //
  851. fatal (
  852. (table == params) ? "V-022" : "P-014",
  853. new Object [] { name });
  854. }
  855. }
  856. // [11] SystemLiteral ::= ('"' [^"]* '"') | ("'" [^']* "'")
  857. // for PUBLIC and SYSTEM literals, also "<?xml ...type='literal'?>'
  858. // NOTE: XML spec should explicitly say that PE ref syntax is
  859. // ignored in PIs, comments, SystemLiterals, and Pubid Literal
  860. // values ... can't process the XML spec's own DTD without doing
  861. // that for comments.
  862. private String getQuotedString (String type, String extra)
  863. throws IOException, SAXException
  864. {
  865. // use in.getc to bypass PE processing
  866. char quote = in.getc ();
  867. if (quote != '\'' && quote != '"')
  868. fatal ("P-015", new Object [] {
  869. messages.getMessage (locale, type, new Object [] { extra })
  870. });
  871. char c;
  872. strTmp = new StringBuffer ();
  873. while ((c = in.getc ()) != quote)
  874. strTmp.append ((char)c);
  875. return strTmp.toString ();
  876. }
  877. private String parsePublicId ()
  878. throws IOException, SAXException
  879. {
  880. // [12] PubidLiteral ::= ('"' PubidChar* '"') | ("'" PubidChar* "'")
  881. // [13] PubidChar ::= #x20|#xd|#xa|[a-zA-Z0-9]|[-'()+,./:=?;!*#@$_%]
  882. String retval = getQuotedString ("F-033", null);
  883. for (int i = 0; i < retval.length (); i++) {
  884. char c = retval.charAt (i);
  885. if (" \r\n-'()+,./:=?;!*#@$_%0123456789".indexOf(c) == -1
  886. && !(c >= 'A' && c <= 'Z')
  887. && !(c >= 'a' && c <= 'z'))
  888. fatal ("P-016", new Object [] { new Character (c) });
  889. }
  890. strTmp = new StringBuffer ();
  891. strTmp.append (retval);
  892. return normalize (false);
  893. }
  894. // [14] CharData ::= [^<&]* - ([^<&]* ']]>' [^<&]*)
  895. // handled by: InputEntity.parsedContent()
  896. private boolean maybeComment (boolean skipStart)
  897. throws IOException, SAXException
  898. {
  899. // [15] Comment ::= '<!--'
  900. // ( (Char - '-') | ('-' (Char - '-'))*
  901. // '-->'
  902. if (!in.peek (skipStart ? "!--" : "<!--", null))
  903. return false;
  904. boolean savedLexicalPE = doLexicalPE;
  905. doLexicalPE = false;
  906. boolean saveCommentText = lexicalHandler != nullHandler;
  907. if (saveCommentText) {
  908. strTmp = new StringBuffer ();
  909. }
  910. oneComment:
  911. for (;;) {
  912. try {
  913. // bypass PE expansion, but permit PEs
  914. // to complete ... valid docs won't care.
  915. for (;;) {
  916. int c = getc ();
  917. if (c == '-') {
  918. c = getc ();
  919. if (c != '-') {
  920. if (saveCommentText)
  921. strTmp.append ('-');
  922. ungetc ();
  923. continue;
  924. }
  925. nextChar ('>', "F-022", null);
  926. break oneComment;
  927. }
  928. if (saveCommentText)
  929. strTmp.append ((char)c);
  930. }
  931. } catch (EndOfInputException e) {
  932. //
  933. // This is fatal EXCEPT when we're processing a PE...
  934. // in which case a validating processor reports an error.
  935. // External PEs are easy to detect; internal ones we
  936. // infer by being an internal entity outside an element.
  937. //
  938. if (inExternalPE || (!donePrologue && in.isInternal ())) {
  939. if (supportValidation && isValidating)
  940. error ("V-021", null);
  941. in = in.pop ();
  942. continue;
  943. }
  944. fatal ("P-017");
  945. }
  946. }
  947. doLexicalPE = savedLexicalPE;
  948. if (saveCommentText) {
  949. // Convert string to array of chars
  950. int length = strTmp.length();
  951. char[] charArray = new char[length];
  952. if (length != 0) {
  953. // XXX Avoid calling getChars on zero-size array as a
  954. // workaround for a bug that occurs in at least JDK1.2.2
  955. // which has since been fixed in JDK1.3
  956. strTmp.getChars(0, length, charArray, 0);
  957. }
  958. lexicalHandler.comment(charArray, 0, length);
  959. }
  960. return true;
  961. }
  962. private boolean maybePI (boolean skipStart)
  963. throws IOException, SAXException
  964. {
  965. // [16] PI ::= '<?' PITarget
  966. // (S (Char* - (Char* '?>' Char*)))?
  967. // '?>'
  968. // [17] PITarget ::= Name - (('X'|'x')('M'|'m')('L'|'l')
  969. boolean savedLexicalPE = doLexicalPE;
  970. if (!in.peek (skipStart ? "?" : "<?", null))
  971. return false;
  972. doLexicalPE = false;
  973. String target = maybeGetName ();
  974. if (target == null)
  975. fatal ("P-018");
  976. if ("xml".equals (target))
  977. fatal ("P-019");
  978. if ("xml".equalsIgnoreCase (target))
  979. fatal ("P-020", new Object [] { target });
  980. if (maybeWhitespace ()) {
  981. strTmp = new StringBuffer ();
  982. try {
  983. for (;;) {
  984. // use in.getc to bypass PE processing
  985. char c = in.getc ();
  986. //Reached the end of PI.
  987. if (c == '?' && in.peekc ('>'))
  988. break;
  989. strTmp.append (c);
  990. }
  991. } catch (EndOfInputException e) {
  992. fatal ("P-021");
  993. }
  994. contentHandler.processingInstruction (target, strTmp.toString ());
  995. } else {
  996. if (!in.peek ("?>", null))
  997. fatal ("P-022");
  998. contentHandler.processingInstruction (target, "");
  999. }
  1000. doLexicalPE = savedLexicalPE;
  1001. return true;
  1002. }
  1003. // [18] CDSect ::= CDStart CData CDEnd
  1004. // [19] CDStart ::= '<![CDATA['
  1005. // [20] CData ::= (Char* - (Char* ']]>' Char*))
  1006. // [21] CDEnd ::= ']]>'
  1007. //
  1008. // ... handled by InputEntity.unparsedContent()
  1009. private void maybeXmlDecl ()
  1010. throws IOException, SAXException
  1011. {
  1012. // [23] XMLDecl ::= '<?xml' VersionInfo EncodingDecl?
  1013. // SDDecl? S? '>'
  1014. if (!in.isXmlDeclOrTextDeclPrefix()) {
  1015. return;
  1016. }
  1017. // Consume '<?xml'
  1018. peek("<?xml");
  1019. readVersion (true, "1.0");
  1020. readEncoding (false);
  1021. readStandalone ();
  1022. maybeWhitespace ();
  1023. if (!peek ("?>")) {
  1024. char c = getc ();
  1025. fatal ("P-023", new Object []
  1026. { Integer.toHexString (c), new Character (c) });
  1027. }
  1028. }
  1029. // collapsing several rules together ...
  1030. // simpler than attribute literals -- no reference parsing!
  1031. private String maybeReadAttribute (String name, boolean must)
  1032. throws IOException, SAXException
  1033. {
  1034. // [24] VersionInfo ::= S 'version' Eq \'|\" versionNum \'|\"
  1035. // [80] EncodingDecl ::= S 'encoding' Eq \'|\" EncName \'|\"
  1036. // [32] SDDecl ::= S 'standalone' Eq \'|\" ... \'|\"
  1037. if (!maybeWhitespace ()) {
  1038. if (!must)
  1039. return null;
  1040. fatal ("P-024", new Object [] { name });
  1041. // NOTREACHED
  1042. }
  1043. if (!peek (name))
  1044. if (must)
  1045. fatal ("P-024", new Object [] { name });
  1046. else {
  1047. // To ensure that the whitespace is there so that when we
  1048. // check for the next attribute we assure that the
  1049. // whitespace still exists.
  1050. ungetc ();
  1051. return null;
  1052. }
  1053. // [25] Eq ::= S? '=' S?
  1054. maybeWhitespace ();
  1055. nextChar ('=', "F-023", null);
  1056. maybeWhitespace ();
  1057. return getQuotedString ("F-035", name);
  1058. }
  1059. private void readVersion (boolean must, String versionNum)
  1060. throws IOException, SAXException
  1061. {
  1062. String value = maybeReadAttribute ("version", must);
  1063. // [26] versionNum ::= ([a-zA-Z0-9_.:]| '-')+
  1064. if (must && value == null)
  1065. fatal ("P-025", new Object [] { versionNum });
  1066. if (value != null) {
  1067. int length = value.length ();
  1068. for (int i = 0; i < length; i++) {
  1069. char c = value.charAt (i);
  1070. if (!( (c >= '0' && c <= '9')
  1071. || c == '_' || c == '.'
  1072. || (c >= 'a' && c <= 'z')
  1073. || (c >= 'A' && c <= 'Z')
  1074. || c == ':' || c == '-')
  1075. )
  1076. fatal ("P-026", new Object [] { value });
  1077. }
  1078. }
  1079. if (value != null && !value.equals (versionNum))
  1080. error ("P-027", new Object [] { versionNum, value });
  1081. }
  1082. private void maybeMisc (boolean eofOK)
  1083. throws IOException, SAXException
  1084. {
  1085. // Misc*
  1086. while (!eofOK || !in.isEOF ()) {
  1087. // [27] Misc ::= Comment | PI | S
  1088. if (maybeComment (false)
  1089. || maybePI (false)
  1090. || maybeWhitespace ())
  1091. continue;
  1092. else
  1093. break;
  1094. }
  1095. }
  1096. // common code used by most markup declarations
  1097. // ... S (Q)Name ...
  1098. private String getMarkupDeclname (String roleId, boolean qname)
  1099. throws IOException, SAXException
  1100. {
  1101. String name;
  1102. whitespace (roleId);
  1103. name = maybeGetName ();
  1104. if (name == null)
  1105. fatal ("P-005", new Object []
  1106. { messages.getMessage (locale, roleId) });
  1107. return name;
  1108. }
  1109. private boolean maybeDoctypeDecl ()
  1110. throws IOException, SAXException
  1111. {
  1112. // [28] doctypedecl ::= '<!DOCTYPE' S Name
  1113. // (S ExternalID)?
  1114. // S? ('[' (markupdecl|PEReference|S)* ']' S?)?
  1115. // '>'
  1116. if (!peek ("<!DOCTYPE")){
  1117. return false;
  1118. }
  1119. else{
  1120. if(disallowDoctypeDecl){
  1121. fatal("P-085", new Object[] {SYSTEM_PROPERTY_DISALLOW_DOCTYPE_DECL} );
  1122. }
  1123. }
  1124. ExternalEntity externalSubset = null;
  1125. rootElementName = getMarkupDeclname ("F-014", true);
  1126. if (maybeWhitespace ()
  1127. && (externalSubset = maybeExternalID ()) != null) {
  1128. lexicalHandler.startDTD(rootElementName, externalSubset.publicId,
  1129. externalSubset.verbatimSystemId);
  1130. maybeWhitespace ();
  1131. } else {
  1132. lexicalHandler.startDTD(rootElementName, null, null);
  1133. }
  1134. if (in.peekc ('[')) {
  1135. for (;;) {
  1136. //Pop PEs when they are done.
  1137. if (in.isEOF () && !in.isDocument ()) {
  1138. in = in.pop ();
  1139. continue;
  1140. }
  1141. if (maybeMarkupDecl ()
  1142. || maybePEReference ()
  1143. || maybeWhitespace ()
  1144. )
  1145. continue;
  1146. else if (peek ("<!["))
  1147. fatal ("P-028");
  1148. else
  1149. break;
  1150. }
  1151. nextChar (']', "F-024", null);
  1152. maybeWhitespace ();
  1153. }
  1154. nextChar ('>', "F-025", null);
  1155. // [30] extSubset ::= TextDecl? extSubsetDecl
  1156. // [31] extSubsetDecl ::= ( markupdecl | conditionalSect
  1157. // | PEReference | S )*
  1158. // ... same as [79] extPE, which is where the code is
  1159. if (externalSubset != null) {
  1160. externalSubset.name = "[dtd]"; // SAX2 ext specifies this name
  1161. externalSubset.isPE = true;
  1162. externalParameterEntity (externalSubset);
  1163. }
  1164. // params are no good to anyone starting now -- bye!
  1165. params.clear ();
  1166. lexicalHandler.endDTD();
  1167. // make sure notations mentioned in attributes
  1168. // and entities were declared ... those are validity
  1169. // errors, but we must always clean up after them!
  1170. Vector v = new Vector ();
  1171. for (Enumeration e = notations.keys ();
  1172. e.hasMoreElements ();
  1173. ) {
  1174. String name = (String) e.nextElement ();
  1175. Object value = notations.get (name);
  1176. if (value == Boolean.TRUE) {
  1177. if (supportValidation && isValidating)
  1178. error ("V-003", new Object [] { name });
  1179. v.addElement (name);
  1180. } else if (value instanceof String) {
  1181. if (supportValidation && isValidating)
  1182. error ("V-004", new Object [] { name });
  1183. v.addElement (name);
  1184. }
  1185. }
  1186. while (!v.isEmpty ()) {
  1187. Object name = v.firstElement ();
  1188. v.removeElement (name);
  1189. notations.remove (name);
  1190. }
  1191. return true;
  1192. }
  1193. private boolean maybeMarkupDecl ()
  1194. throws IOException, SAXException
  1195. {
  1196. // [29] markupdecl ::= elementdecl | Attlistdecl
  1197. // | EntityDecl | NotationDecl | PI | Comment
  1198. return maybeElementDecl ()
  1199. || maybeAttlistDecl ()
  1200. || maybeEntityDecl ()
  1201. || maybeNotationDecl ()
  1202. || maybePI (false)
  1203. || maybeComment (false)
  1204. ;
  1205. }
  1206. private void readStandalone ()
  1207. throws IOException, SAXException
  1208. {
  1209. String value = maybeReadAttribute ("standalone", false);
  1210. // [32] SDDecl ::= ... "yes" or "no"
  1211. if (value == null || "no".equals (value))
  1212. return;
  1213. if ("yes".equals (value)) {
  1214. isStandalone = true;
  1215. return;
  1216. }
  1217. fatal ("P-029", new Object [] { value });
  1218. }
  1219. private static final String XmlLang = "xml:lang";
  1220. private boolean isXmlLang (String value)
  1221. {
  1222. // [33] LanguageId ::= Langcode ('-' Subcode)*
  1223. // [34] Langcode ::= ISO639Code | IanaCode | UserCode
  1224. // [35] ISO639Code ::= [a-zA-Z] [a-zA-Z]
  1225. // [36] IanaCode ::= [iI] '-' SubCode
  1226. // [37] UserCode ::= [xX] '-' SubCode
  1227. // [38] SubCode ::= [a-zA-Z]+
  1228. // the ISO and IANA codes (and subcodes) are registered,
  1229. // but that's neither a WF nor a validity constraint.
  1230. int nextSuffix;
  1231. char c;
  1232. if (value.length () < 2)
  1233. return false;
  1234. c = value.charAt (1);
  1235. if (c == '-') { // IANA, or user, code
  1236. c = value.charAt (0);
  1237. if (!(c == 'i' || c == 'I' || c == 'x' || c == 'X'))
  1238. return false;
  1239. nextSuffix = 1;
  1240. } else if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
  1241. // 2 letter ISO code, or error
  1242. c = value.charAt (0);
  1243. if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')))
  1244. return false;
  1245. nextSuffix = 2;
  1246. } else
  1247. return false;
  1248. // here "suffix" ::= '-' [a-zA-Z]+ suffix*
  1249. while (nextSuffix < value.length ()) {
  1250. c = value.charAt (nextSuffix);
  1251. if (c != '-')
  1252. break;
  1253. while (++nextSuffix < value.length ()) {
  1254. c = value.charAt (nextSuffix);
  1255. if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')))
  1256. break;
  1257. }
  1258. }
  1259. return value.length () == nextSuffix && c != '-';
  1260. }
  1261. //
  1262. // CHAPTER 3: Logical Structures
  1263. //
  1264. private boolean maybeElement (ElementValidator validator)
  1265. throws IOException, SAXException
  1266. {
  1267. // [39] element ::= EmptyElemTag | Stag content ETag
  1268. // [40] STag ::= '<' Name (S Attribute)* S? '>'
  1269. NameCacheEntry name;
  1270. ElementDecl element;
  1271. boolean haveAttributes = false;
  1272. boolean hasContent = true;
  1273. int startLine;
  1274. // the leading "<" has already been consumed
  1275. name = maybeGetNameCacheEntry ();
  1276. // n.b. InputEntity guarantees 1+N char pushback always,
  1277. // and maybeGetName won't use more than one to see if
  1278. // it's instead "<?", "<!--", "<![CDATA[", or an error.
  1279. if (name == null)
  1280. return false;
  1281. // XXX Test for namespace conformance here
  1282. // if (namespaces) {
  1283. // some code testing name.name
  1284. // }
  1285. // report validity errors ASAP
  1286. if (validator != null)
  1287. validator.consume (name.name);
  1288. element = (ElementDecl) elements.get (name.name);
  1289. if (supportValidation && isValidating) {
  1290. if (element == null || element.contentType == null) {
  1291. error ("V-005", new Object [] { name.name });
  1292. // minimize repetitive diagnostics
  1293. element = new ElementDecl (name.name);
  1294. element.contentType = strANY;
  1295. elements.put (name.name, element);
  1296. }
  1297. if (validator == null
  1298. && rootElementName != null
  1299. && !rootElementName.equals (name.name))
  1300. error ("V-006", new Object [] { name.name, rootElementName });
  1301. }
  1302. // save the line number here so we can give better diagnostics
  1303. // by identifying where the element started; WF errors may be
  1304. // reported thousands of lines "late".
  1305. startLine = in.getLineNumber ();
  1306. // Invariant: attTmp and nsAttTmp are empty except briefly in this
  1307. // method they are not empty iff haveAttributes is true
  1308. // Track whether we saw whitespace before an attribute;
  1309. // in some cases it's required, though superfluous
  1310. boolean sawWhite = in.maybeWhitespace ();
  1311. // These are exceptions from the first pass; they should be ignored
  1312. // if there's a second pass, but reported otherwise. A second pass
  1313. // occurs when a namespace declaration is found in the first pass.
  1314. Vector exceptions = null;
  1315. // SAX2 Namespace processing
  1316. if (namespaces) {
  1317. nsSupport.pushContext();
  1318. seenNSDecl = false;
  1319. }
  1320. // Each pass through this loop reads
  1321. // Name eq AttValue S?
  1322. // Loop exits on ">", "/>", error, or when the elementAttributeLimit has been reached
  1323. for (int attributeCount = 0 ; ; attributeCount++ ) {
  1324. if (attributeCount > elementAttributeLimit ){
  1325. fatal ("P-087", new Object[] {new Integer(elementAttributeLimit)});
  1326. }
  1327. if (in.peekc ('>'))
  1328. break;
  1329. // [44] EmptyElementTag ::= '<' Name (S Attribute)* S? '/>'
  1330. if (in.peekc ('/')) {
  1331. hasContent = false;
  1332. break;
  1333. }
  1334. //Need to have a whitespace between attributes.
  1335. if (!sawWhite)
  1336. fatal ("P-030");
  1337. // [41] Attribute ::= Name Eq AttValue
  1338. String attQName;
  1339. AttributeDecl info;
  1340. String value;
  1341. attQName = maybeGetName ();
  1342. // Need to do this as we have already consumed the
  1343. // whitespace and didn't see the end tag.
  1344. if (attQName == null)
  1345. fatal ("P-031", new Object [] { new Character (getc ()) });
  1346. if (attTmp.getValue (attQName) != null)
  1347. fatal ("P-032", new Object [] { attQName });
  1348. // [25] Eq ::= S? '=' S?
  1349. in.maybeWhitespace ();
  1350. nextChar ('=', "F-026", attQName);
  1351. in.maybeWhitespace ();
  1352. // We are not in the DTD => PEs are not recognized => we no
  1353. // longer need to expand PEs => don't expand PEs in AttValue =>
  1354. // doLexicalPE = false and call parseLiteral(isEntityValue =
  1355. // false) both
  1356. doLexicalPE = false;
  1357. parseLiteral (false);
  1358. // We are no longer in the DTD so we never need to expand PEs
  1359. sawWhite = in.maybeWhitespace ();
  1360. // normalize and check values right away.
  1361. info = (element == null)
  1362. ? null
  1363. : (AttributeDecl) element.attributes.get (attQName);
  1364. if (info == null) {
  1365. if (supportValidation && isValidating)
  1366. error ("V-007", new Object [] { attQName, name.name });
  1367. value = strTmp.toString ();
  1368. } else {
  1369. if (!AttributeDecl.CDATA.equals (info.type)) {
  1370. value = normalize (!info.isFromInternalSubset);
  1371. if (supportValidation && isValidating)
  1372. validateAttributeSyntax (info, value);
  1373. } else
  1374. value = strTmp.toString ();
  1375. if (supportValidation && isValidating
  1376. && info.isFixed
  1377. && !value.equals (info.defaultValue))
  1378. error ("V-008",
  1379. new Object [] {attQName, name.name, info.defaultValue});
  1380. }
  1381. // assert(value != null)
  1382. if (XmlLang.equals (attQName) && !isXmlLang (value))
  1383. error ("P-033", new Object [] { value });
  1384. String type = (info == null) ? AttributeDecl.CDATA : info.type;
  1385. String defaultValue = (info == null) ? null : info.defaultValue;
  1386. if (namespaces) {
  1387. exceptions = processAttributeNS(attQName, type, value,
  1388. defaultValue, true, false,
  1389. exceptions);
  1390. } else {
  1391. // No namespaces case
  1392. attTmp.addAttribute("", "", attQName, type, value,
  1393. defaultValue, true);
  1394. }
  1395. haveAttributes = true;
  1396. }
  1397. if (element != null)
  1398. attTmp.setIdAttributeName (element.id);
  1399. // if we had ATTLIST decls, handle required & defaulted attributes
  1400. // before telling next layer about this element
  1401. if (element != null && element.attributes.size () != 0) {
  1402. haveAttributes = defaultAttributes(element) || haveAttributes;
  1403. }
  1404. // Ensure that this element's namespace declarations apply to all of
  1405. // this element's attributes as well. If there was a Namespace
  1406. // declaration, we have to make a second pass just to be safe -- this
  1407. // will happen very rarely, possibly only once for each document.
  1408. if (seenNSDecl) {
  1409. // assert(namespaces == true)
  1410. int length = attTmp.getLength();
  1411. for (int i = 0; i < length; i++) {
  1412. String attQName = attTmp.getQName(i);
  1413. if (attQName.startsWith("xmlns")) {
  1414. // Could be a namespace declaration
  1415. if (attQName.length() == 5 || attQName.charAt(5) == ':') {
  1416. // Default or non-default NS declaration
  1417. continue;
  1418. }
  1419. }
  1420. // assert(not a namespace declaration)
  1421. String attName[] = processName(attQName, true, false);
  1422. attTmp.setURI(i, attName[0]);
  1423. attTmp.setLocalName(i, attName[1]);
  1424. }
  1425. } else if (exceptions != null && errHandler != null) {
  1426. for (int i = 0; i < exceptions.size(); i++) {
  1427. errHandler.error((SAXParseException)(exceptions.elementAt(i)));
  1428. }
  1429. }
  1430. // OK, finally report the event.
  1431. if (namespaces) {
  1432. String[] parts = processName(name.name, false, false);
  1433. contentHandler.startElement(parts[0], parts[1], parts[2], attTmp);
  1434. } else {
  1435. contentHandler.startElement("", "", name.name, attTmp);
  1436. }
  1437. // Clear temporaries only when necessary because this may be
  1438. // expensive and a doc may have lots of elements w/o attributes
  1439. if (haveAttributes) {
  1440. attTmp.clear();
  1441. if (supportValidation && isValidating && namespaces && !prefixes) {
  1442. nsAttTmp.removeAllElements();
  1443. }
  1444. }
  1445. // prepare to validate the content of this element.
  1446. // in nonvalidating parsers, this accepts ANY content
  1447. validator = newValidator (element);
  1448. if (hasContent) {
  1449. content (element, false, validator);
  1450. // [42] ETag ::= '</' Name S? '>'
  1451. // ... content swallowed "</"
  1452. if (!in.peek (name.name, name.chars))
  1453. fatal ("P-034", new Object []
  1454. { name.name, new Integer (startLine) });
  1455. in.maybeWhitespace ();
  1456. }
  1457. nextChar ('>', "F-027", name.name);
  1458. validator.done ();
  1459. if (namespaces) {
  1460. // Split the name. Unfortunately, we can't always reuse the
  1461. // info from the startElement event above b/c this element may
  1462. // have subelements and a global temporary is used.
  1463. String[] parts = processName(name.name, false, false);
  1464. // Report appropriate events...
  1465. contentHandler.endElement(parts[0], parts[1], parts[2]);
  1466. Enumeration prefixes = nsSupport.getDeclaredPrefixes();
  1467. while (prefixes.hasMoreElements()) {
  1468. String prefix = (String)prefixes.nextElement();
  1469. contentHandler.endPrefixMapping(prefix);
  1470. }
  1471. nsSupport.popContext();
  1472. } else {
  1473. contentHandler.endElement("", "", name.name);
  1474. }
  1475. return true;
  1476. }
  1477. /**
  1478. * Process attributes for namespace support. This is mostly common
  1479. * code that gets called from two places and was factored out. The
  1480. * <code>isDefaulting</code> param specifies where the code is called
  1481. * from.
  1482. *
  1483. * @param isDefaulting true iff we are processing this attribute from
  1484. * the <code>defaultAttributes(...)</code> method
  1485. *
  1486. * The namespace processing code is derived from the SAX2 ParserAdapter
  1487. * code. This code should be kept in sync with ParserAdapter bug
  1488. * fixes.
  1489. *
  1490. * Note: Modifies <code>seenNSDecl</code> iff a xmlns attribute, ie a
  1491. * namespace decl, was found. Modifies <code>attTmp</code> and
  1492. * <code>nsAttTmp</code>.
  1493. */
  1494. private Vector processAttributeNS(String attQName, String type,
  1495. String value, String defaultValue,
  1496. boolean isSpecified, boolean isDefaulting,
  1497. Vector exceptions)
  1498. throws SAXException
  1499. {
  1500. // assert(namespaces == true)
  1501. nonNamespace:
  1502. if (attQName.startsWith("xmlns")) {
  1503. // Could be a namespace declaration
  1504. boolean defaultNSDecl = attQName.length() == 5;
  1505. if (!defaultNSDecl && attQName.charAt(5) != ':') {
  1506. // Not a namespace declaration
  1507. break nonNamespace;
  1508. }
  1509. // Must be some kind of namespace declaration
  1510. String prefix;
  1511. if (defaultNSDecl) {
  1512. // Default namespace, so use empty string as prefix
  1513. prefix = "";
  1514. } else {
  1515. // Non-default namespace decl, extract the prefix
  1516. prefix = attQName.substring(6);
  1517. }
  1518. if (!nsSupport.declarePrefix(prefix, value)) {
  1519. error("P-083", new Object[] { prefix });
  1520. }
  1521. contentHandler.startPrefixMapping(prefix, value);
  1522. // We may need to add this attribute to appropriate lists
  1523. if (prefixes) {
  1524. attTmp.addAttribute("", prefix, attQName.intern(),
  1525. type, value, defaultValue, isSpecified);
  1526. } else if (supportValidation && isValidating && !isDefaulting) {
  1527. // Add this namespace attribute to a different list that
  1528. // will be used to check for #REQUIRED attributes later.
  1529. // Since "prefixes" is false, these are not reported to the
  1530. // ContentHandler. This step is not needed during the
  1531. // second pass of attribute processing where default values
  1532. // are provided.
  1533. nsAttTmp.addElement(attQName);
  1534. }
  1535. seenNSDecl = true;
  1536. return exceptions;
  1537. }
  1538. // This isn't a namespace declaration.
  1539. try {
  1540. String attName[] = processName(attQName, true, true);
  1541. attTmp.addAttribute(attName[0], attName[1], attName[2], type,
  1542. value, defaultValue, isSpecified);
  1543. } catch (SAXException e) {
  1544. if (exceptions == null) {
  1545. exceptions = new Vector();
  1546. }
  1547. exceptions.addElement(e);
  1548. attTmp.addAttribute("", attQName, attQName, type, value,
  1549. defaultValue, isSpecified);
  1550. }
  1551. return exceptions;
  1552. }
  1553. /**
  1554. * Process a qualified (prefixed) name.
  1555. *
  1556. * <p>If the name has an undeclared prefix, use only the qname
  1557. * and make an ErrorHandler.error callback in case the app is
  1558. * interested.</p>
  1559. *
  1560. * @param qName The qualified (prefixed) name.
  1561. * @param isAttribute true if this is an attribute name.
  1562. * @return The name split into three parts.
  1563. * @exception org.xml.sax.SAXException The client may throw
  1564. * an exception if there is an error callback.
  1565. */
  1566. private String[] processName(String qName, boolean isAttribute,
  1567. boolean useException)
  1568. throws SAXException
  1569. {
  1570. // assert(namespaces == true)
  1571. String parts[] = nsSupport.processName(qName, namePartsTmp,
  1572. isAttribute);
  1573. if (parts == null) {
  1574. parts = new String[3];
  1575. // SAX should use "" instead of null for parts 0 and 1 ???
  1576. parts[0] = "";
  1577. String localName = XmlNames.getLocalPart(qName);
  1578. parts[1] = localName != null ? localName.intern() : "";
  1579. parts[2] = qName.intern();
  1580. String messageId = "P-084";
  1581. Object[] parameters = new Object[] { qName };
  1582. if (useException) {
  1583. throw new SAXParseException(
  1584. messages.getMessage(locale, messageId, parameters),
  1585. locator);
  1586. }
  1587. error(messageId, parameters);
  1588. }
  1589. return parts;
  1590. }
  1591. /**
  1592. * To validate, subclassers should create an object that can
  1593. * accept valid streams of element names, text, and terminate.
  1594. */
  1595. // package private ... overriden in validating subclass
  1596. ElementValidator newValidator (ElementDecl element)
  1597. {
  1598. return ElementValidator.ANY; // "ANY" content is OK
  1599. }
  1600. /**
  1601. * To validate, subclassers should at this time make sure that
  1602. * values are of the declared types:<UL>
  1603. * <LI> ID and IDREF(S) values are Names
  1604. * <LI> NMTOKEN(S) are Nmtokens
  1605. * <LI> ENUMERATION values match one of the tokens
  1606. * <LI> NOTATION values match a notation name
  1607. * <LI> ENTITIY(IES) values match an unparsed external entity
  1608. * </UL>
  1609. *
  1610. * <P> Separately, make sure IDREF values match some ID
  1611. * provided in the document (in the afterRoot method).
  1612. */
  1613. // package private
  1614. void validateAttributeSyntax (AttributeDecl attr, String value)
  1615. throws SAXException
  1616. {
  1617. return;
  1618. }
  1619. /**
  1620. * Provide default attributes for an element and check for #REQUIRED
  1621. * attributes.
  1622. *
  1623. * Note: this method accesses <code>attTmp</code> and
  1624. * <code>nsAttTmp</code>
  1625. */
  1626. private boolean defaultAttributes(ElementDecl element)
  1627. throws SAXException
  1628. {
  1629. boolean didDefault = false;
  1630. // Go through all declared attributes and:
  1631. // 1) Default anything the document didn't provide.
  1632. // 2) Check #REQUIRED values.
  1633. for (Enumeration e = element.attributes.keys();
  1634. e.hasMoreElements(); ) {
  1635. // Declared attribute name
  1636. String declAttName = (String)e.nextElement();
  1637. if (attTmp.getValue(declAttName) != null) {
  1638. // Attribute already has value so no defaulting necessary
  1639. continue;
  1640. }
  1641. // If we get here, then declared attribute is not in the list
  1642. // of attributes to be reported to ContentHandler.
  1643. // Get more info on the declared attribute
  1644. AttributeDecl info =
  1645. (AttributeDecl)element.attributes.get(declAttName);
  1646. // If this is a #REQUIRED attribute...
  1647. if (supportValidation && isValidating && info.isRequired) {
  1648. // Under certain conditions, check the auxiliary nsAttTmp
  1649. // list for #REQUIRED attributes since these are not in the
  1650. // list to be reported to the ContentHandler.
  1651. if (namespaces && !prefixes) {
  1652. if (nsAttTmp.contains(declAttName)) {
  1653. // Namespace attribute is #REQUIRED and already has
  1654. // a value
  1655. continue;
  1656. }
  1657. }
  1658. error("V-009", new Object [] { declAttName });
  1659. }
  1660. String defaultValue = info.defaultValue;
  1661. if (defaultValue != null) {
  1662. if (supportValidation && isValidating
  1663. && isStandalone && !info.isFromInternalSubset)
  1664. error ("V-010", new Object [] { declAttName });
  1665. if (namespaces) {
  1666. processAttributeNS(declAttName, info.type, defaultValue,
  1667. defaultValue, false, true, null);
  1668. } else {
  1669. attTmp.addAttribute("", "", declAttName, info.type,
  1670. defaultValue, defaultValue, false);
  1671. }
  1672. didDefault = true;
  1673. }
  1674. }
  1675. return didDefault;
  1676. }
  1677. // parses content inside a given element (or parsed entity), optionally
  1678. // allowing EOF (when expanding internal or external entities) and
  1679. // optionally validating elements/#PCDATA that we see
  1680. private void content (
  1681. ElementDecl element,
  1682. boolean allowEOF,
  1683. ElementValidator validator
  1684. ) throws IOException, SAXException
  1685. {
  1686. for (;;) {
  1687. // [43] content ::= (element|CharData|Reference
  1688. // |CDSect|PI|Comment)*
  1689. // markup?
  1690. if (in.peekc ('<')) {
  1691. if (maybeElement (validator))
  1692. continue;
  1693. // Three cases: Error, and either EOF or ETag.
  1694. // Here we check Etag as a common exit.
  1695. if (in.peekc ('/'))
  1696. return;
  1697. // Less commonly, it's a comment, PI, CDATA ...
  1698. if (maybeComment (true) || maybePI (true))
  1699. continue;
  1700. // ... CDATA are specially delimited characters; can be
  1701. // #PCDATA or whitespace (the latter has validity issues).
  1702. if (in.peek("![CDATA[", null)) {
  1703. lexicalHandler.startCDATA();
  1704. in.unparsedContent(contentHandler, validator,
  1705. (element != null) && element.ignoreWhitespace,
  1706. (isStandalone
  1707. && supportValidation && isValidating
  1708. && !element.isFromInternalSubset)
  1709. ? "V-023"
  1710. : null
  1711. );
  1712. lexicalHandler.endCDATA();
  1713. continue;
  1714. }
  1715. // ... or a grammatical error (WF violation).
  1716. char c = getc ();
  1717. fatal ("P-079", new Object [] {
  1718. Integer.toHexString (c), new Character (c) });
  1719. // NOTREACHED
  1720. }
  1721. // characters? ... whitespace or #PCDATA
  1722. if (element != null
  1723. && element.ignoreWhitespace
  1724. && in.ignorableWhitespace (contentHandler)) {
  1725. // XXX prefer to report validity error before the
  1726. // whitespace was reported ...
  1727. if (supportValidation && isValidating
  1728. && isStandalone && !element.isFromInternalSubset)
  1729. error ("V-011", new Object [] { element.name });
  1730. continue;
  1731. }
  1732. if (in.parsedContent (contentHandler, validator))
  1733. continue;
  1734. if (in.isEOF ())
  1735. break;
  1736. // else MUST be an entity reference
  1737. if (!maybeReferenceInContent (element, validator))
  1738. throw new InternalError ();
  1739. }
  1740. if (!allowEOF)
  1741. fatal ("P-035");
  1742. }
  1743. private boolean maybeElementDecl ()
  1744. throws IOException, SAXException
  1745. {
  1746. // [45] elementDecl ::= '<!ELEMENT' S Name S contentspec S? '>'
  1747. // [46] contentspec ::= 'EMPTY' | 'ANY' | Mixed | children
  1748. InputEntity start = peekDeclaration ("!ELEMENT");
  1749. if (start == null)
  1750. return false;
  1751. // n.b. for content models where inter-element whitespace is
  1752. // ignorable, we mark that fact here.
  1753. String name = getMarkupDeclname ("F-015", true);
  1754. ElementDecl element = (ElementDecl) elements.get (name);
  1755. boolean declEffective = false;
  1756. if (element != null) {
  1757. if (element.contentType != null) {
  1758. if (supportValidation && isValidating
  1759. && element.contentType != null)
  1760. error ("V-012", new Object [] { name });
  1761. // don't override previous declaration
  1762. element = new ElementDecl (name);
  1763. } // else <!ATTLIST name ...> came first
  1764. } else {
  1765. element = new ElementDecl (name);
  1766. if (!ignoreDeclarations) {
  1767. elements.put (element.name, element);
  1768. declEffective = true;
  1769. }
  1770. }
  1771. element.isFromInternalSubset = !inExternalPE;
  1772. whitespace ("F-000");
  1773. if (peek (strEMPTY)) {
  1774. element.contentType = strEMPTY;
  1775. element.ignoreWhitespace = true;
  1776. } else if (peek (strANY)) {
  1777. element.contentType = strANY;
  1778. element.ignoreWhitespace = false;
  1779. } else
  1780. element.contentType = getMixedOrChildren (element);
  1781. maybeWhitespace ();
  1782. char c = getc ();
  1783. if (c != '>')
  1784. fatal ("P-036", new Object [] { name, new Character (c) });
  1785. if (supportValidation && isValidating && start != in)
  1786. error ("V-013", null);
  1787. if (declEffective) {
  1788. declHandler.elementDecl(element.name, element.contentType);
  1789. }
  1790. return true;
  1791. }
  1792. // We're leaving the content model as a regular expression;
  1793. // it's an efficient natural way to express such things, and
  1794. // libraries often interpret them. No whitespace in the
  1795. // model we store, though!
  1796. private String getMixedOrChildren (ElementDecl element)
  1797. throws IOException, SAXException
  1798. {
  1799. InputEntity start;
  1800. // [47] children ::= (choice|seq) ('?'|'*'|'+')?
  1801. strTmp = new StringBuffer ();
  1802. nextChar ('(', "F-028", element.name);
  1803. start = in;
  1804. maybeWhitespace ();
  1805. strTmp.append ('(');
  1806. if (peek ("#PCDATA")) {
  1807. strTmp.append ("#PCDATA");
  1808. getMixed (element.name, start);
  1809. element.ignoreWhitespace = false;
  1810. } else {
  1811. element.model = getcps (element.name, start);
  1812. element.ignoreWhitespace = true;
  1813. }
  1814. return strTmp.toString ();
  1815. }
  1816. // package private -- overridden by validating subclass
  1817. ContentModel newContentModel (String tag)
  1818. {
  1819. return null;
  1820. }
  1821. // package private -- overridden by validating subclass
  1822. ContentModel newContentModel (char type, ContentModel next)
  1823. {
  1824. return null;
  1825. }
  1826. // '(' S? already consumed
  1827. // matching ')' must be in "start" entity if validating
  1828. private ContentModel getcps (
  1829. String element,
  1830. InputEntity start
  1831. ) throws IOException, SAXException
  1832. {
  1833. // [48] cp ::= (Name|choice|seq) ('?'|'*'|'+')?
  1834. // [49] choice ::= '(' S? cp (S? '|' S? cp)* S? ')'
  1835. // [50] seq ::= '(' S? cp (S? ',' S? cp)* S? ')'
  1836. boolean decided = false;
  1837. char type = 0;
  1838. ContentModel retval, current, temp;
  1839. retval = current = temp = null;
  1840. do {
  1841. String tag;
  1842. tag = maybeGetName ();
  1843. if (tag != null) {
  1844. strTmp.append (tag);
  1845. temp = getFrequency (newContentModel (tag));
  1846. } else if (peek ("(")) {
  1847. InputEntity next = in;
  1848. strTmp.append ('(');
  1849. maybeWhitespace ();
  1850. temp = getFrequency (getcps (element, next));
  1851. } else
  1852. fatal ((type == 0) ? "P-039" :
  1853. ((type == ',') ? "P-037" : "P-038"),
  1854. new Object [] { new Character (getc ()) });
  1855. maybeWhitespace ();
  1856. if (decided) {
  1857. char c = getc ();
  1858. if (current != null) {
  1859. current.next = newContentModel (type, temp);
  1860. current = current.next;
  1861. }
  1862. if (c == type) {
  1863. strTmp.append (type);
  1864. maybeWhitespace ();
  1865. continue;
  1866. } else if (c == '\u0029') { // rparen
  1867. ungetc ();
  1868. continue;
  1869. } else {
  1870. fatal ((type == 0) ? "P-041" : "P-040",
  1871. new Object [] {
  1872. new Character (c),
  1873. new Character (type)
  1874. });
  1875. }
  1876. } else {
  1877. type = getc ();
  1878. if (type == '|' || type == ',') {
  1879. decided = true;
  1880. retval = current = newContentModel (type, temp);
  1881. } else {
  1882. retval = current = temp;
  1883. ungetc ();
  1884. continue;
  1885. }
  1886. strTmp.append (type);
  1887. }
  1888. maybeWhitespace ();
  1889. } while (!peek (")"));
  1890. if (supportValidation && isValidating && in != start)
  1891. error ("V-014", new Object [] { element });
  1892. strTmp.append (')');
  1893. return getFrequency (retval);
  1894. }
  1895. private ContentModel getFrequency (ContentModel original)
  1896. throws IOException, SAXException
  1897. {
  1898. char c = getc ();
  1899. if (c == '?' || c == '+' || c == '*') {
  1900. strTmp.append (c);
  1901. if (original == null)
  1902. return null;
  1903. if (original.type == 0) { // foo* etc
  1904. original.type = c;
  1905. return original;
  1906. }
  1907. return newContentModel (c, original);
  1908. } else {
  1909. ungetc ();
  1910. return original;
  1911. }
  1912. }
  1913. // '(' S? '#PCDATA' already consumed
  1914. // matching ')' must be in "start" entity if validating
  1915. private void getMixed (String element, InputEntity start)
  1916. throws IOException, SAXException
  1917. {
  1918. // [51] Mixed ::= '(' S? '#PCDATA' (S? '|' S? Name)* S? ')*'
  1919. // | '(' S? '#PCDATA' S? ')'
  1920. maybeWhitespace ();
  1921. if (peek ("\u0029*") || peek ("\u0029")) {
  1922. if (supportValidation && isValidating && in != start)
  1923. error ("V-014", new Object [] { element });
  1924. strTmp.append (')');
  1925. return;
  1926. }
  1927. Vector v = null;
  1928. if (supportValidation && isValidating)
  1929. v = new Vector ();
  1930. while (peek ("|")) {
  1931. String name;
  1932. strTmp.append ('|');
  1933. maybeWhitespace ();
  1934. name = maybeGetName ();
  1935. if (name == null)
  1936. fatal ("P-042", new Object []
  1937. { element, Integer.toHexString (getc ()) });
  1938. if (supportValidation && isValidating) {
  1939. if (v.contains (name))
  1940. error ("V-015", new Object [] { name });
  1941. else
  1942. v.addElement (name);
  1943. }
  1944. strTmp.append (name);
  1945. maybeWhitespace ();
  1946. }
  1947. if (!peek ("\u0029*")) // right paren
  1948. fatal ("P-043", new Object []
  1949. { element, new Character (getc ()) });
  1950. if (supportValidation && isValidating && in != start)
  1951. error ("V-014", new Object [] { element });
  1952. strTmp.append (')');
  1953. }
  1954. private boolean maybeAttlistDecl ()
  1955. throws IOException, SAXException
  1956. {
  1957. // [52] AttlistDecl ::= '<!ATTLIST' S Name AttDef* S? '>'
  1958. InputEntity start = peekDeclaration ("!ATTLIST");
  1959. if (start == null)
  1960. return false;
  1961. String name = getMarkupDeclname ("F-016", true);
  1962. ElementDecl element = (ElementDecl) elements.get (name);
  1963. if (element == null) {
  1964. // not yet declared -- no problem.
  1965. element = new ElementDecl (name);
  1966. if (!ignoreDeclarations)
  1967. elements.put (name, element);
  1968. }
  1969. maybeWhitespace ();
  1970. while (!peek (">")) {
  1971. // [53] AttDef ::= S Name S AttType S DefaultDecl
  1972. // [54] AttType ::= StringType | TokenizedType | EnumeratedType
  1973. name = maybeGetName ();
  1974. if (name == null)
  1975. fatal ("P-044", new Object [] { new Character (getc ()) });
  1976. whitespace ("F-001");
  1977. AttributeDecl a = new AttributeDecl (name);
  1978. a.isFromInternalSubset = !inExternalPE;
  1979. // Note: use the type constants from AttributeDecl
  1980. // so that "==" may be used (faster)
  1981. // [55] StringType ::= 'CDATA'
  1982. if (peek (AttributeDecl.CDATA))
  1983. a.type = AttributeDecl.CDATA;
  1984. // [56] TokenizedType ::= 'ID' | 'IDREF' | 'IDREFS'
  1985. // | 'ENTITY' | 'ENTITIES'
  1986. // | 'NMTOKEN' | 'NMTOKENS'
  1987. // n.b. if "IDREFS" is there, both "ID" and "IDREF"
  1988. // match peekahead ... so this order matters!
  1989. else if (peek (AttributeDecl.IDREFS))
  1990. a.type = AttributeDecl.IDREFS;
  1991. else if (peek (AttributeDecl.IDREF))
  1992. a.type = AttributeDecl.IDREF;
  1993. else if (peek (AttributeDecl.ID)) {
  1994. a.type = AttributeDecl.ID;
  1995. if (element.id != null) {
  1996. if (supportValidation && isValidating)
  1997. error ("V-016", new Object [] { element.id });
  1998. } else
  1999. element.id = name;
  2000. } else if (peek (AttributeDecl.ENTITY))
  2001. a.type = AttributeDecl.ENTITY;
  2002. else if (peek (AttributeDecl.ENTITIES))
  2003. a.type = AttributeDecl.ENTITIES;
  2004. else if (peek (AttributeDecl.NMTOKENS))
  2005. a.type = AttributeDecl.NMTOKENS;
  2006. else if (peek (AttributeDecl.NMTOKEN))
  2007. a.type = AttributeDecl.NMTOKEN;
  2008. // [57] EnumeratedType ::= NotationType | Enumeration
  2009. // [58] NotationType ::= 'NOTATION' S '(' S? Name
  2010. // (S? '|' S? Name)* S? ')'
  2011. else if (peek (AttributeDecl.NOTATION)) {
  2012. a.type = AttributeDecl.NOTATION;
  2013. whitespace ("F-002");
  2014. nextChar ('(', "F-029", null);
  2015. maybeWhitespace ();
  2016. Vector v = new Vector ();
  2017. do {
  2018. if ((name = maybeGetName ()) == null)
  2019. fatal ("P-068");
  2020. // permit deferred declarations
  2021. if (supportValidation && isValidating
  2022. && notations.get (name) == null)
  2023. notations.put (name, name);
  2024. v.addElement (name);
  2025. maybeWhitespace ();
  2026. if (peek ("|"))
  2027. maybeWhitespace ();
  2028. } while (!peek (")"));
  2029. a.values = new String [v.size ()];
  2030. for (int i = 0; i < v.size (); i++)
  2031. a.values [i] = (String)v.elementAt (i);
  2032. // [59] Enumeration ::= '(' S? Nmtoken (S? '|' Nmtoken)* S? ')'
  2033. } else if (peek ("(")) {
  2034. a.type = AttributeDecl.ENUMERATION;
  2035. maybeWhitespace ();
  2036. Vector v = new Vector ();
  2037. do {
  2038. name = getNmtoken ();
  2039. v.addElement (name);
  2040. maybeWhitespace ();
  2041. if (peek ("|"))
  2042. maybeWhitespace ();
  2043. } while (!peek (")"));
  2044. a.values = new String [v.size ()];
  2045. for (int i = 0; i < v.size (); i++)
  2046. a.values [i] = (String)v.elementAt (i);
  2047. } else
  2048. fatal ("P-045",
  2049. new Object [] { name, new Character (getc ()) });
  2050. // [60] DefaultDecl ::= '#REQUIRED' | '#IMPLIED'
  2051. // | (('#FIXED' S)? AttValue)
  2052. whitespace ("F-003");
  2053. if (peek ("#REQUIRED")) {
  2054. a.valueDefault = AttributeDecl.REQUIRED;
  2055. a.isRequired = true;
  2056. } else if (peek ("#FIXED")) {
  2057. if (supportValidation && isValidating
  2058. && a.type == AttributeDecl.ID)
  2059. error ("V-017", new Object [] { a.name });
  2060. a.valueDefault = AttributeDecl.FIXED;
  2061. a.isFixed = true;
  2062. whitespace ("F-004");
  2063. // Don't expand PEs in AttValue => doLexicalPE = false and
  2064. // call parseLiteral(isEntityValue = false) both
  2065. doLexicalPE = false;
  2066. parseLiteral(false);
  2067. // We are in DTD so set this back to true
  2068. doLexicalPE = true;
  2069. if (a.type != AttributeDecl.CDATA)
  2070. a.defaultValue = normalize (false);
  2071. else
  2072. a.defaultValue = strTmp.toString ();
  2073. if (a.type != AttributeDecl.CDATA)
  2074. validateAttributeSyntax (a, a.defaultValue);
  2075. } else if (peek ("#IMPLIED")) {
  2076. a.valueDefault = AttributeDecl.IMPLIED;
  2077. } else {
  2078. if (supportValidation && isValidating
  2079. && a.type == AttributeDecl.ID)
  2080. error ("V-018", new Object [] { a.name });
  2081. // By default a.valueDefault == null here
  2082. // Don't expand PEs in AttValue => doLexicalPE = false and
  2083. // call parseLiteral(isEntityValue = false) both
  2084. doLexicalPE = false;
  2085. parseLiteral(false);
  2086. // We are in DTD so set this back to true
  2087. doLexicalPE = true;
  2088. if (a.type != AttributeDecl.CDATA)
  2089. a.defaultValue = normalize (false);
  2090. else
  2091. a.defaultValue = strTmp.toString ();
  2092. if (a.type != AttributeDecl.CDATA)
  2093. validateAttributeSyntax (a, a.defaultValue);
  2094. }
  2095. if (XmlLang.equals (a.name)
  2096. && a.defaultValue != null
  2097. && !isXmlLang (a.defaultValue))
  2098. error ("P-033", new Object [] { a.defaultValue });
  2099. if (!ignoreDeclarations
  2100. && element.attributes.get (a.name) == null) {
  2101. element.attributes.put (a.name, a);
  2102. // Report attribute declaration to SAX DeclHandler
  2103. String saxType;
  2104. if (a.type == AttributeDecl.ENUMERATION
  2105. || a.type == AttributeDecl.NOTATION) {
  2106. StringBuffer fullType = new StringBuffer();
  2107. if (a.type == AttributeDecl.NOTATION) {
  2108. fullType.append(a.type);
  2109. fullType.append(" ");
  2110. }
  2111. if (a.values.length > 1) {
  2112. fullType.append("(");
  2113. }
  2114. for (int i = 0; i < a.values.length; i++) {
  2115. fullType.append(a.values[i]);
  2116. if (i + 1 < a.values.length) {
  2117. fullType.append("|");
  2118. }
  2119. }
  2120. if (a.values.length > 1) {
  2121. fullType.append(")");
  2122. }
  2123. saxType = fullType.toString();
  2124. } else {
  2125. saxType = a.type;
  2126. }
  2127. declHandler.attributeDecl(element.name, a.name, saxType,
  2128. a.valueDefault, a.defaultValue);
  2129. }
  2130. maybeWhitespace ();
  2131. }
  2132. if (supportValidation && isValidating && start != in)
  2133. error ("V-013", null);
  2134. return true;
  2135. }
  2136. // used when parsing literal attribute values,
  2137. // or public identifiers.
  2138. //
  2139. // input in strTmp
  2140. private String normalize (boolean invalidIfNeeded)
  2141. throws SAXException
  2142. {
  2143. // this can allocate an extra string...
  2144. String s = strTmp.toString ();
  2145. String s2 = s.trim ();
  2146. boolean didStrip = false;
  2147. if (s != s2) {
  2148. s = s2;
  2149. s2 = null;
  2150. didStrip = true;
  2151. }
  2152. strTmp = new StringBuffer ();
  2153. for (int i = 0; i < s.length (); i++) {
  2154. char c = s.charAt (i);
  2155. if (!XmlChars.isSpace (c)) {
  2156. strTmp.append (c);
  2157. continue;
  2158. }
  2159. strTmp.append (' ');
  2160. while (++i < s.length () && XmlChars.isSpace (s.charAt (i)))
  2161. didStrip = true;
  2162. i--;
  2163. }
  2164. if (supportValidation && isValidating && isStandalone) {
  2165. if (invalidIfNeeded && (s2 == null || didStrip))
  2166. // XXX would like to tell the name of the attribute
  2167. // which shouldn't have needed normalization
  2168. error ("V-019", null);
  2169. }
  2170. if (didStrip)
  2171. return strTmp.toString ();
  2172. else
  2173. return s;
  2174. }
  2175. private boolean maybeConditionalSect ()
  2176. throws IOException, SAXException
  2177. {
  2178. // [61] conditionalSect ::= includeSect | ignoreSect
  2179. if (!peek ("<!["))
  2180. return false;
  2181. String keyword;
  2182. InputEntity start = in;
  2183. maybeWhitespace ();
  2184. if ((keyword = maybeGetName ()) == null)
  2185. fatal ("P-046");
  2186. maybeWhitespace ();
  2187. nextChar ('[', "F-030", null);
  2188. // [62] includeSect ::= '<![' S? 'INCLUDE' S? '['
  2189. // extSubsetDecl ']]>'
  2190. if ("INCLUDE".equals (keyword)) {
  2191. for (;;) {
  2192. while (in.isEOF () && in != start)
  2193. in = in.pop ();
  2194. if (in.isEOF ()) {
  2195. if (supportValidation && isValidating)
  2196. error ("V-020", null);
  2197. in = in.pop ();
  2198. }
  2199. if (peek ("]]>"))
  2200. break;
  2201. doLexicalPE = false;
  2202. if (maybeWhitespace ())
  2203. continue;
  2204. if (maybePEReference ())
  2205. continue;
  2206. doLexicalPE = true;
  2207. if (maybeMarkupDecl () || maybeConditionalSect ())
  2208. continue;
  2209. fatal ("P-047");
  2210. }
  2211. // [63] ignoreSect ::= '<![' S? 'IGNORE' S? '['
  2212. // ignoreSectcontents ']]>'
  2213. // [64] ignoreSectcontents ::= Ignore ('<!['
  2214. // ignoreSectcontents ']]>' Ignore)*
  2215. // [65] Ignore ::= Char* - (Char* ('<![' | ']]>') Char*)
  2216. } else if ("IGNORE".equals (keyword)) {
  2217. int nestlevel = 1;
  2218. // ignoreSectcontents
  2219. doLexicalPE = false;
  2220. while (nestlevel > 0) {
  2221. char c = getc (); // will pop input entities
  2222. if (c == '<') {
  2223. if (peek ("!["))
  2224. nestlevel++;
  2225. } else if (c == ']') {
  2226. if (peek ("]>"))
  2227. nestlevel--;
  2228. } else
  2229. continue;
  2230. }
  2231. } else
  2232. fatal ("P-048", new Object [] { keyword });
  2233. return true;
  2234. }
  2235. //
  2236. // CHAPTER 4: Physical Structures
  2237. //
  2238. private boolean maybeReferenceInContent (
  2239. ElementDecl element,
  2240. ElementValidator validator
  2241. ) throws IOException, SAXException
  2242. {
  2243. // [66] CharRef ::= ('&#' [0-9]+) | ('&#x' [0-9a-fA-F]*) ';'
  2244. // [67] Reference ::= EntityRef | CharRef
  2245. // [68] EntityRef ::= '&' Name ';'
  2246. if (!in.peekc ('&'))
  2247. return false;
  2248. if (!in.peekc ('#')) {
  2249. String name = maybeGetName ();
  2250. if (name == null)
  2251. fatal ("P-009");
  2252. nextChar (';', "F-020", name);
  2253. expandEntityInContent (element, name, validator);
  2254. return true;
  2255. }
  2256. validator.text ();
  2257. contentHandler.characters (charTmp, 0,
  2258. surrogatesToCharTmp (parseCharNumber ()));
  2259. return true;
  2260. }
  2261. // parse decimal or hex numeric character reference
  2262. private int parseCharNumber ()
  2263. throws SAXException, IOException
  2264. {
  2265. char c;
  2266. int retval = 0;
  2267. // n.b. we ignore overflow ...
  2268. if (getc () != 'x') {
  2269. ungetc ();
  2270. for (;;) {
  2271. c = getc ();
  2272. if (c >= '0' && c <= '9') {
  2273. retval *= 10;
  2274. retval += (c - '0');
  2275. continue;
  2276. }
  2277. if (c == ';')
  2278. return retval;
  2279. fatal ("P-049");
  2280. }
  2281. } else for (;;) {
  2282. c = getc ();
  2283. if (c >= '0' && c <= '9') {
  2284. retval <<= 4;
  2285. retval += (c - '0');
  2286. continue;
  2287. }
  2288. if (c >= 'a' && c <= 'f') {
  2289. retval <<= 4;
  2290. retval += 10 + (c - 'a');
  2291. continue;
  2292. }
  2293. if (c >= 'A' && c <= 'F') {
  2294. retval <<= 4;
  2295. retval += 10 + (c - 'A');
  2296. continue;
  2297. }
  2298. if (c == ';')
  2299. return retval;
  2300. fatal ("P-050");
  2301. }
  2302. }
  2303. // parameter is a UCS-4 character ... i.e. not just 16 bit UNICODE,
  2304. // though still subject to the 'Char' construct in XML
  2305. private int surrogatesToCharTmp (int ucs4)
  2306. throws SAXException
  2307. {
  2308. if (ucs4 <= 0xffff) {
  2309. if (XmlChars.isChar (ucs4)) {
  2310. charTmp [0] = (char) ucs4;
  2311. return 1;
  2312. }
  2313. } else if (ucs4 <= 0x0010ffff) {
  2314. // we represent these as UNICODE surrogate pairs
  2315. ucs4 -= 0x10000;
  2316. charTmp [0] = (char) (0xd800 | ((ucs4 >> 10) & 0x03ff));
  2317. charTmp [1] = (char) (0xdc00 | (ucs4 & 0x03ff));
  2318. return 2;
  2319. }
  2320. fatal ("P-051", new Object [] { Integer.toHexString (ucs4) });
  2321. // NOTREACHED
  2322. return -1;
  2323. }
  2324. private void expandEntityInContent (
  2325. ElementDecl element,
  2326. String name,
  2327. ElementValidator validator
  2328. ) throws SAXException, IOException
  2329. {
  2330. Object entity = entities.get (name);
  2331. InputEntity last = in;
  2332. if (entity == null) {
  2333. //
  2334. // Note: much confusion about whether spec requires such
  2335. // errors to be fatal in many cases, but none about whether
  2336. // it allows "normal" errors to be unrecoverable!
  2337. //
  2338. fatal ("P-014", new Object [] { name });
  2339. }
  2340. //throw fatal error when entity expansion count reaches the limit set by application
  2341. //if we don't want to have any costraint on number of entity that can be expanaded
  2342. //set the DEFAULT_ENTITY_EXPANSION_LIMIT to -1.
  2343. if(entityExpansionLimit != -1 && entityExpansionCount++ >= entityExpansionLimit){
  2344. fatal ("P-086", new Object[] {new Integer(entityExpansionLimit)});
  2345. };
  2346. //only for SECURITY_DEBUG
  2347. //System.out.println("Entity Expansion Count = " + entityExpansionCount + " :: Entity Name = " + name );
  2348. if (entity instanceof InternalEntity) {
  2349. InternalEntity e = (InternalEntity) entity;
  2350. //
  2351. // we need to expand both entities and markup here...
  2352. //
  2353. if (supportValidation && isValidating
  2354. && isStandalone
  2355. && !e.isFromInternalSubset)
  2356. error ("V-002", new Object [] { name });
  2357. pushReader (e.buf, name, true);
  2358. content (element, true, validator);
  2359. if (in != last && !in.isEOF ()) {
  2360. while (in.isInternal ())
  2361. in = in.pop ();
  2362. fatal ("P-052", new Object [] { name });
  2363. }
  2364. lexicalHandler.endEntity(name);
  2365. in = in.pop ();
  2366. } else if (entity instanceof ExternalEntity) {
  2367. ExternalEntity e = (ExternalEntity) entity;
  2368. if (e.notation != null)
  2369. fatal ("P-053", new Object [] { name });
  2370. if (supportValidation && isValidating
  2371. && isStandalone
  2372. && !e.isFromInternalSubset)
  2373. error ("V-002", new Object [] { name });
  2374. externalParsedEntity (element, e, validator);
  2375. } else
  2376. throw new InternalError (name);
  2377. }
  2378. private boolean maybePEReference ()
  2379. throws IOException, SAXException
  2380. {
  2381. // This is the SYNTACTIC version of this construct.
  2382. // When processing external entities, there is also
  2383. // a LEXICAL version; see getc() and doLexicalPE.
  2384. // [69] PEReference ::= '%' Name ';'
  2385. if (!in.peekc ('%'))
  2386. return false;
  2387. String name = maybeGetName ();
  2388. Object entity;
  2389. if (name == null)
  2390. fatal ("P-011");
  2391. nextChar (';', "F-021", name);
  2392. entity = params.get (name);
  2393. if (entity instanceof InternalEntity) {
  2394. InternalEntity value = (InternalEntity) entity;
  2395. pushReader (value.buf, name, false);
  2396. } else if (entity instanceof ExternalEntity) {
  2397. externalParameterEntity ((ExternalEntity)entity);
  2398. } else if (entity == null) {
  2399. //
  2400. // NOTE: by treating undefined parameter entities as
  2401. // nonfatal, we are assuming that the contradiction
  2402. // between them being a WFC versus a VC is resolved in
  2403. // favor of the latter. Further, we are assuming that
  2404. // validating parsers should behave like nonvalidating
  2405. // ones in such a case: ignoring further declarations.
  2406. //
  2407. ignoreDeclarations = true;
  2408. if (supportValidation && isValidating)
  2409. error ("V-022", new Object [] { name });
  2410. else
  2411. warning ("V-022", new Object [] { name });
  2412. }
  2413. return true;
  2414. }
  2415. private boolean maybeEntityDecl ()
  2416. throws IOException, SAXException
  2417. {
  2418. // [70] EntityDecl ::= GEDecl | PEDecl
  2419. // [71] GEDecl ::= '<!ENTITY' S Name S EntityDef S? '>'
  2420. // [72] PEDecl ::= '<!ENTITY' S '%' S Name S PEDEF S? '>'
  2421. // [73] EntityDef ::= EntityValue | (ExternalID NDataDecl?)
  2422. // [74] PEDef ::= EntityValue | ExternalID
  2423. //
  2424. InputEntity start = peekDeclaration ("!ENTITY");
  2425. if (start == null)
  2426. return false;
  2427. String entityName;
  2428. SimpleHashtable defns;
  2429. ExternalEntity externalId;
  2430. boolean doStore;
  2431. // PE expansion gets selectively turned off several places:
  2432. // in ENTITY declarations (here), in comments, in PIs.
  2433. // Here, we allow PE entities to be declared, and allows
  2434. // literals to include PE refs without the added spaces
  2435. // required with their expansion in markup decls.
  2436. doLexicalPE = false;
  2437. whitespace ("F-005");
  2438. if (in.peekc ('%')) {
  2439. whitespace ("F-006");
  2440. defns = params;
  2441. } else
  2442. defns = entities;
  2443. ungetc (); // leave some whitespace
  2444. doLexicalPE = true;
  2445. entityName = getMarkupDeclname ("F-017", false);
  2446. whitespace ("F-007");
  2447. externalId = maybeExternalID ();
  2448. //
  2449. // first definition sticks ... e.g. internal subset PEs are used
  2450. // to override DTD defaults. It's also an "error" to incorrectly
  2451. // redefine builtin internal entities, but since reporting such
  2452. // errors is optional we only give warnings ("just in case") for
  2453. // non-parameter entities.
  2454. //
  2455. doStore = (defns.get (entityName) == null);
  2456. if (!doStore && defns == entities)
  2457. warning ("P-054", new Object [] { entityName });
  2458. // if we skipped a PE, ignore declarations since the
  2459. // PE might have included an ovrriding declaration
  2460. doStore &= !ignoreDeclarations;
  2461. // internal entities
  2462. if (externalId == null) {
  2463. char value [];
  2464. InternalEntity entity;
  2465. doLexicalPE = false; // "ab%bar;cd" -maybe-> "abcd"
  2466. parseLiteral (true);
  2467. doLexicalPE = true;
  2468. if (doStore) {
  2469. value = new char [strTmp.length ()];
  2470. if (value.length != 0)
  2471. strTmp.getChars (0, value.length, value, 0);
  2472. entity = new InternalEntity (entityName, value);
  2473. entity.isPE = (defns == params);
  2474. entity.isFromInternalSubset = !inExternalPE;
  2475. defns.put (entityName, entity);
  2476. // Report event
  2477. if (defns == params) {
  2478. entityName = "%" + entityName;
  2479. }
  2480. declHandler.internalEntityDecl(entityName, new String(value));
  2481. }
  2482. // external entities (including unparsed)
  2483. } else {
  2484. // [76] NDataDecl ::= S 'NDATA' S Name
  2485. if (defns == entities && maybeWhitespace ()
  2486. && peek ("NDATA")) {
  2487. externalId.notation = getMarkupDeclname ("F-018", false);
  2488. // flag undeclared notation for checking after
  2489. // the DTD is fully processed
  2490. if (supportValidation && isValidating
  2491. && notations.get (externalId.notation) == null)
  2492. notations.put (externalId.notation, Boolean.TRUE);
  2493. }
  2494. externalId.name = entityName;
  2495. externalId.isPE = (defns == params);
  2496. externalId.isFromInternalSubset = !inExternalPE;
  2497. if (doStore) {
  2498. defns.put (entityName, externalId);
  2499. if (externalId.notation != null) {
  2500. dtdHandler.unparsedEntityDecl (entityName,
  2501. externalId.publicId, externalId.systemId,
  2502. externalId.notation);
  2503. } else {
  2504. // Parsed external entity, either general or parameter
  2505. if (defns == params) {
  2506. entityName = "%" + entityName;
  2507. }
  2508. declHandler.externalEntityDecl(entityName,
  2509. externalId.publicId, externalId.systemId);
  2510. }
  2511. }
  2512. }
  2513. maybeWhitespace ();
  2514. nextChar ('>', "F-031", entityName);
  2515. if (supportValidation && isValidating && start != in)
  2516. error ("V-013", null);
  2517. return true;
  2518. }
  2519. private ExternalEntity maybeExternalID ()
  2520. throws IOException, SAXException
  2521. {
  2522. // [75] ExternalID ::= 'SYSTEM' S SystemLiteral
  2523. // | 'PUBLIC' S' PubidLiteral S Systemliteral
  2524. String temp = null;
  2525. ExternalEntity retval;
  2526. if (peek ("PUBLIC")) {
  2527. whitespace ("F-009");
  2528. temp = parsePublicId ();
  2529. } else if (!peek ("SYSTEM"))
  2530. return null;
  2531. retval = new ExternalEntity (in);
  2532. retval.publicId = temp;
  2533. whitespace ("F-008");
  2534. retval.verbatimSystemId = getQuotedString("F-034", null);
  2535. retval.systemId = resolveURI(retval.verbatimSystemId);
  2536. return retval;
  2537. }
  2538. private String parseSystemId()
  2539. throws IOException, SAXException
  2540. {
  2541. String uri = getQuotedString("F-034", null);
  2542. return resolveURI(uri);
  2543. }
  2544. private String resolveURI(String uri)
  2545. throws SAXException
  2546. {
  2547. int temp = uri.indexOf (':');
  2548. // resolve relative URIs ... must do it here since
  2549. // it's relative to the source file holding the URI!
  2550. // "new java.net.URL (URL, string)" conforms to RFC 1630,
  2551. // but we can't use that except when the URI is a URL.
  2552. // The entity resolver is allowed to handle URIs that are
  2553. // not URLs, so we pass URIs through with scheme intact
  2554. if (temp == -1 || uri.indexOf ('/') < temp) {
  2555. String baseURI;
  2556. baseURI = in.getSystemId ();
  2557. if (baseURI == null)
  2558. fatal ("P-055", new Object [] { uri });
  2559. if (uri.length () == 0)
  2560. uri = ".";
  2561. baseURI = baseURI.substring (0, baseURI.lastIndexOf ('/') + 1);
  2562. if (uri.charAt (0) != '/')
  2563. uri = baseURI + uri;
  2564. else {
  2565. // We have relative URI that begins with a '/'
  2566. // Extract scheme including colon from baseURI
  2567. String baseURIScheme;
  2568. int colonIndex = baseURI.indexOf(':');
  2569. if (colonIndex == -1) {
  2570. // Base URI does not have a scheme so default to
  2571. // "file:" scheme
  2572. baseURIScheme = "file:";
  2573. } else {
  2574. baseURIScheme = baseURI.substring(0, colonIndex + 1);
  2575. }
  2576. uri = baseURIScheme + uri;
  2577. }
  2578. // letting other code map any "/xxx/../" or "/./" to "/",
  2579. // since all URIs must handle it the same.
  2580. }
  2581. // check for fragment ID in URI
  2582. if (uri.indexOf ('#') != -1)
  2583. error ("P-056", new Object [] { uri });
  2584. return uri;
  2585. }
  2586. private void maybeTextDecl ()
  2587. throws IOException, SAXException
  2588. {
  2589. // [77] TextDecl ::= '<?xml' VersionInfo? EncodingDecl S? '?>'
  2590. if (!in.isXmlDeclOrTextDeclPrefix()) {
  2591. return;
  2592. }
  2593. // Consume '<?xml'
  2594. peek("<?xml");
  2595. readVersion (false, "1.0");
  2596. readEncoding (true);
  2597. maybeWhitespace ();
  2598. if (!peek ("?>"))
  2599. fatal ("P-057");
  2600. }
  2601. // returns true except in case of nonvalidating parser which
  2602. // chose to ignore the entity.
  2603. private boolean externalParsedEntity (
  2604. ElementDecl element,
  2605. ExternalEntity next,
  2606. ElementValidator validator
  2607. ) throws IOException, SAXException
  2608. {
  2609. // [78] ExtParsedEnt ::= TextDecl? content
  2610. if (!pushReader (next)) {
  2611. if (!isInAttribute) {
  2612. lexicalHandler.endEntity(next.name);
  2613. }
  2614. return false;
  2615. }
  2616. maybeTextDecl ();
  2617. content (element, true, validator);
  2618. if (!in.isEOF ())
  2619. fatal ("P-058", new Object [] { next.name });
  2620. in = in.pop ();
  2621. if (!isInAttribute) {
  2622. lexicalHandler.endEntity(next.name);
  2623. }
  2624. return true;
  2625. }
  2626. private void externalParameterEntity (ExternalEntity next)
  2627. throws IOException, SAXException
  2628. {
  2629. //
  2630. // Reap the intended benefits of standalone declarations:
  2631. // don't deal with external parameter entities, except to
  2632. // validate the standalone declaration.
  2633. //
  2634. // XXX perhaps: also add an option to skip reading external
  2635. // PEs when not validating, so this behaves like the parsers
  2636. // in Gecko and IE5. Means setting ignoreDeclarations ...
  2637. //
  2638. if (isStandalone && fastStandalone)
  2639. return;
  2640. // n.b. "in external parameter entities" (and external
  2641. // DTD subset, same grammar) parameter references can
  2642. // occur "within" markup declarations ... expansions can
  2643. // cross syntax rules. Flagged here; affects getc().
  2644. // [79] ExtPE ::= TextDecl? extSubsetDecl
  2645. // [31] extSubsetDecl ::= ( markupdecl | conditionalSect
  2646. // | PEReference | S )*
  2647. InputEntity pe;
  2648. inExternalPE = true;
  2649. // Check for common case of file not found and throw a
  2650. // SAXParseException
  2651. try {
  2652. // XXX if this returns false ...
  2653. pushReader (next);
  2654. } catch (IOException e) {
  2655. fatal ("P-082", new Object [] { next.systemId }, e);
  2656. }
  2657. pe = in;
  2658. // Check for common case of bad URL and throw a SAXParseException.
  2659. // For bad URL case, JDK does not throw an exception when
  2660. // URLConnection.getInputStream() is called but later when the app
  2661. // tries to read from the stream in maybeTextDecl().
  2662. try {
  2663. maybeTextDecl ();
  2664. } catch (IOException e) {
  2665. // Pop invalid InputEntity so Locator info will be correct
  2666. in = in.pop ();
  2667. fatal ("P-082", new Object [] { next.systemId }, e);
  2668. }
  2669. while (!pe.isEOF ()) {
  2670. // pop internal PEs (and whitespace before/after)
  2671. if (in.isEOF ()) {
  2672. in = in.pop ();
  2673. continue;
  2674. }
  2675. doLexicalPE = false;
  2676. if (maybeWhitespace ())
  2677. continue;
  2678. if (maybePEReference ())
  2679. continue;
  2680. doLexicalPE = true;
  2681. if (maybeMarkupDecl () || maybeConditionalSect ())
  2682. continue;
  2683. break;
  2684. }
  2685. // if (in != pe) throw new InternalError ("who popped my PE?");
  2686. if (!pe.isEOF ())
  2687. fatal ("P-059", new Object [] { in.getName () });
  2688. in = in.pop ();
  2689. inExternalPE = !in.isDocument ();
  2690. doLexicalPE = false;
  2691. }
  2692. private void readEncoding (boolean must)
  2693. throws IOException, SAXException
  2694. {
  2695. // [81] EncName ::= [A-Za-z] ([A-Za-z0-9._] | '-')*
  2696. String name = maybeReadAttribute ("encoding", must);
  2697. if (name == null)
  2698. return;
  2699. for (int i = 0; i < name.length (); i++) {
  2700. char c = name.charAt (i);
  2701. if ((c >= 'A' && c <= 'Z')
  2702. || (c >= 'a' && c <= 'z'))
  2703. continue;
  2704. if (i != 0
  2705. && ((c >= '0' && c <= '9')
  2706. || c == '-'
  2707. || c == '_'
  2708. || c == '.'
  2709. ))
  2710. continue;
  2711. fatal ("P-060", new Object [] { new Character (c) });
  2712. }
  2713. //
  2714. // This should be the encoding in use, and it's even an error for
  2715. // it to be anything else (in certain cases that are impractical to
  2716. // to test, and may even be insufficient). So, we do the best we
  2717. // can, and warn if things look suspicious. Note that Java doesn't
  2718. // uniformly expose the encodings, and that the names it uses
  2719. // internally are nonstandard. Also, that the XML spec allows
  2720. // such "errors" not to be reported at all.
  2721. //
  2722. String currentEncoding = in.getEncoding ();
  2723. if (currentEncoding != null
  2724. && !name.equalsIgnoreCase (currentEncoding))
  2725. warning ("P-061", new Object [] { name, currentEncoding });
  2726. }
  2727. private boolean maybeNotationDecl ()
  2728. throws IOException, SAXException
  2729. {
  2730. // [82] NotationDecl ::= '<!NOTATION' S Name S
  2731. // (ExternalID | PublicID) S? '>'
  2732. // [83] PublicID ::= 'PUBLIC' S PubidLiteral
  2733. InputEntity start = peekDeclaration ("!NOTATION");
  2734. if (start == null)
  2735. return false;
  2736. String name = getMarkupDeclname ("F-019", false);
  2737. ExternalEntity entity = new ExternalEntity (in);
  2738. whitespace ("F-011");
  2739. if (peek ("PUBLIC")) {
  2740. whitespace ("F-009");
  2741. entity.publicId = parsePublicId ();
  2742. if (maybeWhitespace ()) {
  2743. if (!peek (">"))
  2744. entity.systemId = parseSystemId ();
  2745. else
  2746. ungetc ();
  2747. }
  2748. } else if (peek ("SYSTEM")) {
  2749. whitespace ("F-008");
  2750. entity.systemId = parseSystemId ();
  2751. } else
  2752. fatal ("P-062");
  2753. maybeWhitespace ();
  2754. nextChar ('>', "F-032", name);
  2755. if (supportValidation && isValidating && start != in)
  2756. error ("V-013", null);
  2757. if (entity.systemId != null && entity.systemId.indexOf ('#') != -1)
  2758. error ("P-056", new Object [] { entity.systemId });
  2759. Object value = notations.get (name);
  2760. if (value != null && value instanceof ExternalEntity)
  2761. warning ("P-063", new Object [] { name });
  2762. // if we skipped a PE, ignore declarations since the
  2763. // PE might have included an ovrriding declaration
  2764. else if (!ignoreDeclarations) {
  2765. notations.put (name, entity);
  2766. dtdHandler.notationDecl (name, entity.publicId,
  2767. entity.systemId);
  2768. }
  2769. return true;
  2770. }
  2771. ////////////////////////////////////////////////////////////////
  2772. //
  2773. // UTILITIES
  2774. //
  2775. ////////////////////////////////////////////////////////////////
  2776. private char getc () throws IOException, SAXException
  2777. {
  2778. if (!(inExternalPE && doLexicalPE)) {
  2779. char c = in.getc ();
  2780. if (c == '%' && doLexicalPE)
  2781. fatal ("P-080");
  2782. return c;
  2783. }
  2784. //
  2785. // External parameter entities get funky processing of '%param;'
  2786. // references. It's not clearly defined in the XML spec; but it
  2787. // boils down to having those refs be _lexical_ in most cases to
  2788. // include partial syntax productions. It also needs selective
  2789. // enabling; "<!ENTITY % foo ...>" must work, for example, and
  2790. // if "bar" is an empty string PE, "ab%bar;cd" becomes "abcd"
  2791. // if it's expanded in a literal, else "ab cd". PEs also do
  2792. // not expand within comments or PIs, and external PEs are only
  2793. // allowed to have markup decls (and so aren't handled lexically).
  2794. //
  2795. // This PE handling should be merged into maybeWhitespace, where
  2796. // it can be dealt with more consistently.
  2797. //
  2798. // Also, there are some validity constraints in this area.
  2799. //
  2800. char c;
  2801. while (in.isEOF ()) {
  2802. if (in.isInternal () || (doLexicalPE && !in.isDocument ()))
  2803. in = in.pop ();
  2804. else {
  2805. fatal ("P-064", new Object [] { in.getName () });
  2806. }
  2807. }
  2808. if ((c = in.getc ()) == '%' && doLexicalPE) {
  2809. // PE ref ::= '%' name ';'
  2810. String name = maybeGetName ();
  2811. Object entity;
  2812. if (name == null)
  2813. fatal ("P-011");
  2814. nextChar (';', "F-021", name);
  2815. entity = params.get (name);
  2816. // push a magic "entity" before and after the
  2817. // real one, so ungetc() behaves uniformly
  2818. pushReader (" ".toCharArray (), null, false);
  2819. if (entity instanceof InternalEntity)
  2820. pushReader (((InternalEntity) entity).buf, name, false);
  2821. else if (entity instanceof ExternalEntity)
  2822. // PEs can't be unparsed!
  2823. // XXX if this returns false ...
  2824. pushReader ((ExternalEntity) entity);
  2825. else if (entity == null)
  2826. // see note in maybePEReference re making this be nonfatal.
  2827. fatal ("V-022");
  2828. else
  2829. throw new InternalError ();
  2830. pushReader (" ".toCharArray (), null, false);
  2831. return in.getc ();
  2832. }
  2833. return c;
  2834. }
  2835. private void ungetc () // throws IOException, SAXException
  2836. { in.ungetc (); }
  2837. private boolean peek (String s) throws IOException, SAXException
  2838. { return in.peek (s, null); }
  2839. // Return the entity starting the specified declaration
  2840. // (for validating declaration nesting) else null.
  2841. private InputEntity peekDeclaration (String s)
  2842. throws IOException, SAXException
  2843. {
  2844. InputEntity start;
  2845. if (!in.peekc ('<'))
  2846. return null;
  2847. start = in;
  2848. if (in.peek (s, null))
  2849. return start;
  2850. in.ungetc ();
  2851. return null;
  2852. }
  2853. private void nextChar (char c, String location, String near)
  2854. throws IOException, SAXException
  2855. {
  2856. while (in.isEOF () && !in.isDocument ())
  2857. in = in.pop ();
  2858. if (!in.peekc (c))
  2859. fatal ("P-008", new Object []
  2860. { new Character (c),
  2861. messages.getMessage (locale, location),
  2862. (near == null ? "" : ('"' + near + '"'))});
  2863. }
  2864. private void pushReader (char buf [], String name, boolean isGeneral)
  2865. throws SAXException
  2866. {
  2867. if (isGeneral && !isInAttribute) {
  2868. lexicalHandler.startEntity(name);
  2869. }
  2870. InputEntity r = InputEntity.getInputEntity (errHandler, locale);
  2871. r.init (buf, name, in, !isGeneral);
  2872. in = r;
  2873. }
  2874. // returns false if the external entity is being ignored ...
  2875. // potentially possible in nonvalidating parsers, but not
  2876. // currently supported. (See notes everywhere this is called;
  2877. // both error handling, and reporting start/stop of entity
  2878. // expansion, are issues! Also, SAX has no way to say "don't
  2879. // read this entity".)
  2880. private boolean pushReader (ExternalEntity next)
  2881. throws SAXException, IOException
  2882. {
  2883. if (!next.isPE && !isInAttribute) {
  2884. lexicalHandler.startEntity(next.name);
  2885. }
  2886. InputEntity r = InputEntity.getInputEntity (errHandler, locale);
  2887. InputSource s = next.getInputSource (resolver);
  2888. r.init (s, next.name, in, next.isPE);
  2889. in = r;
  2890. return true;
  2891. }
  2892. // error handling convenience routines
  2893. private void warning (String messageId, Object parameters [])
  2894. throws SAXException
  2895. {
  2896. SAXParseException x;
  2897. x = new SAXParseException (
  2898. messages.getMessage (locale, messageId, parameters),
  2899. locator);
  2900. // continuable, minor ... "this may matter to you..."
  2901. errHandler.warning (x);
  2902. }
  2903. // package private ... normally returns.
  2904. void error (String messageId, Object parameters [])
  2905. throws SAXException
  2906. {
  2907. SAXParseException x = new SAXParseException (
  2908. messages.getMessage (locale, messageId, parameters),
  2909. locator);
  2910. // continuable, major ... e.g. invalid document
  2911. errHandler.error (x);
  2912. }
  2913. private void fatal (String message) throws SAXException
  2914. {
  2915. fatal (message, null, null);
  2916. }
  2917. private void fatal (String message, Object parameters [])
  2918. throws SAXException
  2919. {
  2920. fatal (message, parameters, null);
  2921. }
  2922. private void fatal (String messageId, Object parameters [], Exception e)
  2923. throws SAXException
  2924. {
  2925. SAXParseException x = new SAXParseException (
  2926. messages.getMessage (locale, messageId, parameters),
  2927. locator, e);
  2928. errHandler.fatalError (x);
  2929. // not continuable ... e.g. basic well-formedness errors
  2930. throw x;
  2931. }
  2932. //
  2933. // LOCATOR -- used for err reporting. the app calls us,
  2934. // we tell where the parsing current event happened.
  2935. //
  2936. class DocLocator implements Locator {
  2937. public String getPublicId ()
  2938. {
  2939. return (in == null) ? null : in.getPublicId ();
  2940. }
  2941. public String getSystemId ()
  2942. {
  2943. return (in == null) ? null : in.getSystemId ();
  2944. }
  2945. public int getLineNumber ()
  2946. {
  2947. return (in == null) ? -1 : in.getLineNumber ();
  2948. }
  2949. public int getColumnNumber ()
  2950. {
  2951. return (in == null) ? -1 : in.getColumnNumber ();
  2952. }
  2953. }
  2954. //
  2955. // Map char arrays to strings ... cuts down both on memory and
  2956. // CPU usage for element/attribute/other names that are reused.
  2957. //
  2958. // Documents typically repeat names a lot, so we more or less
  2959. // intern all the strings within the document; since some strings
  2960. // are repeated in multiple documents (e.g. stylesheets) we go
  2961. // a bit further, and intern globally.
  2962. //
  2963. static class NameCache {
  2964. //
  2965. // Unless we auto-grow this, the default size should be a
  2966. // reasonable bit larger than needed for most XML files
  2967. // we've yet seen (and be prime). If it's too small, the
  2968. // penalty is just excess cache collisions.
  2969. //
  2970. NameCacheEntry hashtable [] = new NameCacheEntry [541];
  2971. //
  2972. // Usually we just want to get the 'symbol' for these chars
  2973. //
  2974. String lookup (char value [], int len)
  2975. {
  2976. return lookupEntry (value, len).name;
  2977. }
  2978. //
  2979. // Sometimes we need to scan the chars in the resulting
  2980. // string, so there's an accessor which exposes them.
  2981. // (Mostly for element end tags.)
  2982. //
  2983. NameCacheEntry lookupEntry (char value [], int len)
  2984. {
  2985. int index = 0;
  2986. NameCacheEntry entry;
  2987. // hashing to get index
  2988. for (int i = 0; i < len; i++)
  2989. index = index * 31 + value [i];
  2990. index &= 0x7fffffff;
  2991. index %= hashtable.length;
  2992. // return entry if one's there ...
  2993. for (entry = hashtable [index];
  2994. entry != null;
  2995. entry = entry.next) {
  2996. if (entry.matches (value, len))
  2997. return entry;
  2998. }
  2999. // else create new one
  3000. entry = new NameCacheEntry ();
  3001. entry.chars = new char [len];
  3002. System.arraycopy (value, 0, entry.chars, 0, len);
  3003. entry.name = new String (entry.chars);
  3004. //
  3005. // NOTE: JDK 1.1 has a fixed size string intern table,
  3006. // with non-GC'd entries. It can panic here; that's a
  3007. // JDK problem, use 1.2 or later with many identifiers.
  3008. //
  3009. entry.name = entry.name.intern (); // "global" intern
  3010. entry.next = hashtable [index];
  3011. hashtable [index] = entry;
  3012. return entry;
  3013. }
  3014. }
  3015. static class NameCacheEntry {
  3016. String name;
  3017. char chars [];
  3018. NameCacheEntry next;
  3019. boolean matches (char value [], int len)
  3020. {
  3021. if (chars.length != len)
  3022. return false;
  3023. for (int i = 0; i < len; i++)
  3024. if (value [i] != chars [i])
  3025. return false;
  3026. return true;
  3027. }
  3028. }
  3029. //
  3030. // A combined handler class that does nothing
  3031. //
  3032. private static class NullHandler extends DefaultHandler
  3033. implements LexicalHandler, DeclHandler
  3034. {
  3035. public void startDTD (String name, String publicId, String systemId) {}
  3036. public void endDTD () {}
  3037. public void startEntity (String name) {}
  3038. public void endEntity (String name) {}
  3039. public void startCDATA () {}
  3040. public void endCDATA () {}
  3041. public void comment (char ch[], int start, int length) {}
  3042. public void elementDecl (String name, String model) {}
  3043. public void attributeDecl (String eName, String aName, String type,
  3044. String valueDefault, String value) {}
  3045. public void internalEntityDecl (String name, String value) {}
  3046. public void externalEntityDecl (String name, String publicId,
  3047. String systemId) {}
  3048. }
  3049. //
  3050. // Message catalog for diagnostics.
  3051. //
  3052. static final Catalog messages = new Catalog();
  3053. static final class Catalog extends MessageCatalog {
  3054. Catalog() {
  3055. super(Parser2.class);
  3056. }
  3057. }
  3058. }