KXmlSerializer.java 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550
  1. /* Copyright (c) 2002,2003, Stefan Haustein, Oberhausen, Rhld., Germany
  2. *
  3. * Permission is hereby granted, free of charge, to any person obtaining a copy
  4. * of this software and associated documentation files (the "Software"), to deal
  5. * in the Software without restriction, including without limitation the rights
  6. * to use, copy, modify, merge, publish, distribute, sublicense, and/or
  7. * sell copies of the Software, and to permit persons to whom the Software is
  8. * furnished to do so, subject to the following conditions:
  9. *
  10. * The above copyright notice and this permission notice shall be included in
  11. * all copies or substantial portions of the Software.
  12. *
  13. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  14. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  15. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  16. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  17. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  18. * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  19. * IN THE SOFTWARE. */
  20. package org.kxml2.io;
  21. import java.io.*;
  22. import org.xmlpull.v1.*;
  23. public class KXmlSerializer implements IXmlSerializer {
  24. // static final String UNDEFINED = ":";
  25. private Writer writer;
  26. private boolean pending;
  27. private int auto;
  28. private int depth;
  29. private String[] elementStack = new String[12];
  30. //nsp/prefix/name
  31. private int[] nspCounts = new int[4];
  32. private String[] nspStack = new String[8];
  33. //prefix/nsp; both empty are ""
  34. private boolean[] indent = new boolean[4];
  35. private boolean unicode;
  36. private String encoding;
  37. private final void check(boolean close) throws IOException {
  38. if (!pending)
  39. return;
  40. depth++;
  41. pending = false;
  42. if (indent.length <= depth) {
  43. boolean[] hlp = new boolean[depth + 4];
  44. System.arraycopy(indent, 0, hlp, 0, depth);
  45. indent = hlp;
  46. }
  47. indent[depth] = indent[depth - 1];
  48. for (int i = nspCounts[depth - 1];
  49. i < nspCounts[depth];
  50. i++) {
  51. writer.write(' ');
  52. writer.write("xmlns");
  53. if (!"".equals(nspStack[i * 2])) {
  54. writer.write(':');
  55. writer.write(nspStack[i * 2]);
  56. }
  57. else if ("".equals(getNamespace()) && !"".equals(nspStack[i * 2 + 1]))
  58. throw new IllegalStateException("Cannot set default namespace for elements in no namespace");
  59. writer.write("=\"");
  60. writeEscaped(nspStack[i * 2 + 1], '"');
  61. writer.write('"');
  62. }
  63. if (nspCounts.length <= depth + 1) {
  64. int[] hlp = new int[depth + 8];
  65. System.arraycopy(nspCounts, 0, hlp, 0, depth + 1);
  66. nspCounts = hlp;
  67. }
  68. nspCounts[depth + 1] = nspCounts[depth];
  69. // nspCounts[depth + 2] = nspCounts[depth];
  70. writer.write(close ? " />" : ">");
  71. }
  72. private final void writeEscaped(String s, int quot)
  73. throws IOException {
  74. for (int i = 0; i < s.length(); i++) {
  75. char c = s.charAt(i);
  76. switch (c) {
  77. case '\n':
  78. case '\r':
  79. case '\t':
  80. if(quot == -1)
  81. writer.write(c);
  82. else
  83. writer.write("&#"+((int) c)+';');
  84. break;
  85. case '&' :
  86. writer.write("&amp;");
  87. break;
  88. case '>' :
  89. writer.write("&gt;");
  90. break;
  91. case '<' :
  92. writer.write("&lt;");
  93. break;
  94. case '"' :
  95. case '\'' :
  96. if (c == quot) {
  97. writer.write(
  98. c == '"' ? "&quot;" : "&apos;");
  99. break;
  100. }
  101. default :
  102. //if(c < ' ')
  103. // throw new IllegalArgumentException("Illegal control code:"+((int) c));
  104. // MH why is @ serialized as &# ???
  105. //if (c >= ' ' && c !='@' && (c < 127 || unicode))
  106. if (c >= ' ' && (c < 127 || unicode))
  107. writer.write(c);
  108. else
  109. writer.write("&#" + ((int) c) + ";");
  110. }
  111. }
  112. }
  113. /*
  114. private final void writeIndent() throws IOException {
  115. writer.write("\r\n");
  116. for (int i = 0; i < depth; i++)
  117. writer.write(' ');
  118. }*/
  119. public void docdecl(String dd) throws IOException {
  120. writer.write("<!DOCTYPE");
  121. writer.write(dd);
  122. writer.write(">");
  123. }
  124. public void endDocument() throws IOException {
  125. while (depth > 0) {
  126. endTag(
  127. elementStack[depth * 3 - 3],
  128. elementStack[depth * 3 - 1]);
  129. }
  130. flush();
  131. }
  132. public void entityRef(String name) throws IOException {
  133. check(false);
  134. writer.write('&');
  135. writer.write(name);
  136. writer.write(';');
  137. }
  138. public boolean getFeature(String name) {
  139. //return false;
  140. return (
  141. "http://xmlpull.org/v1/doc/features.html#indent-output"
  142. .equals(
  143. name))
  144. ? indent[depth]
  145. : false;
  146. }
  147. public String getPrefix(String namespace, boolean create) {
  148. try {
  149. return getPrefix(namespace, false, create);
  150. }
  151. catch (IOException e) {
  152. throw new RuntimeException(e.toString());
  153. }
  154. }
  155. private final String getPrefix(
  156. String namespace,
  157. boolean includeDefault,
  158. boolean create)
  159. throws IOException {
  160. for (int i = nspCounts[depth + 1] * 2 - 2;
  161. i >= 0;
  162. i -= 2) {
  163. if (nspStack[i + 1].equals(namespace)
  164. && (includeDefault || !nspStack[i].equals(""))) {
  165. String cand = nspStack[i];
  166. for (int j = i + 2;
  167. j < nspCounts[depth + 1] * 2;
  168. j++) {
  169. if (nspStack[j].equals(cand)) {
  170. cand = null;
  171. break;
  172. }
  173. }
  174. if (cand != null)
  175. return cand;
  176. }
  177. }
  178. if (!create)
  179. return null;
  180. String prefix;
  181. if ("".equals(namespace))
  182. prefix = "";
  183. else {
  184. do {
  185. prefix = "n" + (auto++);
  186. for (int i = nspCounts[depth + 1] * 2 - 2;
  187. i >= 0;
  188. i -= 2) {
  189. if (prefix.equals(nspStack[i])) {
  190. prefix = null;
  191. break;
  192. }
  193. }
  194. }
  195. while (prefix == null);
  196. }
  197. boolean p = pending;
  198. pending = false;
  199. setPrefix(prefix, namespace);
  200. pending = p;
  201. return prefix;
  202. }
  203. public Object getProperty(String name) {
  204. throw new RuntimeException("Unsupported property");
  205. }
  206. public void ignorableWhitespace(String s)
  207. throws IOException {
  208. text(s);
  209. }
  210. public void setFeature(String name, boolean value) {
  211. if ("http://xmlpull.org/v1/doc/features.html#indent-output"
  212. .equals(name)) {
  213. indent[depth] = value;
  214. }
  215. else
  216. throw new RuntimeException("Unsupported Feature");
  217. }
  218. public void setProperty(String name, Object value) {
  219. throw new RuntimeException(
  220. "Unsupported Property:" + value);
  221. }
  222. public void setPrefix(String prefix, String namespace)
  223. throws IOException {
  224. check(false);
  225. if (prefix == null)
  226. prefix = "";
  227. if (namespace == null)
  228. namespace = "";
  229. String defined = getPrefix(namespace, true, false);
  230. // boil out if already defined
  231. if (prefix.equals(defined))
  232. return;
  233. int pos = (nspCounts[depth + 1]++) << 1;
  234. if (nspStack.length < pos + 1) {
  235. String[] hlp = new String[nspStack.length + 16];
  236. System.arraycopy(nspStack, 0, hlp, 0, pos);
  237. nspStack = hlp;
  238. }
  239. nspStack[pos++] = prefix;
  240. nspStack[pos] = namespace;
  241. }
  242. public void setOutput(Writer writer) {
  243. this.writer = writer;
  244. // elementStack = new String[12]; //nsp/prefix/name
  245. //nspCounts = new int[4];
  246. //nspStack = new String[8]; //prefix/nsp
  247. //indent = new boolean[4];
  248. nspCounts[0] = 2;
  249. nspCounts[1] = 2;
  250. nspStack[0] = "";
  251. nspStack[1] = "";
  252. nspStack[2] = "xml";
  253. nspStack[3] = "http://www.w3.org/XML/1998/namespace";
  254. pending = false;
  255. auto = 0;
  256. depth = 0;
  257. unicode = false;
  258. }
  259. public void setOutput(OutputStream os, String encoding)
  260. throws IOException {
  261. if (os == null)
  262. throw new IllegalArgumentException("kxml: os=null");
  263. setOutput(
  264. encoding == null
  265. ? new OutputStreamWriter(os)
  266. : new OutputStreamWriter(os, encoding));
  267. this.encoding = encoding;
  268. if (encoding != null
  269. && encoding.toLowerCase().startsWith("utf"))
  270. unicode = true;
  271. }
  272. public void startDocument(
  273. String encoding,
  274. Boolean standalone)
  275. throws IOException {
  276. writer.write("<?xml version='1.0' ");
  277. if (encoding != null) {
  278. this.encoding = encoding;
  279. if (encoding.toLowerCase().startsWith("utf"))
  280. unicode = true;
  281. }
  282. if (this.encoding != null) {
  283. writer.write("encoding='");
  284. writer.write(this.encoding);
  285. writer.write("' ");
  286. }
  287. if (standalone != null) {
  288. writer.write("standalone='");
  289. writer.write(
  290. standalone.booleanValue() ? "yes" : "no");
  291. writer.write("' ");
  292. }
  293. writer.write("?>");
  294. }
  295. public IXmlSerializer startTag(String namespace, String name)
  296. throws IOException {
  297. check(false);
  298. // if (namespace == null)
  299. // namespace = "";
  300. if (indent[depth]) {
  301. writer.write("\r\n");
  302. for (int i = 0; i < depth; i++)
  303. writer.write(" ");
  304. }
  305. int esp = depth * 3;
  306. if (elementStack.length < esp + 3) {
  307. String[] hlp = new String[elementStack.length + 12];
  308. System.arraycopy(elementStack, 0, hlp, 0, esp);
  309. elementStack = hlp;
  310. }
  311. String prefix =
  312. namespace == null
  313. ? ""
  314. : getPrefix(namespace, true, true);
  315. if ("".equals(namespace)) {
  316. for (int i = nspCounts[depth];
  317. i < nspCounts[depth + 1];
  318. i++) {
  319. if ("".equals(nspStack[i * 2]) && !"".equals(nspStack[i * 2 + 1])) {
  320. throw new IllegalStateException("Cannot set default namespace for elements in no namespace");
  321. }
  322. }
  323. }
  324. elementStack[esp++] = namespace;
  325. elementStack[esp++] = prefix;
  326. elementStack[esp] = name;
  327. writer.write('<');
  328. if (!"".equals(prefix)) {
  329. writer.write(prefix);
  330. writer.write(':');
  331. }
  332. writer.write(name);
  333. pending = true;
  334. return this;
  335. }
  336. public IXmlSerializer attribute(
  337. String namespace,
  338. String name,
  339. String value)
  340. throws IOException {
  341. if (!pending)
  342. throw new IllegalStateException("illegal position for attribute");
  343. // int cnt = nspCounts[depth];
  344. if (namespace == null)
  345. namespace = "";
  346. // depth--;
  347. // pending = false;
  348. String prefix =
  349. "".equals(namespace)
  350. ? ""
  351. : getPrefix(namespace, false, true);
  352. // pending = true;
  353. // depth++;
  354. /* if (cnt != nspCounts[depth]) {
  355. writer.write(' ');
  356. writer.write("xmlns");
  357. if (nspStack[cnt * 2] != null) {
  358. writer.write(':');
  359. writer.write(nspStack[cnt * 2]);
  360. }
  361. writer.write("=\"");
  362. writeEscaped(nspStack[cnt * 2 + 1], '"');
  363. writer.write('"');
  364. }
  365. */
  366. writer.write(' ');
  367. if (!"".equals(prefix)) {
  368. writer.write(prefix);
  369. writer.write(':');
  370. }
  371. writer.write(name);
  372. writer.write('=');
  373. char q = value.indexOf('"') == -1 ? '"' : '\'';
  374. writer.write(q);
  375. writeEscaped(value, q);
  376. writer.write(q);
  377. return this;
  378. }
  379. public void flush() throws IOException {
  380. check(false);
  381. writer.flush();
  382. }
  383. /*
  384. public void close() throws IOException {
  385. check();
  386. writer.close();
  387. }
  388. */
  389. public IXmlSerializer endTag(String namespace, String name)
  390. throws IOException {
  391. if (!pending)
  392. depth--;
  393. // if (namespace == null)
  394. // namespace = "";
  395. if ((namespace == null
  396. && elementStack[depth * 3] != null)
  397. || (namespace != null
  398. && !namespace.equals(elementStack[depth * 3]))
  399. || !elementStack[depth * 3 + 2].equals(name))
  400. throw new IllegalArgumentException("</{"+namespace+"}"+name+"> does not match start");
  401. if (pending) {
  402. check(true);
  403. depth--;
  404. }
  405. else {
  406. if (indent[depth + 1]) {
  407. writer.write("\r\n");
  408. for (int i = 0; i < depth; i++)
  409. writer.write(" ");
  410. }
  411. writer.write("</");
  412. String prefix = elementStack[depth * 3 + 1];
  413. if (!"".equals(prefix)) {
  414. writer.write(prefix);
  415. writer.write(':');
  416. }
  417. writer.write(name);
  418. writer.write('>');
  419. }
  420. nspCounts[depth + 1] = nspCounts[depth];
  421. return this;
  422. }
  423. public String getNamespace() {
  424. return getDepth() == 0 ? null : elementStack[getDepth() * 3 - 3];
  425. }
  426. public String getName() {
  427. return getDepth() == 0 ? null : elementStack[getDepth() * 3 - 1];
  428. }
  429. public int getDepth() {
  430. return pending ? depth + 1 : depth;
  431. }
  432. public IXmlSerializer text(String text) throws IOException {
  433. check(false);
  434. indent[depth] = false;
  435. writeEscaped(text, -1);
  436. return this;
  437. }
  438. public IXmlSerializer text(char[] text, int start, int len)
  439. throws IOException {
  440. text(new String(text, start, len));
  441. return this;
  442. }
  443. public void cdsect(String data) throws IOException {
  444. check(false);
  445. writer.write("<![CDATA[");
  446. writer.write(data);
  447. writer.write("]]>");
  448. }
  449. public void comment(String comment) throws IOException {
  450. check(false);
  451. writer.write("<!--");
  452. writer.write(comment);
  453. writer.write("-->");
  454. }
  455. public void processingInstruction(String pi)
  456. throws IOException {
  457. check(false);
  458. writer.write("<?");
  459. writer.write(pi);
  460. writer.write("?>");
  461. }
  462. }