DomParser.java 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805
  1. /* DomParser.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.util;
  32. import java.util.Enumeration;
  33. import java.util.Locale;
  34. import org.xml.sax.*;
  35. import org.xml.sax.helpers.AttributesImpl;
  36. import org.xml.sax.helpers.NamespaceSupport;
  37. import org.xml.sax.ext.DeclHandler;
  38. import org.xml.sax.ext.DefaultHandler2;
  39. import org.xml.sax.ext.LexicalHandler;
  40. import org.w3c.dom.*;
  41. /**
  42. * This parser emits SAX2 parsing events as it traverses a DOM tree, using
  43. * any conformant implementation of DOM. It exposes all SAX1 features,
  44. * and the following SAX2 features and properties (as
  45. * identified by standard URIs which are not fully provided here). Note
  46. * that if a Level 1 DOM implementation is given, then this behaves as if
  47. * namespaces were disabled, and namespace prefixes were enabled. </p>
  48. *
  49. * <table border="1" width='100%' cellpadding='3' cellspacing='0'>
  50. * <tr bgcolor='#ccccff'>
  51. * <th><font size='+1'>Name</font></th>
  52. * <th><font size='+1'>Notes</font></th></tr>
  53. *
  54. * <tr><td colspan=2><center><em>Features ... URL prefix is
  55. * <b>http://xml.org/sax/features/</b></em></center></td></tr>
  56. *
  57. * <tr><td>(URL)/external-general-entities</td>
  58. * <td>false (does no parsing)</td></tr>
  59. * <tr><td>(URL)/external-parameter-entities</td>
  60. * <td>false (does no parsing)</td></tr>
  61. * <tr><td>(URL)/namespaces</td>
  62. * <td>Value is fixed at <em>true</em></td></tr>
  63. * <tr><td>(URL)/namespace-prefixes</td>
  64. * <td>Value is settable, defaulting to <em>false</em>
  65. * (<code>xmlns</code> attributes hidden, and names aren't prefixed)
  66. * </td></tr>
  67. * <tr><td>(URL)/string-interning</td>
  68. * <td>Value is fixed at <em>false</em> (DOM provides no
  69. * guarantees as to interning)</td></tr>
  70. * <tr><td>(URL)/validation</td>
  71. * <td>false (does no parsing)</td></tr>
  72. * <tr><td>(URL)/lexical-handler/parameter-entities</td>
  73. * <td>false (DOM doesn't do parameter entities)</td></tr>
  74. *
  75. * <tr><td colspan=2><center><em>Properties ... URL prefix is
  76. * <b>http://xml.org/sax/properties/</b></em></center></td></tr>
  77. *
  78. *
  79. * <tr><td>(URL)/dom-node</td>
  80. * <td>This property may be set before parsing to hold a DOM
  81. * <em>Document</em> node; any arguments given to <em>parse</em>
  82. * methods are ignored. When retrieved
  83. * during a parse, this value contains the "current" DOM node.
  84. * </td></tr>
  85. * <tr><td>(URL)/declaration-handler</td>
  86. * <td>A declaration handler may be provided. Declaration of external
  87. * general entities is exposed, but not parameter entities; none of the
  88. * entity names reported here will begin with "%". </td></tr>
  89. * <tr><td>(URL)/lexical-handler</td>
  90. * <td>A lexical handler may be provided. While the start and end of
  91. * any external subset are reported, expansion of other parameter
  92. * entities (e.g. inside attribute list declarations) is not exposed.
  93. * Expansion of general entities within attributes is also not exposed
  94. * (see below).</td></tr>
  95. * </table>
  96. *
  97. * <P> The consequences of modifying a DOM document tree as it is being walked
  98. * by this "parser" are unspecified; don't do it! </P>
  99. *
  100. * @author David Brownell
  101. */
  102. final public class DomParser implements XMLReader
  103. {
  104. // Stuff used internally to route events correctly
  105. private DefaultHandler2 defaultHandler = new DefaultHandler2 ();
  106. // per-parse SAX stuff
  107. private ContentHandler contentHandler = defaultHandler;
  108. private DTDHandler dtdHandler = defaultHandler;
  109. private DeclHandler declHandler = defaultHandler;
  110. private LexicalHandler lexicalHandler = defaultHandler;
  111. // shared context
  112. private ErrorHandler errHandler = defaultHandler;
  113. private EntityResolver resolver = defaultHandler;
  114. private Locale locale = Locale.getDefault ();
  115. // parser state
  116. private Node start;
  117. private Node current;
  118. private boolean isL2;
  119. private boolean showNamespaces = true;
  120. private boolean showXML1_0 = false;
  121. private NamespaceSupport prefixStack = new NamespaceSupport ();
  122. private boolean isDocument;
  123. /**
  124. * Constructs an unitialized <b>SAX2</b> parser.
  125. */
  126. public DomParser () {
  127. }
  128. /**
  129. * Constructs an <b>SAX2</b> parser initialized to traverse the specified
  130. * DOM tree. If the node is a document, the startDocument() and
  131. * endDocument() calls bracket the calls exposing children.
  132. */
  133. public DomParser (Node node) {
  134. setStart (node);
  135. }
  136. // stuff that most components in an application should be sharing:
  137. // resolver and error locale.
  138. /**
  139. * <b>SAX2</b>: Returns the object used when resolving external
  140. * entities during parsing (both general and parameter entities).
  141. */
  142. public EntityResolver getEntityResolver ()
  143. {
  144. return resolver;
  145. }
  146. /**
  147. * <b>SAX1</b>: Provides an object which may be used when resolving external
  148. * entities during parsing (both general and parameter entities).
  149. */
  150. public void setEntityResolver (EntityResolver resolver)
  151. {
  152. if (resolver == null)
  153. resolver = defaultHandler;
  154. this.resolver = resolver;
  155. }
  156. /**
  157. * <b>SAX1</b>: Identifies the locale which the parser should use for the
  158. * diagnostics it provides.
  159. *
  160. * @exception SAXException as defined in the specification for
  161. * <em>org.xml.sax.Parser.setLocale()</em>
  162. */
  163. public void setLocale (Locale locale)
  164. throws SAXException
  165. {
  166. if (locale == null)
  167. locale = Locale.getDefault ();
  168. this.locale = locale;
  169. }
  170. // different modules will tend to handle error handling the same,
  171. // but it may not be the same through the whole app
  172. /**
  173. * <b>SAX2</b>: Returns the object used to receive callbacks for XML
  174. * errors of all levels (fatal, nonfatal, warning).
  175. */
  176. public ErrorHandler getErrorHandler ()
  177. {
  178. return errHandler;
  179. }
  180. /**
  181. * <b>SAX1</b>: Provides an object which receives callbacks for XML errors
  182. * of all levels (fatal, nonfatal, warning).
  183. */
  184. public void setErrorHandler (ErrorHandler handler)
  185. {
  186. if (handler == null)
  187. handler = defaultHandler;
  188. errHandler = handler;
  189. }
  190. // stuff different parts of a module will handle differently
  191. /**
  192. * <b>SAX2</b>: Returns the object used to report the logical
  193. * content of an XML document.
  194. */
  195. public ContentHandler getContentHandler ()
  196. {
  197. return contentHandler;
  198. }
  199. /**
  200. * <b>SAX2</b>: Assigns the object used to report the logical
  201. * content of an XML document.
  202. */
  203. public void setContentHandler (ContentHandler handler)
  204. {
  205. if (handler == null)
  206. handler = defaultHandler;
  207. contentHandler = handler;
  208. }
  209. /**
  210. * <b>SAX2</b>: Returns the object used to process declarations related
  211. * to notations and unparsed entities.
  212. */
  213. public DTDHandler getDTDHandler ()
  214. {
  215. return dtdHandler;
  216. }
  217. /**
  218. * <b>SAX1</b>: Provides an object which may be used to intercept
  219. * declarations related to notations and unparsed entities.
  220. */
  221. public void setDTDHandler (DTDHandler handler)
  222. {
  223. if (handler == null)
  224. handler = defaultHandler;
  225. dtdHandler = handler;
  226. }
  227. /**
  228. * <b>SAX1</b>: Parses the previously provided DOM document (the
  229. * input parameter is ignored). When this returns, that same
  230. * document may be parsed again without needing a "reset".
  231. *
  232. * @param uri ignored (pass an empty string)
  233. * @exception SAXException as defined in the specification for
  234. * <em>org.xml.sax.Parser.parse()</em>
  235. */
  236. public void parse (String uri) throws SAXException
  237. {
  238. parse ();
  239. }
  240. /**
  241. * <b>SAX1</b>: Parses the previously provided DOM document (the
  242. * input parameter is ignored). When this returns, that same
  243. * document may be parsed again without needing a "reset".
  244. *
  245. * @param input ignored
  246. * @exception SAXException as defined in the specification for
  247. * <em>org.xml.sax.Parser.parse()</em>
  248. */
  249. public void parse (InputSource input) throws SAXException
  250. {
  251. parse ();
  252. }
  253. private void parse () throws SAXException
  254. {
  255. try {
  256. walk ();
  257. } finally {
  258. if (isDocument)
  259. contentHandler.endDocument ();
  260. current = null;
  261. prefixStack.reset ();
  262. }
  263. }
  264. private boolean getIsL2 (Node node)
  265. {
  266. DOMImplementation impl;
  267. Document doc;
  268. if (node instanceof Document)
  269. doc = (Document) node;
  270. else
  271. doc = node.getOwnerDocument ();
  272. if (doc == null)
  273. throw new RuntimeException ("? unowned node - L2 DTD ?");
  274. impl = doc.getImplementation ();
  275. return impl.hasFeature ("XML", "2.0");
  276. }
  277. private static final String FEATURES = "http://xml.org/sax/features/";
  278. private static final String HANDLERS = "http://xml.org/sax/properties/";
  279. /**
  280. * <b>SAX2</b>: Tells whether this parser supports the specified feature.
  281. */
  282. public boolean getFeature (String name)
  283. throws SAXNotRecognizedException, SAXNotSupportedException
  284. {
  285. // basically, none are relevant -- they relate more to
  286. // parsing than to walking a "parse tree".
  287. // FIXME: DOM feature to expose interning?
  288. if ((FEATURES + "validation").equals (name)
  289. || (FEATURES + "external-general-entities")
  290. .equals (name)
  291. || (FEATURES + "external-parameter-entities")
  292. .equals (name)
  293. || (FEATURES + "string-interning").equals (name)
  294. )
  295. return false;
  296. if ((FEATURES + "namespaces").equals (name))
  297. return showNamespaces;
  298. if ((FEATURES + "namespace-prefixes").equals (name))
  299. return showXML1_0;
  300. throw new SAXNotRecognizedException (name);
  301. }
  302. /**
  303. * <b>SAX2</b>: Returns the specified property. At this time only
  304. * the declaration and lexical handlers, and current the "DOM" node,
  305. * are supported.
  306. */
  307. public Object getProperty (String name)
  308. throws SAXNotRecognizedException, SAXNotSupportedException
  309. {
  310. if ((HANDLERS + "declaration-handler").equals (name))
  311. return declHandler == defaultHandler ? null : declHandler;
  312. if ((HANDLERS + "lexical-handler").equals (name))
  313. return lexicalHandler == defaultHandler ? null : lexicalHandler;
  314. if ((HANDLERS + "dom-node").equals (name))
  315. return current;
  316. // unknown properties
  317. throw new SAXNotRecognizedException (name);
  318. }
  319. /**
  320. * <b>SAX2</b>: Sets the state of features supported in this parser.
  321. * Only the namespace support features are mutable.
  322. */
  323. public void setFeature (String name, boolean state)
  324. throws SAXNotRecognizedException, SAXNotSupportedException
  325. {
  326. if (current != null)
  327. throw new IllegalStateException ("feature change midparse");
  328. boolean value = getFeature (name);
  329. if (value == state)
  330. return;
  331. if ((FEATURES + "namespaces").equals (name)) {
  332. if (!showXML1_0 && state == false)
  333. throw new SAXNotSupportedException ("Illegal namespace "
  334. + "processing configuration");
  335. showNamespaces = state;
  336. return;
  337. }
  338. if ((FEATURES + "namespace-prefixes").equals (name)) {
  339. if (!showNamespaces && state == false)
  340. throw new SAXNotSupportedException ("Illegal namespace "
  341. + "processing configuration");
  342. showXML1_0 = state;
  343. return;
  344. }
  345. throw new SAXNotSupportedException (name);
  346. }
  347. /**
  348. * <b>SAX2</b>: Assigns the specified property. At this time only
  349. * declaration and lexical handlers, and the initial DOM document, are
  350. * supported. These must not be changed to values of the wrong type.
  351. * Like SAX1 handlers, these handlers may be changed at any time.
  352. * Like SAX1 input source or document URI, the initial DOM document
  353. * may not be changed during a parse.
  354. */
  355. public void setProperty (String name, Object state)
  356. throws SAXNotRecognizedException, SAXNotSupportedException
  357. {
  358. if ((HANDLERS + "declaration-handler").equals (name)) {
  359. if (!(state instanceof DeclHandler || state == null))
  360. throw new SAXNotSupportedException (name);
  361. declHandler = (DeclHandler) state;
  362. return;
  363. }
  364. if ((HANDLERS + "lexical-handler").equals (name)) {
  365. if (!(state instanceof LexicalHandler || state == null))
  366. throw new SAXNotSupportedException (name);
  367. lexicalHandler = (LexicalHandler) state;
  368. return;
  369. }
  370. if ((HANDLERS + "dom-node").equals (name)) {
  371. if (state == null || state instanceof Node) {
  372. if (current != null)
  373. throw new SAXNotSupportedException (
  374. "property is readonly during parse: " + name);
  375. setStart ((Node) state);
  376. return;
  377. }
  378. throw new SAXNotSupportedException ("not a DOM Node");
  379. }
  380. // unknown properties
  381. throw new SAXNotRecognizedException (name);
  382. }
  383. private void setStart (Node property)
  384. {
  385. start = property;
  386. if (start != null) {
  387. isL2 = getIsL2 (start);
  388. isDocument = (start instanceof Document);
  389. }
  390. }
  391. //
  392. // Non-recursive walk, using DOM state when backtracking is needed
  393. //
  394. private void walk ()
  395. throws SAXException
  396. {
  397. int type;
  398. NamedNodeMap nodes;
  399. int length;
  400. AttributesImpl attrs = new AttributesImpl ();
  401. char chars [];
  402. String ns, local;
  403. synchronized (this) {
  404. if (current != null)
  405. throw new IllegalStateException ("already walking tree");
  406. // JVM guarantees assignments are atomic; so no other
  407. // thread could get this far till this walk's done.
  408. current = start;
  409. }
  410. for (;;) {
  411. type = current.getNodeType ();
  412. //
  413. // First, visit the current node, including any "start" calls
  414. //
  415. switch (type) {
  416. case Node.DOCUMENT_NODE:
  417. contentHandler.startDocument ();
  418. break;
  419. case Node.ELEMENT_NODE:
  420. nodes = current.getAttributes ();
  421. length = nodes.getLength ();
  422. prefixStack.pushContext ();
  423. for (int i = 0; i < length; i++) {
  424. Attr attr = (Attr) nodes.item (i);
  425. String name = attr.getNodeName ();
  426. if (showNamespaces && name.startsWith ("xmlns")) {
  427. String prefix;
  428. String uri;
  429. // NOTE: DOM L2 (CR2+ and REC) violate the
  430. // Namespaces REC, treat "xmlns" like a strange
  431. // attribute instead of a magic token
  432. if ("xmlns".equals (name))
  433. prefix = "";
  434. else
  435. prefix = name.substring (6);
  436. uri = attr.getNodeValue ();
  437. prefixStack.declarePrefix (prefix, uri);
  438. contentHandler.startPrefixMapping (prefix, uri);
  439. if (!showXML1_0)
  440. continue;
  441. }
  442. //
  443. // NOTE: DOM doesn't record the attribute type info
  444. // which SAX exposes; so this always reports CDATA.
  445. //
  446. // NOTE: SAX doesn't expose the isSpecified info which
  447. // DOM exposes; that's discarded here. Similarly with
  448. // the information DOM hides inside itself about what
  449. // the default values for an attribute are.
  450. //
  451. if (showNamespaces) {
  452. if (isL2) {
  453. if ((ns = attr.getNamespaceURI ()) == null)
  454. ns = "";
  455. // Note: SAX2 and DOM handle "local" names
  456. // differently
  457. if ((local = attr.getLocalName ()) == null)
  458. local = name;
  459. } else {
  460. // XXX
  461. throw new RuntimeException (
  462. "NYI, ns lookup when parsing L1 DOM");
  463. }
  464. } else
  465. ns = local = "";
  466. attrs.addAttribute (ns, local, name,
  467. "CDATA", attr.getNodeValue ());
  468. }
  469. if (showNamespaces) {
  470. if (isL2) {
  471. if ((ns = current.getNamespaceURI ()) == null)
  472. ns = "";
  473. // Note: SAX2 and DOM handle "local" names differently
  474. if ((local = current.getLocalName ()) == null)
  475. local = current.getNodeName ();
  476. } else {
  477. // XXX
  478. throw new RuntimeException (
  479. "NYI, ns lookup when parsing L1 DOM");
  480. }
  481. } else
  482. ns = local = "";
  483. contentHandler.startElement (ns, local,
  484. current.getNodeName (), attrs);
  485. if (length != 0)
  486. attrs.clear ();
  487. break;
  488. case Node.CDATA_SECTION_NODE:
  489. lexicalHandler.startCDATA ();
  490. chars = current.getNodeValue ().toCharArray ();
  491. contentHandler.characters (chars, 0, chars.length);
  492. lexicalHandler.endCDATA ();
  493. break;
  494. case Node.COMMENT_NODE:
  495. chars = current.getNodeValue ().toCharArray ();
  496. lexicalHandler.comment (chars, 0, chars.length);
  497. break;
  498. case Node.DOCUMENT_TYPE_NODE:
  499. {
  500. DocumentType doctype = (DocumentType) current;
  501. //
  502. // Only DOM L2 supports recreating even some DTDs in full.
  503. //
  504. if (isL2) {
  505. lexicalHandler.startDTD (doctype.getName (),
  506. doctype.getPublicId (),
  507. doctype.getSystemId ());
  508. } else
  509. lexicalHandler.startDTD (doctype.getName (),
  510. null, null);
  511. //
  512. // The only sure way to recreate is to provide both the
  513. // internal and external subsets. Otherwise, only part
  514. // of the job can be done ... because from the DTD, DOM
  515. // discards both the critical data, like the attribute and
  516. // element declarations, as well as the PIs and comments
  517. // that are used to hold their documentation.
  518. //
  519. // Even the entity and notation declarations that it can
  520. // expose can't be recorded without proprietary extensions.
  521. //
  522. // We construct a comment to tell what we know about how
  523. // (in)complete this particular really DTD is.
  524. //
  525. {
  526. String message;
  527. char buf [];
  528. //
  529. // Though DOM L2 lets the whole doctype be recreated,
  530. // SAX2 can't represent it (input or output).
  531. // So this will be the typical case.
  532. //
  533. if (isL2 && doctype.getInternalSubset () != null)
  534. message =
  535. " Full DTD known; can't be shown using SAX2. ";
  536. //
  537. // Otherwise, we'll concoct a partial DTD. If there's
  538. // any more data here at all, it was provided using a
  539. // (proprietary) extension to DOM.
  540. //
  541. else
  542. message =
  543. " This DTD was was recreated using incomplete DOM L2 records. ";
  544. buf = message.toCharArray ();
  545. lexicalHandler.comment (buf, 0, buf.length);
  546. }
  547. // report notations first
  548. nodes = doctype.getNotations ();
  549. length = nodes.getLength ();
  550. for (int i = 0; i < length; i++) {
  551. Notation notation = (Notation) nodes.item (i);
  552. dtdHandler.notationDecl (
  553. notation.getNodeName (),
  554. notation.getPublicId (),
  555. notation.getSystemId ());
  556. }
  557. // then parsed and unparsed external general entities
  558. nodes = doctype.getEntities ();
  559. length = nodes.getLength ();
  560. for (int i = 0; i < length; i++) {
  561. Entity entity = (Entity) nodes.item (i);
  562. String notation = entity.getNotationName ();
  563. if (notation != null)
  564. dtdHandler.unparsedEntityDecl (
  565. entity.getNodeName (),
  566. entity.getPublicId (),
  567. entity.getSystemId (),
  568. notation);
  569. else if (entity.getSystemId () != null)
  570. declHandler.externalEntityDecl (
  571. entity.getNodeName (),
  572. entity.getPublicId (),
  573. entity.getSystemId ());
  574. //
  575. // NOTE: DOM doesn't clearly provide internal
  576. // entity support; but in case someone tries to
  577. // fudge such support, we defend ourselves above.
  578. //
  579. // NOTE: DOM doesn't expose parameter entities
  580. // (thank you thank you thank you thank you)
  581. //
  582. }
  583. //
  584. // NOTE: DOM (levels 1 and 2) doesn't expose real
  585. // typing information (element or attribute decls),
  586. // as exposed by SAX2 declaration handlers.
  587. //
  588. lexicalHandler.endDTD ();
  589. }
  590. break;
  591. case Node.ENTITY_REFERENCE_NODE:
  592. // this isn't done except (a) in content, and
  593. // (b) not within a start tag (att value)
  594. lexicalHandler.startEntity (current.getNodeName ());
  595. break;
  596. case Node.PROCESSING_INSTRUCTION_NODE:
  597. contentHandler.processingInstruction (
  598. current.getNodeName (), current.getNodeValue ());
  599. break;
  600. case Node.TEXT_NODE:
  601. chars = current.getNodeValue ().toCharArray ();
  602. contentHandler.characters (chars, 0, chars.length);
  603. break;
  604. default:
  605. // e.g. fragments, entities, notations, attributes
  606. throw new SAXException ("Illegal DOM Node type in Document: "
  607. + current.getNodeType ());
  608. }
  609. //
  610. // Then, pick the next node to visit. If the next node isn't
  611. // a child, an "end" call may be needed before moving on.
  612. // If there's no next node, we're done.
  613. //
  614. Node next;
  615. switch (type) {
  616. case Node.DOCUMENT_NODE:
  617. case Node.ELEMENT_NODE:
  618. case Node.ENTITY_REFERENCE_NODE:
  619. //
  620. // For elements that can have children, visit those
  621. // children before any siblings (i.e. depth first)
  622. // and after visiting this node (i.e. preorder)
  623. //
  624. next = current.getFirstChild ();
  625. if (next != null) {
  626. current = next;
  627. break;
  628. }
  629. //
  630. // Else treat this like other childless nodes, but
  631. // handle this node's "end" immediately.
  632. //
  633. callEnd (current);
  634. // FALLTHROUGH
  635. case Node.CDATA_SECTION_NODE:
  636. case Node.COMMENT_NODE:
  637. case Node.DOCUMENT_TYPE_NODE:
  638. case Node.ENTITY_NODE:
  639. case Node.PROCESSING_INSTRUCTION_NODE:
  640. case Node.TEXT_NODE:
  641. //
  642. // Use next sibling, if there is one.
  643. // Else, climb up a level (calling "end")
  644. // until we find an ancestral sibling
  645. // or until we we climb off the top (FINISH)
  646. //
  647. for (;;) {
  648. if ((next = current.getNextSibling ()) != null)
  649. break;
  650. current = current.getParentNode ();
  651. if (current == null || current == start)
  652. return;
  653. callEnd (current);
  654. }
  655. current = next;
  656. break;
  657. default:
  658. throw new SAXException (
  659. "Illegal DOM Node type found: " + current.getNodeType ());
  660. }
  661. }
  662. }
  663. private void callEnd (Node node) throws SAXException
  664. {
  665. switch (node.getNodeType ()) {
  666. // only these three container types may ever be found
  667. // directly inside a Document.
  668. case Node.DOCUMENT_NODE:
  669. // for SAX conformance, endDocument must always
  670. // be called ... it's done in a "finally" clause)
  671. return;
  672. case Node.ELEMENT_NODE:
  673. if (showNamespaces) {
  674. if (isL2)
  675. contentHandler.endElement (
  676. node.getNamespaceURI (),
  677. node.getLocalName (),
  678. node.getNodeName ());
  679. else
  680. // XXX
  681. throw new RuntimeException (
  682. "NYI, ns lookup when parsing L1 DOM");
  683. for (Enumeration e = prefixStack.getDeclaredPrefixes ();
  684. e.hasMoreElements ();
  685. ) {
  686. contentHandler.endPrefixMapping ((String) e.nextElement ());
  687. }
  688. } else
  689. contentHandler.endElement ("", "", node.getNodeName ());
  690. prefixStack.popContext ();
  691. return;
  692. case Node.ENTITY_REFERENCE_NODE:
  693. // see above -- in content, outside start tags.
  694. lexicalHandler.endEntity (node.getNodeName ());
  695. return;
  696. // these can be given at the top level
  697. case Node.DOCUMENT_FRAGMENT_NODE:
  698. case Node.ATTRIBUTE_NODE:
  699. return;
  700. default:
  701. throw new SAXException (
  702. "Illegal DOM container type found: "
  703. + current.getNodeType ());
  704. }
  705. }
  706. }