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.

XMLWriter.java 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  1. /* *******************************************************************
  2. * Copyright (c) 1999-2001 Xerox Corporation,
  3. * 2002 Palo Alto Research Center, Incorporated (PARC).
  4. * All rights reserved.
  5. * This program and the accompanying materials are made available
  6. * under the terms of the Eclipse Public License v1.0
  7. * which accompanies this distribution and is available at
  8. * http://www.eclipse.org/legal/epl-v10.html
  9. *
  10. * Contributors:
  11. * Xerox/PARC initial implementation
  12. * ******************************************************************/
  13. package org.aspectj.testing.xml;
  14. import java.io.PrintWriter;
  15. import java.util.List;
  16. import java.util.Stack;
  17. import org.aspectj.util.LangUtil;
  18. /**
  19. * Manage print stream to an XML document.
  20. * This tracks start/end elements and signals on error,
  21. * and optionally lineates buffer by accumulating
  22. * up to a maximum width (including indent).
  23. * This also has utilities for un/flattening lists and
  24. * rendering buffer.
  25. */
  26. public class XMLWriter {
  27. static final String SP = " ";
  28. static final String TAB = SP + SP;
  29. /** maximum value for maxWidth, when flowing buffer */
  30. public static final int MAX_WIDTH = 8000;
  31. /** default value for maxWidth, when flowing buffer */
  32. public static final int DEFAULT_WIDTH = 80;
  33. /** extremely inefficient! */
  34. public static String attributeValue(String input) {
  35. // if (-1 != input.indexOf("&")) {
  36. // String saved = input;
  37. // input = LangUtil.replace(input, "&", "ampamp;");
  38. // if (-1 == input.indexOf("&")) {
  39. // input = saved;
  40. // } else {
  41. // input = LangUtil.replace(input, "&", "&");
  42. // input = LangUtil.replace(input, "ampamp;", "&");
  43. // }
  44. // } else if (-1 != input.indexOf("&")) {
  45. // input = LangUtil.replace(input, "&", "&");
  46. // }
  47. input = input.replace('"', '~');
  48. input = input.replace('&', '=');
  49. return input;
  50. }
  51. /** @return name="{attributeValue({value})" */
  52. public static String makeAttribute(String name, String value) {
  53. return (name + "=\"" + attributeValue(value) + "\"");
  54. }
  55. /** same as flattenList, except also normalize \ -> / */
  56. public static String flattenFiles(String[] strings) {
  57. return flattenList(strings).replace('\\', '/');
  58. }
  59. /** same as flattenList, except also normalize \ -> / */
  60. public static String flattenFiles(List paths) {
  61. return flattenList(paths).replace('\\', '/');
  62. }
  63. /**
  64. * Expand comma-delimited String into list of values, without trimming
  65. * @param list List of items to print - null is silently ignored,
  66. * so for empty items use ""
  67. * @return String[]{} for null input, String[] {input} for input without comma,
  68. * or new String[] {items..} otherwise
  69. * @throws IllegalArgumentException if {any item}.toString() contains a comma
  70. */
  71. public static String[] unflattenList(String input) {
  72. return (String[]) LangUtil.commaSplit(input).toArray(new String[0]);
  73. }
  74. /** flatten input and add to list */
  75. public static void addFlattenedItems(List list, String input) {
  76. LangUtil.throwIaxIfNull(list, "list");
  77. if (null != input) {
  78. String[] items = XMLWriter.unflattenList(input);
  79. if (!LangUtil.isEmpty(items)) {
  80. for (String item : items) {
  81. if (!LangUtil.isEmpty(item)) {
  82. list.add(item);
  83. }
  84. }
  85. }
  86. }
  87. }
  88. /**
  89. * Collapse list into a single comma-delimited value (e.g., for list buffer)
  90. * @param list List of items to print - null is silently ignored,
  91. * so for empty items use ""
  92. * @return item{,item}...
  93. * @throws IllegalArgumentException if {any item}.toString() contains a comma
  94. */
  95. public static String flattenList(List list) {
  96. if ((null == list) || (0 == list.size())) {
  97. return "";
  98. }
  99. return flattenList(list.toArray());
  100. }
  101. /**
  102. * Collapse list into a single comma-delimited value (e.g., for list buffer)
  103. * @param list the String[] items to print - null is silently ignored,
  104. * so for empty items use ""
  105. * @return item{,item}...
  106. * @throws IllegalArgumentException if list[i].toString() contains a comma
  107. */
  108. public static String flattenList(Object[] list) {
  109. StringBuffer sb = new StringBuffer();
  110. if (null != list) {
  111. boolean printed = false;
  112. for (Object o : list) {
  113. if (null != o) {
  114. if (printed) {
  115. sb.append(",");
  116. } else {
  117. printed = true;
  118. }
  119. String s = o.toString();
  120. if (s.contains(",")) {
  121. throw new IllegalArgumentException("comma in " + s);
  122. }
  123. sb.append(s);
  124. }
  125. }
  126. }
  127. return sb.toString();
  128. }
  129. /** output sink */
  130. PrintWriter out;
  131. /** stack of String element names */
  132. Stack stack = new Stack();
  133. /** false if doing attributes */
  134. boolean attributesDone = true;
  135. /** current element prefix */
  136. String indent = "";
  137. /** maximum width (in char) of indent and buffer when flowing */
  138. int maxWidth;
  139. /**
  140. * Current text being flowed.
  141. * length() is always less than maxWidth.
  142. */
  143. StringBuffer buffer;
  144. /** @param out PrintWriter to print to - not null */
  145. public XMLWriter(PrintWriter out) {
  146. LangUtil.throwIaxIfNull(out, "out");
  147. this.out = out;
  148. buffer = new StringBuffer();
  149. maxWidth = DEFAULT_WIDTH;
  150. }
  151. /**
  152. * Set maximum width (in chars) of buffer to accumulate.
  153. * @param maxWidth int 0..MAX_WIDTH for maximum number of char to accumulate
  154. */
  155. public void setMaxWidth(int maxWidth) {
  156. if (0 > maxWidth) {
  157. this.maxWidth = 0;
  158. } else if (MAX_WIDTH < maxWidth) {
  159. this.maxWidth = MAX_WIDTH;
  160. } else {
  161. this.maxWidth = maxWidth;
  162. }
  163. }
  164. /** shortcut for entire element */
  165. public void printElement(String name, String attributes) {
  166. if (!attributesDone) throw new IllegalStateException("finish attributes");
  167. if (0 != buffer.length()) { // output on subelement
  168. outPrintln(buffer + ">");
  169. buffer.setLength(0);
  170. }
  171. String oldIndent = indent;
  172. if (0 < stack.size()) {
  173. indent += TAB;
  174. ((StackElement) stack.peek()).numChildren++;
  175. }
  176. outPrintln(indent + "<" + name + " " + attributes + "/>");
  177. indent = oldIndent;
  178. }
  179. /**
  180. * Start element only
  181. * @param name the String label of the element
  182. * @param closeTag if true, delimit the end of the starting tag
  183. */
  184. public void startElement(String name, boolean closeTag) {
  185. startElement(name, "", closeTag);
  186. }
  187. /**
  188. * Start element with buffer on the same line.
  189. * This does not flow buffer.
  190. * @param name String tag for the element
  191. * @param attr {name="value"}.. where value
  192. * is a product of attributeValue(String)
  193. */
  194. public void startElement(String name, String attr, boolean closeTag) {
  195. if (!attributesDone) throw new IllegalStateException("finish attributes");
  196. LangUtil.throwIaxIfFalse(!LangUtil.isEmpty(name), "empty name");
  197. if (0 != buffer.length()) { // output on subelement
  198. outPrintln(buffer + ">");
  199. buffer.setLength(0);
  200. }
  201. if (0 < stack.size()) {
  202. indent += TAB;
  203. }
  204. StringBuffer sb = new StringBuffer();
  205. sb.append(indent);
  206. sb.append("<");
  207. sb.append(name);
  208. if (!LangUtil.isEmpty(attr)) {
  209. sb.append(" ");
  210. sb.append(attr.trim());
  211. }
  212. attributesDone = closeTag;
  213. if (closeTag) {
  214. sb.append(">");
  215. outPrintln(sb.toString());
  216. } else if (maxWidth <= sb.length()) {
  217. outPrintln(sb.toString());
  218. } else {
  219. if (0 != this.buffer.length()) {
  220. throw new IllegalStateException("expected empty attributes starting " + name);
  221. }
  222. this.buffer.append(sb.toString());
  223. }
  224. if (0 < stack.size()) {
  225. ((StackElement) stack.peek()).numChildren++;
  226. }
  227. stack.push(new StackElement(name));
  228. }
  229. /**
  230. * @param name should be the same as that given to start the element
  231. * @throws IllegalStateException if start element does not match
  232. */
  233. public void endElement(String name) {
  234. // int level = stack.size();
  235. String err = null;
  236. StackElement element = null;
  237. if (0 == stack.size()) {
  238. err = "empty stack";
  239. } else {
  240. element = (StackElement) stack.pop();
  241. if (!element.name.equals(name)) {
  242. err = "expecting element " + element.name;
  243. }
  244. }
  245. if (null != err) {
  246. err = "endElement(" + name + ") " + stack + ": " + err;
  247. throw new IllegalStateException(err);
  248. }
  249. if (0 < element.numChildren) {
  250. outPrintln(indent + "</" + name + ">");
  251. } else if (0 < buffer.length()) {
  252. outPrintln(buffer + "/>");
  253. buffer.setLength(0);
  254. } else {
  255. outPrintln(indent + "/>");
  256. }
  257. if (!attributesDone) {
  258. attributesDone = true;
  259. }
  260. if (0 < stack.size()) {
  261. indent = indent.substring(0, indent.length() - TAB.length());
  262. }
  263. }
  264. /**
  265. * Print name=value if neither is null and name is not empty after trimming,
  266. * accumulating these until they are greater than maxWidth or buffer are
  267. * terminated with endAttributes(..) or endElement(..).
  268. * @param value the String to convert as attribute value - ignored if null
  269. * @param name the String to use as the attribute name
  270. * @throws IllegalArgumentException if name is null or empty after trimming
  271. */
  272. public void printAttribute(String name, String value) {
  273. if (attributesDone) throw new IllegalStateException("not in attributes");
  274. if (null == value) {
  275. return;
  276. }
  277. if ((null == name) || (0 == name.trim().length())) {
  278. throw new IllegalArgumentException("no name=" + name + "=" + value);
  279. }
  280. String newAttr = name + "=\"" + attributeValue(value) + "\"";
  281. int indentLen = indent.length();
  282. int bufferLen = buffer.length();
  283. int newAttrLen = (0 == bufferLen ? indentLen : 0) + newAttr.length();
  284. if (maxWidth > (bufferLen + newAttrLen)) {
  285. buffer.append(" ");
  286. buffer.append(newAttr);
  287. } else { // at least print old attributes; maybe also new
  288. if (0 < bufferLen) {
  289. outPrintln(buffer.toString());
  290. buffer.setLength(0);
  291. }
  292. buffer.append(indent + SP + newAttr);
  293. }
  294. }
  295. public void endAttributes() {
  296. if (attributesDone) throw new IllegalStateException("not in attributes");
  297. attributesDone = true;
  298. }
  299. public void printComment(String comment) {
  300. if (!attributesDone) throw new IllegalStateException("in attributes");
  301. outPrintln(indent + "<!-- " + comment + "-->");
  302. }
  303. public void close() {
  304. if (null != out) {
  305. out.close();
  306. }
  307. }
  308. public void println(String string) {
  309. outPrintln(string);
  310. }
  311. private void outPrintln(String s) {
  312. if (null == out) {
  313. throw new IllegalStateException("used after close");
  314. }
  315. out.println(s);
  316. }
  317. static class StackElement {
  318. String name;
  319. int numChildren;
  320. public StackElement(String name) {
  321. this.name = name;
  322. }
  323. }
  324. }