Mercurial > dive4elements > framework
comparison artifacts-common/src/main/java/org/dive4elements/artifacts/common/utils/XMLUtils.java @ 472:783cc1b6b615
Moved directories to org.dive4elements
author | Sascha L. Teichmann <teichmann@intevation.de> |
---|---|
date | Thu, 25 Apr 2013 10:53:15 +0200 |
parents | artifacts-common/src/main/java/de/intevation/artifacts/common/utils/XMLUtils.java@cdc8b7c64856 |
children | 415df0fc4fa1 |
comparison
equal
deleted
inserted
replaced
471:1a87cb24a446 | 472:783cc1b6b615 |
---|---|
1 /* | |
2 * Copyright (c) 2010 by Intevation GmbH | |
3 * | |
4 * This program is free software under the LGPL (>=v2.1) | |
5 * Read the file LGPL.txt coming with the software for details | |
6 * or visit http://www.gnu.org/licenses/ if it does not exist. | |
7 */ | |
8 | |
9 package de.intevation.artifacts.common.utils; | |
10 | |
11 import java.util.List; | |
12 import java.util.ArrayList; | |
13 import java.util.Map; | |
14 import java.util.LinkedHashMap; | |
15 import java.util.zip.GZIPInputStream; | |
16 import java.util.zip.GZIPOutputStream; | |
17 | |
18 import java.io.ByteArrayInputStream; | |
19 import java.io.FileInputStream; | |
20 import java.io.BufferedInputStream; | |
21 import java.io.ByteArrayOutputStream; | |
22 import java.io.File; | |
23 import java.io.IOException; | |
24 import java.io.InputStream; | |
25 import java.io.OutputStream; | |
26 import java.io.StringWriter; | |
27 | |
28 import javax.xml.namespace.NamespaceContext; | |
29 import javax.xml.namespace.QName; | |
30 | |
31 import javax.xml.parsers.DocumentBuilderFactory; | |
32 import javax.xml.parsers.ParserConfigurationException; | |
33 | |
34 import javax.xml.transform.Transformer; | |
35 import javax.xml.transform.TransformerConfigurationException; | |
36 import javax.xml.transform.TransformerException; | |
37 import javax.xml.transform.TransformerFactory; | |
38 import javax.xml.transform.TransformerFactoryConfigurationError; | |
39 | |
40 import javax.xml.transform.dom.DOMSource; | |
41 | |
42 import javax.xml.transform.stream.StreamResult; | |
43 | |
44 import javax.xml.xpath.XPath; | |
45 import javax.xml.xpath.XPathConstants; | |
46 import javax.xml.xpath.XPathExpressionException; | |
47 import javax.xml.xpath.XPathFactory; | |
48 import javax.xml.xpath.XPathVariableResolver; | |
49 | |
50 import org.apache.log4j.Logger; | |
51 | |
52 import org.w3c.dom.Attr; | |
53 import org.w3c.dom.Document; | |
54 import org.w3c.dom.Element; | |
55 import org.w3c.dom.Node; | |
56 | |
57 import org.xml.sax.SAXException; | |
58 | |
59 /** | |
60 * Some helper functions to ease work with XML concering namespaces, XPATH | |
61 * and so on. | |
62 * @author <a href="mailto:sascha.teichmann@intevation.de">Sascha L. Teichmann</a> | |
63 */ | |
64 public final class XMLUtils | |
65 { | |
66 /** W3C URL of XForms. */ | |
67 public static final String XFORM_URL = "http://www.w3.org/2002/xforms"; | |
68 | |
69 /** W3C prefix of XForms. */ | |
70 public static final String XFORM_PREFIX = "xform"; | |
71 | |
72 /** Logger for this class. */ | |
73 private static Logger logger = Logger.getLogger(XMLUtils.class); | |
74 | |
75 private XMLUtils() { | |
76 } | |
77 | |
78 /** | |
79 * Helper class to generate elements and attributes with | |
80 * namespaces. | |
81 */ | |
82 public static class ElementCreator | |
83 { | |
84 /** Owner document of the elements to be created. */ | |
85 protected Document document; | |
86 | |
87 /** Namespace to be used. */ | |
88 protected String ns; | |
89 | |
90 /** Prefix to be used. */ | |
91 protected String prefix; | |
92 | |
93 /** | |
94 * Constructor to create an element/attribute creator | |
95 * with a given namespace and namespace prefix using a | |
96 * given owner document. | |
97 * @param document The owning document | |
98 * @param ns The namespace | |
99 * @param prefix The namespace prefix | |
100 */ | |
101 public ElementCreator(Document document, String ns, String prefix) { | |
102 this.document = document; | |
103 this.ns = ns; | |
104 this.prefix = prefix; | |
105 } | |
106 | |
107 /** | |
108 * Creates a new element using the owning document with | |
109 * the this creators namespace and namespace prefix. | |
110 * @param name The name of the element | |
111 * @return The new element | |
112 */ | |
113 public Element create(String name) { | |
114 Element element = document.createElementNS(ns, name); | |
115 element.setPrefix(prefix); | |
116 return element; | |
117 } | |
118 | |
119 /** | |
120 * Adds a new attribute and its value to a given element. | |
121 * It does not set the namespace prefix. | |
122 * @param element The element to add the attribute to | |
123 * @param name The name of the attribute | |
124 * @param value The value of the attribute | |
125 */ | |
126 public void addAttr(Element element, String name, String value) { | |
127 addAttr(element, name, value, false); | |
128 } | |
129 | |
130 /** | |
131 * Adds a new attribute and its value to a given element. | |
132 * If the namespace prefix is used is decided by the 'addPrefix' flag. | |
133 * @param element The element to add the attribute to | |
134 * @param name The name of the attribute | |
135 * @param value The value of the attribute | |
136 * @param addPrefix If true the creators namespace prefix is | |
137 * set on the attribute. | |
138 */ | |
139 public void addAttr( | |
140 Element element, | |
141 String name, | |
142 String value, | |
143 boolean addPrefix | |
144 ) { | |
145 if (addPrefix) { | |
146 Attr attr = document.createAttributeNS(ns, name); | |
147 attr.setValue(value); | |
148 attr.setPrefix(prefix); | |
149 | |
150 element.setAttributeNode(attr); | |
151 } | |
152 else { | |
153 element.setAttribute(name, value); | |
154 } | |
155 } | |
156 } // class ElementCreator | |
157 | |
158 /** | |
159 * Creates a new XML document | |
160 * @return the new XML document ot null if something went wrong during | |
161 * creation. | |
162 */ | |
163 public static final Document newDocument() { | |
164 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); | |
165 factory.setNamespaceAware(true); | |
166 | |
167 try { | |
168 return factory.newDocumentBuilder().newDocument(); | |
169 } | |
170 catch (ParserConfigurationException pce) { | |
171 logger.error(pce.getLocalizedMessage(), pce); | |
172 } | |
173 return null; | |
174 } | |
175 | |
176 | |
177 /** | |
178 * Create xml/string representation of element (nested in otherwise empty | |
179 * document). | |
180 * @param element element to inspect in string. | |
181 * @return string with xml representation of element. | |
182 */ | |
183 public final static String toString(Node node) { | |
184 Document doc = newDocument(); | |
185 doc.appendChild(doc.importNode(node,true)); | |
186 return toString(doc); | |
187 } | |
188 | |
189 | |
190 /** | |
191 * Loads a XML document namespace aware from a file | |
192 * @param file The file to load. | |
193 * @return the XML document or null if something went wrong | |
194 * during loading. | |
195 */ | |
196 public static final Document parseDocument(File file) { | |
197 InputStream inputStream = null; | |
198 try { | |
199 inputStream = new BufferedInputStream(new FileInputStream(file)); | |
200 return parseDocument(inputStream); | |
201 } | |
202 catch (IOException ioe) { | |
203 logger.error(ioe.getLocalizedMessage(), ioe); | |
204 } | |
205 finally { | |
206 if (inputStream != null) { | |
207 try { inputStream.close(); } | |
208 catch (IOException ioe) {} | |
209 } | |
210 } | |
211 return null; | |
212 } | |
213 | |
214 /** | |
215 * Parses a String to a xml document. | |
216 * | |
217 * @param string The xml string | |
218 * @return the XML document or null if something went wrong. | |
219 */ | |
220 public static final Document parseDocument(String string) { | |
221 InputStream inputStream = new ByteArrayInputStream(string.getBytes()); | |
222 return parseDocument(inputStream); | |
223 } | |
224 | |
225 | |
226 public static final Document parseDocument(InputStream inputStream) { | |
227 return parseDocument(inputStream, Boolean.TRUE); | |
228 } | |
229 | |
230 public static final Document parseDocument( | |
231 InputStream inputStream, | |
232 Boolean namespaceAware | |
233 ) { | |
234 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); | |
235 | |
236 if (namespaceAware != null) { | |
237 factory.setNamespaceAware(namespaceAware.booleanValue()); | |
238 } | |
239 | |
240 try { | |
241 return factory.newDocumentBuilder().parse(inputStream); | |
242 } | |
243 catch (ParserConfigurationException pce) { | |
244 logger.error(pce.getLocalizedMessage(), pce); | |
245 } | |
246 catch (SAXException se) { | |
247 logger.error(se.getLocalizedMessage(), se); | |
248 } | |
249 catch (IOException ioe) { | |
250 logger.error(ioe.getLocalizedMessage(), ioe); | |
251 } | |
252 return null; | |
253 } | |
254 | |
255 /** | |
256 * Creates a new XPath without a namespace context. | |
257 * @return the new XPath. | |
258 */ | |
259 public static final XPath newXPath() { | |
260 return newXPath(null, null); | |
261 } | |
262 | |
263 /** | |
264 * Creates a new XPath with a given namespace context. | |
265 * @param namespaceContext The namespace context to be used or null | |
266 * if none should be used. | |
267 * @return The new XPath | |
268 */ | |
269 public static final XPath newXPath( | |
270 NamespaceContext namespaceContext, | |
271 XPathVariableResolver resolver) | |
272 { | |
273 XPathFactory factory = XPathFactory.newInstance(); | |
274 XPath xpath = factory.newXPath(); | |
275 if (namespaceContext != null) { | |
276 xpath.setNamespaceContext(namespaceContext); | |
277 } | |
278 | |
279 if (resolver != null) { | |
280 xpath.setXPathVariableResolver(resolver); | |
281 } | |
282 return xpath; | |
283 } | |
284 | |
285 /** | |
286 * Evaluates an XPath query on a given object and returns the result | |
287 * as a given type. No namespace context is used. | |
288 * @param root The object which is used as the root of the tree to | |
289 * be searched in. | |
290 * @param query The XPath query | |
291 * @param returnTyp The type of the result. | |
292 * @return The result of type 'returnTyp' or null if something | |
293 * went wrong during XPath evaluation. | |
294 */ | |
295 public static final Object xpath( | |
296 Object root, | |
297 String query, | |
298 QName returnTyp | |
299 ) { | |
300 return xpath(root, query, returnTyp, null); | |
301 } | |
302 | |
303 /** | |
304 * Evaluates an XPath query on a given object and returns the result | |
305 * as a string. A given namespace context is used. | |
306 * @param root The object which is used as the root of the tree to | |
307 * be searched in. | |
308 * @param query The XPath query | |
309 * @param namespaceContext The namespace context to be used or null | |
310 * if none should be used. | |
311 * @return The result of the query or null if something went wrong | |
312 * during XPath evaluation. | |
313 */ | |
314 public static final String xpathString( | |
315 Object root, String query, NamespaceContext namespaceContext | |
316 ) { | |
317 return (String)xpath( | |
318 root, query, XPathConstants.STRING, namespaceContext); | |
319 } | |
320 | |
321 /** | |
322 * Evaluates an XPath query on a given object and returns the result | |
323 * as a given type. Optionally a namespace context is used. | |
324 * @param root The object which is used as the root of the tree to | |
325 * be searched in. | |
326 * @param query The XPath query | |
327 * @param returnType The type of the result. | |
328 * @param namespaceContext The namespace context to be used or null | |
329 * if none should be used. | |
330 * @return The result of type 'returnTyp' or null if something | |
331 * went wrong during XPath evaluation. | |
332 */ | |
333 public static final Object xpath( | |
334 Object root, | |
335 String query, | |
336 QName returnType, | |
337 NamespaceContext namespaceContext | |
338 ) { | |
339 return xpath(root, query, returnType, namespaceContext, null); | |
340 } | |
341 | |
342 public static final Object xpath( | |
343 Object root, | |
344 String query, | |
345 QName returnType, | |
346 NamespaceContext namespaceContext, | |
347 Map<String, String> variables) | |
348 { | |
349 if (root == null) { | |
350 return null; | |
351 } | |
352 | |
353 XPathVariableResolver resolver = variables != null | |
354 ? new MapXPathVariableResolver(variables) | |
355 : null; | |
356 | |
357 try { | |
358 XPath xpath = newXPath(namespaceContext, resolver); | |
359 if (xpath != null) { | |
360 return xpath.evaluate(query, root, returnType); | |
361 } | |
362 } | |
363 catch (XPathExpressionException xpee) { | |
364 logger.error(xpee.getLocalizedMessage(), xpee); | |
365 } | |
366 | |
367 return null; | |
368 } | |
369 | |
370 /** | |
371 * Streams out an XML document to a given output stream. | |
372 * @param document The document to be streamed out. | |
373 * @param out The output stream to be used. | |
374 * @return true if operation succeeded else false. | |
375 */ | |
376 public static boolean toStream(Document document, OutputStream out) { | |
377 try { | |
378 Transformer transformer = | |
379 TransformerFactory.newInstance().newTransformer(); | |
380 DOMSource source = new DOMSource(document); | |
381 StreamResult result = new StreamResult(out); | |
382 transformer.transform(source, result); | |
383 return true; | |
384 } | |
385 catch (TransformerConfigurationException tce) { | |
386 logger.error(tce.getLocalizedMessage(), tce); | |
387 } | |
388 catch (TransformerFactoryConfigurationError tfce) { | |
389 logger.error(tfce.getLocalizedMessage(), tfce); | |
390 } | |
391 catch (TransformerException te) { | |
392 logger.error(te.getLocalizedMessage(), te); | |
393 } | |
394 | |
395 return false; | |
396 } | |
397 | |
398 public static String toString(Document document) { | |
399 try { | |
400 Transformer transformer = | |
401 TransformerFactory.newInstance().newTransformer(); | |
402 DOMSource source = new DOMSource(document); | |
403 StringWriter out = new StringWriter(); | |
404 StreamResult result = new StreamResult(out); | |
405 transformer.transform(source, result); | |
406 out.flush(); | |
407 return out.toString(); | |
408 } | |
409 catch (TransformerConfigurationException tce) { | |
410 logger.error(tce.getLocalizedMessage(), tce); | |
411 } | |
412 catch (TransformerFactoryConfigurationError tfce) { | |
413 logger.error(tfce.getLocalizedMessage(), tfce); | |
414 } | |
415 catch (TransformerException te) { | |
416 logger.error(te.getLocalizedMessage(), te); | |
417 } | |
418 | |
419 return null; | |
420 } | |
421 | |
422 public static byte [] toByteArray(Document document) { | |
423 return toByteArray(document, false); | |
424 } | |
425 | |
426 /** | |
427 * Transforms an XML document into a byte array. | |
428 * @param document The document to be streamed out. | |
429 * @param compress The document should be compressed, too. | |
430 * @return the byte array or null if operation failed or | |
431 * document is null. | |
432 */ | |
433 public static byte [] toByteArray(Document document, boolean compress) { | |
434 if (document != null) { | |
435 try { | |
436 ByteArrayOutputStream baos = new ByteArrayOutputStream(); | |
437 OutputStream out = compress | |
438 ? new GZIPOutputStream(baos) | |
439 : baos; | |
440 boolean success = toStream(document, out); | |
441 out.flush(); | |
442 out.close(); | |
443 return success | |
444 ? baos.toByteArray() | |
445 : null; | |
446 } | |
447 catch (IOException ioe) { | |
448 logger.error(ioe); | |
449 } | |
450 } | |
451 return null; | |
452 } | |
453 | |
454 public static Document fromByteArray(byte [] data) { | |
455 return fromByteArray(data, false); | |
456 } | |
457 | |
458 public static Document fromByteArray(byte [] data, boolean decompress) { | |
459 if (data != null) { | |
460 InputStream in = new ByteArrayInputStream(data); | |
461 try { | |
462 if (decompress) { | |
463 in = new GZIPInputStream(in); | |
464 } | |
465 return parseDocument(in); | |
466 } | |
467 catch (IOException ioe) { | |
468 logger.error(ioe); | |
469 } | |
470 finally { | |
471 try { | |
472 in.close(); | |
473 } | |
474 catch (IOException ioe) { | |
475 logger.error(ioe); | |
476 } | |
477 } | |
478 } | |
479 return null; | |
480 } | |
481 | |
482 private static class BuildResult { | |
483 List<Node> children; | |
484 Map<String, String> attributes; | |
485 BuildResult() { | |
486 children = new ArrayList<Node>(); | |
487 attributes = new LinkedHashMap<String, String>(); | |
488 } | |
489 | |
490 void setAttributes(Element element) { | |
491 for (Map.Entry<String, String> entry: attributes.entrySet()) { | |
492 element.setAttribute(entry.getKey(), entry.getValue()); | |
493 } | |
494 } | |
495 | |
496 void finish(Element element) { | |
497 setAttributes(element); | |
498 for (Node child: children) { | |
499 element.appendChild(child); | |
500 } | |
501 } | |
502 | |
503 void add(Node node) { | |
504 children.add(node); | |
505 } | |
506 | |
507 void add(String key, Object value) { | |
508 attributes.put(key, value != null ? value.toString() : "null"); | |
509 } | |
510 | |
511 int numChildren() { | |
512 return children.size(); | |
513 } | |
514 | |
515 Node firstChild() { | |
516 return children.get(0); | |
517 } | |
518 } // class BuildResult | |
519 | |
520 private static BuildResult recursiveBuild( | |
521 List list, | |
522 Document document | |
523 ) { | |
524 BuildResult result = new BuildResult(); | |
525 for (Object entry: list) { | |
526 if (entry instanceof Map) { | |
527 BuildResult subResult = recursiveBuild( | |
528 (Map<String, Object>)entry, document); | |
529 if (subResult.numChildren() == 1) { | |
530 result.add(subResult.firstChild()); | |
531 } | |
532 else { | |
533 Element element = document.createElement("map"); | |
534 subResult.finish(element); | |
535 result.add(element); | |
536 } | |
537 } | |
538 else if (entry instanceof List) { | |
539 Element element = document.createElement("list"); | |
540 BuildResult subResult = recursiveBuild((List)entry, document); | |
541 subResult.finish(element); | |
542 result.add(element); | |
543 } | |
544 else { | |
545 Element element = document.createElement("entry"); | |
546 element.setAttribute( | |
547 "value", | |
548 entry != null ? entry.toString() : "null"); | |
549 } | |
550 } | |
551 return result; | |
552 } | |
553 | |
554 private static BuildResult recursiveBuild( | |
555 Map<String, Object> map, | |
556 Document document | |
557 ) { | |
558 BuildResult result = new BuildResult(); | |
559 | |
560 List<Node> nodes = new ArrayList<Node>(); | |
561 for (Map.Entry<String, Object> entry: map.entrySet()) { | |
562 Object value = entry.getValue(); | |
563 if (value instanceof Map) { | |
564 Element element = document.createElement(entry.getKey()); | |
565 BuildResult subResult = recursiveBuild( | |
566 (Map<String, Object>)value, document); | |
567 subResult.finish(element); | |
568 result.add(element); | |
569 } | |
570 else if (value instanceof List) { | |
571 Element element = document.createElement(entry.getKey()); | |
572 BuildResult subResult = recursiveBuild((List)value, document); | |
573 subResult.finish(element); | |
574 result.add(element); | |
575 } | |
576 else { | |
577 result.add(entry.getKey(), value); | |
578 } | |
579 } | |
580 return result; | |
581 } | |
582 | |
583 public static Document jsonToXML(String input) { | |
584 Document document = newDocument(); | |
585 | |
586 if (document == null) { | |
587 return null; | |
588 } | |
589 | |
590 Map<String, Object> map; | |
591 try { | |
592 map = JSON.parse(input); | |
593 } | |
594 catch (IOException ioe) { | |
595 logger.error(ioe); | |
596 return null; | |
597 } | |
598 | |
599 BuildResult roots = recursiveBuild(map, document); | |
600 | |
601 int N = roots.children.size(); | |
602 | |
603 if (N == 1) { | |
604 document.appendChild(roots.children.get(0)); | |
605 } | |
606 else if (N > 1) { | |
607 Node root = document.createElement("root"); | |
608 for (int i = 0; i < N; ++i) { | |
609 root.appendChild(roots.children.get(i)); | |
610 } | |
611 document.appendChild(root); | |
612 } | |
613 | |
614 return document; | |
615 } | |
616 } | |
617 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 : |