TransformerImpl.java 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806
  1. /* TransformerImpl.java --
  2. Copyright (C) 2004,2005,2006 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.transform;
  32. import gnu.java.lang.CPStringBuilder;
  33. import java.io.BufferedOutputStream;
  34. import java.io.FileOutputStream;
  35. import java.io.IOException;
  36. import java.io.OutputStream;
  37. import java.io.UnsupportedEncodingException;
  38. import java.io.Writer;
  39. import java.net.MalformedURLException;
  40. import java.net.UnknownServiceException;
  41. import java.net.URL;
  42. import java.net.URLConnection;
  43. import java.util.Collection;
  44. import java.util.Iterator;
  45. import java.util.LinkedList;
  46. import java.util.List;
  47. import java.util.Properties;
  48. import java.util.StringTokenizer;
  49. import javax.xml.namespace.QName;
  50. import javax.xml.transform.ErrorListener;
  51. import javax.xml.transform.OutputKeys;
  52. import javax.xml.transform.Result;
  53. import javax.xml.transform.Source;
  54. import javax.xml.transform.Transformer;
  55. import javax.xml.transform.TransformerConfigurationException;
  56. import javax.xml.transform.TransformerException;
  57. import javax.xml.transform.URIResolver;
  58. import javax.xml.transform.dom.DOMSource;
  59. import javax.xml.transform.dom.DOMResult;
  60. import javax.xml.transform.sax.SAXResult;
  61. import javax.xml.transform.stream.StreamResult;
  62. import org.w3c.dom.Document;
  63. import org.w3c.dom.DocumentType;
  64. import org.w3c.dom.DOMImplementation;
  65. import org.w3c.dom.Node;
  66. import org.w3c.dom.Text;
  67. import org.xml.sax.ContentHandler;
  68. import org.xml.sax.SAXException;
  69. import org.xml.sax.ext.LexicalHandler;
  70. import gnu.xml.dom.DomDoctype;
  71. import gnu.xml.dom.DomDocument;
  72. import gnu.xml.dom.ls.WriterOutputStream;
  73. /**
  74. * The transformation process for a given stylesheet.
  75. *
  76. * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
  77. */
  78. class TransformerImpl
  79. extends Transformer
  80. {
  81. final TransformerFactoryImpl factory;
  82. final Stylesheet stylesheet;
  83. URIResolver uriResolver;
  84. ErrorListener errorListener;
  85. Properties outputProperties;
  86. TransformerImpl(TransformerFactoryImpl factory,
  87. Stylesheet stylesheet,
  88. Properties outputProperties)
  89. throws TransformerConfigurationException
  90. {
  91. this.factory = factory;
  92. uriResolver = factory.userResolver;
  93. errorListener = factory.userListener;
  94. this.stylesheet = stylesheet;
  95. this.outputProperties = outputProperties;
  96. if (stylesheet != null)
  97. {
  98. // Set up parameter context for this transformer
  99. stylesheet.bindings.push(Bindings.PARAM);
  100. }
  101. }
  102. public void transform(Source xmlSource, Result outputTarget)
  103. throws TransformerException
  104. {
  105. // Get the source tree
  106. DOMSource source;
  107. synchronized (factory.resolver)
  108. {
  109. factory.resolver.setUserResolver(uriResolver);
  110. factory.resolver.setUserListener(errorListener);
  111. source = factory.resolver.resolveDOM(xmlSource, null, null);
  112. }
  113. Node context = source.getNode();
  114. Document doc = (context instanceof Document) ? (Document) context :
  115. context.getOwnerDocument();
  116. if (doc instanceof DomDocument)
  117. {
  118. // Suppress mutation events
  119. ((DomDocument) doc).setBuilding(true);
  120. }
  121. // Get the result tree
  122. Node parent = null, nextSibling = null;
  123. if (outputTarget instanceof DOMResult)
  124. {
  125. DOMResult dr = (DOMResult) outputTarget;
  126. parent = dr.getNode();
  127. nextSibling = dr.getNextSibling();
  128. Document rdoc = (parent instanceof Document) ? (Document) parent :
  129. parent.getOwnerDocument();
  130. if (rdoc instanceof DomDocument)
  131. {
  132. // Suppress mutation events and allow multiple root elements
  133. DomDocument drdoc = (DomDocument) rdoc;
  134. drdoc.setBuilding(true);
  135. drdoc.setCheckWellformedness(false);
  136. }
  137. }
  138. boolean created = false;
  139. // Transformation
  140. if (stylesheet != null)
  141. {
  142. if (parent == null)
  143. {
  144. // Create a new document to hold the result
  145. DomDocument resultDoc = new DomDocument();
  146. resultDoc.setBuilding(true);
  147. resultDoc.setCheckWellformedness(false);
  148. parent = resultDoc;
  149. created = true;
  150. }
  151. // Make a copy of the source node, and strip it
  152. context = context.cloneNode(true);
  153. strip(stylesheet, context);
  154. // XSLT transformation
  155. try
  156. {
  157. // Set output properties in the underlying stylesheet
  158. ((TransformerOutputProperties) outputProperties).apply();
  159. stylesheet.initTopLevelVariables(context);
  160. TemplateNode t = stylesheet.getTemplate(null, context, false);
  161. if (t != null)
  162. {
  163. stylesheet.current = context;
  164. t.apply(stylesheet, null, context, 1, 1, parent, nextSibling);
  165. }
  166. }
  167. catch (TransformerException e)
  168. {
  169. // Done transforming, reset document
  170. if (doc instanceof DomDocument)
  171. ((DomDocument) doc).setBuilding(false);
  172. throw e;
  173. }
  174. }
  175. else
  176. {
  177. // Identity transform
  178. Node clone = context.cloneNode(true);
  179. if (context.getNodeType() != Node.DOCUMENT_NODE)
  180. {
  181. Document resultDoc;
  182. if (parent == null)
  183. {
  184. // Create a new document to hold the result
  185. DomDocument rd = new DomDocument();
  186. rd.setBuilding(true);
  187. rd.setCheckWellformedness(false);
  188. parent = resultDoc = rd;
  189. created = true;
  190. }
  191. else
  192. {
  193. resultDoc = (parent instanceof Document) ?
  194. (Document) parent :
  195. parent.getOwnerDocument();
  196. }
  197. Document sourceDoc = context.getOwnerDocument();
  198. if (sourceDoc != resultDoc)
  199. clone = resultDoc.adoptNode(clone);
  200. if (nextSibling != null)
  201. parent.insertBefore(clone, nextSibling);
  202. else
  203. parent.appendChild(clone);
  204. }
  205. else
  206. {
  207. // Cannot append document to another tree
  208. parent = clone;
  209. created = true;
  210. }
  211. }
  212. String method = outputProperties.getProperty(OutputKeys.METHOD);
  213. int outputMethod = "html".equals(method) ? Stylesheet.OUTPUT_HTML :
  214. "text".equals(method) ? Stylesheet.OUTPUT_TEXT :
  215. Stylesheet.OUTPUT_XML;
  216. String encoding = outputProperties.getProperty(OutputKeys.ENCODING);
  217. String publicId = outputProperties.getProperty(OutputKeys.DOCTYPE_PUBLIC);
  218. String systemId = outputProperties.getProperty(OutputKeys.DOCTYPE_SYSTEM);
  219. String version = outputProperties.getProperty(OutputKeys.VERSION);
  220. boolean omitXmlDeclaration =
  221. "yes".equals(outputProperties.getProperty(OutputKeys.OMIT_XML_DECLARATION));
  222. boolean standalone =
  223. "yes".equals(outputProperties.getProperty(OutputKeys.STANDALONE));
  224. String mediaType = outputProperties.getProperty(OutputKeys.MEDIA_TYPE);
  225. String cdataSectionElements =
  226. outputProperties.getProperty(OutputKeys.CDATA_SECTION_ELEMENTS);
  227. boolean indent =
  228. "yes".equals(outputProperties.getProperty(OutputKeys.INDENT));
  229. if (created && parent instanceof DomDocument)
  230. {
  231. // Discover document element
  232. DomDocument resultDoc = (DomDocument) parent;
  233. Node root = resultDoc.getDocumentElement();
  234. // Add doctype if specified
  235. if (publicId != null || systemId != null)
  236. {
  237. if (root != null)
  238. {
  239. // We must know the name of the root element to
  240. // create the document type
  241. DocumentType doctype = new DomDoctype(resultDoc,
  242. root.getNodeName(),
  243. publicId,
  244. systemId);
  245. resultDoc.insertBefore(doctype, root);
  246. }
  247. }
  248. resultDoc.setBuilding(false);
  249. resultDoc.setCheckWellformedness(true);
  250. }
  251. else if (publicId != null || systemId != null)
  252. {
  253. switch (parent.getNodeType())
  254. {
  255. case Node.DOCUMENT_NODE:
  256. case Node.DOCUMENT_FRAGMENT_NODE:
  257. Document resultDoc = (parent instanceof Document) ?
  258. (Document) parent :
  259. parent.getOwnerDocument();
  260. DOMImplementation impl = resultDoc.getImplementation();
  261. Node root = resultDoc.getDocumentElement();
  262. if (root != null)
  263. {
  264. DocumentType doctype =
  265. impl.createDocumentType(root.getNodeName(),
  266. publicId,
  267. systemId);
  268. resultDoc.insertBefore(doctype, root);
  269. }
  270. }
  271. }
  272. if (version != null)
  273. parent.setUserData("version", version, stylesheet);
  274. if (omitXmlDeclaration)
  275. parent.setUserData("omit-xml-declaration", "yes", stylesheet);
  276. if (standalone)
  277. parent.setUserData("standalone", "yes", stylesheet);
  278. if (mediaType != null)
  279. parent.setUserData("media-type", mediaType, stylesheet);
  280. if (cdataSectionElements != null)
  281. {
  282. List list = new LinkedList();
  283. StringTokenizer st = new StringTokenizer(cdataSectionElements);
  284. while (st.hasMoreTokens())
  285. {
  286. String name = st.nextToken();
  287. String localName = name;
  288. String uri = null;
  289. String prefix = null;
  290. int ci = name.indexOf(':');
  291. if (ci != -1)
  292. {
  293. // Use namespaces defined on xsl:output node to resolve
  294. // namespaces for QName
  295. prefix = name.substring(0, ci);
  296. localName = name.substring(ci + 1);
  297. uri = stylesheet.output.lookupNamespaceURI(prefix);
  298. }
  299. list.add(new QName(uri, localName, prefix));
  300. }
  301. if (!list.isEmpty())
  302. {
  303. Document resultDoc = (parent instanceof Document) ?
  304. (Document) parent :
  305. parent.getOwnerDocument();
  306. convertCdataSectionElements(resultDoc, parent, list);
  307. }
  308. }
  309. if (indent)
  310. {
  311. if (created && parent instanceof DomDocument)
  312. {
  313. DomDocument domDoc = (DomDocument) parent;
  314. domDoc.setBuilding(true);
  315. domDoc.setCheckWellformedness(false);
  316. }
  317. parent.normalize();
  318. if (stylesheet != null)
  319. strip(stylesheet, parent);
  320. Document resultDoc = (parent instanceof Document) ?
  321. (Document) parent :
  322. parent.getOwnerDocument();
  323. reindent(resultDoc, parent, 0);
  324. if (created && parent instanceof DomDocument)
  325. {
  326. DomDocument domDoc = (DomDocument) parent;
  327. domDoc.setBuilding(false);
  328. domDoc.setCheckWellformedness(true);
  329. }
  330. }
  331. // Render result to the target device
  332. if (outputTarget instanceof DOMResult)
  333. {
  334. if (created)
  335. {
  336. DOMResult dr = (DOMResult) outputTarget;
  337. dr.setNode(parent);
  338. dr.setNextSibling(null);
  339. }
  340. }
  341. else if (outputTarget instanceof StreamResult)
  342. {
  343. StreamResult sr = (StreamResult) outputTarget;
  344. IOException ex = null;
  345. try
  346. {
  347. writeStreamResult(parent, sr, outputMethod, encoding);
  348. }
  349. catch (UnsupportedEncodingException e)
  350. {
  351. try
  352. {
  353. writeStreamResult(parent, sr, outputMethod, "UTF-8");
  354. }
  355. catch (IOException e2)
  356. {
  357. ex = e2;
  358. }
  359. }
  360. catch (IOException e)
  361. {
  362. ex = e;
  363. }
  364. if (ex != null)
  365. {
  366. if (errorListener != null)
  367. errorListener.error(new TransformerException(ex));
  368. else
  369. ex.printStackTrace(System.err);
  370. }
  371. }
  372. else if (outputTarget instanceof SAXResult)
  373. {
  374. SAXResult sr = (SAXResult) outputTarget;
  375. try
  376. {
  377. ContentHandler ch = sr.getHandler();
  378. LexicalHandler lh = sr.getLexicalHandler();
  379. if (lh == null && ch instanceof LexicalHandler)
  380. lh = (LexicalHandler) ch;
  381. SAXSerializer serializer = new SAXSerializer();
  382. serializer.serialize(parent, ch, lh);
  383. }
  384. catch (SAXException e)
  385. {
  386. if (errorListener != null)
  387. errorListener.error(new TransformerException(e));
  388. else
  389. e.printStackTrace(System.err);
  390. }
  391. }
  392. }
  393. /**
  394. * Strip whitespace from the source tree.
  395. */
  396. static boolean strip(Stylesheet stylesheet, Node node)
  397. throws TransformerConfigurationException
  398. {
  399. short nt = node.getNodeType();
  400. if (nt == Node.ENTITY_REFERENCE_NODE)
  401. {
  402. // Replace entity reference with its content
  403. Node parent = node.getParentNode();
  404. Node nextSibling = node.getNextSibling();
  405. Node child = node.getFirstChild();
  406. while (child != null)
  407. {
  408. Node next = child.getNextSibling();
  409. node.removeChild(child);
  410. if (nextSibling != null)
  411. parent.insertBefore(child, nextSibling);
  412. else
  413. parent.appendChild(child);
  414. child = next;
  415. }
  416. return true;
  417. }
  418. if (nt == Node.TEXT_NODE || nt == Node.CDATA_SECTION_NODE)
  419. {
  420. // Denormalize text into whitespace and non-whitespace nodes
  421. String text = node.getNodeValue();
  422. String[] tokens = tokenizeWhitespace(text);
  423. if (tokens.length > 1)
  424. {
  425. node.setNodeValue(tokens[0]);
  426. Node parent = node.getParentNode();
  427. Node nextSibling = node.getNextSibling();
  428. Document doc = node.getOwnerDocument();
  429. for (int i = 1; i < tokens.length; i++)
  430. {
  431. Node newChild = (nt == Node.CDATA_SECTION_NODE) ?
  432. doc.createCDATASection(tokens[i]) :
  433. doc.createTextNode(tokens[i]);
  434. if (nextSibling != null)
  435. parent.insertBefore(newChild, nextSibling);
  436. else
  437. parent.appendChild(newChild);
  438. }
  439. }
  440. return !stylesheet.isPreserved((Text) node, true);
  441. }
  442. else
  443. {
  444. Node child = node.getFirstChild();
  445. while (child != null)
  446. {
  447. boolean remove = strip(stylesheet, child);
  448. Node next = child.getNextSibling();
  449. if (remove)
  450. node.removeChild(child);
  451. child = next;
  452. }
  453. }
  454. return false;
  455. }
  456. /**
  457. * Tokenize the specified text into contiguous whitespace-only and
  458. * non-whitespace chunks.
  459. */
  460. private static String[] tokenizeWhitespace(String text)
  461. {
  462. int len = text.length();
  463. int start = 0, end = len - 1;
  464. // Find index of text start
  465. for (int i = 0; i < len; i++)
  466. {
  467. char c = text.charAt(i);
  468. boolean whitespace = (c == ' ' || c == '\n' || c == '\t' || c == '\r');
  469. if (whitespace)
  470. start++;
  471. else
  472. break;
  473. }
  474. if (start == end) // all whitespace
  475. return new String[] { text };
  476. // Find index of text end
  477. for (int i = end; i > start; i--)
  478. {
  479. char c = text.charAt(i);
  480. boolean whitespace = (c == ' ' || c == '\n' || c == '\t' || c == '\r');
  481. if (whitespace)
  482. end--;
  483. else
  484. break;
  485. }
  486. if (start == 0 && end == len - 1) // all non-whitespace
  487. return new String[] { text };
  488. // whitespace, then text, then whitespace
  489. String[] ret = (start > 0 && end < len - 1) ?
  490. new String[3] : new String[2];
  491. int i = 0;
  492. if (start > 0)
  493. ret[i++] = text.substring(0, start);
  494. ret[i++] = text.substring(start, end + 1);
  495. if (end < len - 1)
  496. ret[i++] = text.substring(end + 1);
  497. return ret;
  498. }
  499. /**
  500. * Obtain a suitable output stream for writing the result to,
  501. * and use the StreamSerializer to write the result tree to the stream.
  502. */
  503. void writeStreamResult(Node node, StreamResult sr, int outputMethod,
  504. String encoding)
  505. throws IOException
  506. {
  507. OutputStream out = null;
  508. boolean created = false;
  509. try
  510. {
  511. out = sr.getOutputStream();
  512. if (out == null)
  513. {
  514. Writer writer = sr.getWriter();
  515. if (writer != null)
  516. out = new WriterOutputStream(writer);
  517. }
  518. if (out == null)
  519. {
  520. String systemId = sr.getSystemId();
  521. try
  522. {
  523. URL url = new URL(systemId);
  524. URLConnection connection = url.openConnection();
  525. // We need to call setDoInput(false), because our
  526. // implementation of the file protocol allows writing
  527. // (unlike Sun), but it will fail with a FileNotFoundException
  528. // if we also open the connection for input and the output
  529. // file doesn't yet exist.
  530. connection.setDoInput(false);
  531. connection.setDoOutput(true);
  532. out = connection.getOutputStream();
  533. }
  534. catch (MalformedURLException e)
  535. {
  536. out = new FileOutputStream(systemId);
  537. }
  538. catch (UnknownServiceException e)
  539. {
  540. URL url = new URL(systemId);
  541. out = new FileOutputStream(url.getPath());
  542. }
  543. created = true;
  544. }
  545. out = new BufferedOutputStream(out);
  546. StreamSerializer serializer =
  547. new StreamSerializer(outputMethod, encoding, null);
  548. if (stylesheet != null)
  549. {
  550. Collection celem = stylesheet.outputCdataSectionElements;
  551. serializer.setCdataSectionElements(celem);
  552. }
  553. serializer.serialize(node, out);
  554. out.flush();
  555. }
  556. finally
  557. {
  558. try
  559. {
  560. if (out != null && created)
  561. out.close();
  562. }
  563. catch (IOException e)
  564. {
  565. if (errorListener != null)
  566. {
  567. try
  568. {
  569. errorListener.error(new TransformerException(e));
  570. }
  571. catch (TransformerException e2)
  572. {
  573. e2.printStackTrace(System.err);
  574. }
  575. }
  576. else
  577. e.printStackTrace(System.err);
  578. }
  579. }
  580. }
  581. void copyChildren(Document dstDoc, Node src, Node dst)
  582. {
  583. Node srcChild = src.getFirstChild();
  584. while (srcChild != null)
  585. {
  586. Node dstChild = dstDoc.adoptNode(srcChild);
  587. dst.appendChild(dstChild);
  588. srcChild = srcChild.getNextSibling();
  589. }
  590. }
  591. public void setParameter(String name, Object value)
  592. {
  593. if (stylesheet != null)
  594. stylesheet.bindings.set(new QName(null, name), value, Bindings.PARAM);
  595. }
  596. public Object getParameter(String name)
  597. {
  598. if (stylesheet != null)
  599. return stylesheet.bindings.get(new QName(null, name), null, 1, 1);
  600. return null;
  601. }
  602. public void clearParameters()
  603. {
  604. if (stylesheet != null)
  605. {
  606. stylesheet.bindings.pop(Bindings.PARAM);
  607. stylesheet.bindings.push(Bindings.PARAM);
  608. }
  609. }
  610. public void setURIResolver(URIResolver resolver)
  611. {
  612. uriResolver = resolver;
  613. }
  614. public URIResolver getURIResolver()
  615. {
  616. return uriResolver;
  617. }
  618. public void setOutputProperties(Properties oformat)
  619. throws IllegalArgumentException
  620. {
  621. if (oformat == null)
  622. outputProperties.clear();
  623. else
  624. outputProperties.putAll(oformat);
  625. }
  626. public Properties getOutputProperties()
  627. {
  628. return (Properties) outputProperties.clone();
  629. }
  630. public void setOutputProperty(String name, String value)
  631. throws IllegalArgumentException
  632. {
  633. outputProperties.put(name, value);
  634. }
  635. public String getOutputProperty(String name)
  636. throws IllegalArgumentException
  637. {
  638. return outputProperties.getProperty(name);
  639. }
  640. public void setErrorListener(ErrorListener listener)
  641. {
  642. errorListener = listener;
  643. }
  644. public ErrorListener getErrorListener()
  645. {
  646. return errorListener;
  647. }
  648. static final String INDENT_WHITESPACE = " ";
  649. /*
  650. * Apply indent formatting to the given tree.
  651. */
  652. void reindent(Document doc, Node node, int offset)
  653. {
  654. if (node.hasChildNodes())
  655. {
  656. boolean markupContent = false;
  657. boolean textContent = false;
  658. List children = new LinkedList();
  659. Node ctx = node.getFirstChild();
  660. while (ctx != null)
  661. {
  662. switch (ctx.getNodeType())
  663. {
  664. case Node.ELEMENT_NODE:
  665. case Node.PROCESSING_INSTRUCTION_NODE:
  666. case Node.DOCUMENT_TYPE_NODE:
  667. markupContent = true;
  668. break;
  669. case Node.TEXT_NODE:
  670. case Node.CDATA_SECTION_NODE:
  671. case Node.ENTITY_REFERENCE_NODE:
  672. case Node.COMMENT_NODE:
  673. textContent = true;
  674. break;
  675. }
  676. children.add(ctx);
  677. ctx = ctx.getNextSibling();
  678. }
  679. if (markupContent)
  680. {
  681. if (textContent)
  682. {
  683. // XXX handle mixed content differently?
  684. }
  685. int nodeType = node.getNodeType();
  686. if (nodeType == Node.DOCUMENT_NODE)
  687. {
  688. for (Iterator i = children.iterator(); i.hasNext(); )
  689. {
  690. ctx = (Node) i.next();
  691. reindent(doc, ctx, offset);
  692. }
  693. }
  694. else
  695. {
  696. CPStringBuilder buf = new CPStringBuilder();
  697. buf.append('\n');
  698. for (int i = 0; i < offset + 1; i++)
  699. buf.append(INDENT_WHITESPACE);
  700. String ws = buf.toString();
  701. for (Iterator i = children.iterator(); i.hasNext(); )
  702. {
  703. ctx = (Node) i.next();
  704. node.insertBefore(doc.createTextNode(ws), ctx);
  705. reindent(doc, ctx, offset + 1);
  706. }
  707. buf = new CPStringBuilder();
  708. buf.append('\n');
  709. for (int i = 0; i < offset; i++)
  710. buf.append(INDENT_WHITESPACE);
  711. ws = buf.toString();
  712. node.appendChild(doc.createTextNode(ws));
  713. }
  714. }
  715. }
  716. }
  717. /**
  718. * Converts the text node children of any cdata-section-elements in the
  719. * tree to CDATA section nodes.
  720. */
  721. void convertCdataSectionElements(Document doc, Node node, List list)
  722. {
  723. if (node.getNodeType() == Node.ELEMENT_NODE)
  724. {
  725. boolean match = false;
  726. for (Iterator i = list.iterator(); i.hasNext(); )
  727. {
  728. QName qname = (QName) i.next();
  729. if (match(qname, node))
  730. {
  731. match = true;
  732. break;
  733. }
  734. }
  735. if (match)
  736. {
  737. Node ctx = node.getFirstChild();
  738. while (ctx != null)
  739. {
  740. if (ctx.getNodeType() == Node.TEXT_NODE)
  741. {
  742. Node cdata = doc.createCDATASection(ctx.getNodeValue());
  743. node.replaceChild(cdata, ctx);
  744. ctx = cdata;
  745. }
  746. ctx = ctx.getNextSibling();
  747. }
  748. }
  749. }
  750. Node ctx = node.getFirstChild();
  751. while (ctx != null)
  752. {
  753. if (ctx.hasChildNodes())
  754. convertCdataSectionElements(doc, ctx, list);
  755. ctx = ctx.getNextSibling();
  756. }
  757. }
  758. boolean match(QName qname, Node node)
  759. {
  760. String ln1 = qname.getLocalPart();
  761. String ln2 = node.getLocalName();
  762. if (ln2 == null)
  763. return ln1.equals(node.getNodeName());
  764. else
  765. {
  766. String uri1 = qname.getNamespaceURI();
  767. String uri2 = node.getNamespaceURI();
  768. return (uri1.equals(uri2) && ln1.equals(ln2));
  769. }
  770. }
  771. }