PageRenderTime 174ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/plugins/XSLT/tags/xslt-0_4/xslt/IndentingTransformer.java

#
Java | 418 lines | 295 code | 92 blank | 31 comment | 65 complexity | bd9f653d10fff73c55b78d7979df8572 MD5 | raw file
Possible License(s): BSD-3-Clause, AGPL-1.0, Apache-2.0, LGPL-2.0, LGPL-3.0, GPL-2.0, CC-BY-SA-3.0, LGPL-2.1, GPL-3.0, MPL-2.0-no-copyleft-exception, IPL-1.0
  1. /*
  2. * AbstractIndentingTransformer.java - Indents XML elements, by adding whitespace where appropriate.
  3. *
  4. * Copyright (c) 2002 Robert McKinnon
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU General Public License
  8. * as published by the Free Software Foundation; either version 2
  9. * of the License, or any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program; if not, write to the Free Software
  18. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  19. *
  20. * email: robmckinnon@users.sourceforge.net
  21. */
  22. package xslt;
  23. import org.xml.sax.Attributes;
  24. import org.xml.sax.SAXException;
  25. import org.xml.sax.ext.DeclHandler;
  26. import org.xml.sax.helpers.AttributesImpl;
  27. import javax.xml.transform.sax.TransformerHandler;
  28. import java.io.IOException;
  29. import java.io.Writer;
  30. /**
  31. * Indents elements, by adding whitespace where appropriate.
  32. * Does not remove blank lines between nodes.
  33. * Does not remove new lines within text nodes.
  34. * Puts element tags immediately following mixed content text on the same line as the text.
  35. *
  36. * @author Robert McKinnon - robmckinnon@users.sourceforge.net
  37. */
  38. public abstract class IndentingTransformer implements TransformerHandler, DeclHandler {
  39. /** buffer to hold character data */
  40. private Writer writer;
  41. private String xml;
  42. private char[] chars;
  43. private boolean isContinue = true;
  44. private boolean isClosingTag = false;
  45. private boolean isEmptyElement = false;
  46. private boolean isDocType = false;
  47. public void characters(char ch[], int start, int length) throws SAXException {
  48. try {
  49. if(isDocType) {
  50. isDocType = false;
  51. } else {
  52. writer.write(ch, start, length);
  53. }
  54. } catch(IOException e) {
  55. throw new SAXException(e);
  56. }
  57. }
  58. public void comment(char ch[], int start, int length) throws SAXException {
  59. try {
  60. int priorSpaceIndex = start - 5;
  61. while(Character.isSpaceChar(ch[priorSpaceIndex]) || ch[priorSpaceIndex] == '\t') {
  62. if(ch[priorSpaceIndex] == '\t') {
  63. writer.write('\t');
  64. } else {
  65. writer.write(' ');
  66. }
  67. priorSpaceIndex--;
  68. }
  69. writer.write("<!--");
  70. writer.write(ch, start, length);
  71. writer.write("-->");
  72. } catch(IOException e) {
  73. throw new SAXException(e);
  74. }
  75. }
  76. public void processingInstruction(String target, String data) throws SAXException {
  77. try {
  78. writer.write("<?");
  79. writer.write(target);
  80. writer.write(" ");
  81. writer.write(data);
  82. writer.write("?>");
  83. } catch(IOException e) {
  84. throw new SAXException(e);
  85. }
  86. }
  87. public void endElement(String namespaceURI, String localName, String qName) throws SAXException {
  88. try {
  89. if(isClosingTag) {
  90. writer.write("</");
  91. writer.write(qName);
  92. writer.write(">");
  93. isClosingTag = false;
  94. }
  95. } catch(IOException e) {
  96. throw new SAXException(e);
  97. }
  98. }
  99. public void startElement(String namespaceURI, String localName, String qName, Attributes atts)
  100. throws SAXException {
  101. try {
  102. writer.write("<");
  103. writer.write(qName);
  104. for(int i = 0; i < atts.getLength(); i++) {
  105. writer.write(' ');
  106. writer.write(atts.getQName(i));
  107. writer.write("=\"");
  108. writer.write(atts.getValue(i));
  109. writer.write('\"');
  110. }
  111. if(isEmptyElement) {
  112. writer.write("/>");
  113. } else {
  114. writer.write(">");
  115. }
  116. } catch(IOException e) {
  117. throw new SAXException(e);
  118. }
  119. }
  120. protected String indentXml(final String xmlString, final Writer outputWriter) throws IOException, SAXException {
  121. this.writer = outputWriter;
  122. this.xml = xmlString;
  123. this.chars = xml.toCharArray();
  124. int start = 0;
  125. int end = 0;
  126. boolean isComment = false;
  127. while(isContinue) {
  128. end = xml.indexOf('<', start);
  129. writeTextPrecedingLessThan(start, end);
  130. if(isContinue) {
  131. start = end;
  132. if(xml.startsWith("<!--", start)) {
  133. end = writeComment(start);
  134. } else if(xml.startsWith("<?", start)) {
  135. end = writeXmlDeclarationOrProcessingInstruction(start);
  136. } else if(xml.startsWith("<!", start)) {
  137. if(xml.startsWith("<![CDATA[", start)) {
  138. end = writeCData(start);
  139. } else {
  140. end = writeDocType(start);
  141. }
  142. } else if(xml.startsWith("</", start)) {
  143. end = writeClosingTag(start);
  144. } else {
  145. end = writeElement(start);
  146. }
  147. start = end;
  148. }
  149. }
  150. return outputWriter.toString();
  151. }
  152. private int writeCData(int start) throws IOException, SAXException {
  153. int end = xml.indexOf("]]>", start);
  154. writeRemaining(start, end);
  155. if(isContinue) {
  156. startCDATA();
  157. end = end + 3;
  158. writer.write(xml, start, end - start);
  159. endCDATA();
  160. }
  161. return end;
  162. }
  163. private int writeElement(int start) throws IOException, SAXException {
  164. int end = xml.indexOf('>', start);
  165. writeRemaining(start, end);
  166. if(isContinue) {
  167. isEmptyElement = (chars[end - 1] == '/');
  168. if(isEmptyElement) {
  169. end--;
  170. }
  171. AttributesImpl attributes = new AttributesImpl();
  172. String elementName = getElementNameAndPopulateAttributes(start, end, attributes);
  173. startElement("", "", elementName, attributes);
  174. if(isEmptyElement) {
  175. endElement("", "", elementName);
  176. end = end + 2;
  177. isEmptyElement = false;
  178. } else {
  179. end = end + 1;
  180. }
  181. }
  182. return end;
  183. }
  184. private String getElementNameAndPopulateAttributes(int start, int end, AttributesImpl attributes) throws SAXException {
  185. int nameEnd = xml.indexOf(' ', start);
  186. if(nameEnd == -1 || end < nameEnd) {
  187. nameEnd = end;
  188. } else if(nameEnd + 1 != end) {
  189. char[] chars = xml.substring(nameEnd + 1, end).toCharArray();
  190. populateAttributes(chars, attributes);
  191. }
  192. String elementName = xml.substring(start + 1, nameEnd);
  193. return elementName;
  194. }
  195. private void populateAttributes(char[] chars, AttributesImpl attributes) throws SAXException {
  196. StringBuffer qName = new StringBuffer();
  197. StringBuffer value = new StringBuffer();
  198. boolean isLastSpace = false;
  199. char quote = '\"';
  200. int i = 0;
  201. while(i < chars.length) {
  202. qName.setLength(0);
  203. value.setLength(0);
  204. while(i < chars.length && Character.isWhitespace(chars[i])) {
  205. i++;
  206. }
  207. if(i < chars.length) {
  208. while(i < chars.length && chars[i] != '=') {
  209. qName.append(chars[i]);
  210. i++;
  211. }
  212. i++; // get past equals
  213. while(i < chars.length && Character.isWhitespace(chars[i])) {
  214. i++;
  215. }
  216. if(i < chars.length) {
  217. if(chars[i] != '\"' && chars[i] != '\'') {
  218. throw new SAXException("value for attribute " + qName.toString().trim() + " must be in quotes");
  219. } else {
  220. quote = chars[i];
  221. i++;
  222. }
  223. }
  224. while(i < chars.length && chars[i] != quote) {
  225. if(Character.isWhitespace(chars[i])) {
  226. if(!isLastSpace) {
  227. value.append(' ');
  228. isLastSpace = true;
  229. } else {
  230. // don't add consequtive space
  231. }
  232. } else {
  233. value.append(chars[i]);
  234. isLastSpace = false;
  235. }
  236. i++;
  237. }
  238. i++; // get past quote
  239. attributes.addAttribute("", "", qName.toString().trim(), "", value.toString().trim());
  240. }
  241. }
  242. }
  243. private int writeClosingTag(int start) throws IOException, SAXException {
  244. int end = xml.indexOf('>', start);
  245. writeRemaining(start, end);
  246. if(isContinue) {
  247. isClosingTag = true;
  248. endElement("", "", xml.substring(start + 2, end).trim());
  249. end = end + 1;
  250. }
  251. return end;
  252. }
  253. private int writeDocType(int start) throws IOException {
  254. int end = xml.indexOf('>', start);
  255. int bracketStart = xml.indexOf('[', start);
  256. if(bracketStart != -1 && bracketStart < end) {
  257. int bracketEnd = xml.indexOf(']', bracketStart);
  258. end = xml.indexOf('>', bracketEnd);
  259. }
  260. writeRemaining(start, end);
  261. if(isContinue) {
  262. end = end + 1;
  263. writer.write("\n");
  264. int length = end - start;
  265. writer.write(xml, start, length);
  266. isDocType = true;
  267. }
  268. return end;
  269. }
  270. private int writeXmlDeclarationOrProcessingInstruction(int start) throws IOException, SAXException {
  271. int end;
  272. if(xml.startsWith("<?xml ", start)) {
  273. end = writeXmlDeclaration(start);
  274. } else {
  275. end = writeProcessingInstruction(start);
  276. }
  277. return end;
  278. }
  279. private int writeProcessingInstruction(int start) throws IOException, SAXException {
  280. int end = xml.indexOf("?>", start);
  281. writeRemaining(start, end);
  282. if(isContinue) {
  283. int targetEnd = xml.indexOf(" ", start);
  284. String target = xml.substring(start + "<?".length(), targetEnd);
  285. String data = xml.substring(targetEnd + 1, end);
  286. processingInstruction(target, data);
  287. end = end + "?>".length();
  288. }
  289. return end;
  290. }
  291. private int writeXmlDeclaration(int start) throws IOException {
  292. int end = xml.indexOf("?>", start);
  293. writeRemaining(start, end);
  294. if(isContinue) {
  295. end = end + "?>".length();
  296. int length = end - start;
  297. writer.write(xml, start, length);
  298. }
  299. return end;
  300. }
  301. private int writeComment(int start) throws IOException, SAXException {
  302. int end = xml.indexOf("-->", start);
  303. writeRemaining(start, end);
  304. if(isContinue) {
  305. int commentTextStart = start + "<!--".length();
  306. int commentTextLength = end - commentTextStart;
  307. comment(chars, commentTextStart, commentTextLength);
  308. end = end + "-->".length();
  309. }
  310. return end;
  311. }
  312. private void writeTextPrecedingLessThan(int start, int end) throws IOException, SAXException {
  313. writeRemaining(start, end);
  314. if(isContinue && end > start) {
  315. int length = end - start;
  316. characters(chars, start, length);
  317. }
  318. }
  319. private void writeRemaining(int start, int end) throws IOException {
  320. if(end == -1) {
  321. int length = xml.length() - start;
  322. writer.write(xml, start, length);
  323. isContinue = false;
  324. }
  325. }
  326. }