1. /*
  2. * Copyright 1999-2004 The Apache Software Foundation.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. /*
  17. * $Id: DOMBuilder.java,v 1.19 2004/02/25 13:07:51 aruny Exp $
  18. */
  19. package com.sun.org.apache.xml.internal.utils;
  20. import java.util.Stack;
  21. import com.sun.org.apache.xml.internal.res.XMLErrorResources;
  22. import com.sun.org.apache.xml.internal.res.XMLMessages;
  23. import org.w3c.dom.Document;
  24. import org.w3c.dom.DocumentFragment;
  25. import org.w3c.dom.Element;
  26. import org.w3c.dom.Node;
  27. import org.w3c.dom.Text;
  28. import org.w3c.dom.CDATASection;
  29. import org.xml.sax.Attributes;
  30. import org.xml.sax.ContentHandler;
  31. import org.xml.sax.Locator;
  32. import org.xml.sax.ext.LexicalHandler;
  33. /**
  34. * This class takes SAX events (in addition to some extra events
  35. * that SAX doesn't handle yet) and adds the result to a document
  36. * or document fragment.
  37. * @xsl.usage general
  38. */
  39. public class DOMBuilder
  40. implements ContentHandler, LexicalHandler
  41. {
  42. /** Root document */
  43. public Document m_doc;
  44. /** Current node */
  45. protected Node m_currentNode = null;
  46. /** First node of document fragment or null if not a DocumentFragment */
  47. public DocumentFragment m_docFrag = null;
  48. /** Vector of element nodes */
  49. protected Stack m_elemStack = new Stack();
  50. /**
  51. * DOMBuilder instance constructor... it will add the DOM nodes
  52. * to the document fragment.
  53. *
  54. * @param doc Root document
  55. * @param node Current node
  56. */
  57. public DOMBuilder(Document doc, Node node)
  58. {
  59. m_doc = doc;
  60. m_currentNode = node;
  61. }
  62. /**
  63. * DOMBuilder instance constructor... it will add the DOM nodes
  64. * to the document fragment.
  65. *
  66. * @param doc Root document
  67. * @param docFrag Document fragment
  68. */
  69. public DOMBuilder(Document doc, DocumentFragment docFrag)
  70. {
  71. m_doc = doc;
  72. m_docFrag = docFrag;
  73. }
  74. /**
  75. * DOMBuilder instance constructor... it will add the DOM nodes
  76. * to the document.
  77. *
  78. * @param doc Root document
  79. */
  80. public DOMBuilder(Document doc)
  81. {
  82. m_doc = doc;
  83. }
  84. /**
  85. * Get the root node of the DOM being created. This
  86. * is either a Document or a DocumentFragment.
  87. *
  88. * @return The root document or document fragment if not null
  89. */
  90. public Node getRootNode()
  91. {
  92. return (null != m_docFrag) ? (Node) m_docFrag : (Node) m_doc;
  93. }
  94. /**
  95. * Get the node currently being processed.
  96. *
  97. * @return the current node being processed
  98. */
  99. public Node getCurrentNode()
  100. {
  101. return m_currentNode;
  102. }
  103. /**
  104. * Return null since there is no Writer for this class.
  105. *
  106. * @return null
  107. */
  108. public java.io.Writer getWriter()
  109. {
  110. return null;
  111. }
  112. /**
  113. * Append a node to the current container.
  114. *
  115. * @param newNode New node to append
  116. */
  117. protected void append(Node newNode) throws org.xml.sax.SAXException
  118. {
  119. Node currentNode = m_currentNode;
  120. if (null != currentNode)
  121. {
  122. currentNode.appendChild(newNode);
  123. // System.out.println(newNode.getNodeName());
  124. }
  125. else if (null != m_docFrag)
  126. {
  127. m_docFrag.appendChild(newNode);
  128. }
  129. else
  130. {
  131. boolean ok = true;
  132. short type = newNode.getNodeType();
  133. if (type == Node.TEXT_NODE)
  134. {
  135. String data = newNode.getNodeValue();
  136. if ((null != data) && (data.trim().length() > 0))
  137. {
  138. throw new org.xml.sax.SAXException(
  139. XMLMessages.createXMLMessage(
  140. XMLErrorResources.ER_CANT_OUTPUT_TEXT_BEFORE_DOC, null)); //"Warning: can't output text before document element! Ignoring...");
  141. }
  142. ok = false;
  143. }
  144. else if (type == Node.ELEMENT_NODE)
  145. {
  146. if (m_doc.getDocumentElement() != null)
  147. {
  148. throw new org.xml.sax.SAXException(
  149. XMLMessages.createXMLMessage(
  150. XMLErrorResources.ER_CANT_HAVE_MORE_THAN_ONE_ROOT, null)); //"Can't have more than one root on a DOM!");
  151. }
  152. }
  153. if (ok)
  154. m_doc.appendChild(newNode);
  155. }
  156. }
  157. /**
  158. * Receive an object for locating the origin of SAX document events.
  159. *
  160. * <p>SAX parsers are strongly encouraged (though not absolutely
  161. * required) to supply a locator: if it does so, it must supply
  162. * the locator to the application by invoking this method before
  163. * invoking any of the other methods in the ContentHandler
  164. * interface.</p>
  165. *
  166. * <p>The locator allows the application to determine the end
  167. * position of any document-related event, even if the parser is
  168. * not reporting an error. Typically, the application will
  169. * use this information for reporting its own errors (such as
  170. * character content that does not match an application's
  171. * business rules). The information returned by the locator
  172. * is probably not sufficient for use with a search engine.</p>
  173. *
  174. * <p>Note that the locator will return correct information only
  175. * during the invocation of the events in this interface. The
  176. * application should not attempt to use it at any other time.</p>
  177. *
  178. * @param locator An object that can return the location of
  179. * any SAX document event.
  180. * @see org.xml.sax.Locator
  181. */
  182. public void setDocumentLocator(Locator locator)
  183. {
  184. // No action for the moment.
  185. }
  186. /**
  187. * Receive notification of the beginning of a document.
  188. *
  189. * <p>The SAX parser will invoke this method only once, before any
  190. * other methods in this interface or in DTDHandler (except for
  191. * setDocumentLocator).</p>
  192. */
  193. public void startDocument() throws org.xml.sax.SAXException
  194. {
  195. // No action for the moment.
  196. }
  197. /**
  198. * Receive notification of the end of a document.
  199. *
  200. * <p>The SAX parser will invoke this method only once, and it will
  201. * be the last method invoked during the parse. The parser shall
  202. * not invoke this method until it has either abandoned parsing
  203. * (because of an unrecoverable error) or reached the end of
  204. * input.</p>
  205. */
  206. public void endDocument() throws org.xml.sax.SAXException
  207. {
  208. // No action for the moment.
  209. }
  210. /**
  211. * Receive notification of the beginning of an element.
  212. *
  213. * <p>The Parser will invoke this method at the beginning of every
  214. * element in the XML document; there will be a corresponding
  215. * endElement() event for every startElement() event (even when the
  216. * element is empty). All of the element's content will be
  217. * reported, in order, before the corresponding endElement()
  218. * event.</p>
  219. *
  220. * <p>If the element name has a namespace prefix, the prefix will
  221. * still be attached. Note that the attribute list provided will
  222. * contain only attributes with explicit values (specified or
  223. * defaulted): #IMPLIED attributes will be omitted.</p>
  224. *
  225. *
  226. * @param ns The namespace of the node
  227. * @param localName The local part of the qualified name
  228. * @param name The element name.
  229. * @param atts The attributes attached to the element, if any.
  230. * @see #endElement
  231. * @see org.xml.sax.Attributes
  232. */
  233. public void startElement(
  234. String ns, String localName, String name, Attributes atts)
  235. throws org.xml.sax.SAXException
  236. {
  237. Element elem;
  238. // Note that the namespace-aware call must be used to correctly
  239. // construct a Level 2 DOM, even for non-namespaced nodes.
  240. if ((null == ns) || (ns.length() == 0))
  241. elem = m_doc.createElementNS(null,name);
  242. else
  243. elem = m_doc.createElementNS(ns, name);
  244. append(elem);
  245. try
  246. {
  247. int nAtts = atts.getLength();
  248. if (0 != nAtts)
  249. {
  250. for (int i = 0; i < nAtts; i++)
  251. {
  252. //System.out.println("type " + atts.getType(i) + " name " + atts.getLocalName(i) );
  253. // First handle a possible ID attribute
  254. if (atts.getType(i).equalsIgnoreCase("ID"))
  255. setIDAttribute(atts.getValue(i), elem);
  256. String attrNS = atts.getURI(i);
  257. if("".equals(attrNS))
  258. attrNS = null; // DOM represents no-namespace as null
  259. // System.out.println("attrNS: "+attrNS+", localName: "+atts.getQName(i)
  260. // +", qname: "+atts.getQName(i)+", value: "+atts.getValue(i));
  261. // Crimson won't let us set an xmlns: attribute on the DOM.
  262. String attrQName = atts.getQName(i);
  263. // In SAX, xmlns: attributes have an empty namespace, while in DOM they
  264. // should have the xmlns namespace
  265. if (attrQName.startsWith("xmlns:") || attrQName.equals("xmlns")) {
  266. attrNS = "http://www.w3.org/2000/xmlns/";
  267. }
  268. // ALWAYS use the DOM Level 2 call!
  269. elem.setAttributeNS(attrNS,attrQName, atts.getValue(i));
  270. }
  271. }
  272. // append(elem);
  273. m_elemStack.push(elem);
  274. m_currentNode = elem;
  275. // append(elem);
  276. }
  277. catch(java.lang.Exception de)
  278. {
  279. // de.printStackTrace();
  280. throw new org.xml.sax.SAXException(de);
  281. }
  282. }
  283. /**
  284. * Receive notification of the end of an element.
  285. *
  286. * <p>The SAX parser will invoke this method at the end of every
  287. * element in the XML document; there will be a corresponding
  288. * startElement() event for every endElement() event (even when the
  289. * element is empty).</p>
  290. *
  291. * <p>If the element name has a namespace prefix, the prefix will
  292. * still be attached to the name.</p>
  293. *
  294. *
  295. * @param ns the namespace of the element
  296. * @param localName The local part of the qualified name of the element
  297. * @param name The element name
  298. */
  299. public void endElement(String ns, String localName, String name)
  300. throws org.xml.sax.SAXException
  301. {
  302. m_elemStack.pop();
  303. m_currentNode = m_elemStack.isEmpty() ? null : (Node)m_elemStack.peek();
  304. }
  305. /**
  306. * Set an ID string to node association in the ID table.
  307. *
  308. * @param id The ID string.
  309. * @param elem The associated ID.
  310. */
  311. public void setIDAttribute(String id, Element elem)
  312. {
  313. // Do nothing. This method is meant to be overiden.
  314. }
  315. /**
  316. * Receive notification of character data.
  317. *
  318. * <p>The Parser will call this method to report each chunk of
  319. * character data. SAX parsers may return all contiguous character
  320. * data in a single chunk, or they may split it into several
  321. * chunks; however, all of the characters in any single event
  322. * must come from the same external entity, so that the Locator
  323. * provides useful information.</p>
  324. *
  325. * <p>The application must not attempt to read from the array
  326. * outside of the specified range.</p>
  327. *
  328. * <p>Note that some parsers will report whitespace using the
  329. * ignorableWhitespace() method rather than this one (validating
  330. * parsers must do so).</p>
  331. *
  332. * @param ch The characters from the XML document.
  333. * @param start The start position in the array.
  334. * @param length The number of characters to read from the array.
  335. * @see #ignorableWhitespace
  336. * @see org.xml.sax.Locator
  337. */
  338. public void characters(char ch[], int start, int length) throws org.xml.sax.SAXException
  339. {
  340. if(isOutsideDocElem()
  341. && com.sun.org.apache.xml.internal.utils.XMLCharacterRecognizer.isWhiteSpace(ch, start, length))
  342. return; // avoid DOM006 Hierarchy request error
  343. if (m_inCData)
  344. {
  345. cdata(ch, start, length);
  346. return;
  347. }
  348. String s = new String(ch, start, length);
  349. Node childNode;
  350. childNode = m_currentNode != null ? m_currentNode.getLastChild(): null;
  351. if( childNode != null && childNode.getNodeType() == Node.TEXT_NODE ){
  352. ((Text)childNode).appendData(s);
  353. }
  354. else{
  355. Text text = m_doc.createTextNode(s);
  356. append(text);
  357. }
  358. }
  359. /**
  360. * If available, when the disable-output-escaping attribute is used,
  361. * output raw text without escaping. A PI will be inserted in front
  362. * of the node with the name "lotusxsl-next-is-raw" and a value of
  363. * "formatter-to-dom".
  364. *
  365. * @param ch Array containing the characters
  366. * @param start Index to start of characters in the array
  367. * @param length Number of characters in the array
  368. */
  369. public void charactersRaw(char ch[], int start, int length)
  370. throws org.xml.sax.SAXException
  371. {
  372. if(isOutsideDocElem()
  373. && com.sun.org.apache.xml.internal.utils.XMLCharacterRecognizer.isWhiteSpace(ch, start, length))
  374. return; // avoid DOM006 Hierarchy request error
  375. String s = new String(ch, start, length);
  376. append(m_doc.createProcessingInstruction("xslt-next-is-raw",
  377. "formatter-to-dom"));
  378. append(m_doc.createTextNode(s));
  379. }
  380. /**
  381. * Report the beginning of an entity.
  382. *
  383. * The start and end of the document entity are not reported.
  384. * The start and end of the external DTD subset are reported
  385. * using the pseudo-name "[dtd]". All other events must be
  386. * properly nested within start/end entity events.
  387. *
  388. * @param name The name of the entity. If it is a parameter
  389. * entity, the name will begin with '%'.
  390. * @see #endEntity
  391. * @see org.xml.sax.ext.DeclHandler#internalEntityDecl
  392. * @see org.xml.sax.ext.DeclHandler#externalEntityDecl
  393. */
  394. public void startEntity(String name) throws org.xml.sax.SAXException
  395. {
  396. // Almost certainly the wrong behavior...
  397. // entityReference(name);
  398. }
  399. /**
  400. * Report the end of an entity.
  401. *
  402. * @param name The name of the entity that is ending.
  403. * @see #startEntity
  404. */
  405. public void endEntity(String name) throws org.xml.sax.SAXException{}
  406. /**
  407. * Receive notivication of a entityReference.
  408. *
  409. * @param name name of the entity reference
  410. */
  411. public void entityReference(String name) throws org.xml.sax.SAXException
  412. {
  413. append(m_doc.createEntityReference(name));
  414. }
  415. /**
  416. * Receive notification of ignorable whitespace in element content.
  417. *
  418. * <p>Validating Parsers must use this method to report each chunk
  419. * of ignorable whitespace (see the W3C XML 1.0 recommendation,
  420. * section 2.10): non-validating parsers may also use this method
  421. * if they are capable of parsing and using content models.</p>
  422. *
  423. * <p>SAX parsers may return all contiguous whitespace in a single
  424. * chunk, or they may split it into several chunks; however, all of
  425. * the characters in any single event must come from the same
  426. * external entity, so that the Locator provides useful
  427. * information.</p>
  428. *
  429. * <p>The application must not attempt to read from the array
  430. * outside of the specified range.</p>
  431. *
  432. * @param ch The characters from the XML document.
  433. * @param start The start position in the array.
  434. * @param length The number of characters to read from the array.
  435. * @see #characters
  436. */
  437. public void ignorableWhitespace(char ch[], int start, int length)
  438. throws org.xml.sax.SAXException
  439. {
  440. if(isOutsideDocElem())
  441. return; // avoid DOM006 Hierarchy request error
  442. String s = new String(ch, start, length);
  443. append(m_doc.createTextNode(s));
  444. }
  445. /**
  446. * Tell if the current node is outside the document element.
  447. *
  448. * @return true if the current node is outside the document element.
  449. */
  450. private boolean isOutsideDocElem()
  451. {
  452. return (null == m_docFrag) && m_elemStack.size() == 0 && (null == m_currentNode || m_currentNode.getNodeType() == Node.DOCUMENT_NODE);
  453. }
  454. /**
  455. * Receive notification of a processing instruction.
  456. *
  457. * <p>The Parser will invoke this method once for each processing
  458. * instruction found: note that processing instructions may occur
  459. * before or after the main document element.</p>
  460. *
  461. * <p>A SAX parser should never report an XML declaration (XML 1.0,
  462. * section 2.8) or a text declaration (XML 1.0, section 4.3.1)
  463. * using this method.</p>
  464. *
  465. * @param target The processing instruction target.
  466. * @param data The processing instruction data, or null if
  467. * none was supplied.
  468. */
  469. public void processingInstruction(String target, String data)
  470. throws org.xml.sax.SAXException
  471. {
  472. append(m_doc.createProcessingInstruction(target, data));
  473. }
  474. /**
  475. * Report an XML comment anywhere in the document.
  476. *
  477. * This callback will be used for comments inside or outside the
  478. * document element, including comments in the external DTD
  479. * subset (if read).
  480. *
  481. * @param ch An array holding the characters in the comment.
  482. * @param start The starting position in the array.
  483. * @param length The number of characters to use from the array.
  484. */
  485. public void comment(char ch[], int start, int length) throws org.xml.sax.SAXException
  486. {
  487. append(m_doc.createComment(new String(ch, start, length)));
  488. }
  489. /** Flag indicating that we are processing a CData section */
  490. protected boolean m_inCData = false;
  491. /**
  492. * Report the start of a CDATA section.
  493. *
  494. * @see #endCDATA
  495. */
  496. public void startCDATA() throws org.xml.sax.SAXException
  497. {
  498. m_inCData = true;
  499. append(m_doc.createCDATASection(""));
  500. }
  501. /**
  502. * Report the end of a CDATA section.
  503. *
  504. * @see #startCDATA
  505. */
  506. public void endCDATA() throws org.xml.sax.SAXException
  507. {
  508. m_inCData = false;
  509. }
  510. /**
  511. * Receive notification of cdata.
  512. *
  513. * <p>The Parser will call this method to report each chunk of
  514. * character data. SAX parsers may return all contiguous character
  515. * data in a single chunk, or they may split it into several
  516. * chunks; however, all of the characters in any single event
  517. * must come from the same external entity, so that the Locator
  518. * provides useful information.</p>
  519. *
  520. * <p>The application must not attempt to read from the array
  521. * outside of the specified range.</p>
  522. *
  523. * <p>Note that some parsers will report whitespace using the
  524. * ignorableWhitespace() method rather than this one (validating
  525. * parsers must do so).</p>
  526. *
  527. * @param ch The characters from the XML document.
  528. * @param start The start position in the array.
  529. * @param length The number of characters to read from the array.
  530. * @see #ignorableWhitespace
  531. * @see org.xml.sax.Locator
  532. */
  533. public void cdata(char ch[], int start, int length) throws org.xml.sax.SAXException
  534. {
  535. if(isOutsideDocElem()
  536. && com.sun.org.apache.xml.internal.utils.XMLCharacterRecognizer.isWhiteSpace(ch, start, length))
  537. return; // avoid DOM006 Hierarchy request error
  538. String s = new String(ch, start, length);
  539. CDATASection section =(CDATASection) m_currentNode.getLastChild();
  540. section.appendData(s);
  541. }
  542. /**
  543. * Report the start of DTD declarations, if any.
  544. *
  545. * Any declarations are assumed to be in the internal subset
  546. * unless otherwise indicated.
  547. *
  548. * @param name The document type name.
  549. * @param publicId The declared public identifier for the
  550. * external DTD subset, or null if none was declared.
  551. * @param systemId The declared system identifier for the
  552. * external DTD subset, or null if none was declared.
  553. * @see #endDTD
  554. * @see #startEntity
  555. */
  556. public void startDTD(String name, String publicId, String systemId)
  557. throws org.xml.sax.SAXException
  558. {
  559. // Do nothing for now.
  560. }
  561. /**
  562. * Report the end of DTD declarations.
  563. *
  564. * @see #startDTD
  565. */
  566. public void endDTD() throws org.xml.sax.SAXException
  567. {
  568. // Do nothing for now.
  569. }
  570. /**
  571. * Begin the scope of a prefix-URI Namespace mapping.
  572. *
  573. * <p>The information from this event is not necessary for
  574. * normal Namespace processing: the SAX XML reader will
  575. * automatically replace prefixes for element and attribute
  576. * names when the http://xml.org/sax/features/namespaces
  577. * feature is true (the default).</p>
  578. *
  579. * <p>There are cases, however, when applications need to
  580. * use prefixes in character data or in attribute values,
  581. * where they cannot safely be expanded automatically; the
  582. * start/endPrefixMapping event supplies the information
  583. * to the application to expand prefixes in those contexts
  584. * itself, if necessary.</p>
  585. *
  586. * <p>Note that start/endPrefixMapping events are not
  587. * guaranteed to be properly nested relative to each-other:
  588. * all startPrefixMapping events will occur before the
  589. * corresponding startElement event, and all endPrefixMapping
  590. * events will occur after the corresponding endElement event,
  591. * but their order is not guaranteed.</p>
  592. *
  593. * @param prefix The Namespace prefix being declared.
  594. * @param uri The Namespace URI the prefix is mapped to.
  595. * @see #endPrefixMapping
  596. * @see #startElement
  597. */
  598. public void startPrefixMapping(String prefix, String uri)
  599. throws org.xml.sax.SAXException
  600. {
  601. /*
  602. // Not sure if this is needed or wanted
  603. // Also, it fails in the stree.
  604. if((null != m_currentNode)
  605. && (m_currentNode.getNodeType() == Node.ELEMENT_NODE))
  606. {
  607. String qname;
  608. if(((null != prefix) && (prefix.length() == 0))
  609. || (null == prefix))
  610. qname = "xmlns";
  611. else
  612. qname = "xmlns:"+prefix;
  613. Element elem = (Element)m_currentNode;
  614. String val = elem.getAttribute(qname); // Obsolete, should be DOM2...?
  615. if(val == null)
  616. {
  617. elem.setAttributeNS("http://www.w3.org/XML/1998/namespace",
  618. qname, uri);
  619. }
  620. }
  621. */
  622. }
  623. /**
  624. * End the scope of a prefix-URI mapping.
  625. *
  626. * <p>See startPrefixMapping for details. This event will
  627. * always occur after the corresponding endElement event,
  628. * but the order of endPrefixMapping events is not otherwise
  629. * guaranteed.</p>
  630. *
  631. * @param prefix The prefix that was being mapping.
  632. * @see #startPrefixMapping
  633. * @see #endElement
  634. */
  635. public void endPrefixMapping(String prefix) throws org.xml.sax.SAXException{}
  636. /**
  637. * Receive notification of a skipped entity.
  638. *
  639. * <p>The Parser will invoke this method once for each entity
  640. * skipped. Non-validating processors may skip entities if they
  641. * have not seen the declarations (because, for example, the
  642. * entity was declared in an external DTD subset). All processors
  643. * may skip external entities, depending on the values of the
  644. * http://xml.org/sax/features/external-general-entities and the
  645. * http://xml.org/sax/features/external-parameter-entities
  646. * properties.</p>
  647. *
  648. * @param name The name of the skipped entity. If it is a
  649. * parameter entity, the name will begin with '%'.
  650. */
  651. public void skippedEntity(String name) throws org.xml.sax.SAXException{}
  652. }