SAXEventSink.java 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604
  1. /* SAXEventSink.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.dom.ls;
  32. import java.util.HashSet;
  33. import java.util.Iterator;
  34. import java.util.LinkedList;
  35. import java.util.List;
  36. import javax.xml.XMLConstants;
  37. import org.w3c.dom.Attr;
  38. import org.w3c.dom.Document;
  39. import org.w3c.dom.DocumentType;
  40. import org.w3c.dom.Element;
  41. import org.w3c.dom.Entity;
  42. import org.w3c.dom.EntityReference;
  43. import org.w3c.dom.NamedNodeMap;
  44. import org.w3c.dom.Node;
  45. import org.w3c.dom.Text;
  46. import org.xml.sax.Attributes;
  47. import org.xml.sax.ContentHandler;
  48. import org.xml.sax.DTDHandler;
  49. import org.xml.sax.Locator;
  50. import org.xml.sax.SAXException;
  51. import org.xml.sax.SAXNotRecognizedException;
  52. import org.xml.sax.SAXNotSupportedException;
  53. import org.xml.sax.XMLReader;
  54. import org.xml.sax.ext.Attributes2;
  55. import org.xml.sax.ext.DeclHandler;
  56. import org.xml.sax.ext.LexicalHandler;
  57. import org.xml.sax.ext.Locator2;
  58. import gnu.xml.dom.DomAttr;
  59. import gnu.xml.dom.DomDocument;
  60. import gnu.xml.dom.DomDoctype;
  61. import gnu.xml.dom.DomNode;
  62. /**
  63. * A SAX content and lexical handler used to construct a DOM document.
  64. *
  65. * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
  66. */
  67. public class SAXEventSink
  68. implements ContentHandler, LexicalHandler, DTDHandler, DeclHandler
  69. {
  70. private static final String XMLNS_URI = XMLConstants.XMLNS_ATTRIBUTE_NS_URI;
  71. private static final String XMLNS_PREFIX = XMLConstants.XMLNS_ATTRIBUTE;
  72. private static final HashSet PREDEFINED_ENTITIES = new HashSet();
  73. static
  74. {
  75. PREDEFINED_ENTITIES.add("amp");
  76. PREDEFINED_ENTITIES.add("lt");
  77. PREDEFINED_ENTITIES.add("gt");
  78. PREDEFINED_ENTITIES.add("quot");
  79. PREDEFINED_ENTITIES.add("apos");
  80. }
  81. private boolean namespaceAware;
  82. boolean ignoreWhitespace;
  83. boolean expandEntityReferences;
  84. boolean ignoreComments;
  85. boolean coalescing;
  86. XMLReader reader; // reference back to the parser to get features
  87. DomDocument doc; // document being constructed
  88. Node ctx; // current context (parent node)
  89. LinkedList entityCtx; // entity context
  90. List pending; // namespace nodes waiting for a declaring element
  91. Locator locator;
  92. boolean inCDATA;
  93. boolean inDTD;
  94. boolean interrupted;
  95. void interrupt()
  96. {
  97. interrupted = true;
  98. }
  99. public Document getDocument()
  100. {
  101. return doc;
  102. }
  103. public void setReader(XMLReader reader)
  104. {
  105. this.reader = reader;
  106. }
  107. // -- ContentHandler2 --
  108. public void setDocumentLocator(Locator locator)
  109. {
  110. this.locator = locator;
  111. }
  112. public void setNamespaceAware(boolean namespaceAware)
  113. {
  114. this.namespaceAware = namespaceAware;
  115. }
  116. public void startDocument()
  117. throws SAXException
  118. {
  119. if (namespaceAware)
  120. {
  121. pending = new LinkedList();
  122. }
  123. doc = new DomDocument();
  124. doc.setStrictErrorChecking(false);
  125. doc.setBuilding(true);
  126. doc.setDefaultAttributes(false);
  127. ctx = doc;
  128. final String FEATURES = "http://xml.org/sax/features/";
  129. final String PROPERTIES = "http://xml.org/sax/properties/";
  130. final String GNU_PROPERTIES = "http://gnu.org/sax/properties/";
  131. if (reader != null)
  132. {
  133. boolean standalone = reader.getFeature(FEATURES + "is-standalone");
  134. doc.setXmlStandalone(standalone);
  135. try
  136. {
  137. String version = (String) reader.getProperty(PROPERTIES +
  138. "document-xml-version");
  139. doc.setXmlVersion(version);
  140. }
  141. catch (SAXNotRecognizedException e)
  142. {
  143. }
  144. catch (SAXNotSupportedException e)
  145. {
  146. }
  147. try
  148. {
  149. String encoding = (String) reader.getProperty(GNU_PROPERTIES +
  150. "document-xml-encoding");
  151. doc.setXmlEncoding(encoding);
  152. }
  153. catch (SAXNotRecognizedException e)
  154. {
  155. }
  156. catch (SAXNotSupportedException e)
  157. {
  158. }
  159. }
  160. if (locator != null && locator instanceof Locator2)
  161. {
  162. String encoding = ((Locator2) locator).getEncoding();
  163. doc.setInputEncoding(encoding);
  164. }
  165. }
  166. public void endDocument()
  167. throws SAXException
  168. {
  169. doc.setStrictErrorChecking(true);
  170. doc.setBuilding(false);
  171. doc.setDefaultAttributes(true);
  172. DomDoctype doctype = (DomDoctype) doc.getDoctype();
  173. if (doctype != null)
  174. {
  175. doctype.makeReadonly();
  176. }
  177. ctx = null;
  178. locator = null;
  179. }
  180. public void startPrefixMapping(String prefix, String uri)
  181. throws SAXException
  182. {
  183. if (namespaceAware)
  184. {
  185. String nsName = (prefix != null && prefix.length() > 0) ?
  186. XMLNS_PREFIX + ":" + prefix : XMLNS_PREFIX;
  187. DomAttr ns = (DomAttr) doc.createAttributeNS(XMLNS_URI, nsName);
  188. ns.setNodeValue(uri);
  189. if (ctx.getNodeType() == Node.ATTRIBUTE_NODE)
  190. {
  191. // Add to owner element
  192. Node target = ((Attr) ctx).getOwnerElement();
  193. target.getAttributes().setNamedItemNS(ns);
  194. }
  195. else
  196. {
  197. // Add to pending list; namespace node will be inserted when
  198. // element is seen
  199. pending.add(ns);
  200. }
  201. }
  202. }
  203. public void endPrefixMapping(String prefix)
  204. throws SAXException
  205. {
  206. }
  207. public void startElement(String uri, String localName, String qName,
  208. Attributes atts)
  209. throws SAXException
  210. {
  211. if (interrupted)
  212. {
  213. return;
  214. }
  215. Element element = createElement(uri, localName, qName, atts);
  216. // add element to context
  217. ctx.appendChild(element);
  218. ctx = element;
  219. }
  220. protected Element createElement(String uri, String localName, String qName,
  221. Attributes atts)
  222. throws SAXException
  223. {
  224. // create element node
  225. Element element = namespaceAware ?
  226. doc.createElementNS(uri, qName) :
  227. doc.createElement(qName);
  228. NamedNodeMap attrs = element.getAttributes();
  229. if (namespaceAware && !pending.isEmpty())
  230. {
  231. // add pending namespace nodes
  232. for (Iterator i = pending.iterator(); i.hasNext(); )
  233. {
  234. Node ns = (Node) i.next();
  235. attrs.setNamedItemNS(ns);
  236. }
  237. pending.clear();
  238. }
  239. // add attributes
  240. int len = atts.getLength();
  241. for (int i = 0; i < len; i++)
  242. {
  243. // create attribute
  244. Attr attr = createAttr(atts, i);
  245. if (attr != null)
  246. {
  247. // add attribute to element
  248. if (namespaceAware)
  249. {
  250. attrs.setNamedItemNS(attr);
  251. }
  252. else
  253. {
  254. attrs.setNamedItem(attr);
  255. }
  256. }
  257. }
  258. return element;
  259. }
  260. protected Attr createAttr(Attributes atts, int index)
  261. {
  262. DomAttr attr;
  263. if (namespaceAware)
  264. {
  265. String a_uri = atts.getURI(index);
  266. String a_qName = atts.getQName(index);
  267. attr = (DomAttr) doc.createAttributeNS(a_uri, a_qName);
  268. }
  269. else
  270. {
  271. String a_qName = atts.getQName(index);
  272. attr = (DomAttr) doc.createAttribute(a_qName);
  273. }
  274. attr.setNodeValue(atts.getValue(index));
  275. if (atts instanceof Attributes2)
  276. {
  277. Attributes2 atts2 = (Attributes2) atts;
  278. // TODO attr.setDeclared(atts2.isDeclared(index));
  279. attr.setSpecified(atts2.isSpecified(index));
  280. }
  281. return attr;
  282. }
  283. public void endElement(String uri, String localName, String qName)
  284. throws SAXException
  285. {
  286. if (interrupted)
  287. {
  288. return;
  289. }
  290. if (namespaceAware)
  291. {
  292. pending.clear();
  293. }
  294. ctx = ctx.getParentNode();
  295. }
  296. public void characters(char[] c, int off, int len)
  297. throws SAXException
  298. {
  299. if (interrupted || len < 1)
  300. {
  301. return;
  302. }
  303. ctx.appendChild(createText(c, off, len));
  304. }
  305. protected Text createText(char[] c, int off, int len)
  306. throws SAXException
  307. {
  308. Text text = (inCDATA && !coalescing) ?
  309. doc.createCDATASection(new String(c, off, len)) :
  310. doc.createTextNode(new String(c, off, len));
  311. return text;
  312. }
  313. public void ignorableWhitespace(char[] c, int off, int len)
  314. throws SAXException
  315. {
  316. if (interrupted)
  317. {
  318. return;
  319. }
  320. if (!ignoreWhitespace)
  321. {
  322. characters(c, off, len);
  323. }
  324. }
  325. public void processingInstruction(String target, String data)
  326. throws SAXException
  327. {
  328. if (interrupted)
  329. {
  330. return;
  331. }
  332. Node pi = createProcessingInstruction(target, data);
  333. ctx.appendChild(pi);
  334. }
  335. protected Node createProcessingInstruction(String target, String data)
  336. {
  337. return doc.createProcessingInstruction(target, data);
  338. }
  339. public void skippedEntity(String name)
  340. throws SAXException
  341. {
  342. // This callback is totally pointless
  343. }
  344. // -- LexicalHandler --
  345. public void startDTD(String name, String publicId, String systemId)
  346. throws SAXException
  347. {
  348. if (interrupted)
  349. {
  350. return;
  351. }
  352. Node doctype = createDocumentType(name, publicId, systemId);
  353. doc.appendChild(doctype);
  354. ctx = doctype;
  355. inDTD = true;
  356. }
  357. protected Node createDocumentType(String name, String publicId,
  358. String systemId)
  359. {
  360. return new DomDoctype(doc, name, publicId, systemId);
  361. }
  362. public void endDTD()
  363. throws SAXException
  364. {
  365. if (interrupted)
  366. {
  367. return;
  368. }
  369. inDTD = false;
  370. ctx = ctx.getParentNode();
  371. }
  372. public void startEntity(String name)
  373. throws SAXException
  374. {
  375. if (interrupted)
  376. return;
  377. DocumentType doctype = doc.getDoctype();
  378. if (doctype == null)
  379. {
  380. throw new SAXException("SAX parser error: " +
  381. "reference to entity in undeclared doctype");
  382. }
  383. if ("[dtd]".equals(name) || name.charAt(0) == '%')
  384. return;
  385. if (PREDEFINED_ENTITIES.contains(name))
  386. return;
  387. // Get entity
  388. NamedNodeMap entities = doctype.getEntities();
  389. Entity entity = (Entity) entities.getNamedItem(name);
  390. if (entity == null)
  391. {
  392. throw new SAXException("SAX parser error: " +
  393. "reference to undeclared entity: " + name);
  394. }
  395. EntityReference ref = doc.createEntityReference(name);
  396. // DomDocument populates with the entity replacement text, remove this
  397. Node child = ref.getFirstChild();
  398. while (child != null)
  399. {
  400. Node nextChild = child.getNextSibling();
  401. ref.removeChild(child);
  402. child = nextChild;
  403. }
  404. ctx.appendChild(ref);
  405. ctx = ref;
  406. }
  407. public void endEntity(String name)
  408. throws SAXException
  409. {
  410. if (interrupted)
  411. return;
  412. if ("[dtd]".equals(name) || name.charAt(0) == '%')
  413. return;
  414. if (PREDEFINED_ENTITIES.contains(name))
  415. return;
  416. // Get entity reference
  417. EntityReference ref = (EntityReference) ctx;
  418. if (!ref.getNodeName().equals(name))
  419. throw new SAXException("expecting end of "+ref.getNodeName()+" entity");
  420. ctx = ctx.getParentNode();
  421. if (ref instanceof DomNode)
  422. ((DomNode) ref).makeReadonly();
  423. if (expandEntityReferences)
  424. {
  425. // Move entity content from reference node onto context
  426. Node child = ref.getFirstChild();
  427. while (child != null)
  428. {
  429. Node nextChild = child.getNextSibling();
  430. ctx.appendChild(child);
  431. child = nextChild;
  432. }
  433. ctx.removeChild(ref);
  434. }
  435. }
  436. public void startCDATA()
  437. throws SAXException
  438. {
  439. inCDATA = true;
  440. }
  441. public void endCDATA()
  442. throws SAXException
  443. {
  444. inCDATA = false;
  445. }
  446. public void comment(char[] c, int off, int len)
  447. throws SAXException
  448. {
  449. if (interrupted)
  450. {
  451. return;
  452. }
  453. Node comment = createComment(c, off, len);
  454. ctx.appendChild(comment);
  455. }
  456. protected Node createComment(char[] c, int off, int len)
  457. {
  458. return doc.createComment(new String(c, off, len));
  459. }
  460. // -- DTDHandler --
  461. public void notationDecl(String name, String publicId, String systemId)
  462. throws SAXException
  463. {
  464. if (interrupted)
  465. {
  466. return;
  467. }
  468. if (!inDTD)
  469. throw new SAXException("notation decl outside DTD");
  470. DomDoctype doctype = (DomDoctype) ctx;
  471. doctype.declareNotation(name, publicId, systemId);
  472. }
  473. public void unparsedEntityDecl(String name, String publicId, String systemId,
  474. String notationName)
  475. throws SAXException
  476. {
  477. if (interrupted)
  478. {
  479. return;
  480. }
  481. if (!inDTD)
  482. throw new SAXException("unparsed entity decl outside DTD");
  483. DomDoctype doctype = (DomDoctype) ctx;
  484. Entity entity = doctype.declareEntity(name, publicId, systemId,
  485. notationName);
  486. }
  487. // -- DeclHandler --
  488. public void elementDecl(String name, String model)
  489. throws SAXException
  490. {
  491. if (interrupted)
  492. {
  493. return;
  494. }
  495. if (!inDTD)
  496. throw new SAXException("element decl outside DTD");
  497. // Ignore fake element declarations generated by ValidationConsumer.
  498. // If an element is not really declared in the DTD it will not be
  499. // declared in the document model.
  500. if (!(ctx instanceof DomDoctype))
  501. {
  502. return;
  503. }
  504. DomDoctype doctype = (DomDoctype) ctx;
  505. doctype.elementDecl(name, model);
  506. }
  507. public void attributeDecl(String eName, String aName, String type,
  508. String mode, String value)
  509. throws SAXException
  510. {
  511. if (interrupted)
  512. {
  513. return;
  514. }
  515. if (!inDTD)
  516. throw new SAXException("attribute decl outside DTD");
  517. DomDoctype doctype = (DomDoctype) ctx;
  518. doctype.attributeDecl(eName, aName, type, mode, value);
  519. }
  520. public void internalEntityDecl(String name, String value)
  521. throws SAXException
  522. {
  523. if (interrupted)
  524. {
  525. return;
  526. }
  527. if (!inDTD)
  528. throw new SAXException("internal entity decl outside DTD");
  529. DomDoctype doctype = (DomDoctype) ctx;
  530. Entity entity = doctype.declareEntity(name, null, null, null);
  531. if (entity != null)
  532. {
  533. Node text = doc.createTextNode(value);
  534. entity.appendChild(text);
  535. }
  536. }
  537. public void externalEntityDecl(String name, String publicId, String systemId)
  538. throws SAXException
  539. {
  540. if (interrupted)
  541. {
  542. return;
  543. }
  544. if (!inDTD)
  545. throw new SAXException("external entity decl outside DTD");
  546. DomDoctype doctype = (DomDoctype) ctx;
  547. Entity entity = doctype.declareEntity(name, publicId, systemId, null);
  548. }
  549. }