ValidationConsumer.java 69 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929
  1. /* ValidationConsumer.java --
  2. Copyright (C) 1999,2000,2001 Free Software Foundation, Inc.
  3. This file is part of GNU Classpath.
  4. GNU Classpath is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2, or (at your option)
  7. any later version.
  8. GNU Classpath is distributed in the hope that it will be useful, but
  9. WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with GNU Classpath; see the file COPYING. If not, write to the
  14. Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  15. 02110-1301 USA.
  16. Linking this library statically or dynamically with other modules is
  17. making a combined work based on this library. Thus, the terms and
  18. conditions of the GNU General Public License cover the whole
  19. combination.
  20. As a special exception, the copyright holders of this library give you
  21. permission to link this library with independent modules to produce an
  22. executable, regardless of the license terms of these independent
  23. modules, and to copy and distribute the resulting executable under
  24. terms of your choice, provided that you also meet, for each linked
  25. independent module, the terms and conditions of the license of that
  26. module. An independent module is a module which is not derived from
  27. or based on this library. If you modify this library, you may extend
  28. this exception to your version of the library, but you are not
  29. obligated to do so. If you do not wish to do so, delete this
  30. exception statement from your version. */
  31. package gnu.xml.pipeline;
  32. import java.io.IOException;
  33. import java.io.StringReader;
  34. import java.io.StringWriter;
  35. import java.util.EmptyStackException;
  36. import java.util.Enumeration;
  37. import java.util.Hashtable;
  38. import java.util.Stack;
  39. import java.util.StringTokenizer;
  40. import java.util.Vector;
  41. import org.xml.sax.Attributes;
  42. import org.xml.sax.EntityResolver;
  43. import org.xml.sax.ErrorHandler;
  44. import org.xml.sax.InputSource;
  45. import org.xml.sax.Locator;
  46. import org.xml.sax.SAXException;
  47. import org.xml.sax.SAXParseException;
  48. import org.xml.sax.XMLReader;
  49. import org.xml.sax.helpers.XMLReaderFactory;
  50. /**
  51. * This class checks SAX2 events to report validity errors; it works as
  52. * both a filter and a terminus on an event pipeline. It relies on the
  53. * producer of SAX events to: </p> <ol>
  54. *
  55. * <li> Conform to the specification of a non-validating XML parser that
  56. * reads all external entities, reported using SAX2 events. </li>
  57. *
  58. * <li> Report ignorable whitespace as such (through the ContentHandler
  59. * interface). This is, strictly speaking, optional for nonvalidating
  60. * XML processors. </li>
  61. *
  62. * <li> Make SAX2 DeclHandler callbacks, with default
  63. * attribute values already normalized (and without "&lt;").</li>
  64. *
  65. * <li> Make SAX2 LexicalHandler startDTD() and endDTD ()
  66. * callbacks. </li>
  67. *
  68. * <li> Act as if the <em>(URI)/namespace-prefixes</em> property were
  69. * set to true, by providing XML 1.0 names and all <code>xmlns*</code>
  70. * attributes (rather than omitting either or both). </li>
  71. *
  72. * </ol>
  73. *
  74. * <p> At this writing, the major SAX2 parsers (such as &AElig;lfred2,
  75. * Crimson, and Xerces) meet these requirements, and this validation
  76. * module is used by the optional &AElig;lfred2 validation support.
  77. * </p>
  78. *
  79. * <p> Note that because this is a layered validator, it has to duplicate some
  80. * work that the parser is doing; there are also other cost to layering.
  81. * However, <em>because of layering it doesn't need a parser</em> in order
  82. * to work! You can use it with anything that generates SAX events, such
  83. * as an application component that wants to detect invalid content in
  84. * a changed area without validating an entire document, or which wants to
  85. * ensure that it doesn't write invalid data to a communications partner.</p>
  86. *
  87. * <p> Also, note that because this is a layered validator, the line numbers
  88. * reported for some errors may seem strange. For example, if an element does
  89. * not permit character content, the validator
  90. * will use the locator provided to it.
  91. * That might reflect the last character of a <em>characters</em> event
  92. * callback, rather than the first non-whitespace character. </p>
  93. *
  94. * <hr />
  95. *
  96. * <!--
  97. * <p> Of interest is the fact that unlike most currently known XML validators,
  98. * this one can report some cases of non-determinism in element content models.
  99. * It is a compile-time option, enabled by default. This will only report
  100. * such XML errors if they relate to content actually appearing in a document;
  101. * content models aren't aggressively scanned for non-deterministic structure.
  102. * Documents which trigger such non-deterministic transitions may be handled
  103. * differently by different validating parsers, without losing conformance
  104. * to the XML specification. </p>
  105. * -->
  106. *
  107. * <p> Current limitations of the validation performed are in roughly three
  108. * categories. </p>
  109. *
  110. * <p> The first category represents constraints which demand violations
  111. * of software layering: exposing lexical details, one of the first things
  112. * that <em>application</em> programming interfaces (APIs) hide. These
  113. * invariably relate to XML entity handling, and to historical oddities
  114. * of the XML validation semantics. Curiously,
  115. * recent (Autumn 1999) conformance testing showed that these constraints are
  116. * among those handled worst by existing XML validating parsers. Arguments
  117. * have been made that each of these VCs should be turned into WFCs (most
  118. * of them) or discarded (popular for the standalone declaration); in short,
  119. * that these are bugs in the XML specification (not all via SGML): </p><ul>
  120. *
  121. * <li> The <em>Proper Declaration/PE Nesting</em> and
  122. * <em>Proper Group/PE Nesting</em> VCs can't be tested because they
  123. * require access to particularly low level lexical level information.
  124. * In essence, the reason XML isn't a simple thing to parse is that
  125. * it's not a context free grammar, and these constraints elevate that
  126. * SGML-derived context sensitivity to the level of a semantic rule.
  127. *
  128. * <li> The <em>Standalone Document Declaration</em> VC can't be
  129. * tested. This is for two reasons. First, this flag isn't made
  130. * available through SAX2. Second, it also requires breaking that
  131. * lexical layering boundary. (If you ever wondered why classes
  132. * in compiler construction or language design barely mention the
  133. * existence of context-sensitive grammars, it's because of messy
  134. * issues like these.)
  135. *
  136. * <li> The <em>Entity Declared</em> VC can't be tested, because it
  137. * also requires breaking that lexical layering boundary! There's also
  138. * another issue: the VC wording (and seemingly intent) is ambiguous.
  139. * (This is still true in the "Second edition" XML spec.)
  140. * Since there is a WFC of the same name, everyone's life would be
  141. * easier if references to undeclared parsed entities were always well
  142. * formedness errors, regardless of whether they're parameter entities
  143. * or not. (Note that nonvalidating parsers are not required
  144. * to report all such well formedness errors if they don't read external
  145. * parameter entities, although currently most XML parsers read them
  146. * in an attempt to avoid problems from inconsistent parser behavior.)
  147. *
  148. * </ul>
  149. *
  150. * <p> The second category of limitations on this validation represent
  151. * constraints associated with information that is not guaranteed to be
  152. * available (or in one case, <em>is guaranteed not to be available</em>,
  153. * through the SAX2 API: </p><ul>
  154. *
  155. * <li> The <em>Unique Element Type Declaration</em> VC may not be
  156. * reportable, if the underlying parser happens not to expose
  157. * multiple declarations. (&AElig;lfred2 reports these validity
  158. * errors directly.)</li>
  159. *
  160. * <li> Similarly, the <em>Unique Notation Name</em> VC, added in the
  161. * 14-January-2000 XML spec errata to restrict typing models used by
  162. * elements, may not be reportable. (&AElig;lfred reports these
  163. * validity errors directly.) </li>
  164. *
  165. * </ul>
  166. *
  167. * <p> A third category relates to ease of implementation. (Think of this
  168. * as "bugs".) The most notable issue here is character handling. Rather
  169. * than attempting to implement the voluminous character tables in the XML
  170. * specification (Appendix B), Unicode rules are used directly from
  171. * the java.lang.Character class. Recent JVMs have begun to diverge from
  172. * the original specification for that class (Unicode 2.0), meaning that
  173. * different JVMs may handle that aspect of conformance differently.
  174. * </p>
  175. *
  176. * <p> Note that for some of the validity errors that SAX2 does not
  177. * expose, a nonvalidating parser is permitted (by the XML specification)
  178. * to report validity errors. When used with a parser that does so for
  179. * the validity constraints mentioned above (or any other SAX2 event
  180. * stream producer that does the same thing), overall conformance is
  181. * substantially improved.
  182. *
  183. * @see gnu.xml.aelfred2.SAXDriver
  184. * @see gnu.xml.aelfred2.XmlReader
  185. *
  186. * @author David Brownell
  187. */
  188. public final class ValidationConsumer extends EventFilter
  189. {
  190. // report error if we happen to notice a non-deterministic choice?
  191. // we won't report buggy content models; just buggy instances
  192. private static final boolean warnNonDeterministic = false;
  193. // for tracking active content models
  194. private String rootName;
  195. private Stack contentStack = new Stack ();
  196. // flags for "saved DTD" processing
  197. private boolean disableDeclarations;
  198. private boolean disableReset;
  199. //
  200. // most VCs get tested when we see element start tags. the per-element
  201. // info (including attributes) recorded here duplicates that found inside
  202. // many nonvalidating parsers, hence dual lookups etc ... that's why a
  203. // layered validator isn't going to be as fast as a non-layered one.
  204. //
  205. // key = element name; value = ElementInfo
  206. private Hashtable elements = new Hashtable ();
  207. // some VCs relate to ID/IDREF/IDREFS attributes
  208. // key = id; value = boolean true (defd) or false (refd)
  209. private Hashtable ids = new Hashtable ();
  210. // we just record declared notation and unparsed entity names.
  211. // the implementation here is simple/slow; these features
  212. // are seldom used, one hopes they'll wither away soon
  213. private Vector notations = new Vector (5, 5);
  214. private Vector nDeferred = new Vector (5, 5);
  215. private Vector unparsed = new Vector (5, 5);
  216. private Vector uDeferred = new Vector (5, 5);
  217. // note: DocBk 3.1.7 XML defines over 2 dozen notations,
  218. // used when defining unparsed entities for graphics
  219. // (and maybe in other places)
  220. /**
  221. * Creates a pipeline terminus which consumes all events passed to
  222. * it; this will report validity errors as if they were fatal errors,
  223. * unless an error handler is assigned.
  224. *
  225. * @see #setErrorHandler
  226. */
  227. // constructor used by PipelineFactory
  228. // ... and want one taking system ID of an external subset
  229. public ValidationConsumer ()
  230. {
  231. this (null);
  232. }
  233. /**
  234. * Creates a pipeline filter which reports validity errors and then
  235. * passes events on to the next consumer if they were not fatal.
  236. *
  237. * @see #setErrorHandler
  238. */
  239. // constructor used by PipelineFactory
  240. // ... and want one taking system ID of an external subset
  241. // (which won't send declaration events)
  242. public ValidationConsumer (EventConsumer next)
  243. {
  244. super (next);
  245. setContentHandler (this);
  246. setDTDHandler (this);
  247. try { setProperty (DECL_HANDLER, this); }
  248. catch (Exception e) { /* "can't happen" */ }
  249. try { setProperty (LEXICAL_HANDLER, this); }
  250. catch (Exception e) { /* "can't happen" */ }
  251. }
  252. private static final String fakeRootName
  253. = ":Nobody:in:their_Right.Mind_would:use:this-name:1x:";
  254. /**
  255. * Creates a validation consumer which is preloaded with the DTD provided.
  256. * It does this by constructing a document with that DTD, then parsing
  257. * that document and recording its DTD declarations. Then it arranges
  258. * not to modify that information.
  259. *
  260. * <p> The resulting validation consumer will only validate against
  261. * the specified DTD, regardless of whether some other DTD is found
  262. * in a document being parsed.
  263. *
  264. * @param rootName The name of the required root element; if this is
  265. * null, any root element name will be accepted.
  266. * @param publicId If non-null and there is a non-null systemId, this
  267. * identifier provides an alternate access identifier for the DTD's
  268. * external subset.
  269. * @param systemId If non-null, this is a URI (normally URL) that
  270. * may be used to access the DTD's external subset.
  271. * @param internalSubset If non-null, holds literal markup declarations
  272. * comprising the DTD's internal subset.
  273. * @param resolver If non-null, this will be provided to the parser for
  274. * use when resolving parameter entities (including any external subset).
  275. * @param resolver If non-null, this will be provided to the parser for
  276. * use when resolving parameter entities (including any external subset).
  277. * @param minimalElement If non-null, a minimal valid document.
  278. *
  279. * @exception SAXNotSupportedException If the default SAX parser does
  280. * not support the standard lexical or declaration handlers.
  281. * @exception SAXParseException If the specified DTD has either
  282. * well-formedness or validity errors
  283. * @exception IOException If the specified DTD can't be read for
  284. * some reason
  285. */
  286. public ValidationConsumer (
  287. String rootName,
  288. String publicId,
  289. String systemId,
  290. String internalSubset,
  291. EntityResolver resolver,
  292. String minimalDocument
  293. ) throws SAXException, IOException
  294. {
  295. this (null);
  296. disableReset = true;
  297. if (rootName == null)
  298. rootName = fakeRootName;
  299. //
  300. // Synthesize document with that DTD; is it possible to do
  301. // better for the declaration of the root element?
  302. //
  303. // NOTE: can't use SAX2 to write internal subsets.
  304. //
  305. StringWriter writer = new StringWriter ();
  306. writer.write ("<!DOCTYPE ");
  307. writer.write (rootName);
  308. if (systemId != null) {
  309. writer.write ("\n ");
  310. if (publicId != null) {
  311. writer.write ("PUBLIC '");
  312. writer.write (publicId);
  313. writer.write ("'\n\t'");
  314. } else
  315. writer.write ("SYSTEM '");
  316. writer.write (systemId);
  317. writer.write ("'");
  318. }
  319. writer.write (" [ ");
  320. if (rootName == fakeRootName) {
  321. writer.write ("\n<!ELEMENT ");
  322. writer.write (rootName);
  323. writer.write (" EMPTY>");
  324. }
  325. if (internalSubset != null)
  326. writer.write (internalSubset);
  327. writer.write ("\n ]>");
  328. if (minimalDocument != null) {
  329. writer.write ("\n");
  330. writer.write (minimalDocument);
  331. writer.write ("\n");
  332. } else {
  333. writer.write (" <");
  334. writer.write (rootName);
  335. writer.write ("/>\n");
  336. }
  337. minimalDocument = writer.toString ();
  338. //
  339. // OK, load it
  340. //
  341. XMLReader producer;
  342. producer = XMLReaderFactory.createXMLReader ();
  343. bind (producer, this);
  344. if (resolver != null)
  345. producer.setEntityResolver (resolver);
  346. InputSource in;
  347. in = new InputSource (new StringReader (minimalDocument));
  348. producer.parse (in);
  349. disableDeclarations = true;
  350. if (rootName == fakeRootName)
  351. this.rootName = null;
  352. }
  353. private void resetState ()
  354. {
  355. if (!disableReset) {
  356. rootName = null;
  357. contentStack.removeAllElements ();
  358. elements.clear ();
  359. ids.clear ();
  360. notations.removeAllElements ();
  361. nDeferred.removeAllElements ();
  362. unparsed.removeAllElements ();
  363. uDeferred.removeAllElements ();
  364. }
  365. }
  366. private void warning (String description)
  367. throws SAXException
  368. {
  369. ErrorHandler errHandler = getErrorHandler ();
  370. Locator locator = getDocumentLocator ();
  371. SAXParseException err;
  372. if (errHandler == null)
  373. return;
  374. if (locator == null)
  375. err = new SAXParseException (description, null, null, -1, -1);
  376. else
  377. err = new SAXParseException (description, locator);
  378. errHandler.warning (err);
  379. }
  380. // package private (for ChildrenRecognizer)
  381. private void error (String description)
  382. throws SAXException
  383. {
  384. ErrorHandler errHandler = getErrorHandler ();
  385. Locator locator = getDocumentLocator ();
  386. SAXParseException err;
  387. if (locator == null)
  388. err = new SAXParseException (description, null, null, -1, -1);
  389. else
  390. err = new SAXParseException (description, locator);
  391. if (errHandler != null)
  392. errHandler.error (err);
  393. else // else we always treat it as fatal!
  394. throw err;
  395. }
  396. private void fatalError (String description)
  397. throws SAXException
  398. {
  399. ErrorHandler errHandler = getErrorHandler ();
  400. Locator locator = getDocumentLocator ();
  401. SAXParseException err;
  402. if (locator != null)
  403. err = new SAXParseException (description, locator);
  404. else
  405. err = new SAXParseException (description, null, null, -1, -1);
  406. if (errHandler != null)
  407. errHandler.fatalError (err);
  408. // we always treat this as fatal, regardless of the handler
  409. throw err;
  410. }
  411. private static boolean isExtender (char c)
  412. {
  413. // [88] Extender ::= ...
  414. return c == 0x00b7 || c == 0x02d0 || c == 0x02d1 || c == 0x0387
  415. || c == 0x0640 || c == 0x0e46 || c == 0x0ec6 || c == 0x3005
  416. || (c >= 0x3031 && c <= 0x3035)
  417. || (c >= 0x309d && c <= 0x309e)
  418. || (c >= 0x30fc && c <= 0x30fe);
  419. }
  420. // use augmented Unicode rules, not full XML rules
  421. private boolean isName (String name, String context, String id)
  422. throws SAXException
  423. {
  424. char buf [] = name.toCharArray ();
  425. boolean pass = true;
  426. if (!Character.isUnicodeIdentifierStart (buf [0])
  427. && ":_".indexOf (buf [0]) == -1)
  428. pass = false;
  429. else {
  430. int max = buf.length;
  431. for (int i = 1; pass && i < max; i++) {
  432. char c = buf [i];
  433. if (!Character.isUnicodeIdentifierPart (c)
  434. && ":-_.".indexOf (c) == -1
  435. && !isExtender (c))
  436. pass = false;
  437. }
  438. }
  439. if (!pass)
  440. error ("In " + context + " for " + id
  441. + ", '" + name + "' is not a name");
  442. return pass; // true == OK
  443. }
  444. // use augmented Unicode rules, not full XML rules
  445. private boolean isNmtoken (String nmtoken, String context, String id)
  446. throws SAXException
  447. {
  448. char buf [] = nmtoken.toCharArray ();
  449. boolean pass = true;
  450. int max = buf.length;
  451. // XXX make this share code with isName
  452. for (int i = 0; pass && i < max; i++) {
  453. char c = buf [i];
  454. if (!Character.isUnicodeIdentifierPart (c)
  455. && ":-_.".indexOf (c) == -1
  456. && !isExtender (c))
  457. pass = false;
  458. }
  459. if (!pass)
  460. error ("In " + context + " for " + id
  461. + ", '" + nmtoken + "' is not a name token");
  462. return pass; // true == OK
  463. }
  464. private void checkEnumeration (String value, String type, String name)
  465. throws SAXException
  466. {
  467. if (!hasMatch (value, type))
  468. // VC: Enumeration
  469. error ("Value '" + value
  470. + "' for attribute '" + name
  471. + "' is not permitted: " + type);
  472. }
  473. // used to test enumerated attributes and mixed content models
  474. // package private
  475. static boolean hasMatch (String value, String orList)
  476. {
  477. int len = value.length ();
  478. int max = orList.length () - len;
  479. for (int start = 0;
  480. (start = orList.indexOf (value, start)) != -1;
  481. start++) {
  482. char c;
  483. if (start > max)
  484. break;
  485. c = orList.charAt (start - 1);
  486. if (c != '|' && c != '('/*)*/)
  487. continue;
  488. c = orList.charAt (start + len);
  489. if (c != '|' && /*(*/ c != ')')
  490. continue;
  491. return true;
  492. }
  493. return false;
  494. }
  495. /**
  496. * <b>LexicalHandler</b> Records the declaration of the root
  497. * element, so it can be verified later.
  498. * Passed to the next consumer, unless this one was
  499. * preloaded with a particular DTD.
  500. */
  501. public void startDTD (String name, String publicId, String systemId)
  502. throws SAXException
  503. {
  504. if (disableDeclarations)
  505. return;
  506. rootName = name;
  507. super.startDTD (name, publicId, systemId);
  508. }
  509. /**
  510. * <b>LexicalHandler</b> Verifies that all referenced notations
  511. * and unparsed entities have been declared.
  512. * Passed to the next consumer, unless this one was
  513. * preloaded with a particular DTD.
  514. */
  515. public void endDTD ()
  516. throws SAXException
  517. {
  518. if (disableDeclarations)
  519. return;
  520. // this is a convenient hook for end-of-dtd checks, but we
  521. // could also trigger it in the first startElement call.
  522. // locator info is more appropriate here though.
  523. // VC: Notation Declared (NDATA can refer to them before decls,
  524. // as can NOTATION attribute enumerations and defaults)
  525. int length = nDeferred.size ();
  526. for (int i = 0; i < length; i++) {
  527. String notation = (String) nDeferred.elementAt (i);
  528. if (!notations.contains (notation)) {
  529. error ("A declaration referred to notation '" + notation
  530. + "' which was never declared");
  531. }
  532. }
  533. nDeferred.removeAllElements ();
  534. // VC: Entity Name (attribute values can refer to them
  535. // before they're declared); VC Attribute Default Legal
  536. length = uDeferred.size ();
  537. for (int i = 0; i < length; i++) {
  538. String entity = (String) uDeferred.elementAt (i);
  539. if (!unparsed.contains (entity)) {
  540. error ("An attribute default referred to entity '" + entity
  541. + "' which was never declared");
  542. }
  543. }
  544. uDeferred.removeAllElements ();
  545. super.endDTD ();
  546. }
  547. // These are interned, so we can rely on "==" to find the type of
  548. // all attributes except enumerations ...
  549. // "(this|or|that|...)" and "NOTATION (this|or|that|...)"
  550. static final String types [] = {
  551. "CDATA",
  552. "ID", "IDREF", "IDREFS",
  553. "NMTOKEN", "NMTOKENS",
  554. "ENTITY", "ENTITIES"
  555. };
  556. /**
  557. * <b>DecllHandler</b> Records attribute declaration for later use
  558. * in validating document content, and checks validity constraints
  559. * that are applicable to attribute declarations.
  560. * Passed to the next consumer, unless this one was
  561. * preloaded with a particular DTD.
  562. */
  563. public void attributeDecl (
  564. String eName,
  565. String aName,
  566. String type,
  567. String mode,
  568. String value
  569. ) throws SAXException
  570. {
  571. if (disableDeclarations)
  572. return;
  573. ElementInfo info = (ElementInfo) elements.get (eName);
  574. AttributeInfo ainfo = new AttributeInfo ();
  575. boolean checkOne = false;
  576. boolean interned = false;
  577. // cheap interning of type names and #FIXED, #REQUIRED
  578. // for faster startElement (we can use "==")
  579. for (int i = 0; i < types.length; i++) {
  580. if (types [i].equals (type)) {
  581. type = types [i];
  582. interned = true;
  583. break;
  584. }
  585. }
  586. if ("#FIXED".equals (mode))
  587. mode = "#FIXED";
  588. else if ("#REQUIRED".equals (mode))
  589. mode = "#REQUIRED";
  590. ainfo.type = type;
  591. ainfo.mode = mode;
  592. ainfo.value = value;
  593. // we might not have seen the content model yet
  594. if (info == null) {
  595. info = new ElementInfo (eName);
  596. elements.put (eName, info);
  597. }
  598. if ("ID" == type) {
  599. checkOne = true;
  600. if (!("#REQUIRED" == mode || "#IMPLIED".equals (mode))) {
  601. // VC: ID Attribute Default
  602. error ("ID attribute '" + aName
  603. + "' must be #IMPLIED or #REQUIRED");
  604. }
  605. } else if (!interned && type.startsWith ("NOTATION ")) {
  606. checkOne = true;
  607. // VC: Notation Attributes (notations must be declared)
  608. StringTokenizer tokens = new StringTokenizer (
  609. type.substring (10, type.lastIndexOf (')')),
  610. "|");
  611. while (tokens.hasMoreTokens ()) {
  612. String token = tokens.nextToken ();
  613. if (!notations.contains (token))
  614. nDeferred.addElement (token);
  615. }
  616. }
  617. if (checkOne) {
  618. for (Enumeration e = info.attributes.keys ();
  619. e.hasMoreElements ();
  620. /* NOP */) {
  621. String name;
  622. AttributeInfo ainfo2;
  623. name = (String) e.nextElement ();
  624. ainfo2 = (AttributeInfo) info.attributes.get (name);
  625. if (type == ainfo2.type || !interned /* NOTATION */) {
  626. // VC: One ID per Element Type
  627. // VC: One Notation per Element TYpe
  628. error ("Element '" + eName
  629. + "' already has an attribute of type "
  630. + (interned ? "NOTATION" : type)
  631. + " ('" + name
  632. + "') so '" + aName
  633. + "' is a validity error");
  634. }
  635. }
  636. }
  637. // VC: Attribute Default Legal
  638. if (value != null) {
  639. if ("CDATA" == type) {
  640. // event source rejected '<'
  641. } else if ("NMTOKEN" == type) {
  642. // VC: Name Token (is a nmtoken)
  643. isNmtoken (value, "attribute default", aName);
  644. } else if ("NMTOKENS" == type) {
  645. // VC: Name Token (is a nmtoken; at least one value)
  646. StringTokenizer tokens = new StringTokenizer (value);
  647. if (!tokens.hasMoreTokens ())
  648. error ("Default for attribute '" + aName
  649. + "' must have at least one name token.");
  650. else do {
  651. String token = tokens.nextToken ();
  652. isNmtoken (token, "attribute default", aName);
  653. } while (tokens.hasMoreTokens ());
  654. } else if ("IDREF" == type || "ENTITY" == type) {
  655. // VC: Entity Name (is a name)
  656. // VC: IDREF (is a name) (is declared)
  657. isName (value, "attribute default", aName);
  658. if ("ENTITY" == type && !unparsed.contains (value))
  659. uDeferred.addElement (value);
  660. } else if ("IDREFS" == type || "ENTITIES" == type) {
  661. // VC: Entity Name (is a name; at least one value)
  662. // VC: IDREF (is a name; at least one value)
  663. StringTokenizer names = new StringTokenizer (value);
  664. if (!names.hasMoreTokens ())
  665. error ("Default for attribute '" + aName
  666. + "' must have at least one name.");
  667. else do {
  668. String name = names.nextToken ();
  669. isName (name, "attribute default", aName);
  670. if ("ENTITIES" == type && !unparsed.contains (name))
  671. uDeferred.addElement (value);
  672. } while (names.hasMoreTokens ());
  673. } else if (type.charAt (0) == '(' /*)*/ ) {
  674. // VC: Enumeration (must match)
  675. checkEnumeration (value, type, aName);
  676. } else if (!interned && checkOne) { /* NOTATION */
  677. // VC: Notation attributes (must be names)
  678. isName (value, "attribute default", aName);
  679. // VC: Notation attributes (must be declared)
  680. if (!notations.contains (value))
  681. nDeferred.addElement (value);
  682. // VC: Enumeration (must match)
  683. checkEnumeration (value, type, aName);
  684. } else if ("ID" != type)
  685. throw new RuntimeException ("illegal attribute type: " + type);
  686. }
  687. if (info.attributes.get (aName) == null)
  688. info.attributes.put (aName, ainfo);
  689. /*
  690. else
  691. warning ("Element '" + eName
  692. + "' already has an attribute named '" + aName + "'");
  693. */
  694. if ("xml:space".equals (aName)) {
  695. if (!("(default|preserve)".equals (type)
  696. || "(preserve|default)".equals (type)
  697. // these next two are arguable; XHTML's DTD doesn't
  698. // deserve errors. After all, it's not like any
  699. // illegal _value_ could pass ...
  700. || "(preserve)".equals (type)
  701. || "(default)".equals (type)
  702. ))
  703. error (
  704. "xml:space attribute type must be like '(default|preserve)'"
  705. + " not '" + type + "'"
  706. );
  707. }
  708. super.attributeDecl (eName, aName, type, mode, value);
  709. }
  710. /**
  711. * <b>DecllHandler</b> Records the element declaration for later use
  712. * when checking document content, and checks validity constraints that
  713. * apply to element declarations. Passed to the next consumer, unless
  714. * this one was preloaded with a particular DTD.
  715. */
  716. public void elementDecl (String name, String model)
  717. throws SAXException
  718. {
  719. if (disableDeclarations)
  720. return;
  721. ElementInfo info = (ElementInfo) elements.get (name);
  722. // we might have seen an attribute decl already
  723. if (info == null) {
  724. info = new ElementInfo (name);
  725. elements.put (name, info);
  726. }
  727. if (info.model != null) {
  728. // NOTE: not all parsers can report such duplicates.
  729. // VC: Unique Element Type Declaration
  730. error ("Element type '" + name
  731. + "' was already declared.");
  732. } else {
  733. info.model = model;
  734. // VC: No Duplicate Types (in mixed content models)
  735. if (model.charAt (1) == '#') // (#PCDATA...
  736. info.getRecognizer (this);
  737. }
  738. super.elementDecl (name, model);
  739. }
  740. /**
  741. * <b>DecllHandler</b> passed to the next consumer, unless this
  742. * one was preloaded with a particular DTD
  743. */
  744. public void internalEntityDecl (String name, String value)
  745. throws SAXException
  746. {
  747. if (!disableDeclarations)
  748. super.internalEntityDecl (name, value);
  749. }
  750. /**
  751. * <b>DecllHandler</b> passed to the next consumer, unless this
  752. * one was preloaded with a particular DTD
  753. */
  754. public void externalEntityDecl (String name,
  755. String publicId, String systemId)
  756. throws SAXException
  757. {
  758. if (!disableDeclarations)
  759. super.externalEntityDecl (name, publicId, systemId);
  760. }
  761. /**
  762. * <b>DTDHandler</b> Records the notation name, for checking
  763. * NOTATIONS attribute values and declararations of unparsed
  764. * entities. Passed to the next consumer, unless this one was
  765. * preloaded with a particular DTD.
  766. */
  767. public void notationDecl (String name, String publicId, String systemId)
  768. throws SAXException
  769. {
  770. if (disableDeclarations)
  771. return;
  772. notations.addElement (name);
  773. super.notationDecl (name, publicId, systemId);
  774. }
  775. /**
  776. * <b>DTDHandler</b> Records the entity name, for checking
  777. * ENTITY and ENTITIES attribute values; records the notation
  778. * name if it hasn't yet been declared. Passed to the next consumer,
  779. * unless this one was preloaded with a particular DTD.
  780. */
  781. public void unparsedEntityDecl (
  782. String name,
  783. String publicId,
  784. String systemId,
  785. String notationName
  786. ) throws SAXException
  787. {
  788. if (disableDeclarations)
  789. return;
  790. unparsed.addElement (name);
  791. if (!notations.contains (notationName))
  792. nDeferred.addElement (notationName);
  793. super.unparsedEntityDecl (name, publicId, systemId, notationName);
  794. }
  795. /**
  796. * <b>ContentHandler</b> Ensures that state from any previous parse
  797. * has been deleted.
  798. * Passed to the next consumer.
  799. */
  800. public void startDocument ()
  801. throws SAXException
  802. {
  803. resetState ();
  804. super.startDocument ();
  805. }
  806. private static boolean isAsciiLetter (char c)
  807. {
  808. return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
  809. }
  810. /**
  811. * <b>ContentHandler</b> Reports a fatal exception. Validating
  812. * XML processors may not skip any entities.
  813. */
  814. public void skippedEntity (String name)
  815. throws SAXException
  816. {
  817. fatalError ("may not skip entities");
  818. }
  819. /*
  820. * SAX2 doesn't expand non-PE refs in attribute defaults...
  821. */
  822. private String expandDefaultRefs (String s)
  823. throws SAXException
  824. {
  825. if (s.indexOf ('&') < 0)
  826. return s;
  827. // FIXME: handle &#nn; &#xnn; &name;
  828. String message = "Can't expand refs in attribute default: " + s;
  829. warning (message);
  830. return s;
  831. }
  832. /**
  833. * <b>ContentHandler</b> Performs validity checks against element
  834. * (and document) content models, and attribute values.
  835. * Passed to the next consumer.
  836. */
  837. public void startElement (
  838. String uri,
  839. String localName,
  840. String qName,
  841. Attributes atts
  842. ) throws SAXException
  843. {
  844. //
  845. // First check content model for the enclosing scope.
  846. //
  847. if (contentStack.isEmpty ()) {
  848. // VC: Root Element Type
  849. if (!qName.equals (rootName)) {
  850. if (rootName == null)
  851. warning ("This document has no DTD, can't be valid");
  852. else
  853. error ("Root element type '" + qName
  854. + "' was declared to be '" + rootName + "'");
  855. }
  856. } else {
  857. Recognizer state = (Recognizer) contentStack.peek ();
  858. if (state != null) {
  859. Recognizer newstate = state.acceptElement (qName);
  860. if (newstate == null)
  861. error ("Element type '" + qName
  862. + "' in element '" + state.type.name
  863. + "' violates content model " + state.type.model
  864. );
  865. if (newstate != state) {
  866. contentStack.pop ();
  867. contentStack.push (newstate);
  868. }
  869. }
  870. }
  871. //
  872. // Then check that this element was declared, and push the
  873. // object used to validate its content model onto our stack.
  874. //
  875. // This is where the recognizer gets created, if needed; if
  876. // it's a "children" (elements) content model, an NDFA is
  877. // created. (One recognizer is used per content type, no
  878. // matter how complex that recognizer is.)
  879. //
  880. ElementInfo info;
  881. info = (ElementInfo) elements.get (qName);
  882. if (info == null || info.model == null) {
  883. // VC: Element Valid (base clause)
  884. error ("Element type '" + qName + "' was not declared");
  885. contentStack.push (null);
  886. // for less diagnostic noise, fake a declaration.
  887. elementDecl (qName, "ANY");
  888. } else
  889. contentStack.push (info.getRecognizer (this));
  890. //
  891. // Then check each attribute present
  892. //
  893. int len;
  894. String aname;
  895. AttributeInfo ainfo;
  896. if (atts != null)
  897. len = atts.getLength ();
  898. else
  899. len = 0;
  900. for (int i = 0; i < len; i++) {
  901. aname = atts.getQName (i);
  902. if (info == null
  903. || (ainfo = (AttributeInfo) info.attributes.get (aname))
  904. == null) {
  905. // VC: Attribute Value Type
  906. error ("Attribute '" + aname
  907. + "' was not declared for element type " + qName);
  908. continue;
  909. }
  910. String value = atts.getValue (i);
  911. // note that "==" for type names and "#FIXED" is correct
  912. // (and fast) since we've interned those literals.
  913. if ("#FIXED" == ainfo.mode) {
  914. String expanded = expandDefaultRefs (ainfo.value);
  915. // VC: Fixed Attribute Default
  916. if (!value.equals (expanded)) {
  917. error ("Attribute '" + aname
  918. + "' must match " + expanded
  919. );
  920. continue;
  921. }
  922. }
  923. if ("CDATA" == ainfo.type)
  924. continue;
  925. //
  926. // For all other attribute types, there are various
  927. // rules to follow.
  928. //
  929. if ("ID" == ainfo.type) {
  930. // VC: ID (must be a name)
  931. if (isName (value, "ID attribute", aname)) {
  932. if (Boolean.TRUE == ids.get (value))
  933. // VC: ID (appears once)
  934. error ("ID attribute " + aname
  935. + " uses an ID value '" + value
  936. + "' which was already declared.");
  937. else
  938. // any forward refs are no longer problems
  939. ids.put (value, Boolean.TRUE);
  940. }
  941. continue;
  942. }
  943. if ("IDREF" == ainfo.type) {
  944. // VC: IDREF (value must be a name)
  945. if (isName (value, "IDREF attribute", aname)) {
  946. // VC: IDREF (must match some ID attribute)
  947. if (ids.get (value) == null)
  948. // new -- assume it's a forward ref
  949. ids.put (value, Boolean.FALSE);
  950. }
  951. continue;
  952. }
  953. if ("IDREFS" == ainfo.type) {
  954. StringTokenizer tokens = new StringTokenizer (value, " ");
  955. if (!tokens.hasMoreTokens ()) {
  956. // VC: IDREF (one or more values)
  957. error ("IDREFS attribute " + aname
  958. + " must have at least one ID ref");
  959. } else do {
  960. String id = tokens.nextToken ();
  961. // VC: IDREF (value must be a name)
  962. if (isName (id, "IDREFS attribute", aname)) {
  963. // VC: IDREF (must match some ID attribute)
  964. if (ids.get (id) == null)
  965. // new -- assume it's a forward ref
  966. ids.put (id, Boolean.FALSE);
  967. }
  968. } while (tokens.hasMoreTokens ());
  969. continue;
  970. }
  971. if ("NMTOKEN" == ainfo.type) {
  972. // VC: Name Token (is a name token)
  973. isNmtoken (value, "NMTOKEN attribute", aname);
  974. continue;
  975. }
  976. if ("NMTOKENS" == ainfo.type) {
  977. StringTokenizer tokens = new StringTokenizer (value, " ");
  978. if (!tokens.hasMoreTokens ()) {
  979. // VC: Name Token (one or more values)
  980. error ("NMTOKENS attribute " + aname
  981. + " must have at least one name token");
  982. } else do {
  983. String token = tokens.nextToken ();
  984. // VC: Name Token (is a name token)
  985. isNmtoken (token, "NMTOKENS attribute", aname);
  986. } while (tokens.hasMoreTokens ());
  987. continue;
  988. }
  989. if ("ENTITY" == ainfo.type) {
  990. if (!unparsed.contains (value))
  991. // VC: Entity Name
  992. error ("Value of attribute '" + aname
  993. + "' refers to unparsed entity '" + value
  994. + "' which was not declared.");
  995. continue;
  996. }
  997. if ("ENTITIES" == ainfo.type) {
  998. StringTokenizer tokens = new StringTokenizer (value, " ");
  999. if (!tokens.hasMoreTokens ()) {
  1000. // VC: Entity Name (one or more values)
  1001. error ("ENTITIES attribute " + aname
  1002. + " must have at least one name token");
  1003. } else do {
  1004. String entity = tokens.nextToken ();
  1005. if (!unparsed.contains (entity))
  1006. // VC: Entity Name
  1007. error ("Value of attribute '" + aname
  1008. + "' refers to unparsed entity '" + entity
  1009. + "' which was not declared.");
  1010. } while (tokens.hasMoreTokens ());
  1011. continue;
  1012. }
  1013. //
  1014. // check for enumerations last; more expensive
  1015. //
  1016. if (ainfo.type.charAt (0) == '(' /*)*/
  1017. || ainfo.type.startsWith ("NOTATION ")
  1018. ) {
  1019. // VC: Enumeration (value must be defined)
  1020. checkEnumeration (value, ainfo.type, aname);
  1021. continue;
  1022. }
  1023. }
  1024. //
  1025. // Last, check that all #REQUIRED attributes were provided
  1026. //
  1027. if (info != null) {
  1028. Hashtable table = info.attributes;
  1029. if (table.size () != 0) {
  1030. Enumeration e = table.keys ();
  1031. // XXX table.keys uses the heap, bleech -- slows things
  1032. while (e.hasMoreElements ()) {
  1033. aname = (String) e.nextElement ();
  1034. ainfo = (AttributeInfo) table.get (aname);
  1035. // "#REQUIRED" mode was interned in attributeDecl
  1036. if ("#REQUIRED" == ainfo.mode
  1037. && atts.getValue (aname) == null) {
  1038. // VC: Required Attribute
  1039. error ("Attribute '" + aname + "' must be specified "
  1040. + "for element type " + qName);
  1041. }
  1042. }
  1043. }
  1044. }
  1045. super.startElement (uri, localName, qName, atts);
  1046. }
  1047. /**
  1048. * <b>ContentHandler</b> Reports a validity error if the element's content
  1049. * model does not permit character data.
  1050. * Passed to the next consumer.
  1051. */
  1052. public void characters (char ch [], int start, int length)
  1053. throws SAXException
  1054. {
  1055. Recognizer state;
  1056. if (contentStack.empty ())
  1057. state = null;
  1058. else
  1059. state = (Recognizer) contentStack.peek ();
  1060. // NOTE: if this ever supports with SAX parsers that don't
  1061. // report ignorable whitespace as such (only XP?), this class
  1062. // needs to morph it into ignorableWhitespace() as needed ...
  1063. if (state != null && !state.acceptCharacters ())
  1064. // VC: Element Valid (clauses three, four -- see recognizer)
  1065. error ("Character content not allowed in element "
  1066. + state.type.name);
  1067. super.characters (ch, start, length);
  1068. }
  1069. /**
  1070. * <b>ContentHandler</b> Reports a validity error if the element's content
  1071. * model does not permit end-of-element yet, or a well formedness error
  1072. * if there was no matching startElement call.
  1073. * Passed to the next consumer.
  1074. */
  1075. public void endElement (String uri, String localName, String qName)
  1076. throws SAXException
  1077. {
  1078. try {
  1079. Recognizer state = (Recognizer) contentStack.pop ();
  1080. if (state != null && !state.completed ())
  1081. // VC: Element valid (clauses two, three, four; see Recognizer)
  1082. error ("Premature end for element '"
  1083. + state.type.name
  1084. + "', content model "
  1085. + state.type.model);
  1086. // could insist on match of start element, but that's
  1087. // something the input stream must to guarantee.
  1088. } catch (EmptyStackException e) {
  1089. fatalError ("endElement without startElement: " + qName
  1090. + ((uri == null)
  1091. ? ""
  1092. : ( " { '" + uri + "', " + localName + " }")));
  1093. }
  1094. super.endElement (uri, localName, qName);
  1095. }
  1096. /**
  1097. * <b>ContentHandler</b> Checks whether all ID values that were
  1098. * referenced have been declared, and releases all resources.
  1099. * Passed to the next consumer.
  1100. *
  1101. * @see #setDocumentLocator
  1102. */
  1103. public void endDocument ()
  1104. throws SAXException
  1105. {
  1106. for (Enumeration idNames = ids.keys ();
  1107. idNames.hasMoreElements ();
  1108. /* NOP */) {
  1109. String id = (String) idNames.nextElement ();
  1110. if (Boolean.FALSE == ids.get (id)) {
  1111. // VC: IDREF (must match ID)
  1112. error ("Undeclared ID value '" + id
  1113. + "' was referred to by an IDREF/IDREFS attribute");
  1114. }
  1115. }
  1116. resetState ();
  1117. super.endDocument ();
  1118. }
  1119. /** Holds per-element declarations */
  1120. static private final class ElementInfo
  1121. {
  1122. String name;
  1123. String model;
  1124. // key = attribute name; value = AttributeInfo
  1125. Hashtable attributes = new Hashtable (11);
  1126. ElementInfo (String n) { name = n; }
  1127. private Recognizer recognizer;
  1128. // for validating content models: one per type, shared,
  1129. // and constructed only on demand ... so unused elements do
  1130. // not need to consume resources.
  1131. Recognizer getRecognizer (ValidationConsumer consumer)
  1132. throws SAXException
  1133. {
  1134. if (recognizer == null) {
  1135. if ("ANY".equals (model))
  1136. recognizer = ANY;
  1137. else if ("EMPTY".equals (model))
  1138. recognizer = new EmptyRecognizer (this);
  1139. else if ('#' == model.charAt (1))
  1140. // n.b. this constructor does a validity check
  1141. recognizer = new MixedRecognizer (this, consumer);
  1142. else
  1143. recognizer = new ChildrenRecognizer (this, consumer);
  1144. }
  1145. return recognizer;
  1146. }
  1147. }
  1148. /** Holds per-attribute declarations */
  1149. static private final class AttributeInfo
  1150. {
  1151. String type;
  1152. String mode; // #REQUIRED, etc (or null)
  1153. String value; // or null
  1154. }
  1155. //
  1156. // Content model validation
  1157. //
  1158. static private final Recognizer ANY = new Recognizer (null);
  1159. // Base class defines the calls used to validate content,
  1160. // and supports the "ANY" content model
  1161. static private class Recognizer
  1162. {
  1163. final ElementInfo type;
  1164. Recognizer (ElementInfo t) { type = t; }
  1165. // return true iff character data is legal here
  1166. boolean acceptCharacters ()
  1167. throws SAXException
  1168. // VC: Element Valid (third and fourth clauses)
  1169. { return true; }
  1170. // null return = failure
  1171. // otherwise, next state (like an FSM)
  1172. // prerequisite: tested that name was declared
  1173. Recognizer acceptElement (String name)
  1174. throws SAXException
  1175. // VC: Element Valid (fourth clause)
  1176. { return this; }
  1177. // return true iff model is completed, can finish
  1178. boolean completed ()
  1179. throws SAXException
  1180. // VC: Element Valid (fourth clause)
  1181. { return true; }
  1182. public String toString ()
  1183. // n.b. "children" is the interesting case!
  1184. { return (type == null) ? "ANY" : type.model; }
  1185. }
  1186. // "EMPTY" content model -- no characters or elements
  1187. private static final class EmptyRecognizer extends Recognizer
  1188. {
  1189. public EmptyRecognizer (ElementInfo type)
  1190. { super (type); }
  1191. // VC: Element Valid (first clause)
  1192. boolean acceptCharacters ()
  1193. { return false; }
  1194. // VC: Element Valid (first clause)
  1195. Recognizer acceptElement (String name)
  1196. { return null; }
  1197. }
  1198. // "Mixed" content model -- ANY, but restricts elements
  1199. private static final class MixedRecognizer extends Recognizer
  1200. {
  1201. private String permitted [];
  1202. // N.B. constructor tests for duplicated element names (VC)
  1203. public MixedRecognizer (ElementInfo t, ValidationConsumer v)
  1204. throws SAXException
  1205. {
  1206. super (t);
  1207. // (#PCDATA...)* or (#PCDATA) ==> ... or empty
  1208. // with the "..." being "|elname|..."
  1209. StringTokenizer tokens = new StringTokenizer (
  1210. t.model.substring (8, t.model.lastIndexOf (')')),
  1211. "|");
  1212. Vector vec = new Vector ();
  1213. while (tokens.hasMoreTokens ()) {
  1214. String token = tokens.nextToken ();
  1215. if (vec.contains (token))
  1216. v.error ("element " + token
  1217. + " is repeated in mixed content model: "
  1218. + t.model);
  1219. else
  1220. vec.addElement (token.intern ());
  1221. }
  1222. permitted = new String [vec.size ()];
  1223. for (int i = 0; i < permitted.length; i++)
  1224. permitted [i] = (String) vec.elementAt (i);
  1225. // in one large machine-derived DTD sample, most of about
  1226. // 250 mixed content models were empty, and 25 had ten or
  1227. // more entries. 2 had over a hundred elements. Linear
  1228. // search isn't obviously wrong.
  1229. }
  1230. // VC: Element Valid (third clause)
  1231. Recognizer acceptElement (String name)
  1232. {
  1233. int length = permitted.length;
  1234. // first pass -- optimistic w.r.t. event source interning
  1235. // (and document validity)
  1236. for (int i = 0; i < length; i++)
  1237. if (permitted [i] == name)
  1238. return this;
  1239. // second pass -- pessimistic w.r.t. event source interning
  1240. for (int i = 0; i < length; i++)
  1241. if (permitted [i].equals (name))
  1242. return this;
  1243. return null;
  1244. }
  1245. }
  1246. // recognizer loop flags, see later
  1247. private static final int F_LOOPHEAD = 0x01;
  1248. private static final int F_LOOPNEXT = 0x02;
  1249. // for debugging -- used to label/count nodes in toString()
  1250. private static int nodeCount;
  1251. /**
  1252. * "Children" content model -- these are nodes in NDFA state graphs.
  1253. * They work in fixed space. Note that these graphs commonly have
  1254. * cycles, handling features such as zero-or-more and one-or-more.
  1255. *
  1256. * <p>It's readonly, so only one copy is ever needed. The content model
  1257. * stack may have any number of pointers into each graph, when a model
  1258. * happens to be needed more than once due to element nesting. Since
  1259. * traversing the graph just moves to another node, and never changes
  1260. * it, traversals never interfere with each other.
  1261. *
  1262. * <p>There is an option to report non-deterministic models. These are
  1263. * always XML errors, but ones which are not often reported despite the
  1264. * fact that they can lead to different validating parsers giving
  1265. * different results for the same input. (The XML spec doesn't require
  1266. * them to be reported.)
  1267. *
  1268. * <p><b>FIXME</b> There's currently at least one known bug here, in that
  1269. * it's not actually detecting the non-determinism it tries to detect.
  1270. * (Of the "optional.xml" test, the once-or-twice-2* tests are all non-D;
  1271. * maybe some others.) This may relate to the issue flagged below as
  1272. * "should not" happen (but it was), which showed up when patching the
  1273. * graph to have one exit node (or more EMPTY nodes).
  1274. */
  1275. private static final class ChildrenRecognizer extends Recognizer
  1276. implements Cloneable
  1277. {
  1278. // for reporting non-deterministic content models
  1279. // ... a waste of space if we're not reporting those!
  1280. // ... along with the 'model' member (in base class)
  1281. private ValidationConsumer consumer;
  1282. // for CHOICE nodes -- each component is an arc that
  1283. // accepts a different NAME (or is EMPTY indicating
  1284. // NDFA termination).
  1285. private Recognizer components [];
  1286. // for NAME/SEQUENCE nodes -- accepts that NAME and
  1287. // then goes to the next node (CHOICE, NAME, EMPTY).
  1288. private String name;
  1289. private Recognizer next;
  1290. // loops always point back to a CHOICE node. we mark such choice
  1291. // nodes (F_LOOPHEAD) for diagnostics and faster deep cloning.
  1292. // We also mark nodes before back pointers (F_LOOPNEXT), to ensure
  1293. // termination when we patch sequences and loops.
  1294. private int flags;
  1295. // prevent a needless indirection between 'this' and 'node'
  1296. private void copyIn (ChildrenRecognizer node)
  1297. {
  1298. // model & consumer are already set
  1299. components = node.components;
  1300. name = node.name;
  1301. next = node.next;
  1302. flags = node.flags;
  1303. }
  1304. // used to construct top level "children" content models,
  1305. public ChildrenRecognizer (ElementInfo type, ValidationConsumer vc)
  1306. {
  1307. this (vc, type);
  1308. populate (type.model.toCharArray (), 0);
  1309. patchNext (new EmptyRecognizer (type), null);
  1310. }
  1311. // used internally; populating is separate
  1312. private ChildrenRecognizer (ValidationConsumer vc, ElementInfo type)
  1313. {
  1314. super (type);
  1315. consumer = vc;
  1316. }
  1317. //
  1318. // When rewriting some graph nodes we need deep clones in one case;
  1319. // mostly shallow clones (what the JVM handles for us) are fine.
  1320. //
  1321. private ChildrenRecognizer shallowClone ()
  1322. {
  1323. try {
  1324. return (ChildrenRecognizer) clone ();
  1325. } catch (CloneNotSupportedException e) {
  1326. throw new Error ("clone");
  1327. }
  1328. }
  1329. private ChildrenRecognizer deepClone ()
  1330. {
  1331. return deepClone (new Hashtable (37));
  1332. }
  1333. private ChildrenRecognizer deepClone (Hashtable table)
  1334. {
  1335. ChildrenRecognizer retval;
  1336. if ((flags & F_LOOPHEAD) != 0) {
  1337. retval = (ChildrenRecognizer) table.get (this);
  1338. if (retval != null)
  1339. return this;
  1340. retval = shallowClone ();
  1341. table.put (this, retval);
  1342. } else
  1343. retval = shallowClone ();
  1344. if (next != null) {
  1345. if (next instanceof ChildrenRecognizer)
  1346. retval.next = ((ChildrenRecognizer)next)
  1347. .deepClone (table);
  1348. else if (!(next instanceof EmptyRecognizer))
  1349. throw new RuntimeException ("deepClone");
  1350. }
  1351. if (components != null) {
  1352. retval.components = new Recognizer [components.length];
  1353. for (int i = 0; i < components.length; i++) {
  1354. Recognizer temp = components [i];
  1355. if (temp == null)
  1356. retval.components [i] = null;
  1357. else if (temp instanceof ChildrenRecognizer)
  1358. retval.components [i] = ((ChildrenRecognizer)temp)
  1359. .deepClone (table);
  1360. else if (!(temp instanceof EmptyRecognizer))
  1361. throw new RuntimeException ("deepClone");
  1362. }
  1363. }
  1364. return retval;
  1365. }
  1366. // connect subgraphs, first to next (sequencing)
  1367. private void patchNext (Recognizer theNext, Hashtable table)
  1368. {
  1369. // backpointers must not be repatched or followed
  1370. if ((flags & F_LOOPNEXT) != 0)
  1371. return;
  1372. // XXX this table "shouldn't" be needed, right?
  1373. // but some choice nodes looped if it isn't there.
  1374. if (table != null && table.get (this) != null)
  1375. return;
  1376. if (table == null)
  1377. table = new Hashtable ();
  1378. // NAME/SEQUENCE
  1379. if (name != null) {
  1380. if (next == null)
  1381. next = theNext;
  1382. else if (next instanceof ChildrenRecognizer) {
  1383. ((ChildrenRecognizer)next).patchNext (theNext, table);
  1384. } else if (!(next instanceof EmptyRecognizer))
  1385. throw new RuntimeException ("patchNext");
  1386. return;
  1387. }
  1388. // CHOICE
  1389. for (int i = 0; i < components.length; i++) {
  1390. if (components [i] == null)
  1391. components [i] = theNext;
  1392. else if (components [i] instanceof ChildrenRecognizer) {
  1393. ((ChildrenRecognizer)components [i])
  1394. .patchNext (theNext, table);
  1395. } else if (!(components [i] instanceof EmptyRecognizer))
  1396. throw new RuntimeException ("patchNext");
  1397. }
  1398. if (table != null && (flags & F_LOOPHEAD) != 0)
  1399. table.put (this, this);
  1400. }
  1401. /**
  1402. * Parses a 'children' spec (or recursively 'cp') and makes this
  1403. * become a regular graph node.
  1404. *
  1405. * @return index after this particle
  1406. */
  1407. private int populate (char parseBuf [], int startPos)
  1408. {
  1409. int nextPos = startPos + 1;
  1410. char c;
  1411. if (nextPos < 0 || nextPos >= parseBuf.length)
  1412. throw new IndexOutOfBoundsException ();
  1413. // Grammar of the string is from the XML spec, but
  1414. // with whitespace removed by the SAX parser.
  1415. // children ::= (choice | seq) ('?' | '*' | '+')?
  1416. // cp ::= (Name | choice | seq) ('?' | '*' | '+')?
  1417. // choice ::= '(' cp ('|' choice)* ')'
  1418. // seq ::= '(' cp (',' choice)* ')'
  1419. // interior nodes only
  1420. // cp ::= name ...
  1421. if (parseBuf [startPos] != '('/*)*/) {
  1422. boolean done = false;
  1423. do {
  1424. switch (c = parseBuf [nextPos]) {
  1425. case '?': case '*': case '+':
  1426. case '|': case ',':
  1427. case /*(*/ ')':
  1428. done = true;
  1429. continue;
  1430. default:
  1431. nextPos++;
  1432. continue;
  1433. }
  1434. } while (!done);
  1435. name = new String (parseBuf, startPos, nextPos - startPos);
  1436. // interior OR toplevel nodes
  1437. // cp ::= choice ..
  1438. // cp ::= seq ..
  1439. } else {
  1440. // collect everything as a separate list, and merge it
  1441. // into "this" later if we can (SEQUENCE or singleton)
  1442. ChildrenRecognizer first;
  1443. first = new ChildrenRecognizer (consumer, type);
  1444. nextPos = first.populate (parseBuf, nextPos);
  1445. c = parseBuf [nextPos++];
  1446. if (c == ',' || c == '|') {
  1447. ChildrenRecognizer current = first;
  1448. char separator = c;
  1449. Vector v = null;
  1450. if (separator == '|') {
  1451. v = new Vector ();
  1452. v.addElement (first);
  1453. }
  1454. do {
  1455. ChildrenRecognizer link;
  1456. link = new ChildrenRecognizer (consumer, type);
  1457. nextPos = link.populate (parseBuf, nextPos);
  1458. if (separator == ',') {
  1459. current.patchNext (link, null);
  1460. current = link;
  1461. } else
  1462. v.addElement (link);
  1463. c = parseBuf [nextPos++];
  1464. } while (c == separator);
  1465. // choice ... collect everything into one array.
  1466. if (separator == '|') {
  1467. // assert v.size() > 1
  1468. components = new Recognizer [v.size ()];
  1469. for (int i = 0; i < components.length; i++) {
  1470. components [i] = (Recognizer)
  1471. v.elementAt (i);
  1472. }
  1473. // assert flags == 0
  1474. // sequence ... merge into "this" to be smaller.
  1475. } else
  1476. copyIn (first);
  1477. // treat singletons like one-node sequences.
  1478. } else
  1479. copyIn (first);
  1480. if (c != /*(*/ ')')
  1481. throw new RuntimeException ("corrupt content model");
  1482. }
  1483. //
  1484. // Arity is optional, and the root of all fun. We keep the
  1485. // FSM state graph simple by only having NAME/SEQUENCE and
  1486. // CHOICE nodes (or EMPTY to terminate a model), easily
  1487. // evaluated. So we rewrite each node that has arity, using
  1488. // those primitives. We create loops here, if needed.
  1489. //
  1490. if (nextPos < parseBuf.length) {
  1491. c = parseBuf [nextPos];
  1492. if (c == '?' || c == '*' || c == '+') {
  1493. nextPos++;
  1494. // Rewrite 'zero-or-one' "?" arity to a CHOICE:
  1495. // - SEQUENCE (clone, what's next)
  1496. // - or, what's next
  1497. // Size cost: N --> N + 1
  1498. if (c == '?') {
  1499. Recognizer once = shallowClone ();
  1500. components = new Recognizer [2];
  1501. components [0] = once;
  1502. // components [1] initted to null
  1503. name = null;
  1504. next = null;
  1505. flags = 0;
  1506. // Rewrite 'zero-or-more' "*" arity to a CHOICE.
  1507. // - LOOP (clone, back to this CHOICE)
  1508. // - or, what's next
  1509. // Size cost: N --> N + 1
  1510. } else if (c == '*') {
  1511. ChildrenRecognizer loop = shallowClone ();
  1512. loop.patchNext (this, null);
  1513. loop.flags |= F_LOOPNEXT;
  1514. flags = F_LOOPHEAD;
  1515. components = new Recognizer [2];
  1516. components [0] = loop;
  1517. // components [1] initted to null
  1518. name = null;
  1519. next = null;
  1520. // Rewrite 'one-or-more' "+" arity to a SEQUENCE.
  1521. // Basically (a)+ --> ((a),(a)*).
  1522. // - this
  1523. // - CHOICE
  1524. // * LOOP (clone, back to the CHOICE)
  1525. // * or, whatever's next
  1526. // Size cost: N --> 2N + 1
  1527. } else if (c == '+') {
  1528. ChildrenRecognizer loop = deepClone ();
  1529. ChildrenRecognizer choice;
  1530. choice = new ChildrenRecognizer (consumer, type);
  1531. loop.patchNext (choice, null);
  1532. loop.flags |= F_LOOPNEXT;
  1533. choice.flags = F_LOOPHEAD;
  1534. choice.components = new Recognizer [2];
  1535. choice.components [0] = loop;
  1536. // choice.components [1] initted to null
  1537. // choice.name, choice.next initted to null
  1538. patchNext (choice, null);
  1539. }
  1540. }
  1541. }
  1542. return nextPos;
  1543. }
  1544. // VC: Element Valid (second clause)
  1545. boolean acceptCharacters ()
  1546. { return false; }
  1547. // VC: Element Valid (second clause)
  1548. Recognizer acceptElement (String type)
  1549. throws SAXException
  1550. {
  1551. // NAME/SEQUENCE
  1552. if (name != null) {
  1553. if (name.equals (type))
  1554. return next;
  1555. return null;
  1556. }
  1557. // CHOICE ... optionally reporting nondeterminism we
  1558. // run across. we won't check out every transition
  1559. // for nondeterminism; only the ones we follow.
  1560. Recognizer retval = null;
  1561. for (int i = 0; i < components.length; i++) {
  1562. Recognizer temp = components [i].acceptElement (type);
  1563. if (temp == null)
  1564. continue;
  1565. else if (!warnNonDeterministic)
  1566. return temp;
  1567. else if (retval == null)
  1568. retval = temp;
  1569. else if (retval != temp)
  1570. consumer.error ("Content model " + this.type.model
  1571. + " is non-deterministic for " + type);
  1572. }
  1573. return retval;
  1574. }
  1575. // VC: Element Valid (second clause)
  1576. boolean completed ()
  1577. throws SAXException
  1578. {
  1579. // expecting a specific element
  1580. if (name != null)
  1581. return false;
  1582. // choice, some sequences
  1583. for (int i = 0; i < components.length; i++) {
  1584. if (components [i].completed ())
  1585. return true;
  1586. }
  1587. return false;
  1588. }
  1589. /** /
  1590. // FOR DEBUGGING ... flattens the graph for printing.
  1591. public String toString ()
  1592. {
  1593. StringBuffer buf = new StringBuffer ();
  1594. // only one set of loop labels can be generated
  1595. // at a time...
  1596. synchronized (ANY) {
  1597. nodeCount = 0;
  1598. toString (buf, new Hashtable ());
  1599. return buf.toString ();
  1600. }
  1601. }
  1602. private void toString (StringBuffer buf, Hashtable table)
  1603. {
  1604. // When we visit a node, label and count it.
  1605. // Nodes are never visited/counted more than once.
  1606. // For small models labels waste space, but if arity
  1607. // mappings were used the savings are substantial.
  1608. // (Plus, the output can be more readily understood.)
  1609. String temp = (String) table.get (this);
  1610. if (temp != null) {
  1611. buf.append ('{');
  1612. buf.append (temp);
  1613. buf.append ('}');
  1614. return;
  1615. } else {
  1616. StringBuffer scratch = new StringBuffer (15);
  1617. if ((flags & F_LOOPHEAD) != 0)
  1618. scratch.append ("loop");
  1619. else
  1620. scratch.append ("node");
  1621. scratch.append ('-');
  1622. scratch.append (++nodeCount);
  1623. temp = scratch.toString ();
  1624. table.put (this, temp);
  1625. buf.append ('[');
  1626. buf.append (temp);
  1627. buf.append (']');
  1628. buf.append (':');
  1629. }
  1630. // NAME/SEQUENCE
  1631. if (name != null) {
  1632. // n.b. some output encodings turn some name chars into '?'
  1633. // e.g. with Japanese names and ASCII output
  1634. buf.append (name);
  1635. if (components != null) // bug!
  1636. buf.append ('$');
  1637. if (next == null)
  1638. buf.append (",*");
  1639. else if (next instanceof EmptyRecognizer) // patch-to-next
  1640. buf.append (",{}");
  1641. else if (next instanceof ChildrenRecognizer) {
  1642. buf.append (',');
  1643. ((ChildrenRecognizer)next).toString (buf, table);
  1644. } else // bug!
  1645. buf.append (",+");
  1646. return;
  1647. }
  1648. // CHOICE
  1649. buf.append ("<");
  1650. for (int i = 0; i < components.length; i++) {
  1651. if (i != 0)
  1652. buf.append ("|");
  1653. if (components [i] instanceof EmptyRecognizer) {
  1654. buf.append ("{}");
  1655. } else if (components [i] == null) { // patch-to-next
  1656. buf.append ('*');
  1657. } else {
  1658. ChildrenRecognizer r;
  1659. r = (ChildrenRecognizer) components [i];
  1660. r.toString (buf, table);
  1661. }
  1662. }
  1663. buf.append (">");
  1664. }
  1665. /**/
  1666. }
  1667. }