Desktop tool for browsing account info from EVE-Online
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

XMLOutputter.java 55KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599
  1. /*--
  2. $Id: XMLOutputter.java,v 1.116 2007/11/10 05:29:01 jhunter Exp $
  3. Copyright (C) 2000-2007 Jason Hunter & Brett McLaughlin.
  4. All rights reserved.
  5. Redistribution and use in source and binary forms, with or without
  6. modification, are permitted provided that the following conditions
  7. are met:
  8. 1. Redistributions of source code must retain the above copyright
  9. notice, this list of conditions, and the following disclaimer.
  10. 2. Redistributions in binary form must reproduce the above copyright
  11. notice, this list of conditions, and the disclaimer that follows
  12. these conditions in the documentation and/or other materials
  13. provided with the distribution.
  14. 3. The name "JDOM" must not be used to endorse or promote products
  15. derived from this software without prior written permission. For
  16. written permission, please contact <request_AT_jdom_DOT_org>.
  17. 4. Products derived from this software may not be called "JDOM", nor
  18. may "JDOM" appear in their name, without prior written permission
  19. from the JDOM Project Management <request_AT_jdom_DOT_org>.
  20. In addition, we request (but do not require) that you include in the
  21. end-user documentation provided with the redistribution and/or in the
  22. software itself an acknowledgement equivalent to the following:
  23. "This product includes software developed by the
  24. JDOM Project (http://www.jdom.org/)."
  25. Alternatively, the acknowledgment may be graphical using the logos
  26. available at http://www.jdom.org/images/logos.
  27. THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  28. WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  29. OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  30. DISCLAIMED. IN NO EVENT SHALL THE JDOM AUTHORS OR THE PROJECT
  31. CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  32. SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  33. LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  34. USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  35. ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  36. OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  37. OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  38. SUCH DAMAGE.
  39. This software consists of voluntary contributions made by many
  40. individuals on behalf of the JDOM Project and was originally
  41. created by Jason Hunter <jhunter_AT_jdom_DOT_org> and
  42. Brett McLaughlin <brett_AT_jdom_DOT_org>. For more information
  43. on the JDOM Project, please see <http://www.jdom.org/>.
  44. */
  45. package org.jdom.output;
  46. import java.io.*;
  47. import java.util.*;
  48. import javax.xml.transform.Result;
  49. import org.jdom.*;
  50. /**
  51. * Outputs a JDOM document as a stream of bytes. The outputter can manage many
  52. * styles of document formatting, from untouched to pretty printed. The default
  53. * is to output the document content exactly as created, but this can be changed
  54. * by setting a new Format object. For pretty-print output, use
  55. * <code>{@link Format#getPrettyFormat()}</code>. For whitespace-normalized
  56. * output, use <code>{@link Format#getCompactFormat()}</code>.
  57. * <p>
  58. * There are <code>{@link #output output(...)}</code> methods to print any of
  59. * the standard JDOM classes, including Document and Element, to either a Writer
  60. * or an OutputStream. <b>Warning</b>: When outputting to a Writer, make sure
  61. * the writer's encoding matches the encoding setting in the Format object. This
  62. * ensures the encoding in which the content is written (controlled by the
  63. * Writer configuration) matches the encoding placed in the document's XML
  64. * declaration (controlled by the XMLOutputter). Because a Writer cannot be
  65. * queried for its encoding, the information must be passed to the Format
  66. * manually in its constructor or via the
  67. * <code>{@link Format#setEncoding}</code> method. The default encoding is
  68. * UTF-8.
  69. * <p>
  70. * The methods <code>{@link #outputString outputString(...)}</code> are for
  71. * convenience only; for top performance you should call one of the <code>{@link
  72. * #output output(...)}</code> methods and pass in your own Writer or
  73. * OutputStream if possible.
  74. * <p>
  75. * XML declarations are always printed on their own line followed by a line
  76. * seperator (this doesn't change the semantics of the document). To omit
  77. * printing of the declaration use
  78. * <code>{@link Format#setOmitDeclaration}</code>. To omit printing of the
  79. * encoding in the declaration use <code>{@link Format#setOmitEncoding}</code>.
  80. * Unfortunatly there is currently no way to know the original encoding of the
  81. * document.
  82. * <p>
  83. * Empty elements are by default printed as &lt;empty/&gt;, but this can be
  84. * configured with <code>{@link Format#setExpandEmptyElements}</code> to cause
  85. * them to be expanded to &lt;empty&gt;&lt;/empty&gt;.
  86. *
  87. * @version $Revision: 1.116 $, $Date: 2007/11/10 05:29:01 $
  88. * @author Brett McLaughlin
  89. * @author Jason Hunter
  90. * @author Jason Reid
  91. * @author Wolfgang Werner
  92. * @author Elliotte Rusty Harold
  93. * @author David &amp; Will (from Post Tool Design)
  94. * @author Dan Schaffer
  95. * @author Alex Chaffee
  96. * @author Bradley S. Huffman
  97. */
  98. public class XMLOutputter implements Cloneable {
  99. private static final String CVS_ID =
  100. "@(#) $RCSfile: XMLOutputter.java,v $ $Revision: 1.116 $ $Date: 2007/11/10 05:29:01 $ $Name: jdom_1_1 $";
  101. // For normal output
  102. private Format userFormat = Format.getRawFormat();
  103. // For xml:space="preserve"
  104. protected static final Format preserveFormat = Format.getRawFormat();
  105. // What's currently in use
  106. protected Format currentFormat = userFormat;
  107. /** Whether output escaping is enabled for the being processed
  108. * Element - default is <code>true</code> */
  109. private boolean escapeOutput = true;
  110. // * * * * * * * * * * Constructors * * * * * * * * * *
  111. // * * * * * * * * * * Constructors * * * * * * * * * *
  112. /**
  113. * This will create an <code>XMLOutputter</code> with the default
  114. * {@link Format} matching {@link Format#getRawFormat}.
  115. */
  116. public XMLOutputter() {
  117. }
  118. /**
  119. * This will create an <code>XMLOutputter</code> with the specified
  120. * format characteristics. Note the format object is cloned internally
  121. * before use.
  122. */
  123. public XMLOutputter(Format format) {
  124. userFormat = (Format) format.clone();
  125. currentFormat = userFormat;
  126. }
  127. /**
  128. * This will create an <code>XMLOutputter</code> with all the
  129. * options as set in the given <code>XMLOutputter</code>. Note
  130. * that <code>XMLOutputter two = (XMLOutputter)one.clone();</code>
  131. * would work equally well.
  132. *
  133. * @param that the XMLOutputter to clone
  134. */
  135. public XMLOutputter(XMLOutputter that) {
  136. this.userFormat = (Format) that.userFormat.clone();
  137. currentFormat = userFormat;
  138. }
  139. // * * * * * * * * * * Set parameters methods * * * * * * * * * *
  140. // * * * * * * * * * * Set parameters methods * * * * * * * * * *
  141. /**
  142. * Sets the new format logic for the outputter. Note the Format
  143. * object is cloned internally before use.
  144. *
  145. * @param newFormat the format to use for output
  146. */
  147. public void setFormat(Format newFormat) {
  148. this.userFormat = (Format) newFormat.clone();
  149. this.currentFormat = userFormat;
  150. }
  151. /**
  152. * Returns the current format in use by the outputter. Note the
  153. * Format object returned is a clone of the one used internally.
  154. */
  155. public Format getFormat() {
  156. return (Format) userFormat.clone();
  157. }
  158. // * * * * * * * * * * Output to a OutputStream * * * * * * * * * *
  159. // * * * * * * * * * * Output to a OutputStream * * * * * * * * * *
  160. /**
  161. * This will print the <code>Document</code> to the given output stream.
  162. * The characters are printed using the encoding specified in the
  163. * constructor, or a default of UTF-8.
  164. *
  165. * @param doc <code>Document</code> to format.
  166. * @param out <code>OutputStream</code> to use.
  167. * @throws IOException - if there's any problem writing.
  168. */
  169. public void output(Document doc, OutputStream out)
  170. throws IOException {
  171. Writer writer = makeWriter(out);
  172. output(doc, writer); // output() flushes
  173. }
  174. /**
  175. * Print out the <code>{@link DocType}</code>.
  176. *
  177. * @param doctype <code>DocType</code> to output.
  178. * @param out <code>OutputStream</code> to use.
  179. */
  180. public void output(DocType doctype, OutputStream out) throws IOException {
  181. Writer writer = makeWriter(out);
  182. output(doctype, writer); // output() flushes
  183. }
  184. /**
  185. * Print out an <code>{@link Element}</code>, including
  186. * its <code>{@link Attribute}</code>s, and all
  187. * contained (child) elements, etc.
  188. *
  189. * @param element <code>Element</code> to output.
  190. * @param out <code>Writer</code> to use.
  191. */
  192. public void output(Element element, OutputStream out) throws IOException {
  193. Writer writer = makeWriter(out);
  194. output(element, writer); // output() flushes
  195. }
  196. /**
  197. * This will handle printing out an <code>{@link
  198. * Element}</code>'s content only, not including its tag, and
  199. * attributes. This can be useful for printing the content of an
  200. * element that contains HTML, like "&lt;description&gt;JDOM is
  201. * &lt;b&gt;fun&gt;!&lt;/description&gt;".
  202. *
  203. * @param element <code>Element</code> to output.
  204. * @param out <code>OutputStream</code> to use.
  205. */
  206. public void outputElementContent(Element element, OutputStream out)
  207. throws IOException {
  208. Writer writer = makeWriter(out);
  209. outputElementContent(element, writer); // output() flushes
  210. }
  211. /**
  212. * This will handle printing out a list of nodes.
  213. * This can be useful for printing the content of an element that
  214. * contains HTML, like "&lt;description&gt;JDOM is
  215. * &lt;b&gt;fun&gt;!&lt;/description&gt;".
  216. *
  217. * @param list <code>List</code> of nodes.
  218. * @param out <code>OutputStream</code> to use.
  219. */
  220. public void output(List list, OutputStream out)
  221. throws IOException {
  222. Writer writer = makeWriter(out);
  223. output(list, writer); // output() flushes
  224. }
  225. /**
  226. * Print out a <code>{@link CDATA}</code> node.
  227. *
  228. * @param cdata <code>CDATA</code> to output.
  229. * @param out <code>OutputStream</code> to use.
  230. */
  231. public void output(CDATA cdata, OutputStream out) throws IOException {
  232. Writer writer = makeWriter(out);
  233. output(cdata, writer); // output() flushes
  234. }
  235. /**
  236. * Print out a <code>{@link Text}</code> node. Perfoms
  237. * the necessary entity escaping and whitespace stripping.
  238. *
  239. * @param text <code>Text</code> to output.
  240. * @param out <code>OutputStream</code> to use.
  241. */
  242. public void output(Text text, OutputStream out) throws IOException {
  243. Writer writer = makeWriter(out);
  244. output(text, writer); // output() flushes
  245. }
  246. /**
  247. * Print out a <code>{@link Comment}</code>.
  248. *
  249. * @param comment <code>Comment</code> to output.
  250. * @param out <code>OutputStream</code> to use.
  251. */
  252. public void output(Comment comment, OutputStream out) throws IOException {
  253. Writer writer = makeWriter(out);
  254. output(comment, writer); // output() flushes
  255. }
  256. /**
  257. * Print out a <code>{@link ProcessingInstruction}</code>.
  258. *
  259. * @param pi <code>ProcessingInstruction</code> to output.
  260. * @param out <code>OutputStream</code> to use.
  261. */
  262. public void output(ProcessingInstruction pi, OutputStream out)
  263. throws IOException {
  264. Writer writer = makeWriter(out);
  265. output(pi, writer); // output() flushes
  266. }
  267. /**
  268. * Print out a <code>{@link EntityRef}</code>.
  269. *
  270. * @param entity <code>EntityRef</code> to output.
  271. * @param out <code>OutputStream</code> to use.
  272. */
  273. public void output(EntityRef entity, OutputStream out) throws IOException {
  274. Writer writer = makeWriter(out);
  275. output(entity, writer); // output() flushes
  276. }
  277. /**
  278. * Get an OutputStreamWriter, using prefered encoding
  279. * (see {@link Format#setEncoding}).
  280. */
  281. private Writer makeWriter(OutputStream out)
  282. throws java.io.UnsupportedEncodingException {
  283. return makeWriter(out, userFormat.encoding);
  284. }
  285. /**
  286. * Get an OutputStreamWriter, use specified encoding.
  287. */
  288. private static Writer makeWriter(OutputStream out, String enc)
  289. throws java.io.UnsupportedEncodingException {
  290. // "UTF-8" is not recognized before JDK 1.1.6, so we'll translate
  291. // into "UTF8" which works with all JDKs.
  292. if ("UTF-8".equals(enc)) {
  293. enc = "UTF8";
  294. }
  295. Writer writer = new BufferedWriter(
  296. (new OutputStreamWriter(
  297. new BufferedOutputStream(out), enc)
  298. ));
  299. return writer;
  300. }
  301. // * * * * * * * * * * Output to a Writer * * * * * * * * * *
  302. // * * * * * * * * * * Output to a Writer * * * * * * * * * *
  303. /**
  304. * This will print the <code>Document</code> to the given Writer.
  305. *
  306. * <p>
  307. * Warning: using your own Writer may cause the outputter's
  308. * preferred character encoding to be ignored. If you use
  309. * encodings other than UTF-8, we recommend using the method that
  310. * takes an OutputStream instead.
  311. * </p>
  312. *
  313. * @param doc <code>Document</code> to format.
  314. * @param out <code>Writer</code> to use.
  315. * @throws IOException - if there's any problem writing.
  316. */
  317. public void output(Document doc, Writer out) throws IOException {
  318. printDeclaration(out, doc, userFormat.encoding);
  319. // Print out root element, as well as any root level
  320. // comments and processing instructions,
  321. // starting with no indentation
  322. List content = doc.getContent();
  323. int size = content.size();
  324. for (int i = 0; i < size; i++) {
  325. Object obj = content.get(i);
  326. if (obj instanceof Element) {
  327. printElement(out, doc.getRootElement(), 0,
  328. createNamespaceStack());
  329. }
  330. else if (obj instanceof Comment) {
  331. printComment(out, (Comment) obj);
  332. }
  333. else if (obj instanceof ProcessingInstruction) {
  334. printProcessingInstruction(out, (ProcessingInstruction) obj);
  335. }
  336. else if (obj instanceof DocType) {
  337. printDocType(out, doc.getDocType());
  338. // Always print line separator after declaration, helps the
  339. // output look better and is semantically inconsequential
  340. out.write(currentFormat.lineSeparator);
  341. }
  342. else {
  343. // XXX if we get here then we have a illegal content, for
  344. // now we'll just ignore it
  345. }
  346. newline(out);
  347. indent(out, 0);
  348. }
  349. // Output final line separator
  350. // We output this no matter what the newline flags say
  351. out.write(currentFormat.lineSeparator);
  352. out.flush();
  353. }
  354. /**
  355. * Print out the <code>{@link DocType}</code>.
  356. *
  357. * @param doctype <code>DocType</code> to output.
  358. * @param out <code>Writer</code> to use.
  359. */
  360. public void output(DocType doctype, Writer out) throws IOException {
  361. printDocType(out, doctype);
  362. out.flush();
  363. }
  364. /**
  365. * Print out an <code>{@link Element}</code>, including
  366. * its <code>{@link Attribute}</code>s, and all
  367. * contained (child) elements, etc.
  368. *
  369. * @param element <code>Element</code> to output.
  370. * @param out <code>Writer</code> to use.
  371. */
  372. public void output(Element element, Writer out) throws IOException {
  373. // If this is the root element we could pre-initialize the
  374. // namespace stack with the namespaces
  375. printElement(out, element, 0, createNamespaceStack());
  376. out.flush();
  377. }
  378. /**
  379. * This will handle printing out an <code>{@link
  380. * Element}</code>'s content only, not including its tag, and
  381. * attributes. This can be useful for printing the content of an
  382. * element that contains HTML, like "&lt;description&gt;JDOM is
  383. * &lt;b&gt;fun&gt;!&lt;/description&gt;".
  384. *
  385. * @param element <code>Element</code> to output.
  386. * @param out <code>Writer</code> to use.
  387. */
  388. public void outputElementContent(Element element, Writer out)
  389. throws IOException {
  390. List content = element.getContent();
  391. printContentRange(out, content, 0, content.size(),
  392. 0, createNamespaceStack());
  393. out.flush();
  394. }
  395. /**
  396. * This will handle printing out a list of nodes.
  397. * This can be useful for printing the content of an element that
  398. * contains HTML, like "&lt;description&gt;JDOM is
  399. * &lt;b&gt;fun&gt;!&lt;/description&gt;".
  400. *
  401. * @param list <code>List</code> of nodes.
  402. * @param out <code>Writer</code> to use.
  403. */
  404. public void output(List list, Writer out)
  405. throws IOException {
  406. printContentRange(out, list, 0, list.size(),
  407. 0, createNamespaceStack());
  408. out.flush();
  409. }
  410. /**
  411. * Print out a <code>{@link CDATA}</code> node.
  412. *
  413. * @param cdata <code>CDATA</code> to output.
  414. * @param out <code>Writer</code> to use.
  415. */
  416. public void output(CDATA cdata, Writer out) throws IOException {
  417. printCDATA(out, cdata);
  418. out.flush();
  419. }
  420. /**
  421. * Print out a <code>{@link Text}</code> node. Perfoms
  422. * the necessary entity escaping and whitespace stripping.
  423. *
  424. * @param text <code>Text</code> to output.
  425. * @param out <code>Writer</code> to use.
  426. */
  427. public void output(Text text, Writer out) throws IOException {
  428. printText(out, text);
  429. out.flush();
  430. }
  431. /**
  432. * Print out a <code>{@link Comment}</code>.
  433. *
  434. * @param comment <code>Comment</code> to output.
  435. * @param out <code>Writer</code> to use.
  436. */
  437. public void output(Comment comment, Writer out) throws IOException {
  438. printComment(out, comment);
  439. out.flush();
  440. }
  441. /**
  442. * Print out a <code>{@link ProcessingInstruction}</code>.
  443. *
  444. * @param pi <code>ProcessingInstruction</code> to output.
  445. * @param out <code>Writer</code> to use.
  446. */
  447. public void output(ProcessingInstruction pi, Writer out)
  448. throws IOException {
  449. boolean currentEscapingPolicy = currentFormat.ignoreTrAXEscapingPIs;
  450. // Output PI verbatim, disregarding TrAX escaping PIs.
  451. currentFormat.setIgnoreTrAXEscapingPIs(true);
  452. printProcessingInstruction(out, pi);
  453. currentFormat.setIgnoreTrAXEscapingPIs(currentEscapingPolicy);
  454. out.flush();
  455. }
  456. /**
  457. * Print out a <code>{@link EntityRef}</code>.
  458. *
  459. * @param entity <code>EntityRef</code> to output.
  460. * @param out <code>Writer</code> to use.
  461. */
  462. public void output(EntityRef entity, Writer out) throws IOException {
  463. printEntityRef(out, entity);
  464. out.flush();
  465. }
  466. // * * * * * * * * * * Output to a String * * * * * * * * * *
  467. // * * * * * * * * * * Output to a String * * * * * * * * * *
  468. /**
  469. * Return a string representing a document. Uses an internal
  470. * StringWriter. Warning: a String is Unicode, which may not match
  471. * the outputter's specified encoding.
  472. *
  473. * @param doc <code>Document</code> to format.
  474. */
  475. public String outputString(Document doc) {
  476. StringWriter out = new StringWriter();
  477. try {
  478. output(doc, out); // output() flushes
  479. } catch (IOException e) { }
  480. return out.toString();
  481. }
  482. /**
  483. * Return a string representing a DocType. Warning: a String is
  484. * Unicode, which may not match the outputter's specified
  485. * encoding.
  486. *
  487. * @param doctype <code>DocType</code> to format.
  488. */
  489. public String outputString(DocType doctype) {
  490. StringWriter out = new StringWriter();
  491. try {
  492. output(doctype, out); // output() flushes
  493. } catch (IOException e) { }
  494. return out.toString();
  495. }
  496. /**
  497. * Return a string representing an element. Warning: a String is
  498. * Unicode, which may not match the outputter's specified
  499. * encoding.
  500. *
  501. * @param element <code>Element</code> to format.
  502. */
  503. public String outputString(Element element) {
  504. StringWriter out = new StringWriter();
  505. try {
  506. output(element, out); // output() flushes
  507. } catch (IOException e) { }
  508. return out.toString();
  509. }
  510. /**
  511. * Return a string representing a list of nodes. The list is
  512. * assumed to contain legal JDOM nodes.
  513. *
  514. * @param list <code>List</code> to format.
  515. */
  516. public String outputString(List list) {
  517. StringWriter out = new StringWriter();
  518. try {
  519. output(list, out); // output() flushes
  520. } catch (IOException e) { }
  521. return out.toString();
  522. }
  523. /**
  524. * Return a string representing a CDATA node. Warning: a String is
  525. * Unicode, which may not match the outputter's specified
  526. * encoding.
  527. *
  528. * @param cdata <code>CDATA</code> to format.
  529. */
  530. public String outputString(CDATA cdata) {
  531. StringWriter out = new StringWriter();
  532. try {
  533. output(cdata, out); // output() flushes
  534. } catch (IOException e) { }
  535. return out.toString();
  536. }
  537. /**
  538. * Return a string representing a Text node. Warning: a String is
  539. * Unicode, which may not match the outputter's specified
  540. * encoding.
  541. *
  542. * @param text <code>Text</code> to format.
  543. */
  544. public String outputString(Text text) {
  545. StringWriter out = new StringWriter();
  546. try {
  547. output(text, out); // output() flushes
  548. } catch (IOException e) { }
  549. return out.toString();
  550. }
  551. /**
  552. * Return a string representing a comment. Warning: a String is
  553. * Unicode, which may not match the outputter's specified
  554. * encoding.
  555. *
  556. * @param comment <code>Comment</code> to format.
  557. */
  558. public String outputString(Comment comment) {
  559. StringWriter out = new StringWriter();
  560. try {
  561. output(comment, out); // output() flushes
  562. } catch (IOException e) { }
  563. return out.toString();
  564. }
  565. /**
  566. * Return a string representing a PI. Warning: a String is
  567. * Unicode, which may not match the outputter's specified
  568. * encoding.
  569. *
  570. * @param pi <code>ProcessingInstruction</code> to format.
  571. */
  572. public String outputString(ProcessingInstruction pi) {
  573. StringWriter out = new StringWriter();
  574. try {
  575. output(pi, out); // output() flushes
  576. } catch (IOException e) { }
  577. return out.toString();
  578. }
  579. /**
  580. * Return a string representing an entity. Warning: a String is
  581. * Unicode, which may not match the outputter's specified
  582. * encoding.
  583. *
  584. * @param entity <code>EntityRef</code> to format.
  585. */
  586. public String outputString(EntityRef entity) {
  587. StringWriter out = new StringWriter();
  588. try {
  589. output(entity, out); // output() flushes
  590. } catch (IOException e) { }
  591. return out.toString();
  592. }
  593. // * * * * * * * * * * Internal printing methods * * * * * * * * * *
  594. // * * * * * * * * * * Internal printing methods * * * * * * * * * *
  595. /**
  596. * This will handle printing of the declaration.
  597. * Assumes XML version 1.0 since we don't directly know.
  598. *
  599. * @param doc <code>Document</code> whose declaration to write.
  600. * @param out <code>Writer</code> to use.
  601. * @param encoding The encoding to add to the declaration
  602. */
  603. protected void printDeclaration(Writer out, Document doc,
  604. String encoding) throws IOException {
  605. // Only print the declaration if it's not being omitted
  606. if (!userFormat.omitDeclaration) {
  607. // Assume 1.0 version
  608. out.write("<?xml version=\"1.0\"");
  609. if (!userFormat.omitEncoding) {
  610. out.write(" encoding=\"" + encoding + "\"");
  611. }
  612. out.write("?>");
  613. // Print new line after decl always, even if no other new lines
  614. // Helps the output look better and is semantically
  615. // inconsequential
  616. out.write(currentFormat.lineSeparator);
  617. }
  618. }
  619. /**
  620. * This handle printing the DOCTYPE declaration if one exists.
  621. *
  622. * @param docType <code>Document</code> whose declaration to write.
  623. * @param out <code>Writer</code> to use.
  624. */
  625. protected void printDocType(Writer out, DocType docType)
  626. throws IOException {
  627. String publicID = docType.getPublicID();
  628. String systemID = docType.getSystemID();
  629. String internalSubset = docType.getInternalSubset();
  630. boolean hasPublic = false;
  631. out.write("<!DOCTYPE ");
  632. out.write(docType.getElementName());
  633. if (publicID != null) {
  634. out.write(" PUBLIC \"");
  635. out.write(publicID);
  636. out.write("\"");
  637. hasPublic = true;
  638. }
  639. if (systemID != null) {
  640. if (!hasPublic) {
  641. out.write(" SYSTEM");
  642. }
  643. out.write(" \"");
  644. out.write(systemID);
  645. out.write("\"");
  646. }
  647. if ((internalSubset != null) && (!internalSubset.equals(""))) {
  648. out.write(" [");
  649. out.write(currentFormat.lineSeparator);
  650. out.write(docType.getInternalSubset());
  651. out.write("]");
  652. }
  653. out.write(">");
  654. }
  655. /**
  656. * This will handle printing of comments.
  657. *
  658. * @param comment <code>Comment</code> to write.
  659. * @param out <code>Writer</code> to use.
  660. */
  661. protected void printComment(Writer out, Comment comment)
  662. throws IOException {
  663. out.write("<!--");
  664. out.write(comment.getText());
  665. out.write("-->");
  666. }
  667. /**
  668. * This will handle printing of processing instructions.
  669. *
  670. * @param pi <code>ProcessingInstruction</code> to write.
  671. * @param out <code>Writer</code> to use.
  672. */
  673. protected void printProcessingInstruction(Writer out, ProcessingInstruction pi
  674. ) throws IOException {
  675. String target = pi.getTarget();
  676. boolean piProcessed = false;
  677. if (currentFormat.ignoreTrAXEscapingPIs == false) {
  678. if (target.equals(Result.PI_DISABLE_OUTPUT_ESCAPING)) {
  679. escapeOutput = false;
  680. piProcessed = true;
  681. }
  682. else if (target.equals(Result.PI_ENABLE_OUTPUT_ESCAPING)) {
  683. escapeOutput = true;
  684. piProcessed = true;
  685. }
  686. }
  687. if (piProcessed == false) {
  688. String rawData = pi.getData();
  689. // Write <?target data?> or if no data then just <?target?>
  690. if (!"".equals(rawData)) {
  691. out.write("<?");
  692. out.write(target);
  693. out.write(" ");
  694. out.write(rawData);
  695. out.write("?>");
  696. }
  697. else {
  698. out.write("<?");
  699. out.write(target);
  700. out.write("?>");
  701. }
  702. }
  703. }
  704. /**
  705. * This will handle printing a <code>{@link EntityRef}</code>.
  706. * Only the entity reference such as <code>&amp;entity;</code>
  707. * will be printed. However, subclasses are free to override
  708. * this method to print the contents of the entity instead.
  709. *
  710. * @param entity <code>EntityRef</code> to output.
  711. * @param out <code>Writer</code> to use. */
  712. protected void printEntityRef(Writer out, EntityRef entity)
  713. throws IOException {
  714. out.write("&");
  715. out.write(entity.getName());
  716. out.write(";");
  717. }
  718. /**
  719. * This will handle printing of <code>{@link CDATA}</code> text.
  720. *
  721. * @param cdata <code>CDATA</code> to output.
  722. * @param out <code>Writer</code> to use.
  723. */
  724. protected void printCDATA(Writer out, CDATA cdata) throws IOException {
  725. String str = (currentFormat.mode == Format.TextMode.NORMALIZE)
  726. ? cdata.getTextNormalize()
  727. : ((currentFormat.mode == Format.TextMode.TRIM) ?
  728. cdata.getText().trim() : cdata.getText());
  729. out.write("<![CDATA[");
  730. out.write(str);
  731. out.write("]]>");
  732. }
  733. /**
  734. * This will handle printing of <code>{@link Text}</code> strings.
  735. *
  736. * @param text <code>Text</code> to write.
  737. * @param out <code>Writer</code> to use.
  738. */
  739. protected void printText(Writer out, Text text) throws IOException {
  740. String str = (currentFormat.mode == Format.TextMode.NORMALIZE)
  741. ? text.getTextNormalize()
  742. : ((currentFormat.mode == Format.TextMode.TRIM) ?
  743. text.getText().trim() : text.getText());
  744. out.write(escapeElementEntities(str));
  745. }
  746. /**
  747. * This will handle printing a string. Escapes the element entities,
  748. * trims interior whitespace, etc. if necessary.
  749. */
  750. private void printString(Writer out, String str) throws IOException {
  751. if (currentFormat.mode == Format.TextMode.NORMALIZE) {
  752. str = Text.normalizeString(str);
  753. }
  754. else if (currentFormat.mode == Format.TextMode.TRIM) {
  755. str = str.trim();
  756. }
  757. out.write(escapeElementEntities(str));
  758. }
  759. /**
  760. * This will handle printing of a <code>{@link Element}</code>,
  761. * its <code>{@link Attribute}</code>s, and all contained (child)
  762. * elements, etc.
  763. *
  764. * @param element <code>Element</code> to output.
  765. * @param out <code>Writer</code> to use.
  766. * @param level <code>int</code> level of indention.
  767. * @param namespaces <code>List</code> stack of Namespaces in scope.
  768. */
  769. protected void printElement(Writer out, Element element,
  770. int level, NamespaceStack namespaces)
  771. throws IOException {
  772. List attributes = element.getAttributes();
  773. List content = element.getContent();
  774. // Check for xml:space and adjust format settings
  775. String space = null;
  776. if (attributes != null) {
  777. space = element.getAttributeValue("space",
  778. Namespace.XML_NAMESPACE);
  779. }
  780. Format previousFormat = currentFormat;
  781. if ("default".equals(space)) {
  782. currentFormat = userFormat;
  783. }
  784. else if ("preserve".equals(space)) {
  785. currentFormat = preserveFormat;
  786. }
  787. // Print the beginning of the tag plus attributes and any
  788. // necessary namespace declarations
  789. out.write("<");
  790. printQualifiedName(out, element);
  791. // Mark our namespace starting point
  792. int previouslyDeclaredNamespaces = namespaces.size();
  793. // Print the element's namespace, if appropriate
  794. printElementNamespace(out, element, namespaces);
  795. // Print out additional namespace declarations
  796. printAdditionalNamespaces(out, element, namespaces);
  797. // Print out attributes
  798. if (attributes != null)
  799. printAttributes(out, attributes, element, namespaces);
  800. // Depending on the settings (newlines, textNormalize, etc), we may
  801. // or may not want to print all of the content, so determine the
  802. // index of the start of the content we're interested
  803. // in based on the current settings.
  804. int start = skipLeadingWhite(content, 0);
  805. int size = content.size();
  806. if (start >= size) {
  807. // Case content is empty or all insignificant whitespace
  808. if (currentFormat.expandEmptyElements) {
  809. out.write("></");
  810. printQualifiedName(out, element);
  811. out.write(">");
  812. }
  813. else {
  814. out.write(" />");
  815. }
  816. }
  817. else {
  818. out.write(">");
  819. // For a special case where the content is only CDATA
  820. // or Text we don't want to indent after the start or
  821. // before the end tag.
  822. if (nextNonText(content, start) < size) {
  823. // Case Mixed Content - normal indentation
  824. newline(out);
  825. printContentRange(out, content, start, size,
  826. level + 1, namespaces);
  827. newline(out);
  828. indent(out, level);
  829. }
  830. else {
  831. // Case all CDATA or Text - no indentation
  832. printTextRange(out, content, start, size);
  833. }
  834. out.write("</");
  835. printQualifiedName(out, element);
  836. out.write(">");
  837. }
  838. // remove declared namespaces from stack
  839. while (namespaces.size() > previouslyDeclaredNamespaces) {
  840. namespaces.pop();
  841. }
  842. // Restore our format settings
  843. currentFormat = previousFormat;
  844. }
  845. /**
  846. * This will handle printing of content within a given range.
  847. * The range to print is specified in typical Java fashion; the
  848. * starting index is inclusive, while the ending index is
  849. * exclusive.
  850. *
  851. * @param content <code>List</code> of content to output
  852. * @param start index of first content node (inclusive.
  853. * @param end index of last content node (exclusive).
  854. * @param out <code>Writer</code> to use.
  855. * @param level <code>int</code> level of indentation.
  856. * @param namespaces <code>List</code> stack of Namespaces in scope.
  857. */
  858. private void printContentRange(Writer out, List content,
  859. int start, int end, int level,
  860. NamespaceStack namespaces)
  861. throws IOException {
  862. boolean firstNode; // Flag for 1st node in content
  863. Object next; // Node we're about to print
  864. int first, index; // Indexes into the list of content
  865. index = start;
  866. while (index < end) {
  867. firstNode = (index == start) ? true : false;
  868. next = content.get(index);
  869. //
  870. // Handle consecutive CDATA, Text, and EntityRef nodes all at once
  871. //
  872. if ((next instanceof Text) || (next instanceof EntityRef)) {
  873. first = skipLeadingWhite(content, index);
  874. // Set index to next node for loop
  875. index = nextNonText(content, first);
  876. // If it's not all whitespace - print it!
  877. if (first < index) {
  878. if (!firstNode)
  879. newline(out);
  880. indent(out, level);
  881. printTextRange(out, content, first, index);
  882. }
  883. continue;
  884. }
  885. //
  886. // Handle other nodes
  887. //
  888. if (!firstNode) {
  889. newline(out);
  890. }
  891. indent(out, level);
  892. if (next instanceof Comment) {
  893. printComment(out, (Comment)next);
  894. }
  895. else if (next instanceof Element) {
  896. printElement(out, (Element)next, level, namespaces);
  897. }
  898. else if (next instanceof ProcessingInstruction) {
  899. printProcessingInstruction(out, (ProcessingInstruction)next);
  900. }
  901. else {
  902. // XXX if we get here then we have a illegal content, for
  903. // now we'll just ignore it (probably should throw
  904. // a exception)
  905. }
  906. index++;
  907. } /* while */
  908. }
  909. /**
  910. * This will handle printing of a sequence of <code>{@link CDATA}</code>
  911. * or <code>{@link Text}</code> nodes. It is an error to have any other
  912. * pass this method any other type of node.
  913. *
  914. * @param content <code>List</code> of content to output
  915. * @param start index of first content node (inclusive).
  916. * @param end index of last content node (exclusive).
  917. * @param out <code>Writer</code> to use.
  918. */
  919. private void printTextRange(Writer out, List content, int start, int end
  920. ) throws IOException {
  921. String previous; // Previous text printed
  922. Object node; // Next node to print
  923. String next; // Next text to print
  924. previous = null;
  925. // Remove leading whitespace-only nodes
  926. start = skipLeadingWhite(content, start);
  927. int size = content.size();
  928. if (start < size) {
  929. // And remove trialing whitespace-only nodes
  930. end = skipTrailingWhite(content, end);
  931. for (int i = start; i < end; i++) {
  932. node = content.get(i);
  933. // Get the unmangled version of the text
  934. // we are about to print
  935. if (node instanceof Text) {
  936. next = ((Text) node).getText();
  937. }
  938. else if (node instanceof EntityRef) {
  939. next = "&" + ((EntityRef) node).getValue() + ";";
  940. }
  941. else {
  942. throw new IllegalStateException("Should see only " +
  943. "CDATA, Text, or EntityRef");
  944. }
  945. // This may save a little time
  946. if (next == null || "".equals(next)) {
  947. continue;
  948. }
  949. // Determine if we need to pad the output (padding is
  950. // only need in trim or normalizing mode)
  951. if (previous != null) { // Not 1st node
  952. if (currentFormat.mode == Format.TextMode.NORMALIZE ||
  953. currentFormat.mode == Format.TextMode.TRIM) {
  954. if ((endsWithWhite(previous)) ||
  955. (startsWithWhite(next))) {
  956. out.write(" ");
  957. }
  958. }
  959. }
  960. // Print the node
  961. if (node instanceof CDATA) {
  962. printCDATA(out, (CDATA) node);
  963. }
  964. else if (node instanceof EntityRef) {
  965. printEntityRef(out, (EntityRef) node);
  966. }
  967. else {
  968. printString(out, next);
  969. }
  970. previous = next;
  971. }
  972. }
  973. }
  974. /**
  975. * This will handle printing of any needed <code>{@link Namespace}</code>
  976. * declarations.
  977. *
  978. * @param ns <code>Namespace</code> to print definition of
  979. * @param out <code>Writer</code> to use.
  980. */
  981. private void printNamespace(Writer out, Namespace ns,
  982. NamespaceStack namespaces)
  983. throws IOException {
  984. String prefix = ns.getPrefix();
  985. String uri = ns.getURI();
  986. // Already printed namespace decl?
  987. if (uri.equals(namespaces.getURI(prefix))) {
  988. return;
  989. }
  990. out.write(" xmlns");
  991. if (!prefix.equals("")) {
  992. out.write(":");
  993. out.write(prefix);
  994. }
  995. out.write("=\"");
  996. out.write(escapeAttributeEntities(uri));
  997. out.write("\"");
  998. namespaces.push(ns);
  999. }
  1000. /**
  1001. * This will handle printing of a <code>{@link Attribute}</code> list.
  1002. *
  1003. * @param attributes <code>List</code> of Attribute objcts
  1004. * @param out <code>Writer</code> to use
  1005. */
  1006. protected void printAttributes(Writer out, List attributes, Element parent,
  1007. NamespaceStack namespaces)
  1008. throws IOException {
  1009. // I do not yet handle the case where the same prefix maps to
  1010. // two different URIs. For attributes on the same element
  1011. // this is illegal; but as yet we don't throw an exception
  1012. // if someone tries to do this
  1013. // Set prefixes = new HashSet();
  1014. for (int i = 0; i < attributes.size(); i++) {
  1015. Attribute attribute = (Attribute) attributes.get(i);
  1016. Namespace ns = attribute.getNamespace();
  1017. if ((ns != Namespace.NO_NAMESPACE) &&
  1018. (ns != Namespace.XML_NAMESPACE)) {
  1019. printNamespace(out, ns, namespaces);
  1020. }
  1021. out.write(" ");
  1022. printQualifiedName(out, attribute);
  1023. out.write("=");
  1024. out.write("\"");
  1025. out.write(escapeAttributeEntities(attribute.getValue()));
  1026. out.write("\"");
  1027. }
  1028. }
  1029. private void printElementNamespace(Writer out, Element element,
  1030. NamespaceStack namespaces)
  1031. throws IOException {
  1032. // Add namespace decl only if it's not the XML namespace and it's
  1033. // not the NO_NAMESPACE with the prefix "" not yet mapped
  1034. // (we do output xmlns="" if the "" prefix was already used and we
  1035. // need to reclaim it for the NO_NAMESPACE)
  1036. Namespace ns = element.getNamespace();
  1037. if (ns == Namespace.XML_NAMESPACE) {
  1038. return;
  1039. }
  1040. if ( !((ns == Namespace.NO_NAMESPACE) &&
  1041. (namespaces.getURI("") == null))) {
  1042. printNamespace(out, ns, namespaces);
  1043. }
  1044. }
  1045. private void printAdditionalNamespaces(Writer out, Element element,
  1046. NamespaceStack namespaces)
  1047. throws IOException {
  1048. List list = element.getAdditionalNamespaces();
  1049. if (list != null) {
  1050. for (int i = 0; i < list.size(); i++) {
  1051. Namespace additional = (Namespace)list.get(i);
  1052. printNamespace(out, additional, namespaces);
  1053. }
  1054. }
  1055. }
  1056. // * * * * * * * * * * Support methods * * * * * * * * * *
  1057. // * * * * * * * * * * Support methods * * * * * * * * * *
  1058. /**
  1059. * This will print a newline only if indent is not null.
  1060. *
  1061. * @param out <code>Writer</code> to use
  1062. */
  1063. private void newline(Writer out) throws IOException {
  1064. if (currentFormat.indent != null) {
  1065. out.write(currentFormat.lineSeparator);
  1066. }
  1067. }
  1068. /**
  1069. * This will print indents only if indent is not null or the empty string.
  1070. *
  1071. * @param out <code>Writer</code> to use
  1072. * @param level current indent level
  1073. */
  1074. private void indent(Writer out, int level) throws IOException {
  1075. if (currentFormat.indent == null ||
  1076. currentFormat.indent.equals("")) {
  1077. return;
  1078. }
  1079. for (int i = 0; i < level; i++) {
  1080. out.write(currentFormat.indent);
  1081. }
  1082. }
  1083. // Returns the index of the first non-all-whitespace CDATA or Text,
  1084. // index = content.size() is returned if content contains
  1085. // all whitespace.
  1086. // @param start index to begin search (inclusive)
  1087. private int skipLeadingWhite(List content, int start) {
  1088. if (start < 0) {
  1089. start = 0;
  1090. }
  1091. int index = start;
  1092. int size = content.size();
  1093. if (currentFormat.mode == Format.TextMode.TRIM_FULL_WHITE
  1094. || currentFormat.mode == Format.TextMode.NORMALIZE
  1095. || currentFormat.mode == Format.TextMode.TRIM) {
  1096. while (index < size) {
  1097. if (!isAllWhitespace(content.get(index))) {
  1098. return index;
  1099. }
  1100. index++;
  1101. }
  1102. }
  1103. return index;
  1104. }
  1105. // Return the index + 1 of the last non-all-whitespace CDATA or
  1106. // Text node, index < 0 is returned
  1107. // if content contains all whitespace.
  1108. // @param start index to begin search (exclusive)
  1109. private int skipTrailingWhite(List content, int start) {
  1110. int size = content.size();
  1111. if (start > size) {
  1112. start = size;
  1113. }
  1114. int index = start;
  1115. if (currentFormat.mode == Format.TextMode.TRIM_FULL_WHITE
  1116. || currentFormat.mode == Format.TextMode.NORMALIZE
  1117. || currentFormat.mode == Format.TextMode.TRIM) {
  1118. while (index >= 0) {
  1119. if (!isAllWhitespace(content.get(index - 1)))
  1120. break;
  1121. --index;
  1122. }
  1123. }
  1124. return index;
  1125. }
  1126. // Return the next non-CDATA, non-Text, or non-EntityRef node,
  1127. // index = content.size() is returned if there is no more non-CDATA,
  1128. // non-Text, or non-EntiryRef nodes
  1129. // @param start index to begin search (inclusive)
  1130. private static int nextNonText(List content, int start) {
  1131. if (start < 0) {
  1132. start = 0;
  1133. }
  1134. int index = start;
  1135. int size = content.size();
  1136. while (index < size) {
  1137. Object node = content.get(index);
  1138. if (!((node instanceof Text) || (node instanceof EntityRef))) {
  1139. return index;
  1140. }
  1141. index++;
  1142. }
  1143. return size;
  1144. }
  1145. // Determine if a Object is all whitespace
  1146. private boolean isAllWhitespace(Object obj) {
  1147. String str = null;
  1148. if (obj instanceof String) {
  1149. str = (String) obj;
  1150. }
  1151. else if (obj instanceof Text) {
  1152. str = ((Text) obj).getText();
  1153. }
  1154. else if (obj instanceof EntityRef) {
  1155. return false;
  1156. }
  1157. else {
  1158. return false;
  1159. }
  1160. for (int i = 0; i < str.length(); i++) {
  1161. if (!Verifier.isXMLWhitespace(str.charAt(i)))
  1162. return false;
  1163. }
  1164. return true;
  1165. }
  1166. // Determine if a string starts with a XML whitespace.
  1167. private boolean startsWithWhite(String str) {
  1168. if ((str != null) &&
  1169. (str.length() > 0) &&
  1170. Verifier.isXMLWhitespace(str.charAt(0))) {
  1171. return true;
  1172. }
  1173. return false;
  1174. }
  1175. // Determine if a string ends with a XML whitespace.
  1176. private boolean endsWithWhite(String str) {
  1177. if ((str != null) &&
  1178. (str.length() > 0) &&
  1179. Verifier.isXMLWhitespace(str.charAt(str.length() - 1))) {
  1180. return true;
  1181. }
  1182. return false;
  1183. }
  1184. /**
  1185. * This will take the pre-defined entities in XML 1.0 and
  1186. * convert their character representation to the appropriate
  1187. * entity reference, suitable for XML attributes. It does not convert
  1188. * the single quote (') because it's not necessary as the outputter
  1189. * writes attributes surrounded by double-quotes.
  1190. *
  1191. * @param str <code>String</code> input to escape.
  1192. * @return <code>String</code> with escaped content.
  1193. */
  1194. public String escapeAttributeEntities(String str) {
  1195. StringBuffer buffer;
  1196. char ch;
  1197. String entity;
  1198. EscapeStrategy strategy = currentFormat.escapeStrategy;
  1199. buffer = null;
  1200. for (int i = 0; i < str.length(); i++) {
  1201. ch = str.charAt(i);
  1202. switch(ch) {
  1203. case '<' :
  1204. entity = "&lt;";
  1205. break;
  1206. case '>' :
  1207. entity = "&gt;";
  1208. break;
  1209. /*
  1210. case '\'' :
  1211. entity = "&apos;";
  1212. break;
  1213. */
  1214. case '\"' :
  1215. entity = "&quot;";
  1216. break;
  1217. case '&' :
  1218. entity = "&amp;";
  1219. break;
  1220. case '\r' :
  1221. entity = "&#xD;";
  1222. break;
  1223. case '\t' :
  1224. entity = "&#x9;";
  1225. break;
  1226. case '\n' :
  1227. entity = "&#xA;";
  1228. break;
  1229. default :
  1230. if (strategy.shouldEscape(ch)) {
  1231. entity = "&#x" + Integer.toHexString(ch) + ";";
  1232. }
  1233. else {
  1234. entity = null;
  1235. }
  1236. break;
  1237. }
  1238. if (buffer == null) {
  1239. if (entity != null) {
  1240. // An entity occurred, so we'll have to use StringBuffer
  1241. // (allocate room for it plus a few more entities).
  1242. buffer = new StringBuffer(str.length() + 20);
  1243. // Copy previous skipped characters and fall through
  1244. // to pickup current character
  1245. buffer.append(str.substring(0, i));
  1246. buffer.append(entity);
  1247. }
  1248. }
  1249. else {
  1250. if (entity == null) {
  1251. buffer.append(ch);
  1252. }
  1253. else {
  1254. buffer.append(entity);
  1255. }
  1256. }
  1257. }
  1258. // If there were any entities, return the escaped characters
  1259. // that we put in the StringBuffer. Otherwise, just return
  1260. // the unmodified input string.
  1261. return (buffer == null) ? str : buffer.toString();
  1262. }
  1263. /**
  1264. * This will take the three pre-defined entities in XML 1.0
  1265. * (used specifically in XML elements) and convert their character
  1266. * representation to the appropriate entity reference, suitable for
  1267. * XML element content.
  1268. *
  1269. * @param str <code>String</code> input to escape.
  1270. * @return <code>String</code> with escaped content.
  1271. */
  1272. public String escapeElementEntities(String str) {
  1273. if (escapeOutput == false) return str;
  1274. StringBuffer buffer;
  1275. char ch;
  1276. String entity;
  1277. EscapeStrategy strategy = currentFormat.escapeStrategy;
  1278. buffer = null;
  1279. for (int i = 0; i < str.length(); i++) {
  1280. ch = str.charAt(i);
  1281. switch(ch) {
  1282. case '<' :
  1283. entity = "&lt;";
  1284. break;
  1285. case '>' :
  1286. entity = "&gt;";
  1287. break;
  1288. case '&' :
  1289. entity = "&amp;";
  1290. break;
  1291. case '\r' :
  1292. entity = "&#xD;";
  1293. break;
  1294. case '\n' :
  1295. entity = currentFormat.lineSeparator;
  1296. break;
  1297. default :
  1298. if (strategy.shouldEscape(ch)) {
  1299. entity = "&#x" + Integer.toHexString(ch) + ";";
  1300. }
  1301. else {
  1302. entity = null;
  1303. }
  1304. break;
  1305. }
  1306. if (buffer == null) {
  1307. if (entity != null) {
  1308. // An entity occurred, so we'll have to use StringBuffer
  1309. // (allocate room for it plus a few more entities).
  1310. buffer = new StringBuffer(str.length() + 20);
  1311. // Copy previous skipped characters and fall through
  1312. // to pickup current character
  1313. buffer.append(str.substring(0, i));
  1314. buffer.append(entity);
  1315. }
  1316. }
  1317. else {
  1318. if (entity == null) {
  1319. buffer.append(ch);
  1320. }
  1321. else {
  1322. buffer.append(entity);
  1323. }
  1324. }
  1325. }
  1326. // If there were any entities, return the escaped characters
  1327. // that we put in the StringBuffer. Otherwise, just return
  1328. // the unmodified input string.
  1329. return (buffer == null) ? str : buffer.toString();
  1330. }
  1331. /**
  1332. * Returns a copy of this XMLOutputter.
  1333. */
  1334. public Object clone() {
  1335. // Implementation notes: Since all state of an XMLOutputter is
  1336. // embodied in simple private instance variables, Object.clone
  1337. // can be used. Note that since Object.clone is totally
  1338. // broken, we must catch an exception that will never be
  1339. // thrown.
  1340. try {
  1341. return super.clone();
  1342. }
  1343. catch (java.lang.CloneNotSupportedException e) {
  1344. // even though this should never ever happen, it's still
  1345. // possible to fool Java into throwing a
  1346. // CloneNotSupportedException. If that happens, we
  1347. // shouldn't swallow it.
  1348. throw new RuntimeException(e.toString());
  1349. }
  1350. }
  1351. /**
  1352. * Return a string listing of the settings for this
  1353. * XMLOutputter instance.
  1354. *
  1355. * @return a string listing the settings for this XMLOutputter instance
  1356. */
  1357. public String toString() {
  1358. StringBuffer buffer = new StringBuffer();
  1359. for (int i = 0; i < userFormat.lineSeparator.length(); i++) {
  1360. char ch = userFormat.lineSeparator.charAt(i);
  1361. switch (ch) {
  1362. case '\r': buffer.append("\\r");
  1363. break;
  1364. case '\n': buffer.append("\\n");
  1365. break;
  1366. case '\t': buffer.append("\\t");
  1367. break;
  1368. default: buffer.append("[" + ((int)ch) + "]");
  1369. break;
  1370. }
  1371. }
  1372. return (
  1373. "XMLOutputter[omitDeclaration = " + userFormat.omitDeclaration + ", " +
  1374. "encoding = " + userFormat.encoding + ", " +
  1375. "omitEncoding = " + userFormat.omitEncoding + ", " +
  1376. "indent = '" + userFormat.indent + "'" + ", " +
  1377. "expandEmptyElements = " + userFormat.expandEmptyElements + ", " +
  1378. "lineSeparator = '" + buffer.toString() + "', " +
  1379. "textMode = " + userFormat.mode + "]"
  1380. );
  1381. }
  1382. /**
  1383. * Factory for making new NamespaceStack objects. The NamespaceStack
  1384. * created is actually an inner class extending the package protected
  1385. * NamespaceStack, as a way to make NamespaceStack "friendly" toward
  1386. * subclassers.
  1387. */
  1388. private NamespaceStack createNamespaceStack() {
  1389. // actually returns a XMLOutputter.NamespaceStack (see below)
  1390. return new NamespaceStack();
  1391. }
  1392. /**
  1393. * Our own null subclass of NamespaceStack. This plays a little
  1394. * trick with Java access protection. We want subclasses of
  1395. * XMLOutputter to be able to override protected methods that
  1396. * declare a NamespaceStack parameter, but we don't want to
  1397. * declare the parent NamespaceStack class as public.
  1398. */
  1399. protected class NamespaceStack
  1400. extends org.jdom.output.NamespaceStack
  1401. {
  1402. }
  1403. // Support method to print a name without using elt.getQualifiedName()
  1404. // and thus avoiding a StringBuffer creation and memory churn
  1405. private void printQualifiedName(Writer out, Element e) throws IOException {
  1406. if (e.getNamespace().getPrefix().length() == 0) {
  1407. out.write(e.getName());
  1408. }
  1409. else {
  1410. out.write(e.getNamespace().getPrefix());
  1411. out.write(':');
  1412. out.write(e.getName());
  1413. }
  1414. }
  1415. // Support method to print a name without using att.getQualifiedName()
  1416. // and thus avoiding a StringBuffer creation and memory churn
  1417. private void printQualifiedName(Writer out, Attribute a) throws IOException {
  1418. String prefix = a.getNamespace().getPrefix();
  1419. if ((prefix != null) && (!prefix.equals(""))) {
  1420. out.write(prefix);
  1421. out.write(':');
  1422. out.write(a.getName());
  1423. }
  1424. else {
  1425. out.write(a.getName());
  1426. }
  1427. }
  1428. // * * * * * * * * * * Deprecated methods * * * * * * * * * *
  1429. /* The methods below here are deprecations of protected methods. We
  1430. * don't usually deprecate protected methods, so they're commented out.
  1431. * They're left here in case this mass deprecation causes people trouble.
  1432. * Since we're getting close to 1.0 it's actually better for people to
  1433. * raise issues early though.
  1434. */
  1435. }