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 :

http://dive4elements.wald.intevation.org