1. /*
  2. * The Apache Software License, Version 1.1
  3. *
  4. *
  5. * Copyright (c) 1999-2004 The Apache Software Foundation.
  6. * All rights reserved.
  7. *
  8. * Redistribution and use in source and binary forms, with or without
  9. * modification, are permitted provided that the following conditions
  10. * are met:
  11. *
  12. * 1. Redistributions of source code must retain the above copyright
  13. * notice, this list of conditions and the following disclaimer.
  14. *
  15. * 2. Redistributions in binary form must reproduce the above copyright
  16. * notice, this list of conditions and the following disclaimer in
  17. * the documentation and/or other materials provided with the
  18. * distribution.
  19. *
  20. * 3. The end-user documentation included with the redistribution,
  21. * if any, must include the following acknowledgment:
  22. * "This product includes software developed by the
  23. * Apache Software Foundation (http://www.apache.org/)."
  24. * Alternately, this acknowledgment may appear in the software itself,
  25. * if and wherever such third-party acknowledgments normally appear.
  26. *
  27. * 4. The names "Xerces" and "Apache Software Foundation" must
  28. * not be used to endorse or promote products derived from this
  29. * software without prior written permission. For written
  30. * permission, please contact apache@apache.org.
  31. *
  32. * 5. Products derived from this software may not be called "Apache",
  33. * nor may "Apache" appear in their name, without prior written
  34. * permission of the Apache Software Foundation.
  35. *
  36. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  37. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  38. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  39. * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
  40. * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  41. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  42. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  43. * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  44. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  45. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  46. * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  47. * SUCH DAMAGE.
  48. * ====================================================================
  49. *
  50. * This software consists of voluntary contributions made by many
  51. * individuals on behalf of the Apache Software Foundation and was
  52. * originally based on software copyright (c) 1999, International
  53. * Business Machines, Inc., http://www.apache.org. For more
  54. * information on the Apache Software Foundation, please see
  55. * <http://www.apache.org/>.
  56. */
  57. package com.sun.org.apache.xerces.internal.impl;
  58. import java.io.IOException;
  59. import com.sun.org.apache.xerces.internal.impl.msg.XMLMessageFormatter;
  60. import com.sun.org.apache.xerces.internal.util.SymbolTable;
  61. import com.sun.org.apache.xerces.internal.util.XMLAttributesImpl;
  62. import com.sun.org.apache.xerces.internal.util.XMLChar;
  63. import com.sun.org.apache.xerces.internal.util.XMLStringBuffer;
  64. import com.sun.org.apache.xerces.internal.xni.Augmentations;
  65. import com.sun.org.apache.xerces.internal.xni.XMLDTDContentModelHandler;
  66. import com.sun.org.apache.xerces.internal.xni.XMLDTDHandler;
  67. import com.sun.org.apache.xerces.internal.xni.XMLResourceIdentifier;
  68. import com.sun.org.apache.xerces.internal.xni.XMLString;
  69. import com.sun.org.apache.xerces.internal.xni.XNIException;
  70. import com.sun.org.apache.xerces.internal.xni.parser.XMLComponent;
  71. import com.sun.org.apache.xerces.internal.xni.parser.XMLComponentManager;
  72. import com.sun.org.apache.xerces.internal.xni.parser.XMLConfigurationException;
  73. import com.sun.org.apache.xerces.internal.xni.parser.XMLDTDScanner;
  74. import com.sun.org.apache.xerces.internal.xni.parser.XMLInputSource;
  75. /**
  76. * This class is responsible for scanning the declarations found
  77. * in the internal and external subsets of a DTD in an XML document.
  78. * The scanner acts as the sources for the DTD information which is
  79. * communicated to the DTD handlers.
  80. * <p>
  81. * This component requires the following features and properties from the
  82. * component manager that uses it:
  83. * <ul>
  84. * <li>http://xml.org/sax/features/validation</li>
  85. * <li>http://apache.org/xml/features/scanner/notify-char-refs</li>
  86. * <li>http://apache.org/xml/properties/internal/symbol-table</li>
  87. * <li>http://apache.org/xml/properties/internal/error-reporter</li>
  88. * <li>http://apache.org/xml/properties/internal/entity-manager</li>
  89. * </ul>
  90. *
  91. * @author Arnaud Le Hors, IBM
  92. * @author Andy Clark, IBM
  93. * @author Glenn Marcy, IBM
  94. * @author Eric Ye, IBM
  95. *
  96. * @version $Id: XMLDTDScannerImpl.java,v 1.49 2004/02/27 20:36:07 mrglavas Exp $
  97. */
  98. public class XMLDTDScannerImpl
  99. extends XMLScanner
  100. implements XMLDTDScanner, XMLComponent, XMLEntityHandler {
  101. //
  102. // Constants
  103. //
  104. // scanner states
  105. /** Scanner state: end of input. */
  106. protected static final int SCANNER_STATE_END_OF_INPUT = 0;
  107. /** Scanner state: text declaration. */
  108. protected static final int SCANNER_STATE_TEXT_DECL = 1;
  109. /** Scanner state: markup declaration. */
  110. protected static final int SCANNER_STATE_MARKUP_DECL = 2;
  111. // recognized features and properties
  112. /** Recognized features. */
  113. private static final String[] RECOGNIZED_FEATURES = {
  114. VALIDATION,
  115. NOTIFY_CHAR_REFS,
  116. };
  117. /** Feature defaults. */
  118. private static final Boolean[] FEATURE_DEFAULTS = {
  119. null,
  120. Boolean.FALSE,
  121. };
  122. /** Recognized properties. */
  123. private static final String[] RECOGNIZED_PROPERTIES = {
  124. SYMBOL_TABLE,
  125. ERROR_REPORTER,
  126. ENTITY_MANAGER,
  127. };
  128. /** Property defaults. */
  129. private static final Object[] PROPERTY_DEFAULTS = {
  130. null,
  131. null,
  132. null,
  133. };
  134. // debugging
  135. /** Debug scanner state. */
  136. private static final boolean DEBUG_SCANNER_STATE = false;
  137. //
  138. // Data
  139. //
  140. // handlers
  141. /** DTD handler. */
  142. protected XMLDTDHandler fDTDHandler;
  143. /** DTD content model handler. */
  144. protected XMLDTDContentModelHandler fDTDContentModelHandler;
  145. // state
  146. /** Scanner state. */
  147. protected int fScannerState;
  148. /** Standalone. */
  149. protected boolean fStandalone;
  150. /** Seen external DTD. */
  151. protected boolean fSeenExternalDTD;
  152. /** Seen external parameter entity. */
  153. protected boolean fSeenExternalPE;
  154. // private data
  155. /** Start DTD called. */
  156. private boolean fStartDTDCalled;
  157. /** Default attribute */
  158. private XMLAttributesImpl fAttributes = new XMLAttributesImpl();
  159. /**
  160. * Stack of content operators (either '|' or ',') in children
  161. * content.
  162. */
  163. private int[] fContentStack = new int[5];
  164. /** Size of content stack. */
  165. private int fContentDepth;
  166. /** Parameter entity stack to check well-formedness. */
  167. private int[] fPEStack = new int[5];
  168. /** Parameter entity stack to report start/end entity calls. */
  169. private boolean[] fPEReport = new boolean[5];
  170. /** Number of opened parameter entities. */
  171. private int fPEDepth;
  172. /** Markup depth. */
  173. private int fMarkUpDepth;
  174. /** Number of opened external entities. */
  175. private int fExtEntityDepth;
  176. /** Number of opened include sections. */
  177. private int fIncludeSectDepth;
  178. // temporary variables
  179. /** Array of 3 strings. */
  180. private String[] fStrings = new String[3];
  181. /** String. */
  182. private XMLString fString = new XMLString();
  183. /** String buffer. */
  184. private XMLStringBuffer fStringBuffer = new XMLStringBuffer();
  185. /** String buffer. */
  186. private XMLStringBuffer fStringBuffer2 = new XMLStringBuffer();
  187. /** Literal text. */
  188. private XMLString fLiteral = new XMLString();
  189. /** Literal text. */
  190. private XMLString fLiteral2 = new XMLString();
  191. /** Enumeration values. */
  192. private String[] fEnumeration = new String[5];
  193. /** Enumeration values count. */
  194. private int fEnumerationCount;
  195. /** Ignore conditional section buffer. */
  196. private XMLStringBuffer fIgnoreConditionalBuffer = new XMLStringBuffer(128);
  197. //
  198. // Constructors
  199. //
  200. /** Default constructor. */
  201. public XMLDTDScannerImpl() {} // <init>()
  202. /** Constructor for he use of non-XMLComponentManagers. */
  203. public XMLDTDScannerImpl(SymbolTable symbolTable,
  204. XMLErrorReporter errorReporter, XMLEntityManager entityManager) {
  205. fSymbolTable = symbolTable;
  206. fErrorReporter = errorReporter;
  207. fEntityManager = entityManager;
  208. entityManager.setProperty(SYMBOL_TABLE, fSymbolTable);
  209. }
  210. //
  211. // XMLDTDScanner methods
  212. //
  213. /**
  214. * Sets the input source.
  215. *
  216. * @param inputSource The input source or null.
  217. *
  218. * @throws IOException Thrown on i/o error.
  219. */
  220. public void setInputSource(XMLInputSource inputSource) throws IOException {
  221. if (inputSource == null) {
  222. // no system id was available
  223. if (fDTDHandler != null) {
  224. fDTDHandler.startDTD(null, null);
  225. fDTDHandler.endDTD(null);
  226. }
  227. return;
  228. }
  229. fEntityManager.setEntityHandler(this);
  230. fEntityManager.startDTDEntity(inputSource);
  231. } // setInputSource(XMLInputSource)
  232. /**
  233. * Scans the external subset of the document.
  234. *
  235. * @param complete True if the scanner should scan the document
  236. * completely, pushing all events to the registered
  237. * document handler. A value of false indicates that
  238. * that the scanner should only scan the next portion
  239. * of the document and return. A scanner instance is
  240. * permitted to completely scan a document if it does
  241. * not support this "pull" scanning model.
  242. *
  243. * @return True if there is more to scan, false otherwise.
  244. */
  245. public boolean scanDTDExternalSubset(boolean complete)
  246. throws IOException, XNIException {
  247. fEntityManager.setEntityHandler(this);
  248. if (fScannerState == SCANNER_STATE_TEXT_DECL) {
  249. fSeenExternalDTD = true;
  250. boolean textDecl = scanTextDecl();
  251. if (fScannerState == SCANNER_STATE_END_OF_INPUT) {
  252. return false;
  253. }
  254. else {
  255. // next state is markup decls regardless of whether there
  256. // is a TextDecl or not
  257. setScannerState(SCANNER_STATE_MARKUP_DECL);
  258. if (textDecl && !complete) {
  259. return true;
  260. }
  261. }
  262. }
  263. // keep dispatching "events"
  264. do {
  265. if (!scanDecls(complete)) {
  266. return false;
  267. }
  268. } while (complete);
  269. // return that there is more to scan
  270. return true;
  271. } // scanDTDExternalSubset(boolean):boolean
  272. /**
  273. * Scans the internal subset of the document.
  274. *
  275. * @param complete True if the scanner should scan the document
  276. * completely, pushing all events to the registered
  277. * document handler. A value of false indicates that
  278. * that the scanner should only scan the next portion
  279. * of the document and return. A scanner instance is
  280. * permitted to completely scan a document if it does
  281. * not support this "pull" scanning model.
  282. * @param standalone True if the document was specified as standalone.
  283. * This value is important for verifying certain
  284. * well-formedness constraints.
  285. * @param hasExternalDTD True if the document has an external DTD.
  286. * This allows the scanner to properly notify
  287. * the handler of the end of the DTD in the
  288. * absence of an external subset.
  289. *
  290. * @return True if there is more to scan, false otherwise.
  291. */
  292. public boolean scanDTDInternalSubset(boolean complete, boolean standalone,
  293. boolean hasExternalSubset)
  294. throws IOException, XNIException {
  295. // reset entity scanner
  296. fEntityScanner = fEntityManager.getEntityScanner();
  297. fEntityManager.setEntityHandler(this);
  298. fStandalone = standalone;
  299. if (fScannerState == SCANNER_STATE_TEXT_DECL) {
  300. // call handler
  301. if (fDTDHandler != null) {
  302. fDTDHandler.startDTD(fEntityScanner, null);
  303. fStartDTDCalled = true;
  304. }
  305. // set starting state for internal subset
  306. setScannerState(SCANNER_STATE_MARKUP_DECL);
  307. }
  308. // keep dispatching "events"
  309. do {
  310. if (!scanDecls(complete)) {
  311. // call handler
  312. if (fDTDHandler != null && hasExternalSubset == false) {
  313. fDTDHandler.endDTD(null);
  314. }
  315. // we're done, set starting state for external subset
  316. setScannerState(SCANNER_STATE_TEXT_DECL);
  317. return false;
  318. }
  319. } while (complete);
  320. // return that there is more to scan
  321. return true;
  322. } // scanDTDInternalSubset(boolean,boolean,boolean):boolean
  323. //
  324. // XMLComponent methods
  325. //
  326. /**
  327. * reset
  328. *
  329. * @param componentManager
  330. */
  331. public void reset(XMLComponentManager componentManager)
  332. throws XMLConfigurationException {
  333. super.reset(componentManager);
  334. init();
  335. } // reset(XMLComponentManager)
  336. // this is made for something like XMLDTDLoader--XMLComponentManager-free operation...
  337. public void reset() {
  338. super.reset();
  339. init();
  340. }
  341. /**
  342. * Returns a list of feature identifiers that are recognized by
  343. * this component. This method may return null if no features
  344. * are recognized by this component.
  345. */
  346. public String[] getRecognizedFeatures() {
  347. return (String[])(RECOGNIZED_FEATURES.clone());
  348. } // getRecognizedFeatures():String[]
  349. /**
  350. * Returns a list of property identifiers that are recognized by
  351. * this component. This method may return null if no properties
  352. * are recognized by this component.
  353. */
  354. public String[] getRecognizedProperties() {
  355. return (String[])(RECOGNIZED_PROPERTIES.clone());
  356. } // getRecognizedProperties():String[]
  357. /**
  358. * Returns the default state for a feature, or null if this
  359. * component does not want to report a default value for this
  360. * feature.
  361. *
  362. * @param featureId The feature identifier.
  363. *
  364. * @since Xerces 2.2.0
  365. */
  366. public Boolean getFeatureDefault(String featureId) {
  367. for (int i = 0; i < RECOGNIZED_FEATURES.length; i++) {
  368. if (RECOGNIZED_FEATURES[i].equals(featureId)) {
  369. return FEATURE_DEFAULTS[i];
  370. }
  371. }
  372. return null;
  373. } // getFeatureDefault(String):Boolean
  374. /**
  375. * Returns the default state for a property, or null if this
  376. * component does not want to report a default value for this
  377. * property.
  378. *
  379. * @param propertyId The property identifier.
  380. *
  381. * @since Xerces 2.2.0
  382. */
  383. public Object getPropertyDefault(String propertyId) {
  384. for (int i = 0; i < RECOGNIZED_PROPERTIES.length; i++) {
  385. if (RECOGNIZED_PROPERTIES[i].equals(propertyId)) {
  386. return PROPERTY_DEFAULTS[i];
  387. }
  388. }
  389. return null;
  390. } // getPropertyDefault(String):Object
  391. //
  392. // XMLDTDSource methods
  393. //
  394. /**
  395. * setDTDHandler
  396. *
  397. * @param dtdHandler
  398. */
  399. public void setDTDHandler(XMLDTDHandler dtdHandler) {
  400. fDTDHandler = dtdHandler;
  401. } // setDTDHandler(XMLDTDHandler)
  402. /**
  403. * getDTDHandler
  404. *
  405. * @return the XMLDTDHandler
  406. */
  407. public XMLDTDHandler getDTDHandler() {
  408. return fDTDHandler;
  409. } // getDTDHandler(): XMLDTDHandler
  410. //
  411. // XMLDTDContentModelSource methods
  412. //
  413. /**
  414. * setDTDContentModelHandler
  415. *
  416. * @param dtdContentModelHandler
  417. */
  418. public void setDTDContentModelHandler(XMLDTDContentModelHandler
  419. dtdContentModelHandler) {
  420. fDTDContentModelHandler = dtdContentModelHandler;
  421. } // setDTDContentModelHandler
  422. /**
  423. * getDTDContentModelHandler
  424. *
  425. * @return XMLDTDContentModelHandler
  426. */
  427. public XMLDTDContentModelHandler getDTDContentModelHandler() {
  428. return fDTDContentModelHandler ;
  429. } // setDTDContentModelHandler
  430. //
  431. // XMLEntityHandler methods
  432. //
  433. /**
  434. * This method notifies of the start of an entity. The DTD has the
  435. * pseudo-name of "[dtd]" parameter entity names start with '%'; and
  436. * general entities are just specified by their name.
  437. *
  438. * @param name The name of the entity.
  439. * @param identifier The resource identifier.
  440. * @param encoding The auto-detected IANA encoding name of the entity
  441. * stream. This value will be null in those situations
  442. * where the entity encoding is not auto-detected (e.g.
  443. * internal entities or a document entity that is
  444. * parsed from a java.io.Reader).
  445. * @param augs Additional information that may include infoset augmentations
  446. *
  447. * @throws XNIException Thrown by handler to signal an error.
  448. */
  449. public void startEntity(String name,
  450. XMLResourceIdentifier identifier,
  451. String encoding, Augmentations augs) throws XNIException {
  452. super.startEntity(name, identifier, encoding, augs);
  453. boolean dtdEntity = name.equals("[dtd]");
  454. if (dtdEntity) {
  455. // call handler
  456. if (fDTDHandler != null && !fStartDTDCalled ) {
  457. fDTDHandler.startDTD(fEntityScanner, null);
  458. }
  459. if (fDTDHandler != null) {
  460. fDTDHandler.startExternalSubset(identifier,null);
  461. }
  462. fEntityManager.startExternalSubset();
  463. fExtEntityDepth++;
  464. }
  465. else if (name.charAt(0) == '%') {
  466. pushPEStack(fMarkUpDepth, fReportEntity);
  467. if (fEntityScanner.isExternal()) {
  468. fExtEntityDepth++;
  469. }
  470. }
  471. // call handler
  472. if (fDTDHandler != null && !dtdEntity && fReportEntity) {
  473. fDTDHandler.startParameterEntity(name, identifier, encoding, augs);
  474. }
  475. } // startEntity(String,XMLResourceIdentifier,String)
  476. /**
  477. * This method notifies the end of an entity. The DTD has the pseudo-name
  478. * of "[dtd]" parameter entity names start with '%'; and general entities
  479. * are just specified by their name.
  480. *
  481. * @param name The name of the entity.
  482. * @param augs Additional information that may include infoset augmentations
  483. *
  484. * @throws XNIException Thrown by handler to signal an error.
  485. */
  486. public void endEntity(String name, Augmentations augs)
  487. throws XNIException {
  488. super.endEntity(name, augs);
  489. // if there is no data after the doctype
  490. //
  491. if (fScannerState == SCANNER_STATE_END_OF_INPUT)
  492. return;
  493. // Handle end of PE
  494. boolean reportEntity = fReportEntity;
  495. if (name.startsWith("%")) {
  496. reportEntity = peekReportEntity();
  497. // check well-formedness of the enity
  498. int startMarkUpDepth = popPEStack();
  499. // throw fatalError if this entity was incomplete and
  500. // was a freestanding decl
  501. if(startMarkUpDepth == 0 &&
  502. startMarkUpDepth < fMarkUpDepth) {
  503. fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
  504. "ILL_FORMED_PARAMETER_ENTITY_WHEN_USED_IN_DECL",
  505. new Object[]{ fEntityManager.fCurrentEntity.name},
  506. XMLErrorReporter.SEVERITY_FATAL_ERROR);
  507. }
  508. if (startMarkUpDepth != fMarkUpDepth) {
  509. reportEntity = false;
  510. if (fValidation) {
  511. // Proper nesting of parameter entities is a Validity Constraint
  512. // and must not be enforced when validation is off
  513. fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
  514. "ImproperDeclarationNesting",
  515. new Object[]{ name },
  516. XMLErrorReporter.SEVERITY_ERROR);
  517. }
  518. }
  519. if (fEntityScanner.isExternal()) {
  520. fExtEntityDepth--;
  521. }
  522. }
  523. // call handler
  524. boolean dtdEntity = name.equals("[dtd]");
  525. if (fDTDHandler != null && !dtdEntity && reportEntity) {
  526. fDTDHandler.endParameterEntity(name, augs);
  527. }
  528. // end DTD
  529. if (dtdEntity) {
  530. if (fIncludeSectDepth != 0) {
  531. reportFatalError("IncludeSectUnterminated", null);
  532. }
  533. fScannerState = SCANNER_STATE_END_OF_INPUT;
  534. // call handler
  535. fEntityManager.endExternalSubset();
  536. if (fDTDHandler != null) {
  537. fDTDHandler.endExternalSubset(null);
  538. fDTDHandler.endDTD(null);
  539. }
  540. fExtEntityDepth--;
  541. }
  542. } // endEntity(String)
  543. // helper methods
  544. /**
  545. * Sets the scanner state.
  546. *
  547. * @param state The new scanner state.
  548. */
  549. protected final void setScannerState(int state) {
  550. fScannerState = state;
  551. if (DEBUG_SCANNER_STATE) {
  552. System.out.print("### setScannerState: ");
  553. System.out.print(getScannerStateName(state));
  554. System.out.println();
  555. }
  556. } // setScannerState(int)
  557. //
  558. // Private methods
  559. //
  560. /** Returns the scanner state name. */
  561. private static String getScannerStateName(int state) {
  562. if (DEBUG_SCANNER_STATE) {
  563. switch (state) {
  564. case SCANNER_STATE_END_OF_INPUT: return "SCANNER_STATE_END_OF_INPUT";
  565. case SCANNER_STATE_TEXT_DECL: return "SCANNER_STATE_TEXT_DECL";
  566. case SCANNER_STATE_MARKUP_DECL: return "SCANNER_STATE_MARKUP_DECL";
  567. }
  568. }
  569. return "??? ("+state+')';
  570. } // getScannerStateName(int):String
  571. protected final boolean scanningInternalSubset() {
  572. return fExtEntityDepth == 0;
  573. }
  574. /**
  575. * start a parameter entity dealing with the textdecl if there is any
  576. *
  577. * @param name The name of the parameter entity to start (without the '%')
  578. * @param literal Whether this is happening within a literal
  579. */
  580. protected void startPE(String name, boolean literal)
  581. throws IOException, XNIException {
  582. int depth = fPEDepth;
  583. String pName = "%"+name;
  584. if (fValidation && !fEntityManager.isDeclaredEntity(pName)) {
  585. fErrorReporter.reportError( XMLMessageFormatter.XML_DOMAIN,"EntityNotDeclared",
  586. new Object[]{name}, XMLErrorReporter.SEVERITY_ERROR);
  587. }
  588. fEntityManager.startEntity(fSymbolTable.addSymbol(pName),
  589. literal);
  590. // if we actually got a new entity and it's external
  591. // parse text decl if there is any
  592. if (depth != fPEDepth && fEntityScanner.isExternal()) {
  593. scanTextDecl();
  594. }
  595. }
  596. /**
  597. * Dispatch an XML "event".
  598. *
  599. * @param complete True if this method is intended to scan
  600. * and dispatch as much as possible.
  601. *
  602. * @return True if a TextDecl was scanned.
  603. *
  604. * @throws IOException Thrown on i/o error.
  605. * @throws XNIException Thrown on parse error.
  606. *
  607. */
  608. protected final boolean scanTextDecl()
  609. throws IOException, XNIException {
  610. // scan XMLDecl
  611. boolean textDecl = false;
  612. if (fEntityScanner.skipString("<?xml")) {
  613. fMarkUpDepth++;
  614. // NOTE: special case where document starts with a PI
  615. // whose name starts with "xml" (e.g. "xmlfoo")
  616. if (isValidNameChar(fEntityScanner.peekChar())) {
  617. fStringBuffer.clear();
  618. fStringBuffer.append("xml");
  619. if (fNamespaces) {
  620. while (isValidNCName(fEntityScanner.peekChar())) {
  621. fStringBuffer.append((char)fEntityScanner.scanChar());
  622. }
  623. }
  624. else {
  625. while (isValidNameChar(fEntityScanner.peekChar())) {
  626. fStringBuffer.append((char)fEntityScanner.scanChar());
  627. }
  628. }
  629. String target =
  630. fSymbolTable.addSymbol(fStringBuffer.ch,
  631. fStringBuffer.offset,
  632. fStringBuffer.length);
  633. scanPIData(target, fString);
  634. }
  635. // standard Text declaration
  636. else {
  637. // pseudo-attribute values
  638. String version = null;
  639. String encoding = null;
  640. scanXMLDeclOrTextDecl(true, fStrings);
  641. textDecl = true;
  642. fMarkUpDepth--;
  643. version = fStrings[0];
  644. encoding = fStrings[1];
  645. fEntityScanner.setEncoding(encoding);
  646. // call handler
  647. if (fDTDHandler != null) {
  648. fDTDHandler.textDecl(version, encoding, null);
  649. }
  650. }
  651. }
  652. fEntityManager.fCurrentEntity.mayReadChunks = true;
  653. return textDecl;
  654. } // scanTextDecl(boolean):boolean
  655. /**
  656. * Scans a processing data. This is needed to handle the situation
  657. * where a document starts with a processing instruction whose
  658. * target name <em>starts with</em> "xml". (e.g. xmlfoo)
  659. *
  660. * @param target The PI target
  661. * @param data The string to fill in with the data
  662. */
  663. protected final void scanPIData(String target, XMLString data)
  664. throws IOException, XNIException {
  665. super.scanPIData(target, data);
  666. fMarkUpDepth--;
  667. // call handler
  668. if (fDTDHandler != null) {
  669. fDTDHandler.processingInstruction(target, data, null);
  670. }
  671. } // scanPIData(String)
  672. /**
  673. * Scans a comment.
  674. * <p>
  675. * <pre>
  676. * [15] Comment ::= '<!--' ((Char - '-') | ('-' (Char - '-')))* '-->'
  677. * </pre>
  678. * <p>
  679. * <strong>Note:</strong> Called after scanning past '<!--'
  680. */
  681. protected final void scanComment() throws IOException, XNIException {
  682. fReportEntity = false;
  683. scanComment(fStringBuffer);
  684. fMarkUpDepth--;
  685. // call handler
  686. if (fDTDHandler != null) {
  687. fDTDHandler.comment(fStringBuffer, null);
  688. }
  689. fReportEntity = true;
  690. } // scanComment()
  691. /**
  692. * Scans an element declaration
  693. * <p>
  694. * <pre>
  695. * [45] elementdecl ::= '<!ELEMENT' S Name S contentspec S? '>'
  696. * [46] contentspec ::= 'EMPTY' | 'ANY' | Mixed | children
  697. * </pre>
  698. * <p>
  699. * <strong>Note:</strong> Called after scanning past '<!ELEMENT'
  700. */
  701. protected final void scanElementDecl() throws IOException, XNIException {
  702. // spaces
  703. fReportEntity = false;
  704. if (!skipSeparator(true, !scanningInternalSubset())) {
  705. reportFatalError("MSG_SPACE_REQUIRED_BEFORE_ELEMENT_TYPE_IN_ELEMENTDECL",
  706. null);
  707. }
  708. // element name
  709. String name = fEntityScanner.scanName();
  710. if (name == null) {
  711. reportFatalError("MSG_ELEMENT_TYPE_REQUIRED_IN_ELEMENTDECL",
  712. null);
  713. }
  714. // spaces
  715. if (!skipSeparator(true, !scanningInternalSubset())) {
  716. reportFatalError("MSG_SPACE_REQUIRED_BEFORE_CONTENTSPEC_IN_ELEMENTDECL",
  717. new Object[]{name});
  718. }
  719. // content model
  720. if (fDTDContentModelHandler != null) {
  721. fDTDContentModelHandler.startContentModel(name, null);
  722. }
  723. String contentModel = null;
  724. fReportEntity = true;
  725. if (fEntityScanner.skipString("EMPTY")) {
  726. contentModel = "EMPTY";
  727. // call handler
  728. if (fDTDContentModelHandler != null) {
  729. fDTDContentModelHandler.empty(null);
  730. }
  731. }
  732. else if (fEntityScanner.skipString("ANY")) {
  733. contentModel = "ANY";
  734. // call handler
  735. if (fDTDContentModelHandler != null) {
  736. fDTDContentModelHandler.any(null);
  737. }
  738. }
  739. else {
  740. if (!fEntityScanner.skipChar('(')) {
  741. reportFatalError("MSG_OPEN_PAREN_OR_ELEMENT_TYPE_REQUIRED_IN_CHILDREN",
  742. new Object[]{name});
  743. }
  744. if (fDTDContentModelHandler != null) {
  745. fDTDContentModelHandler.startGroup(null);
  746. }
  747. fStringBuffer.clear();
  748. fStringBuffer.append('(');
  749. fMarkUpDepth++;
  750. skipSeparator(false, !scanningInternalSubset());
  751. // Mixed content model
  752. if (fEntityScanner.skipString("#PCDATA")) {
  753. scanMixed(name);
  754. }
  755. else { // children content
  756. scanChildren(name);
  757. }
  758. contentModel = fStringBuffer.toString();
  759. }
  760. // call handler
  761. if (fDTDContentModelHandler != null) {
  762. fDTDContentModelHandler.endContentModel(null);
  763. }
  764. fReportEntity = false;
  765. skipSeparator(false, !scanningInternalSubset());
  766. // end
  767. if (!fEntityScanner.skipChar('>')) {
  768. reportFatalError("ElementDeclUnterminated", new Object[]{name});
  769. }
  770. fReportEntity = true;
  771. fMarkUpDepth--;
  772. // call handler
  773. if (fDTDHandler != null) {
  774. fDTDHandler.elementDecl(name, contentModel, null);
  775. }
  776. } // scanElementDecl()
  777. /**
  778. * scan Mixed content model
  779. * This assumes the content model has been parsed up to #PCDATA and
  780. * can simply append to fStringBuffer.
  781. * <pre>
  782. * [51] Mixed ::= '(' S? '#PCDATA' (S? '|' S? Name)* S? ')*'
  783. * | '(' S? '#PCDATA' S? ')'
  784. * </pre>
  785. *
  786. * @param elName The element type name this declaration is about.
  787. *
  788. * <strong>Note:</strong> Called after scanning past '(#PCDATA'.
  789. */
  790. private final void scanMixed(String elName)
  791. throws IOException, XNIException {
  792. String childName = null;
  793. fStringBuffer.append("#PCDATA");
  794. // call handler
  795. if (fDTDContentModelHandler != null) {
  796. fDTDContentModelHandler.pcdata(null);
  797. }
  798. skipSeparator(false, !scanningInternalSubset());
  799. while (fEntityScanner.skipChar('|')) {
  800. fStringBuffer.append('|');
  801. // call handler
  802. if (fDTDContentModelHandler != null) {
  803. fDTDContentModelHandler.separator(XMLDTDContentModelHandler.SEPARATOR_CHOICE,
  804. null);
  805. }
  806. skipSeparator(false, !scanningInternalSubset());
  807. childName = fEntityScanner.scanName();
  808. if (childName == null) {
  809. reportFatalError("MSG_ELEMENT_TYPE_REQUIRED_IN_MIXED_CONTENT",
  810. new Object[]{elName});
  811. }
  812. fStringBuffer.append(childName);
  813. // call handler
  814. if (fDTDContentModelHandler != null) {
  815. fDTDContentModelHandler.element(childName, null);
  816. }
  817. skipSeparator(false, !scanningInternalSubset());
  818. }
  819. // The following check must be done in a single call (as opposed to one
  820. // for ')' and then one for '*') to guarantee that callbacks are
  821. // properly nested. We do not want to trigger endEntity too early in
  822. // case we cross the boundary of an entity between the two characters.
  823. if (fEntityScanner.skipString(")*")) {
  824. fStringBuffer.append(")*");
  825. // call handler
  826. if (fDTDContentModelHandler != null) {
  827. fDTDContentModelHandler.endGroup(null);
  828. fDTDContentModelHandler.occurrence(XMLDTDContentModelHandler.OCCURS_ZERO_OR_MORE,
  829. null);
  830. }
  831. }
  832. else if (childName != null) {
  833. reportFatalError("MixedContentUnterminated",
  834. new Object[]{elName});
  835. }
  836. else if (fEntityScanner.skipChar(')')){
  837. fStringBuffer.append(')');
  838. // call handler
  839. if (fDTDContentModelHandler != null) {
  840. fDTDContentModelHandler.endGroup(null);
  841. }
  842. }
  843. else {
  844. reportFatalError("MSG_CLOSE_PAREN_REQUIRED_IN_CHILDREN",
  845. new Object[]{elName});
  846. }
  847. fMarkUpDepth--;
  848. // we are done
  849. }
  850. /**
  851. * scan children content model
  852. * This assumes it can simply append to fStringBuffer.
  853. * <pre>
  854. * [47] children ::= (choice | seq) ('?' | '*' | '+')?
  855. * [48] cp ::= (Name | choice | seq) ('?' | '*' | '+')?
  856. * [49] choice ::= '(' S? cp ( S? '|' S? cp )+ S? ')'
  857. * [50] seq ::= '(' S? cp ( S? ',' S? cp )* S? ')'
  858. * </pre>
  859. *
  860. * @param elName The element type name this declaration is about.
  861. *
  862. * <strong>Note:</strong> Called after scanning past the first open
  863. * paranthesis.
  864. */
  865. private final void scanChildren(String elName)
  866. throws IOException, XNIException {
  867. fContentDepth = 0;
  868. pushContentStack(0);
  869. int currentOp = 0;
  870. int c;
  871. while (true) {
  872. if (fEntityScanner.skipChar('(')) {
  873. fMarkUpDepth++;
  874. fStringBuffer.append('(');
  875. // call handler
  876. if (fDTDContentModelHandler != null) {
  877. fDTDContentModelHandler.startGroup(null);
  878. }
  879. // push current op on stack and reset it
  880. pushContentStack(currentOp);
  881. currentOp = 0;
  882. skipSeparator(false, !scanningInternalSubset());
  883. continue;
  884. }
  885. skipSeparator(false, !scanningInternalSubset());
  886. String childName = fEntityScanner.scanName();
  887. if (childName == null) {
  888. reportFatalError("MSG_OPEN_PAREN_OR_ELEMENT_TYPE_REQUIRED_IN_CHILDREN",
  889. new Object[]{elName});
  890. return;
  891. }
  892. // call handler
  893. if (fDTDContentModelHandler != null) {
  894. fDTDContentModelHandler.element(childName, null);
  895. }
  896. fStringBuffer.append(childName);
  897. c = fEntityScanner.peekChar();
  898. if (c == '?' || c == '*' || c == '+') {
  899. // call handler
  900. if (fDTDContentModelHandler != null) {
  901. short oc;
  902. if (c == '?') {
  903. oc = XMLDTDContentModelHandler.OCCURS_ZERO_OR_ONE;
  904. }
  905. else if (c == '*') {
  906. oc = XMLDTDContentModelHandler.OCCURS_ZERO_OR_MORE;
  907. }
  908. else {
  909. oc = XMLDTDContentModelHandler.OCCURS_ONE_OR_MORE;
  910. }
  911. fDTDContentModelHandler.occurrence(oc, null);
  912. }
  913. fEntityScanner.scanChar();
  914. fStringBuffer.append((char)c);
  915. }
  916. while (true) {
  917. skipSeparator(false, !scanningInternalSubset());
  918. c = fEntityScanner.peekChar();
  919. if (c == ',' && currentOp != '|') {
  920. currentOp = c;
  921. // call handler
  922. if (fDTDContentModelHandler != null) {
  923. fDTDContentModelHandler.separator(XMLDTDContentModelHandler.SEPARATOR_SEQUENCE,
  924. null);
  925. }
  926. fEntityScanner.scanChar();
  927. fStringBuffer.append(',');
  928. break;
  929. }
  930. else if (c == '|' && currentOp != ',') {
  931. currentOp = c;
  932. // call handler
  933. if (fDTDContentModelHandler != null) {
  934. fDTDContentModelHandler.separator(XMLDTDContentModelHandler.SEPARATOR_CHOICE,
  935. null);
  936. }
  937. fEntityScanner.scanChar();
  938. fStringBuffer.append('|');
  939. break;
  940. }
  941. else if (c != ')') {
  942. reportFatalError("MSG_CLOSE_PAREN_REQUIRED_IN_CHILDREN",
  943. new Object[]{elName});
  944. }
  945. // call handler
  946. if (fDTDContentModelHandler != null) {
  947. fDTDContentModelHandler.endGroup(null);
  948. }
  949. // restore previous op
  950. currentOp = popContentStack();
  951. short oc;
  952. // The following checks must be done in a single call (as
  953. // opposed to one for ')' and then one for '?', '*', and '+')
  954. // to guarantee that callbacks are properly nested. We do not
  955. // want to trigger endEntity too early in case we cross the
  956. // boundary of an entity between the two characters.
  957. if (fEntityScanner.skipString(")?")) {
  958. fStringBuffer.append(")?");
  959. // call handler
  960. if (fDTDContentModelHandler != null) {
  961. oc = XMLDTDContentModelHandler.OCCURS_ZERO_OR_ONE;
  962. fDTDContentModelHandler.occurrence(oc, null);
  963. }
  964. }
  965. else if (fEntityScanner.skipString(")+")) {
  966. fStringBuffer.append(")+");
  967. // call handler
  968. if (fDTDContentModelHandler != null) {
  969. oc = XMLDTDContentModelHandler.OCCURS_ONE_OR_MORE;
  970. fDTDContentModelHandler.occurrence(oc, null);
  971. }
  972. }
  973. else if (fEntityScanner.skipString(")*")) {
  974. fStringBuffer.append(")*");
  975. // call handler
  976. if (fDTDContentModelHandler != null) {
  977. oc = XMLDTDContentModelHandler.OCCURS_ZERO_OR_MORE;
  978. fDTDContentModelHandler.occurrence(oc, null);
  979. }
  980. }
  981. else {
  982. // no occurrence specified
  983. fEntityScanner.scanChar();
  984. fStringBuffer.append(')');
  985. }
  986. fMarkUpDepth--;
  987. if (fContentDepth == 0) {
  988. return;
  989. }
  990. }
  991. skipSeparator(false, !scanningInternalSubset());
  992. }
  993. }
  994. /**
  995. * Scans an attlist declaration
  996. * <p>
  997. * <pre>
  998. * [52] AttlistDecl ::= '<!ATTLIST' S Name AttDef* S? '>'
  999. * [53] AttDef ::= S Name S AttType S DefaultDecl
  1000. * </pre>
  1001. * <p>
  1002. * <strong>Note:</strong> Called after scanning past '<!ATTLIST'
  1003. */
  1004. protected final void scanAttlistDecl() throws IOException, XNIException {
  1005. // spaces
  1006. fReportEntity = false;
  1007. if (!skipSeparator(true, !scanningInternalSubset())) {
  1008. reportFatalError("MSG_SPACE_REQUIRED_BEFORE_ELEMENT_TYPE_IN_ATTLISTDECL",
  1009. null);
  1010. }
  1011. // element name
  1012. String elName = fEntityScanner.scanName();
  1013. if (elName == null) {
  1014. reportFatalError("MSG_ELEMENT_TYPE_REQUIRED_IN_ATTLISTDECL",
  1015. null);
  1016. }
  1017. // call handler
  1018. if (fDTDHandler != null) {
  1019. fDTDHandler.startAttlist(elName, null);
  1020. }
  1021. // spaces
  1022. if (!skipSeparator(true, !scanningInternalSubset())) {
  1023. // no space, is it the end yet?
  1024. if (fEntityScanner.skipChar('>')) {
  1025. // yes, stop here
  1026. // call handler
  1027. if (fDTDHandler != null) {
  1028. fDTDHandler.endAttlist(null);
  1029. }
  1030. fMarkUpDepth--;
  1031. return;
  1032. }
  1033. else {
  1034. reportFatalError("MSG_SPACE_REQUIRED_BEFORE_ATTRIBUTE_NAME_IN_ATTDEF",
  1035. new Object[]{elName});
  1036. }
  1037. }
  1038. // definitions
  1039. while (!fEntityScanner.skipChar('>')) {
  1040. String name = fEntityScanner.scanName();
  1041. if (name == null) {
  1042. reportFatalError("AttNameRequiredInAttDef",
  1043. new Object[]{elName});
  1044. }
  1045. // spaces
  1046. if (!skipSeparator(true, !scanningInternalSubset())) {
  1047. reportFatalError("MSG_SPACE_REQUIRED_BEFORE_ATTTYPE_IN_ATTDEF",
  1048. new Object[]{elName, name});
  1049. }
  1050. // type
  1051. String type = scanAttType(elName, name);
  1052. // spaces
  1053. if (!skipSeparator(true, !scanningInternalSubset())) {
  1054. reportFatalError("MSG_SPACE_REQUIRED_BEFORE_DEFAULTDECL_IN_ATTDEF",
  1055. new Object[]{elName, name});
  1056. }
  1057. // default decl
  1058. String defaultType = scanAttDefaultDecl(elName, name,
  1059. type,
  1060. fLiteral, fLiteral2);
  1061. // REVISIT: Should we do anything with the non-normalized
  1062. // default attribute value? -Ac
  1063. // yes--according to bug 5073. - neilg
  1064. // call handler
  1065. if (fDTDHandler != null) {
  1066. String[] enumeration = null;
  1067. if (fEnumerationCount != 0) {
  1068. enumeration = new String[fEnumerationCount];
  1069. System.arraycopy(fEnumeration, 0, enumeration,
  1070. 0, fEnumerationCount);
  1071. }
  1072. // Determine whether the default value to be passed should be null.
  1073. // REVISIT: should probably check whether fLiteral.ch is null instead. LM.
  1074. if (defaultType!=null && (defaultType.equals("#REQUIRED") ||
  1075. defaultType.equals("#IMPLIED"))) {
  1076. fDTDHandler.attributeDecl(elName, name, type, enumeration,
  1077. defaultType, null, null, null);
  1078. }
  1079. else {
  1080. fDTDHandler.attributeDecl(elName, name, type, enumeration,
  1081. defaultType, fLiteral, fLiteral2, null);
  1082. }
  1083. }
  1084. skipSeparator(false, !scanningInternalSubset());
  1085. }
  1086. // call handler
  1087. if (fDTDHandler != null) {
  1088. fDTDHandler.endAttlist(null);
  1089. }
  1090. fMarkUpDepth--;
  1091. fReportEntity = true;
  1092. } // scanAttlistDecl()
  1093. /**
  1094. * Scans an attribute type definition
  1095. * <p>
  1096. * <pre>
  1097. * [54] AttType ::= StringType | TokenizedType | EnumeratedType
  1098. * [55] StringType ::= 'CDATA'
  1099. * [56] TokenizedType ::= 'ID'
  1100. * | 'IDREF'
  1101. * | 'IDREFS'
  1102. * | 'ENTITY'
  1103. * | 'ENTITIES'
  1104. * | 'NMTOKEN'
  1105. * | 'NMTOKENS'
  1106. * [57] EnumeratedType ::= NotationType | Enumeration
  1107. * [58] NotationType ::= 'NOTATION' S '(' S? Name (S? '|' S? Name)* S? ')'
  1108. * [59] Enumeration ::= '(' S? Nmtoken (S? '|' S? Nmtoken)* S? ')'
  1109. * </pre>
  1110. * <p>
  1111. * <strong>Note:</strong> Called after scanning past '<!ATTLIST'
  1112. *
  1113. * @param elName The element type name this declaration is about.
  1114. * @param atName The attribute name this declaration is about.
  1115. */
  1116. private final String scanAttType(String elName, String atName)
  1117. throws IOException, XNIException {
  1118. String type = null;
  1119. fEnumerationCount = 0;
  1120. /*
  1121. * Watchout: the order here is important: when a string happens to
  1122. * be a substring of another string, the longer one needs to be
  1123. * looked for first!!
  1124. */
  1125. if (fEntityScanner.skipString("CDATA")) {
  1126. type = "CDATA";
  1127. }
  1128. else if (fEntityScanner.skipString("IDREFS")) {
  1129. type = "IDREFS";
  1130. }
  1131. else if (fEntityScanner.skipString("IDREF")) {
  1132. type = "IDREF";
  1133. }
  1134. else if (fEntityScanner.skipString("ID")) {
  1135. type = "ID";
  1136. }
  1137. else if (fEntityScanner.skipString("ENTITY")) {
  1138. type = "ENTITY";
  1139. }
  1140. else if (fEntityScanner.skipString("ENTITIES")) {
  1141. type = "ENTITIES";
  1142. }
  1143. else if (fEntityScanner.skipString("NMTOKENS")) {
  1144. type = "NMTOKENS";
  1145. }
  1146. else if (fEntityScanner.skipString("NMTOKEN")) {
  1147. type = "NMTOKEN";
  1148. }
  1149. else if (fEntityScanner.skipString("NOTATION")) {
  1150. type = "NOTATION";
  1151. // spaces
  1152. if (!skipSeparator(true, !scanningInternalSubset())) {
  1153. reportFatalError("MSG_SPACE_REQUIRED_AFTER_NOTATION_IN_NOTATIONTYPE",
  1154. new Object[]{elName, atName});
  1155. }
  1156. // open paren
  1157. int c = fEntityScanner.scanChar();
  1158. if (c != '(') {
  1159. reportFatalError("MSG_OPEN_PAREN_REQUIRED_IN_NOTATIONTYPE",
  1160. new Object[]{elName, atName});
  1161. }
  1162. fMarkUpDepth++;
  1163. do {
  1164. skipSeparator(false, !scanningInternalSubset());
  1165. String aName = fEntityScanner.scanName();
  1166. if (aName == null) {
  1167. reportFatalError("MSG_NAME_REQUIRED_IN_NOTATIONTYPE",
  1168. new Object[]{elName, atName});
  1169. }
  1170. ensureEnumerationSize(fEnumerationCount + 1);
  1171. fEnumeration[fEnumerationCount++] = aName;
  1172. skipSeparator(false, !scanningInternalSubset());
  1173. c = fEntityScanner.scanChar();
  1174. } while (c == '|');
  1175. if (c != ')') {
  1176. reportFatalError("NotationTypeUnterminated",
  1177. new Object[]{elName, atName});
  1178. }
  1179. fMarkUpDepth--;
  1180. }
  1181. else { // Enumeration
  1182. type = "ENUMERATION";
  1183. // open paren
  1184. int c = fEntityScanner.scanChar();
  1185. if (c != '(') {
  1186. // "OPEN_PAREN_REQUIRED_BEFORE_ENUMERATION_IN_ATTRDECL",
  1187. reportFatalError("AttTypeRequiredInAttDef",
  1188. new Object[]{elName, atName});
  1189. }
  1190. fMarkUpDepth++;
  1191. do {
  1192. skipSeparator(false, !scanningInternalSubset());
  1193. String token = fEntityScanner.scanNmtoken();
  1194. if (token == null) {
  1195. reportFatalError("MSG_NMTOKEN_REQUIRED_IN_ENUMERATION",
  1196. new Object[]{elName, atName});
  1197. }
  1198. ensureEnumerationSize(fEnumerationCount + 1);
  1199. fEnumeration[fEnumerationCount++] = token;
  1200. skipSeparator(false, !scanningInternalSubset());
  1201. c = fEntityScanner.scanChar();
  1202. } while (c == '|');
  1203. if (c != ')') {
  1204. reportFatalError("EnumerationUnterminated",
  1205. new Object[]{elName, atName});
  1206. }
  1207. fMarkUpDepth--;
  1208. }
  1209. return type;
  1210. } // scanAttType():String
  1211. /**
  1212. * Scans an attribute default declaration
  1213. * <p>
  1214. * <pre>
  1215. * [60] DefaultDecl ::= '#REQUIRED' | '#IMPLIED' | (('#FIXED' S)? AttValue)
  1216. * </pre>
  1217. *
  1218. * @param name The name of the attribute being scanned.
  1219. * @param defaultVal The string to fill in with the default value.
  1220. */
  1221. protected final String scanAttDefaultDecl(String elName, String atName,
  1222. String type,
  1223. XMLString defaultVal,
  1224. XMLString nonNormalizedDefaultVal)
  1225. throws IOException, XNIException {
  1226. String defaultType = null;
  1227. fString.clear();
  1228. defaultVal.clear();
  1229. if (fEntityScanner.skipString("#REQUIRED")) {
  1230. defaultType = "#REQUIRED";
  1231. }
  1232. else if (fEntityScanner.skipString("#IMPLIED")) {
  1233. defaultType = "#IMPLIED";
  1234. }
  1235. else {
  1236. if (fEntityScanner.skipString("#FIXED")) {
  1237. defaultType = "#FIXED";
  1238. // spaces
  1239. if (!skipSeparator(true, !scanningInternalSubset())) {
  1240. reportFatalError("MSG_SPACE_REQUIRED_AFTER_FIXED_IN_DEFAULTDECL",
  1241. new Object[]{elName, atName});
  1242. }
  1243. }
  1244. // AttValue
  1245. boolean isVC = !fStandalone && (fSeenExternalDTD || fSeenExternalPE) ;
  1246. scanAttributeValue(defaultVal, nonNormalizedDefaultVal, atName, isVC, elName);
  1247. }
  1248. return defaultType;
  1249. } // ScanAttDefaultDecl
  1250. /**
  1251. * Scans an entity declaration
  1252. * <p>
  1253. * <pre>
  1254. * [70] EntityDecl ::= GEDecl | PEDecl
  1255. * [71] GEDecl ::= '<!ENTITY' S Name S EntityDef S? '>'
  1256. * [72] PEDecl ::= '<!ENTITY' S '%' S Name S PEDef S? '>'
  1257. * [73] EntityDef ::= EntityValue | (ExternalID NDataDecl?)
  1258. * [74] PEDef ::= EntityValue | ExternalID
  1259. * [75] ExternalID ::= 'SYSTEM' S SystemLiteral
  1260. * | 'PUBLIC' S PubidLiteral S SystemLiteral
  1261. * [76] NDataDecl ::= S 'NDATA' S Name
  1262. * </pre>
  1263. * <p>
  1264. * <strong>Note:</strong> Called after scanning past '<!ENTITY'
  1265. */
  1266. private final void scanEntityDecl() throws IOException, XNIException {
  1267. boolean isPEDecl = false;
  1268. boolean sawPERef = false;
  1269. fReportEntity = false;
  1270. if (fEntityScanner.skipSpaces()) {
  1271. if (!fEntityScanner.skipChar('%')) {
  1272. isPEDecl = false; // <!ENTITY x "x">
  1273. }
  1274. else if (skipSeparator(true, !scanningInternalSubset())) {
  1275. // <!ENTITY % x "x">
  1276. isPEDecl = true;
  1277. }
  1278. else if (scanningInternalSubset()) {
  1279. reportFatalError("MSG_SPACE_REQUIRED_BEFORE_ENTITY_NAME_IN_ENTITYDECL",
  1280. null);
  1281. isPEDecl = true;
  1282. }
  1283. else if (fEntityScanner.peekChar() == '%') {
  1284. // <!ENTITY %%x; "x"> is legal
  1285. skipSeparator(false, !scanningInternalSubset());
  1286. isPEDecl = true;
  1287. }
  1288. else {
  1289. sawPERef = true;
  1290. }
  1291. }
  1292. else if (scanningInternalSubset() || !fEntityScanner.skipChar('%')) {
  1293. // <!ENTITY[^ ]...> or <!ENTITY[^ %]...>
  1294. reportFatalError("MSG_SPACE_REQUIRED_BEFORE_ENTITY_NAME_IN_ENTITYDECL",
  1295. null);
  1296. isPEDecl = false;
  1297. }
  1298. else if (fEntityScanner.skipSpaces()) {
  1299. // <!ENTITY% ...>
  1300. reportFatalError("MSG_SPACE_REQUIRED_BEFORE_PERCENT_IN_PEDECL",
  1301. null);
  1302. isPEDecl = false;
  1303. }
  1304. else {
  1305. sawPERef = true;
  1306. }
  1307. if (sawPERef) {
  1308. while (true) {
  1309. String peName = fEntityScanner.scanName();
  1310. if (peName == null) {
  1311. reportFatalError("NameRequiredInPEReference", null);
  1312. }
  1313. else if (!fEntityScanner.skipChar(';')) {
  1314. reportFatalError("SemicolonRequiredInPEReference",
  1315. new Object[]{peName});
  1316. }
  1317. else {
  1318. startPE(peName, false);
  1319. }
  1320. fEntityScanner.skipSpaces();
  1321. if (!fEntityScanner.skipChar('%'))
  1322. break;
  1323. if (!isPEDecl) {
  1324. if (skipSeparator(true, !scanningInternalSubset())) {
  1325. isPEDecl = true;
  1326. break;
  1327. }
  1328. isPEDecl = fEntityScanner.skipChar('%');
  1329. }
  1330. }
  1331. }
  1332. // name
  1333. String name = null;
  1334. if(fNamespaces) {
  1335. name = fEntityScanner.scanNCName();
  1336. } else {
  1337. name = fEntityScanner.scanName();
  1338. }
  1339. if (name == null) {
  1340. reportFatalError("MSG_ENTITY_NAME_REQUIRED_IN_ENTITYDECL", null);
  1341. }
  1342. // spaces
  1343. if (!skipSeparator(true, !scanningInternalSubset())) {
  1344. if(fNamespaces && fEntityScanner.peekChar() == ':') {
  1345. fEntityScanner.scanChar();
  1346. XMLStringBuffer colonName = new XMLStringBuffer(name);
  1347. colonName.append(":");
  1348. String str = fEntityScanner.scanName();
  1349. if (str != null)
  1350. colonName.append(str);
  1351. reportFatalError("ColonNotLegalWithNS", new Object[] {colonName.toString()});
  1352. if (!skipSeparator(true, !scanningInternalSubset())) {
  1353. reportFatalError("MSG_SPACE_REQUIRED_AFTER_ENTITY_NAME_IN_ENTITYDECL",
  1354. new Object[]{name});
  1355. }
  1356. } else {
  1357. reportFatalError("MSG_SPACE_REQUIRED_AFTER_ENTITY_NAME_IN_ENTITYDECL",
  1358. new Object[]{name});
  1359. }
  1360. }
  1361. // external id
  1362. scanExternalID(fStrings, false);
  1363. String systemId = fStrings[0];
  1364. String publicId = fStrings[1];
  1365. if (isPEDecl && systemId != null) {
  1366. fSeenExternalPE = true;
  1367. }
  1368. String notation = null;
  1369. // NDATA
  1370. boolean sawSpace = skipSeparator(true, !scanningInternalSubset());
  1371. if (!isPEDecl && fEntityScanner.skipString("NDATA")) {
  1372. // check whether there was space before NDATA
  1373. if (!sawSpace) {
  1374. reportFatalError("MSG_SPACE_REQUIRED_BEFORE_NDATA_IN_UNPARSED_ENTITYDECL",
  1375. new Object[]{name});
  1376. }
  1377. // spaces
  1378. if (!skipSeparator(true, !scanningInternalSubset())) {
  1379. reportFatalError("MSG_SPACE_REQUIRED_BEFORE_NOTATION_NAME_IN_UNPARSED_ENTITYDECL",
  1380. new Object[]{name});
  1381. }
  1382. notation = fEntityScanner.scanName();
  1383. if (notation == null) {
  1384. reportFatalError("MSG_NOTATION_NAME_REQUIRED_FOR_UNPARSED_ENTITYDECL",
  1385. new Object[]{name});
  1386. }
  1387. }
  1388. // internal entity
  1389. if (systemId == null) {
  1390. scanEntityValue(fLiteral, fLiteral2);
  1391. // since we need it's value anyway, let's snag it so it doesn't get corrupted
  1392. // if a new load takes place before we store the entity values
  1393. fStringBuffer.clear();
  1394. fStringBuffer2.clear();
  1395. fStringBuffer.append(fLiteral.ch, fLiteral.offset, fLiteral.length);
  1396. fStringBuffer2.append(fLiteral2.ch, fLiteral2.offset, fLiteral2.length);
  1397. }
  1398. // skip possible trailing space
  1399. skipSeparator(false, !scanningInternalSubset());
  1400. // end
  1401. if (!fEntityScanner.skipChar('>')) {
  1402. reportFatalError("EntityDeclUnterminated", new Object[]{name});
  1403. }
  1404. fMarkUpDepth--;
  1405. // register entity and make callback
  1406. if (isPEDecl) {
  1407. name = "%" + name;
  1408. }
  1409. if (systemId != null) {
  1410. String baseSystemId = fEntityScanner.getBaseSystemId();
  1411. if (notation != null) {
  1412. fEntityManager.addUnparsedEntity(name, publicId, systemId, baseSystemId, notation);
  1413. }
  1414. else {
  1415. fEntityManager.addExternalEntity(name, publicId, systemId,
  1416. baseSystemId);
  1417. }
  1418. if (fDTDHandler != null) {
  1419. fResourceIdentifier.setValues(publicId, systemId, baseSystemId, XMLEntityManager.expandSystemId(systemId, baseSystemId, false));
  1420. if (notation != null) {
  1421. fDTDHandler.unparsedEntityDecl(name, fResourceIdentifier,
  1422. notation, null);
  1423. }
  1424. else {
  1425. fDTDHandler.externalEntityDecl(name, fResourceIdentifier, null);
  1426. }
  1427. }
  1428. }
  1429. else {
  1430. fEntityManager.addInternalEntity(name, fStringBuffer.toString());
  1431. if (fDTDHandler != null) {
  1432. fDTDHandler.internalEntityDecl(name, fStringBuffer, fStringBuffer2, null);
  1433. }
  1434. }
  1435. fReportEntity = true;
  1436. } // scanEntityDecl()
  1437. /**
  1438. * Scans an entity value.
  1439. *
  1440. * @param value The string to fill in with the value.
  1441. * @param nonNormalizedValue The string to fill in with the
  1442. * non-normalized value.
  1443. *
  1444. * <strong>Note:</strong> This method uses fString, fStringBuffer (through
  1445. * the use of scanCharReferenceValue), and fStringBuffer2, anything in them
  1446. * at the time of calling is lost.
  1447. */
  1448. protected final void scanEntityValue(XMLString value,
  1449. XMLString nonNormalizedValue)
  1450. throws IOException, XNIException
  1451. {
  1452. int quote = fEntityScanner.scanChar();
  1453. if (quote != '\'' && quote != '"') {
  1454. reportFatalError("OpenQuoteMissingInDecl", null);
  1455. }
  1456. // store at which depth of entities we start
  1457. int entityDepth = fEntityDepth;
  1458. XMLString literal = fString;
  1459. XMLString literal2 = fString;
  1460. if (fEntityScanner.scanLiteral(quote, fString) != quote) {
  1461. fStringBuffer.clear();
  1462. fStringBuffer2.clear();
  1463. do {
  1464. fStringBuffer.append(fString);
  1465. fStringBuffer2.append(fString);
  1466. if (fEntityScanner.skipChar('&')) {
  1467. if (fEntityScanner.skipChar('#')) {
  1468. fStringBuffer2.append("&#");
  1469. scanCharReferenceValue(fStringBuffer, fStringBuffer2);
  1470. }
  1471. else {
  1472. fStringBuffer.append('&');
  1473. fStringBuffer2.append('&');
  1474. String eName = fEntityScanner.scanName();
  1475. if (eName == null) {
  1476. reportFatalError("NameRequiredInReference",
  1477. null);
  1478. }
  1479. else {
  1480. fStringBuffer.append(eName);
  1481. fStringBuffer2.append(eName);
  1482. }
  1483. if (!fEntityScanner.skipChar(';')) {
  1484. reportFatalError("SemicolonRequiredInReference",
  1485. new Object[]{eName});
  1486. }
  1487. else {
  1488. fStringBuffer.append(';');
  1489. fStringBuffer2.append(';');
  1490. }
  1491. }
  1492. }
  1493. else if (fEntityScanner.skipChar('%')) {
  1494. while (true) {
  1495. fStringBuffer2.append('%');
  1496. String peName = fEntityScanner.scanName();
  1497. if (peName == null) {
  1498. reportFatalError("NameRequiredInPEReference",
  1499. null);
  1500. }
  1501. else if (!fEntityScanner.skipChar(';')) {
  1502. reportFatalError("SemicolonRequiredInPEReference",
  1503. new Object[]{peName});
  1504. }
  1505. else {
  1506. if (scanningInternalSubset()) {
  1507. reportFatalError("PEReferenceWithinMarkup",
  1508. new Object[]{peName});
  1509. }
  1510. fStringBuffer2.append(peName);
  1511. fStringBuffer2.append(';');
  1512. }
  1513. startPE(peName, true);
  1514. // REVISIT: [Q] Why do we skip spaces here? -Ac
  1515. // REVISIT: This will make returning the non-
  1516. // normalized value harder. -Ac
  1517. fEntityScanner.skipSpaces();
  1518. if (!fEntityScanner.skipChar('%'))
  1519. break;
  1520. }
  1521. }
  1522. else {
  1523. int c = fEntityScanner.peekChar();
  1524. if (XMLChar.isHighSurrogate(c)) {
  1525. scanSurrogates(fStringBuffer2);
  1526. }
  1527. else if (isInvalidLiteral(c)) {
  1528. reportFatalError("InvalidCharInLiteral",
  1529. new Object[]{Integer.toHexString(c)});
  1530. fEntityScanner.scanChar();
  1531. }
  1532. // if it's not the delimiting quote or if it is but from a
  1533. // different entity than the one this literal started from,
  1534. // simply append the character to our buffer
  1535. else if (c != quote || entityDepth != fEntityDepth) {
  1536. fStringBuffer.append((char)c);
  1537. fStringBuffer2.append((char)c);
  1538. fEntityScanner.scanChar();
  1539. }
  1540. }
  1541. } while (fEntityScanner.scanLiteral(quote, fString) != quote);
  1542. fStringBuffer.append(fString);
  1543. fStringBuffer2.append(fString);
  1544. literal = fStringBuffer;
  1545. literal2 = fStringBuffer2;
  1546. }
  1547. value.setValues(literal);
  1548. nonNormalizedValue.setValues(literal2);
  1549. if (!fEntityScanner.skipChar(quote)) {
  1550. reportFatalError("CloseQuoteMissingInDecl", null);
  1551. }
  1552. } // scanEntityValue(XMLString,XMLString):void
  1553. /**
  1554. * Scans a notation declaration
  1555. * <p>
  1556. * <pre>
  1557. * [82] NotationDecl ::= '<!NOTATION' S Name S (ExternalID|PublicID) S? '>'
  1558. * [83] PublicID ::= 'PUBLIC' S PubidLiteral
  1559. * </pre>
  1560. * <p>
  1561. * <strong>Note:</strong> Called after scanning past '<!NOTATION'
  1562. */
  1563. private final void scanNotationDecl() throws IOException, XNIException {
  1564. // spaces
  1565. fReportEntity = false;
  1566. if (!skipSeparator(true, !scanningInternalSubset())) {
  1567. reportFatalError("MSG_SPACE_REQUIRED_BEFORE_NOTATION_NAME_IN_NOTATIONDECL",
  1568. null);
  1569. }
  1570. // notation name
  1571. String name = null;
  1572. if(fNamespaces) {
  1573. name = fEntityScanner.scanNCName();
  1574. } else {
  1575. name = fEntityScanner.scanName();
  1576. }
  1577. if (name == null) {
  1578. reportFatalError("MSG_NOTATION_NAME_REQUIRED_IN_NOTATIONDECL",
  1579. null);
  1580. }
  1581. // spaces
  1582. if (!skipSeparator(true, !scanningInternalSubset())) {
  1583. // check for invalid ":"
  1584. if(fNamespaces && fEntityScanner.peekChar() == ':') {
  1585. fEntityScanner.scanChar();
  1586. XMLStringBuffer colonName = new XMLStringBuffer(name);
  1587. colonName.append(":");
  1588. colonName.append(fEntityScanner.scanName());
  1589. reportFatalError("ColonNotLegalWithNS", new Object[] {colonName.toString()});
  1590. skipSeparator(true, !scanningInternalSubset());
  1591. } else {
  1592. reportFatalError("MSG_SPACE_REQUIRED_AFTER_NOTATION_NAME_IN_NOTATIONDECL",
  1593. new Object[]{name});
  1594. }
  1595. }
  1596. // external id
  1597. scanExternalID(fStrings, true);
  1598. String systemId = fStrings[0];
  1599. String publicId = fStrings[1];
  1600. String baseSystemId = fEntityScanner.getBaseSystemId();
  1601. if (systemId == null && publicId == null) {
  1602. reportFatalError("ExternalIDorPublicIDRequired",
  1603. new Object[]{name});
  1604. }
  1605. // skip possible trailing space
  1606. skipSeparator(false, !scanningInternalSubset());
  1607. // end
  1608. if (!fEntityScanner.skipChar('>')) {
  1609. reportFatalError("NotationDeclUnterminated", new Object[]{name});
  1610. }
  1611. fMarkUpDepth--;
  1612. // call handler
  1613. if (fDTDHandler != null) {
  1614. fResourceIdentifier.setValues(publicId, systemId, baseSystemId, XMLEntityManager.expandSystemId(systemId, baseSystemId, false));
  1615. fDTDHandler.notationDecl(name, fResourceIdentifier, null);
  1616. }
  1617. fReportEntity = true;
  1618. } // scanNotationDecl()
  1619. /**
  1620. * Scans a conditional section. If it's a section to ignore the whole
  1621. * section gets scanned through and this method only returns after the
  1622. * closing bracket has been found. When it's an include section though, it
  1623. * returns to let the main loop take care of scanning it. In that case the
  1624. * end of the section if handled by the main loop (scanDecls).
  1625. * <p>
  1626. * <pre>
  1627. * [61] conditionalSect ::= includeSect | ignoreSect
  1628. * [62] includeSect ::= '<![' S? 'INCLUDE' S? '[' extSubsetDecl ']]>'
  1629. * [63] ignoreSect ::= '<![' S? 'IGNORE' S? '[' ignoreSectContents* ']]>'
  1630. * [64] ignoreSectContents ::= Ignore ('<![' ignoreSectContents ']]>' Ignore)*
  1631. * [65] Ignore ::= Char* - (Char* ('<![' | ']]>') Char*)
  1632. * </pre>
  1633. * <p>
  1634. * <strong>Note:</strong> Called after scanning past '<![' */
  1635. private final void scanConditionalSect(int currPEDepth)
  1636. throws IOException, XNIException {
  1637. fReportEntity = false;
  1638. skipSeparator(false, !scanningInternalSubset());
  1639. if (fEntityScanner.skipString("INCLUDE")) {
  1640. skipSeparator(false, !scanningInternalSubset());
  1641. if(currPEDepth != fPEDepth && fValidation) {
  1642. fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
  1643. "INVALID_PE_IN_CONDITIONAL",
  1644. new Object[]{ fEntityManager.fCurrentEntity.name},
  1645. XMLErrorReporter.SEVERITY_ERROR);
  1646. }
  1647. // call handler
  1648. if (!fEntityScanner.skipChar('[')) {
  1649. reportFatalError("MSG_MARKUP_NOT_RECOGNIZED_IN_DTD", null);
  1650. }
  1651. if (fDTDHandler != null) {
  1652. fDTDHandler.startConditional(XMLDTDHandler.CONDITIONAL_INCLUDE,
  1653. null);
  1654. }
  1655. fIncludeSectDepth++;
  1656. // just stop there and go back to the main loop
  1657. fReportEntity = true;
  1658. }
  1659. else if (fEntityScanner.skipString("IGNORE")) {
  1660. skipSeparator(false, !scanningInternalSubset());
  1661. if(currPEDepth != fPEDepth && fValidation) {
  1662. fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
  1663. "INVALID_PE_IN_CONDITIONAL",
  1664. new Object[]{ fEntityManager.fCurrentEntity.name},
  1665. XMLErrorReporter.SEVERITY_ERROR);
  1666. }
  1667. // call handler
  1668. if (fDTDHandler != null) {
  1669. fDTDHandler.startConditional(XMLDTDHandler.CONDITIONAL_IGNORE,
  1670. null);
  1671. }
  1672. if (!fEntityScanner.skipChar('[')) {
  1673. reportFatalError("MSG_MARKUP_NOT_RECOGNIZED_IN_DTD", null);
  1674. }
  1675. fReportEntity = true;
  1676. int initialDepth = ++fIncludeSectDepth;
  1677. if (fDTDHandler != null) {
  1678. fIgnoreConditionalBuffer.clear();
  1679. }
  1680. while (true) {
  1681. if (fEntityScanner.skipChar('<')) {
  1682. if (fDTDHandler != null) {
  1683. fIgnoreConditionalBuffer.append('<');
  1684. }
  1685. //
  1686. // These tests are split so that we handle cases like
  1687. // '<<![' and '<!<![' which we might otherwise miss.
  1688. //
  1689. if (fEntityScanner.skipChar('!')) {
  1690. if(fEntityScanner.skipChar('[')) {
  1691. if (fDTDHandler != null) {
  1692. fIgnoreConditionalBuffer.append("![");
  1693. }
  1694. fIncludeSectDepth++;
  1695. } else {
  1696. if (fDTDHandler != null) {
  1697. fIgnoreConditionalBuffer.append("!");
  1698. }
  1699. }
  1700. }
  1701. }
  1702. else if (fEntityScanner.skipChar(']')) {
  1703. if (fDTDHandler != null) {
  1704. fIgnoreConditionalBuffer.append(']');
  1705. }
  1706. //
  1707. // The same thing goes for ']<![' and '<]]>', etc.
  1708. //
  1709. if (fEntityScanner.skipChar(']')) {
  1710. if (fDTDHandler != null) {
  1711. fIgnoreConditionalBuffer.append(']');
  1712. }
  1713. while (fEntityScanner.skipChar(']')) {
  1714. /* empty loop body */
  1715. if (fDTDHandler != null) {
  1716. fIgnoreConditionalBuffer.append(']');
  1717. }
  1718. }
  1719. if (fEntityScanner.skipChar('>')) {
  1720. if (fIncludeSectDepth-- == initialDepth) {
  1721. fMarkUpDepth--;
  1722. // call handler
  1723. if (fDTDHandler != null) {
  1724. fLiteral.setValues(fIgnoreConditionalBuffer.ch, 0,
  1725. fIgnoreConditionalBuffer.length - 2);
  1726. fDTDHandler.ignoredCharacters(fLiteral, null);
  1727. fDTDHandler.endConditional(null);
  1728. }
  1729. return;
  1730. } else if(fDTDHandler != null) {
  1731. fIgnoreConditionalBuffer.append('>');
  1732. }
  1733. }
  1734. }
  1735. }
  1736. else {
  1737. int c = fEntityScanner.scanChar();
  1738. if (fScannerState == SCANNER_STATE_END_OF_INPUT) {
  1739. reportFatalError("IgnoreSectUnterminated", null);
  1740. return;
  1741. }
  1742. if (fDTDHandler != null) {
  1743. fIgnoreConditionalBuffer.append((char)c);
  1744. }
  1745. }
  1746. }
  1747. }
  1748. else {
  1749. reportFatalError("MSG_MARKUP_NOT_RECOGNIZED_IN_DTD", null);
  1750. }
  1751. } // scanConditionalSect()
  1752. /**
  1753. * Dispatch an XML "event".
  1754. *
  1755. * @param complete True if this method is intended to scan
  1756. * and dispatch as much as possible.
  1757. *
  1758. * @return True if there is more to scan.
  1759. *
  1760. * @throws IOException Thrown on i/o error.
  1761. * @throws XNIException Thrown on parse error.
  1762. *
  1763. */
  1764. protected final boolean scanDecls(boolean complete)
  1765. throws IOException, XNIException {
  1766. skipSeparator(false, true);
  1767. boolean again = true;
  1768. while (again && fScannerState == SCANNER_STATE_MARKUP_DECL) {
  1769. again = complete;
  1770. if (fEntityScanner.skipChar('<')) {
  1771. fMarkUpDepth++;
  1772. if (fEntityScanner.skipChar('?')) {
  1773. scanPI();
  1774. }
  1775. else if (fEntityScanner.skipChar('!')) {
  1776. if (fEntityScanner.skipChar('-')) {
  1777. if (!fEntityScanner.skipChar('-')) {
  1778. reportFatalError("MSG_MARKUP_NOT_RECOGNIZED_IN_DTD",
  1779. null);
  1780. } else {
  1781. scanComment();
  1782. }
  1783. }
  1784. else if (fEntityScanner.skipString("ELEMENT")) {
  1785. scanElementDecl();
  1786. }
  1787. else if (fEntityScanner.skipString("ATTLIST")) {
  1788. scanAttlistDecl();
  1789. }
  1790. else if (fEntityScanner.skipString("ENTITY")) {
  1791. scanEntityDecl();
  1792. }
  1793. else if (fEntityScanner.skipString("NOTATION")) {
  1794. scanNotationDecl();
  1795. }
  1796. else if (fEntityScanner.skipChar('[') &&
  1797. !scanningInternalSubset()) {
  1798. scanConditionalSect(fPEDepth);
  1799. }
  1800. else {
  1801. fMarkUpDepth--;
  1802. reportFatalError("MSG_MARKUP_NOT_RECOGNIZED_IN_DTD",
  1803. null);
  1804. }
  1805. }
  1806. else {
  1807. fMarkUpDepth--;
  1808. reportFatalError("MSG_MARKUP_NOT_RECOGNIZED_IN_DTD", null);
  1809. }
  1810. }
  1811. else if (fIncludeSectDepth > 0 && fEntityScanner.skipChar(']')) {
  1812. // end of conditional section?
  1813. if (!fEntityScanner.skipChar(']')
  1814. || !fEntityScanner.skipChar('>')) {
  1815. reportFatalError("IncludeSectUnterminated", null);
  1816. }
  1817. // call handler
  1818. if (fDTDHandler != null) {
  1819. fDTDHandler.endConditional(null);
  1820. }
  1821. // decreaseMarkupDepth();
  1822. fIncludeSectDepth--;
  1823. fMarkUpDepth--;
  1824. }
  1825. else if (scanningInternalSubset() &&
  1826. fEntityScanner.peekChar() == ']') {
  1827. // this is the end of the internal subset, let's stop here
  1828. return false;
  1829. }
  1830. else if (fEntityScanner.skipSpaces()) {
  1831. // simply skip
  1832. }
  1833. else {
  1834. reportFatalError("MSG_MARKUP_NOT_RECOGNIZED_IN_DTD", null);
  1835. // Skip the part in error
  1836. int ch;
  1837. do {
  1838. // Ignore the current character
  1839. fEntityScanner.scanChar();
  1840. // Skip any separators
  1841. skipSeparator(false, true);
  1842. // Keeping getting the next character,
  1843. // until it's one of the expected ones
  1844. ch = fEntityScanner.peekChar();
  1845. } while (ch != '<' && ch != ']' && !XMLChar.isSpace(ch));
  1846. }
  1847. skipSeparator(false, true);
  1848. }
  1849. return fScannerState != SCANNER_STATE_END_OF_INPUT;
  1850. }
  1851. /**
  1852. * Skip separator. This is typically just whitespace but it can also be one
  1853. * or more parameter entity references.
  1854. * <p>
  1855. * If there are some it "expands them" by calling the corresponding entity
  1856. * from the entity manager.
  1857. * <p>
  1858. * This is recursive and will process has many refs as possible.
  1859. *
  1860. * @param spaceRequired Specify whether some leading whitespace should be
  1861. * found
  1862. * @param lookForPERefs Specify whether parameter entity references should
  1863. * be looked for
  1864. * @return True if any leading whitespace was found or the end of a
  1865. * parameter entity was crossed.
  1866. */
  1867. private boolean skipSeparator(boolean spaceRequired, boolean lookForPERefs)
  1868. throws IOException, XNIException
  1869. {
  1870. int depth = fPEDepth;
  1871. boolean sawSpace = fEntityScanner.skipSpaces();
  1872. if (!lookForPERefs || !fEntityScanner.skipChar('%')) {
  1873. return !spaceRequired || sawSpace || (depth != fPEDepth);
  1874. }
  1875. while (true) {
  1876. String name = fEntityScanner.scanName();
  1877. if (name == null) {
  1878. reportFatalError("NameRequiredInPEReference", null);
  1879. }
  1880. else if (!fEntityScanner.skipChar(';')) {
  1881. reportFatalError("SemicolonRequiredInPEReference",
  1882. new Object[]{name});
  1883. }
  1884. startPE(name, false);
  1885. fEntityScanner.skipSpaces();
  1886. if (!fEntityScanner.skipChar('%'))
  1887. return true;
  1888. }
  1889. }
  1890. /*
  1891. * Element Children Content Stack
  1892. */
  1893. private final void pushContentStack(int c) {
  1894. if (fContentStack.length == fContentDepth) {
  1895. int[] newStack = new int[fContentDepth * 2];
  1896. System.arraycopy(fContentStack, 0, newStack, 0, fContentDepth);
  1897. fContentStack = newStack;
  1898. }
  1899. fContentStack[fContentDepth++] = c;
  1900. }
  1901. private final int popContentStack() {
  1902. return fContentStack[--fContentDepth];
  1903. }
  1904. /*
  1905. * Parameter Entity Stack
  1906. */
  1907. private final void pushPEStack(int depth, boolean report) {
  1908. if (fPEStack.length == fPEDepth) {
  1909. int[] newIntStack = new int[fPEDepth * 2];
  1910. System.arraycopy(fPEStack, 0, newIntStack, 0, fPEDepth);
  1911. fPEStack = newIntStack;
  1912. // report end/start calls
  1913. boolean[] newBooleanStack = new boolean[fPEDepth * 2];
  1914. System.arraycopy(fPEReport, 0, newBooleanStack, 0, fPEDepth);
  1915. fPEReport = newBooleanStack;
  1916. }
  1917. fPEReport[fPEDepth] = report;
  1918. fPEStack[fPEDepth++] = depth;
  1919. }
  1920. /** pop the stack */
  1921. private final int popPEStack() {
  1922. return fPEStack[--fPEDepth];
  1923. }
  1924. /** look at the top of the stack */
  1925. private final boolean peekReportEntity() {
  1926. return fPEReport[fPEDepth-1];
  1927. }
  1928. /*
  1929. * Utility method
  1930. */
  1931. private final void ensureEnumerationSize(int size) {
  1932. if (fEnumeration.length == size) {
  1933. String[] newEnum = new String[size * 2];
  1934. System.arraycopy(fEnumeration, 0, newEnum, 0, size);
  1935. fEnumeration = newEnum;
  1936. }
  1937. }
  1938. // private methods
  1939. private void init() {
  1940. // reset state related data
  1941. fStartDTDCalled = false;
  1942. fExtEntityDepth = 0;
  1943. fIncludeSectDepth = 0;
  1944. fMarkUpDepth = 0;
  1945. fPEDepth = 0;
  1946. fStandalone = false;
  1947. fSeenExternalDTD = false;
  1948. fSeenExternalPE = false;
  1949. // set starting state
  1950. setScannerState(SCANNER_STATE_TEXT_DECL);
  1951. }
  1952. } // class XMLDTDScannerImpl