DomConsumer.java 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968
  1. /* DomConsumer.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 gnu.xml.util.DomParser;
  33. import org.xml.sax.Attributes;
  34. import org.xml.sax.ContentHandler;
  35. import org.xml.sax.DTDHandler;
  36. import org.xml.sax.ErrorHandler;
  37. import org.xml.sax.Locator;
  38. import org.xml.sax.SAXException;
  39. import org.xml.sax.SAXNotRecognizedException;
  40. import org.xml.sax.SAXParseException;
  41. import org.xml.sax.ext.DeclHandler;
  42. import org.xml.sax.ext.LexicalHandler;
  43. import org.xml.sax.helpers.AttributesImpl;
  44. import org.w3c.dom.Attr;
  45. import org.w3c.dom.CDATASection;
  46. import org.w3c.dom.CharacterData;
  47. import org.w3c.dom.Document;
  48. import org.w3c.dom.DOMImplementation;
  49. import org.w3c.dom.Element;
  50. import org.w3c.dom.EntityReference;
  51. import org.w3c.dom.Node;
  52. import org.w3c.dom.ProcessingInstruction;
  53. import org.w3c.dom.Text;
  54. /**
  55. * This consumer builds a DOM Document from its input, acting either as a
  56. * pipeline terminus or as an intermediate buffer. When a document's worth
  57. * of events has been delivered to this consumer, that document is read with
  58. * a {@link DomParser} and sent to the next consumer. It is also available
  59. * as a read-once property.
  60. *
  61. * <p>The DOM tree is constructed as faithfully as possible. There are some
  62. * complications since a DOM should expose behaviors that can't be implemented
  63. * without API backdoors into that DOM, and because some SAX parsers don't
  64. * report all the information that DOM permits to be exposed. The general
  65. * problem areas involve information from the Document Type Declaration (DTD).
  66. * DOM only represents a limited subset, but has some behaviors that depend
  67. * on much deeper knowledge of a document's DTD. You shouldn't have much to
  68. * worry about unless you change handling of "noise" nodes from its default
  69. * setting (which ignores them all); note if you use JAXP to populate your
  70. * DOM trees, it wants to save "noise" nodes by default. (Such nodes include
  71. * ignorable whitespace, comments, entity references and CDATA boundaries.)
  72. * Otherwise, your
  73. * main worry will be if you use a SAX parser that doesn't flag ignorable
  74. * whitespace unless it's validating (few don't).
  75. *
  76. * <p> The SAX2 events used as input must contain XML Names for elements
  77. * and attributes, with original prefixes. In SAX2,
  78. * this is optional unless the "namespace-prefixes" parser feature is set.
  79. * Moreover, many application components won't provide completely correct
  80. * structures anyway. <em>Before you convert a DOM to an output document,
  81. * you should plan to postprocess it to create or repair such namespace
  82. * information.</em> The {@link NSFilter} pipeline stage does such work.
  83. *
  84. * <p> <em>Note: changes late in DOM L2 process made it impractical to
  85. * attempt to create the DocumentType node in any implementation-neutral way,
  86. * much less to populate it (L1 didn't support even creating such nodes).
  87. * To create and populate such a node, subclass the inner
  88. * {@link DomConsumer.Handler} class and teach it about the backdoors into
  89. * whatever DOM implementation you want. It's possible that some revised
  90. * DOM API (L3?) will make this problem solvable again. </em>
  91. *
  92. * @see DomParser
  93. *
  94. * @author David Brownell
  95. */
  96. public class DomConsumer implements EventConsumer
  97. {
  98. private Class domImpl;
  99. private boolean hidingCDATA = true;
  100. private boolean hidingComments = true;
  101. private boolean hidingWhitespace = true;
  102. private boolean hidingReferences = true;
  103. private Handler handler;
  104. private ErrorHandler errHandler;
  105. private EventConsumer next;
  106. // FIXME: this can't be a generic pipeline stage just now,
  107. // since its input became a Class not a String (to be turned
  108. // into a class, using the right class loader)
  109. /**
  110. * Configures this pipeline terminus to use the specified implementation
  111. * of DOM when constructing its result value.
  112. *
  113. * @param impl class implementing {@link org.w3c.dom.Document Document}
  114. * which publicly exposes a default constructor
  115. *
  116. * @exception SAXException when there is a problem creating an
  117. * empty DOM document using the specified implementation
  118. */
  119. public DomConsumer (Class impl)
  120. throws SAXException
  121. {
  122. domImpl = impl;
  123. handler = new Handler (this);
  124. }
  125. /**
  126. * This is the hook through which a subclass provides a handler
  127. * which knows how to access DOM extensions, specific to some
  128. * implementation, to record additional data in a DOM.
  129. * Treat this as part of construction; don't call it except
  130. * before (or between) parses.
  131. */
  132. protected void setHandler (Handler h)
  133. {
  134. handler = h;
  135. }
  136. private Document emptyDocument ()
  137. throws SAXException
  138. {
  139. try {
  140. return (Document) domImpl.newInstance ();
  141. } catch (IllegalAccessException e) {
  142. throw new SAXException ("can't access constructor: "
  143. + e.getMessage ());
  144. } catch (InstantiationException e) {
  145. throw new SAXException ("can't instantiate Document: "
  146. + e.getMessage ());
  147. }
  148. }
  149. /**
  150. * Configures this consumer as a buffer/filter, using the specified
  151. * DOM implementation when constructing its result value.
  152. *
  153. * <p> This event consumer acts as a buffer and filter, in that it
  154. * builds a DOM tree and then writes it out when <em>endDocument</em>
  155. * is invoked. Because of the limitations of DOM, much information
  156. * will as a rule not be seen in that replay. To get a full fidelity
  157. * copy of the input event stream, use a {@link TeeConsumer}.
  158. *
  159. * @param impl class implementing {@link org.w3c.dom.Document Document}
  160. * which publicly exposes a default constructor
  161. * @param next receives a "replayed" sequence of parse events when
  162. * the <em>endDocument</em> method is invoked.
  163. *
  164. * @exception SAXException when there is a problem creating an
  165. * empty DOM document using the specified DOM implementation
  166. */
  167. public DomConsumer (Class impl, EventConsumer n)
  168. throws SAXException
  169. {
  170. this (impl);
  171. next = n;
  172. }
  173. /**
  174. * Returns the document constructed from the preceding
  175. * sequence of events. This method should not be
  176. * used again until another sequence of events has been
  177. * given to this EventConsumer.
  178. */
  179. final public Document getDocument ()
  180. {
  181. return handler.clearDocument ();
  182. }
  183. public void setErrorHandler (ErrorHandler handler)
  184. {
  185. errHandler = handler;
  186. }
  187. /**
  188. * Returns true if the consumer is hiding entity references nodes
  189. * (the default), and false if EntityReference nodes should
  190. * instead be created. Such EntityReference nodes will normally be
  191. * empty, unless an implementation arranges to populate them and then
  192. * turn them back into readonly objects.
  193. *
  194. * @see #setHidingReferences
  195. */
  196. final public boolean isHidingReferences ()
  197. { return hidingReferences; }
  198. /**
  199. * Controls whether the consumer will hide entity expansions,
  200. * or will instead mark them with entity reference nodes.
  201. *
  202. * @see #isHidingReferences
  203. * @param flag False if entity reference nodes will appear
  204. */
  205. final public void setHidingReferences (boolean flag)
  206. { hidingReferences = flag; }
  207. /**
  208. * Returns true if the consumer is hiding comments (the default),
  209. * and false if they should be placed into the output document.
  210. *
  211. * @see #setHidingComments
  212. */
  213. public final boolean isHidingComments ()
  214. { return hidingComments; }
  215. /**
  216. * Controls whether the consumer is hiding comments.
  217. *
  218. * @see #isHidingComments
  219. */
  220. public final void setHidingComments (boolean flag)
  221. { hidingComments = flag; }
  222. /**
  223. * Returns true if the consumer is hiding ignorable whitespace
  224. * (the default), and false if such whitespace should be placed
  225. * into the output document as children of element nodes.
  226. *
  227. * @see #setHidingWhitespace
  228. */
  229. public final boolean isHidingWhitespace ()
  230. { return hidingWhitespace; }
  231. /**
  232. * Controls whether the consumer hides ignorable whitespace
  233. *
  234. * @see #isHidingComments
  235. */
  236. public final void setHidingWhitespace (boolean flag)
  237. { hidingWhitespace = flag; }
  238. /**
  239. * Returns true if the consumer is saving CDATA boundaries, or
  240. * false (the default) otherwise.
  241. *
  242. * @see #setHidingCDATA
  243. */
  244. final public boolean isHidingCDATA ()
  245. { return hidingCDATA; }
  246. /**
  247. * Controls whether the consumer will save CDATA boundaries.
  248. *
  249. * @see #isHidingCDATA
  250. * @param flag True to treat CDATA text differently from other
  251. * text nodes
  252. */
  253. final public void setHidingCDATA (boolean flag)
  254. { hidingCDATA = flag; }
  255. /** Returns the document handler being used. */
  256. final public ContentHandler getContentHandler ()
  257. { return handler; }
  258. /** Returns the DTD handler being used. */
  259. final public DTDHandler getDTDHandler ()
  260. { return handler; }
  261. /**
  262. * Returns the lexical handler being used.
  263. * (DOM construction can't really use declaration handlers.)
  264. */
  265. final public Object getProperty (String id)
  266. throws SAXNotRecognizedException
  267. {
  268. if ("http://xml.org/sax/properties/lexical-handler".equals (id))
  269. return handler;
  270. if ("http://xml.org/sax/properties/declaration-handler".equals (id))
  271. return handler;
  272. throw new SAXNotRecognizedException (id);
  273. }
  274. EventConsumer getNext () { return next; }
  275. ErrorHandler getErrorHandler () { return errHandler; }
  276. /**
  277. * Class used to intercept various parsing events and use them to
  278. * populate a DOM document. Subclasses would typically know and use
  279. * backdoors into specific DOM implementations, used to implement
  280. * DTD-related functionality.
  281. *
  282. * <p> Note that if this ever throws a DOMException (runtime exception)
  283. * that will indicate a bug in the DOM (e.g. doesn't support something
  284. * per specification) or the parser (e.g. emitted an illegal name, or
  285. * accepted illegal input data). </p>
  286. */
  287. public static class Handler
  288. implements ContentHandler, LexicalHandler,
  289. DTDHandler, DeclHandler
  290. {
  291. protected DomConsumer consumer;
  292. private DOMImplementation impl;
  293. private Document document;
  294. private boolean isL2;
  295. private Locator locator;
  296. private Node top;
  297. private boolean inCDATA;
  298. private boolean mergeCDATA;
  299. private boolean inDTD;
  300. private String currentEntity;
  301. private boolean recreatedAttrs;
  302. private AttributesImpl attributes = new AttributesImpl ();
  303. /**
  304. * Subclasses may use SAX2 events to provide additional
  305. * behaviors in the resulting DOM.
  306. */
  307. protected Handler (DomConsumer consumer)
  308. throws SAXException
  309. {
  310. this.consumer = consumer;
  311. document = consumer.emptyDocument ();
  312. impl = document.getImplementation ();
  313. isL2 = impl.hasFeature ("XML", "2.0");
  314. }
  315. private void fatal (String message, Exception x)
  316. throws SAXException
  317. {
  318. SAXParseException e;
  319. ErrorHandler errHandler = consumer.getErrorHandler ();
  320. if (locator == null)
  321. e = new SAXParseException (message, null, null, -1, -1, x);
  322. else
  323. e = new SAXParseException (message, locator, x);
  324. if (errHandler != null)
  325. errHandler.fatalError (e);
  326. throw e;
  327. }
  328. /**
  329. * Returns and forgets the document produced. If the handler is
  330. * reused, a new document may be created.
  331. */
  332. Document clearDocument ()
  333. {
  334. Document retval = document;
  335. document = null;
  336. locator = null;
  337. return retval;
  338. }
  339. /**
  340. * Returns the document under construction.
  341. */
  342. protected Document getDocument ()
  343. { return document; }
  344. /**
  345. * Returns the current node being populated. This is usually
  346. * an Element or Document, but it might be an EntityReference
  347. * node if some implementation-specific code knows how to put
  348. * those into the result tree and later mark them as readonly.
  349. */
  350. protected Node getTop ()
  351. { return top; }
  352. // SAX1
  353. public void setDocumentLocator (Locator locator)
  354. {
  355. this.locator = locator;
  356. }
  357. // SAX1
  358. public void startDocument ()
  359. throws SAXException
  360. {
  361. if (document == null)
  362. try {
  363. if (isL2) {
  364. // couple to original implementation
  365. document = impl.createDocument (null, "foo", null);
  366. document.removeChild (document.getFirstChild ());
  367. } else {
  368. document = consumer.emptyDocument ();
  369. }
  370. } catch (Exception e) {
  371. fatal ("DOM create document", e);
  372. }
  373. top = document;
  374. }
  375. // SAX1
  376. public void endDocument ()
  377. throws SAXException
  378. {
  379. try {
  380. if (consumer.getNext () != null && document != null) {
  381. DomParser parser = new DomParser (document);
  382. EventFilter.bind (parser, consumer.getNext ());
  383. parser.parse ("ignored");
  384. }
  385. } finally {
  386. top = null;
  387. }
  388. }
  389. // SAX1
  390. public void processingInstruction (String target, String data)
  391. throws SAXException
  392. {
  393. // we can't create populated entity ref nodes using
  394. // only public DOM APIs (they've got to be readonly)
  395. if (currentEntity != null)
  396. return;
  397. ProcessingInstruction pi;
  398. if (isL2
  399. // && consumer.isUsingNamespaces ()
  400. && target.indexOf (':') != -1)
  401. namespaceError (
  402. "PI target name is namespace nonconformant: "
  403. + target);
  404. if (inDTD)
  405. return;
  406. pi = document.createProcessingInstruction (target, data);
  407. top.appendChild (pi);
  408. }
  409. /**
  410. * Subclasses may overrride this method to provide a more efficient
  411. * way to construct text nodes.
  412. * Typically, copying the text into a single character array will
  413. * be more efficient than doing that as well as allocating other
  414. * needed for a String, including an internal StringBuffer.
  415. * Those additional memory and CPU costs can be incurred later,
  416. * if ever needed.
  417. * Unfortunately the standard DOM factory APIs encourage those costs
  418. * to be incurred early.
  419. */
  420. protected Text createText (
  421. boolean isCDATA,
  422. char ch [],
  423. int start,
  424. int length
  425. ) {
  426. String value = new String (ch, start, length);
  427. if (isCDATA)
  428. return document.createCDATASection (value);
  429. else
  430. return document.createTextNode (value);
  431. }
  432. // SAX1
  433. public void characters (char ch [], int start, int length)
  434. throws SAXException
  435. {
  436. // we can't create populated entity ref nodes using
  437. // only public DOM APIs (they've got to be readonly
  438. // at creation time)
  439. if (currentEntity != null)
  440. return;
  441. Node lastChild = top.getLastChild ();
  442. // merge consecutive text or CDATA nodes if appropriate.
  443. if (lastChild instanceof Text) {
  444. if (consumer.isHidingCDATA ()
  445. // consecutive Text content ... always merge
  446. || (!inCDATA
  447. && !(lastChild instanceof CDATASection))
  448. // consecutive CDATASection content ... don't
  449. // merge between sections, only within them
  450. || (inCDATA && mergeCDATA
  451. && lastChild instanceof CDATASection)
  452. ) {
  453. CharacterData last = (CharacterData) lastChild;
  454. String value = new String (ch, start, length);
  455. last.appendData (value);
  456. return;
  457. }
  458. }
  459. if (inCDATA && !consumer.isHidingCDATA ()) {
  460. top.appendChild (createText (true, ch, start, length));
  461. mergeCDATA = true;
  462. } else
  463. top.appendChild (createText (false, ch, start, length));
  464. }
  465. // SAX2
  466. public void skippedEntity (String name)
  467. throws SAXException
  468. {
  469. // this callback is useless except to report errors, since
  470. // we can't know if the ref was in content, within an
  471. // attribute, within a declaration ... only one of those
  472. // cases supports more intelligent action than a panic.
  473. fatal ("skipped entity: " + name, null);
  474. }
  475. // SAX2
  476. public void startPrefixMapping (String prefix, String uri)
  477. throws SAXException
  478. {
  479. // reconstruct "xmlns" attributes deleted by all
  480. // SAX2 parsers without "namespace-prefixes" = true
  481. if ("".equals (prefix))
  482. attributes.addAttribute ("", "", "xmlns",
  483. "CDATA", uri);
  484. else
  485. attributes.addAttribute ("", "", "xmlns:" + prefix,
  486. "CDATA", uri);
  487. recreatedAttrs = true;
  488. }
  489. // SAX2
  490. public void endPrefixMapping (String prefix)
  491. throws SAXException
  492. { }
  493. // SAX2
  494. public void startElement (
  495. String uri,
  496. String localName,
  497. String qName,
  498. Attributes atts
  499. ) throws SAXException
  500. {
  501. // we can't create populated entity ref nodes using
  502. // only public DOM APIs (they've got to be readonly)
  503. if (currentEntity != null)
  504. return;
  505. // parser discarded basic information; DOM tree isn't writable
  506. // without massaging to assign prefixes to all nodes.
  507. // the "NSFilter" class does that massaging.
  508. if (qName.length () == 0)
  509. qName = localName;
  510. Element element;
  511. int length = atts.getLength ();
  512. if (!isL2) {
  513. element = document.createElement (qName);
  514. // first the explicit attributes ...
  515. length = atts.getLength ();
  516. for (int i = 0; i < length; i++)
  517. element.setAttribute (atts.getQName (i),
  518. atts.getValue (i));
  519. // ... then any recreated ones (DOM deletes duplicates)
  520. if (recreatedAttrs) {
  521. recreatedAttrs = false;
  522. length = attributes.getLength ();
  523. for (int i = 0; i < length; i++)
  524. element.setAttribute (attributes.getQName (i),
  525. attributes.getValue (i));
  526. attributes.clear ();
  527. }
  528. top.appendChild (element);
  529. top = element;
  530. return;
  531. }
  532. // For an L2 DOM when namespace use is enabled, use
  533. // createElementNS/createAttributeNS except when
  534. // (a) it's an element in the default namespace, or
  535. // (b) it's an attribute with no prefix
  536. String namespace;
  537. if (localName.length () != 0)
  538. namespace = (uri.length () == 0) ? null : uri;
  539. else
  540. namespace = getNamespace (getPrefix (qName), atts);
  541. if (namespace == null)
  542. element = document.createElement (qName);
  543. else
  544. element = document.createElementNS (namespace, qName);
  545. populateAttributes (element, atts);
  546. if (recreatedAttrs) {
  547. recreatedAttrs = false;
  548. // ... DOM deletes any duplicates
  549. populateAttributes (element, attributes);
  550. attributes.clear ();
  551. }
  552. top.appendChild (element);
  553. top = element;
  554. }
  555. final static String xmlnsURI = "http://www.w3.org/2000/xmlns/";
  556. private void populateAttributes (Element element, Attributes attrs)
  557. throws SAXParseException
  558. {
  559. int length = attrs.getLength ();
  560. for (int i = 0; i < length; i++) {
  561. String type = attrs.getType (i);
  562. String value = attrs.getValue (i);
  563. String name = attrs.getQName (i);
  564. String local = attrs.getLocalName (i);
  565. String uri = attrs.getURI (i);
  566. // parser discarded basic information, DOM tree isn't writable
  567. if (name.length () == 0)
  568. name = local;
  569. // all attribute types other than these three may not
  570. // contain scoped names... enumerated attributes get
  571. // reported as NMTOKEN, except for NOTATION values
  572. if (!("CDATA".equals (type)
  573. || "NMTOKEN".equals (type)
  574. || "NMTOKENS".equals (type))) {
  575. if (value.indexOf (':') != -1) {
  576. namespaceError (
  577. "namespace nonconformant attribute value: "
  578. + "<" + element.getNodeName ()
  579. + " " + name + "='" + value + "' ...>");
  580. }
  581. }
  582. // xmlns="" is legal (undoes default NS)
  583. // xmlns:foo="" is illegal
  584. String prefix = getPrefix (name);
  585. String namespace;
  586. if ("xmlns".equals (prefix)) {
  587. if ("".equals (value))
  588. namespaceError ("illegal null namespace decl, " + name);
  589. namespace = xmlnsURI;
  590. } else if ("xmlns".equals (name))
  591. namespace = xmlnsURI;
  592. else if (prefix == null)
  593. namespace = null;
  594. else if (!"".equals(uri) && uri.length () != 0)
  595. namespace = uri;
  596. else
  597. namespace = getNamespace (prefix, attrs);
  598. if (namespace == null)
  599. element.setAttribute (name, value);
  600. else
  601. element.setAttributeNS (namespace, name, value);
  602. }
  603. }
  604. private String getPrefix (String name)
  605. {
  606. int temp;
  607. if ((temp = name.indexOf (':')) > 0)
  608. return name.substring (0, temp);
  609. return null;
  610. }
  611. // used with SAX1-level parser output
  612. private String getNamespace (String prefix, Attributes attrs)
  613. throws SAXParseException
  614. {
  615. String namespace;
  616. String decl;
  617. // defaulting
  618. if (prefix == null) {
  619. decl = "xmlns";
  620. namespace = attrs.getValue (decl);
  621. if ("".equals (namespace))
  622. return null;
  623. else if (namespace != null)
  624. return namespace;
  625. // "xmlns" is like a keyword
  626. // ... according to the Namespace REC, but DOM L2 CR2+
  627. // and Infoset violate that by assigning a namespace.
  628. // that conflict is resolved elsewhere.
  629. } else if ("xmlns".equals (prefix))
  630. return null;
  631. // "xml" prefix is fixed
  632. else if ("xml".equals (prefix))
  633. return "http://www.w3.org/XML/1998/namespace";
  634. // otherwise, expect a declaration
  635. else {
  636. decl = "xmlns:" + prefix;
  637. namespace = attrs.getValue (decl);
  638. }
  639. // if we found a local declaration, great
  640. if (namespace != null)
  641. return namespace;
  642. // ELSE ... search up the tree we've been building
  643. for (Node n = top;
  644. n != null && n.getNodeType () != Node.DOCUMENT_NODE;
  645. n = n.getParentNode ()) {
  646. if (n.getNodeType () == Node.ENTITY_REFERENCE_NODE)
  647. continue;
  648. Element e = (Element) n;
  649. Attr attr = e.getAttributeNode (decl);
  650. if (attr != null)
  651. return attr.getNodeValue ();
  652. }
  653. // see above re "xmlns" as keyword
  654. if ("xmlns".equals (decl))
  655. return null;
  656. namespaceError ("Undeclared namespace prefix: " + prefix);
  657. return null;
  658. }
  659. // SAX2
  660. public void endElement (String uri, String localName, String qName)
  661. throws SAXException
  662. {
  663. // we can't create populated entity ref nodes using
  664. // only public DOM APIs (they've got to be readonly)
  665. if (currentEntity != null)
  666. return;
  667. top = top.getParentNode ();
  668. }
  669. // SAX1 (mandatory reporting if validating)
  670. public void ignorableWhitespace (char ch [], int start, int length)
  671. throws SAXException
  672. {
  673. if (consumer.isHidingWhitespace ())
  674. return;
  675. characters (ch, start, length);
  676. }
  677. // SAX2 lexical event
  678. public void startCDATA ()
  679. throws SAXException
  680. {
  681. inCDATA = true;
  682. // true except for the first fragment of a cdata section
  683. mergeCDATA = false;
  684. }
  685. // SAX2 lexical event
  686. public void endCDATA ()
  687. throws SAXException
  688. {
  689. inCDATA = false;
  690. }
  691. // SAX2 lexical event
  692. //
  693. // this SAX2 callback merges two unrelated things:
  694. // - Declaration of the root element type ... belongs with
  695. // the other DTD declaration methods, NOT HERE.
  696. // - IDs for the optional external subset ... belongs here
  697. // with other lexical information.
  698. //
  699. // ...and it doesn't include the internal DTD subset, desired
  700. // both to support DOM L2 and to enable "pass through" processing
  701. //
  702. public void startDTD (String name, String publicId, String SystemId)
  703. throws SAXException
  704. {
  705. // need to filter out comments and PIs within the DTD
  706. inDTD = true;
  707. }
  708. // SAX2 lexical event
  709. public void endDTD ()
  710. throws SAXException
  711. {
  712. inDTD = false;
  713. }
  714. // SAX2 lexical event
  715. public void comment (char ch [], int start, int length)
  716. throws SAXException
  717. {
  718. Node comment;
  719. // we can't create populated entity ref nodes using
  720. // only public DOM APIs (they've got to be readonly)
  721. if (consumer.isHidingComments ()
  722. || inDTD
  723. || currentEntity != null)
  724. return;
  725. comment = document.createComment (new String (ch, start, length));
  726. top.appendChild (comment);
  727. }
  728. /**
  729. * May be overridden by subclasses to return true, indicating
  730. * that entity reference nodes can be populated and then made
  731. * read-only.
  732. */
  733. public boolean canPopulateEntityRefs ()
  734. { return false; }
  735. // SAX2 lexical event
  736. public void startEntity (String name)
  737. throws SAXException
  738. {
  739. // are we ignoring what would be contents of an
  740. // entity ref, since we can't populate it?
  741. if (currentEntity != null)
  742. return;
  743. // Are we hiding all entity boundaries?
  744. if (consumer.isHidingReferences ())
  745. return;
  746. // SAX2 shows parameter entities; DOM hides them
  747. if (name.charAt (0) == '%' || "[dtd]".equals (name))
  748. return;
  749. // Since we can't create a populated entity ref node in any
  750. // standard way, we create an unpopulated one.
  751. EntityReference ref = document.createEntityReference (name);
  752. top.appendChild (ref);
  753. top = ref;
  754. // ... allowing subclasses to populate them
  755. if (!canPopulateEntityRefs ())
  756. currentEntity = name;
  757. }
  758. // SAX2 lexical event
  759. public void endEntity (String name)
  760. throws SAXException
  761. {
  762. if (name.charAt (0) == '%' || "[dtd]".equals (name))
  763. return;
  764. if (name.equals (currentEntity))
  765. currentEntity = null;
  766. if (!consumer.isHidingReferences ())
  767. top = top.getParentNode ();
  768. }
  769. // SAX1 DTD event
  770. public void notationDecl (
  771. String name,
  772. String publicId, String SystemId
  773. ) throws SAXException
  774. {
  775. /* IGNORE -- no public DOM API lets us store these
  776. * into the doctype node
  777. */
  778. }
  779. // SAX1 DTD event
  780. public void unparsedEntityDecl (
  781. String name,
  782. String publicId, String SystemId,
  783. String notationName
  784. ) throws SAXException
  785. {
  786. /* IGNORE -- no public DOM API lets us store these
  787. * into the doctype node
  788. */
  789. }
  790. // SAX2 declaration event
  791. public void elementDecl (String name, String model)
  792. throws SAXException
  793. {
  794. /* IGNORE -- no content model support in DOM L2 */
  795. }
  796. // SAX2 declaration event
  797. public void attributeDecl (
  798. String eName,
  799. String aName,
  800. String type,
  801. String mode,
  802. String value
  803. ) throws SAXException
  804. {
  805. /* IGNORE -- no attribute model support in DOM L2 */
  806. }
  807. // SAX2 declaration event
  808. public void internalEntityDecl (String name, String value)
  809. throws SAXException
  810. {
  811. /* IGNORE -- no public DOM API lets us store these
  812. * into the doctype node
  813. */
  814. }
  815. // SAX2 declaration event
  816. public void externalEntityDecl (
  817. String name,
  818. String publicId,
  819. String SystemId
  820. ) throws SAXException
  821. {
  822. /* IGNORE -- no public DOM API lets us store these
  823. * into the doctype node
  824. */
  825. }
  826. //
  827. // These really should offer the option of nonfatal handling,
  828. // like other validity errors, though that would cause major
  829. // chaos in the DOM data structures. DOM is already spec'd
  830. // to treat many of these as fatal, so this is consistent.
  831. //
  832. private void namespaceError (String description)
  833. throws SAXParseException
  834. {
  835. SAXParseException err;
  836. err = new SAXParseException (description, locator);
  837. throw err;
  838. }
  839. }
  840. }