001: package com.ice.jcvsweb.helper;
002:
003: import java.io.ByteArrayOutputStream;
004: import java.io.BufferedReader;
005: import java.io.IOException;
006: import java.io.InputStream;
007: import java.io.File;
008: import java.io.FileInputStream;
009: import java.io.FileNotFoundException;
010: import java.io.OutputStream;
011: import java.io.PrintWriter;
012: import java.io.Reader;
013: import java.io.StringReader;
014: import java.io.StringWriter;
015: import java.util.List;
016: import java.util.Locale;
017: import java.util.Vector;
018: import java.util.Enumeration;
019: import java.util.MissingResourceException;
020: import java.util.Properties;
021:
022: import org.apache.xpath.XPathAPI;
023:
024: import org.apache.xalan.serialize.Method;
025: import org.apache.xalan.serialize.Serializer;
026: import org.apache.xalan.serialize.SerializerFactory;
027:
028: import javax.xml.parsers.DocumentBuilder;
029: import javax.xml.parsers.DocumentBuilderFactory;
030: import javax.xml.parsers.SAXParser;
031: import javax.xml.parsers.SAXParserFactory;
032: import javax.xml.parsers.ParserConfigurationException;
033:
034: import javax.xml.transform.OutputKeys;
035: import javax.xml.transform.Transformer;
036: import javax.xml.transform.TransformerFactory;
037: import javax.xml.transform.TransformerException;
038: import javax.xml.transform.TransformerConfigurationException;
039: import javax.xml.transform.dom.DOMSource;
040: import javax.xml.transform.stream.StreamResult;
041:
042: import org.w3c.dom.Document;
043: import org.w3c.dom.DocumentType;
044: import org.w3c.dom.DOMImplementation;
045: import org.w3c.dom.DOMException;
046: import org.w3c.dom.Element;
047: import org.w3c.dom.NamedNodeMap;
048: import org.w3c.dom.Node;
049: import org.w3c.dom.NodeList;
050: import org.w3c.dom.Text;
051: import org.xml.sax.Attributes;
052: import org.xml.sax.InputSource;
053: import org.xml.sax.SAXException;
054: import org.xml.sax.helpers.DefaultHandler;
055:
056: /**
057: * This class provides many utility methods related to XML.
058: *
059: * @author Tim Endres,
060: * <a href="mailto:time@BlockIslandGroup.com">time@BlockIslandGroup.com</a>
061: *
062: */
063:
064: public class XMLHelper {
065:
066: public static void cleanURLS(Document xmlDoc, String xPath) {
067: }
068:
069: public static void parse(InputStream xmlStream,
070: DefaultHandler handler) throws IOException, SAXException {
071: try {
072: SAXParserFactory saxFactory = SAXParserFactory
073: .newInstance();
074:
075: SAXParser saxParser = saxFactory.newSAXParser();
076:
077: saxParser.parse(new InputSource(xmlStream), handler);
078: } catch (ParserConfigurationException ex) {
079: throw new SAXException(ex);
080: }
081: }
082:
083: public static Document parseDocument(String xmlStr)
084: throws SAXException {
085: return XMLHelper.parseDocument(new InputSource(
086: new StringReader(xmlStr)));
087: }
088:
089: public static Document parseDocument(InputStream xmlStream)
090: throws SAXException {
091: return XMLHelper.parseDocument(new InputSource(xmlStream));
092: }
093:
094: public static Document parseDocument(Reader xmlReader)
095: throws SAXException {
096: return XMLHelper.parseDocument(new InputSource(xmlReader));
097: }
098:
099: public static Document parseDocument(InputSource xmlSource)
100: throws SAXException {
101: Document xmlDoc = null;
102:
103: try {
104: DocumentBuilderFactory domFactory = DocumentBuilderFactory
105: .newInstance();
106:
107: DocumentBuilder domParser = domFactory.newDocumentBuilder();
108:
109: xmlDoc = domParser.parse(xmlSource);
110: } catch (IOException ex) {
111: throw new SAXException(ex.getMessage(), ex);
112: } catch (ParserConfigurationException ex) {
113: throw new SAXException(ex.getMessage(), ex);
114: }
115:
116: return xmlDoc;
117: }
118:
119: public static Document createNewDocument() throws SAXException {
120: try {
121: DocumentBuilder docBuilder = DocumentBuilderFactory
122: .newInstance().newDocumentBuilder();
123:
124: DOMImplementation domImpl = docBuilder
125: .getDOMImplementation();
126:
127: DocumentType docType = domImpl.createDocumentType(
128: "qualifiedName", null, null);
129:
130: return domImpl.createDocument(null, "xml", null);
131: } catch (DOMException ex) {
132: throw new SAXException(ex.getMessage(), ex);
133: } catch (ParserConfigurationException ex) {
134: throw new SAXException(ex.getMessage(), ex);
135: }
136: }
137:
138: public static StringBuffer getNodePath(Node n) {
139: return XMLHelper.getNodePath(new StringBuffer(), n);
140: }
141:
142: public static StringBuffer getNodePath(StringBuffer s, Node n) {
143: Node p = n.getParentNode();
144:
145: if (p == null || p.getNodeType() == Node.DOCUMENT_NODE) {
146: s.append(n.getNodeName());
147: } else {
148: XMLHelper.getNodePath(s, p);
149: s.append("/");
150: s.append(n.getNodeName());
151: }
152:
153: return s;
154: }
155:
156: /**
157: * Set an element's value using TEXT subelements. If the element,
158: * specified by an xpath, does not exist, it will be created. The
159: * method will then remove all TEXT subelements from the element,
160: * and then, if the value is not null, add the value as a child
161: * TEXT element.
162: *
163: * @param xmlDoc The document in which to set the element value.
164: * @param elem The element for which we are setting the value.
165: * @param value The value to set, can be null.
166: *
167: */
168: public static void setElementValue(Document xmlDoc, String xPath,
169: String value) throws SAXException {
170: Element elem = XMLHelper.getElement(xmlDoc, xPath);
171:
172: if (elem == null) {
173: elem = XMLHelper.createElement(xmlDoc, xPath);
174: }
175:
176: if (elem == null) {
177: throw new SAXException("unable to create element '" + xPath
178: + "'");
179: }
180:
181: XMLHelper.setElementValue(xmlDoc, elem, value);
182: }
183:
184: /**
185: * Set an element's value using TEXT subelements. This method
186: * will first remove all TEXT subelements from the element, and
187: * then, if the value is not null, add the value as a child TEXT
188: * element.
189: *
190: * @param xmlDoc The document in which to set the element value.
191: * @param elem The element for which we are setting the value.
192: * @param value The value to set, can be null.
193: *
194: */
195: public static void setElementValue(Document xmlDoc, Element elem,
196: String value) throws SAXException {
197: // first, remove all child text nodes. which is trickier
198: // than you think. the NodeList implementation is "live"!
199: // thus, as you remove the children, the NodeList changes!!
200: // to simplify, we Vectorize first, then work the Vector.
201: if (elem.hasChildNodes()) {
202: Vector v = new Vector();
203: NodeList chlist = elem.getChildNodes();
204: for (int i = 0, sz = chlist.getLength(); i < sz; ++i) {
205: Node chn = chlist.item(i);
206: if (chn.getNodeType() == Node.TEXT_NODE)
207: v.addElement(chn);
208: }
209:
210: for (Enumeration e = v.elements(); e.hasMoreElements();) {
211: Node chn = (Node) e.nextElement();
212: elem.removeChild(chn);
213: }
214: }
215:
216: // finally, add on the value we wish to set
217: if (value != null) {
218: elem.appendChild(xmlDoc.createTextNode(value));
219: }
220: }
221:
222: public static Element createElement(Document xmlDoc, String xPath)
223: throws SAXException {
224: int index = xPath.lastIndexOf("/");
225:
226: String name = null;
227: Element elem = null;
228: Element parent = null;
229: if (index == -1) {
230: parent = xmlDoc.getDocumentElement();
231: name = xPath;
232: } else {
233: name = xPath.substring(index + 1);
234: String subPath = xPath.substring(0, index);
235: parent = getElement(xmlDoc, subPath);
236: }
237:
238: if (parent != null) {
239: elem = xmlDoc.createElement(name);
240: if (elem != null) {
241: parent.appendChild(elem);
242: } else {
243: throw new SAXException("could not create node '"
244: + xPath + "'");
245: }
246: } else {
247: throw new SAXException("could not find parent of '" + xPath
248: + "'");
249: }
250:
251: return elem;
252: }
253:
254: /**
255: * Get an element specified by an xpath. Return the element
256: * or null if it does not exist.
257: *
258: * @param xmlDoc The document containing the element.
259: * @param elemXPath The xpath that specifies the element.
260: * @return The element or null if it does not exist.
261: */
262:
263: public static Element getElement(Document xmlDoc, String elemXPath)
264: throws SAXException {
265: return XMLHelper.selectElement(xmlDoc.getDocumentElement(),
266: elemXPath);
267: }
268:
269: /**
270: * Get the value of an element specified by an xpath. The
271: * value is the concatenation of all TEXT child elements.
272: * If the element does not exist, null is returned. If the
273: * element exists, but has no children, an empty string is
274: * returned. It is therefore impossible to detect whether
275: * there are no children, or children with empty values.
276: *
277: * @param xmlDoc The document containing the element.
278: * @param elemXPath The xpath that specifies the element.
279: * @return The element's value, or null if it does not exist.
280: */
281: public static String getElementValue(Document xmlDoc,
282: String elemXPath) throws SAXException {
283: String result = null;
284:
285: Element elem = XMLHelper.getElement(xmlDoc, elemXPath);
286:
287: if (elem == null)
288: throw new SAXException("no node '" + elemXPath + "'");
289:
290: result = XMLHelper.getElementValue(elem);
291:
292: return result;
293: }
294:
295: /**
296: * Get the value of an element. The value is the concatenation
297: * of all TEXT child elements. If the element has no children,
298: * an empty string is returned. It is therefore impossible to
299: * detect whether there are no children, or children with empty
300: * values.
301: *
302: * @param elem The element to get the value of.
303: * @return The element's value.
304: */
305: public static String getElementValue(Element elem)
306: throws SAXException {
307: NodeList chlist = elem.getChildNodes();
308: StringBuffer result = new StringBuffer(256);
309:
310: for (int i = 0, sz = chlist.getLength(); i < sz; ++i) {
311: Node chn = chlist.item(i);
312: if (chn.getNodeType() == Node.TEXT_NODE
313: || chn.getNodeType() == Node.CDATA_SECTION_NODE) {
314: result.append(((Text) chn).getData());
315: }
316: }
317:
318: return result.toString();
319: }
320:
321: public static Element getChildElementByTagName(Element parent,
322: String tagName) {
323: Element result = null;
324:
325: NodeList nl = parent.getChildNodes();
326: for (int i = 0, sz = nl.getLength(); i < sz; ++i) {
327: Node n = nl.item(i);
328: if (n.getNodeType() == Node.ELEMENT_NODE
329: && n.getNodeName().equals(tagName)) {
330: result = (Element) n;
331: break;
332: }
333: }
334:
335: return result;
336: }
337:
338: /**
339: * Get the value of an element. The value is the concatenation
340: * of all TEXT child elements. If the element has no children,
341: * an empty string is returned. It is therefore impossible to
342: * detect whether there are no children, or children with empty
343: * values.
344: *
345: * @param elem The element to get the value of.
346: * @param childTagName The child element's tagname.
347: * @return The child element's value.
348: */
349: public static String getChildElementValue(Element elem,
350: String childTagName) throws SAXException {
351: String result = null;
352:
353: Element child = XMLHelper.getChildElementByTagName(elem,
354: childTagName);
355:
356: if (child != null) {
357: result = XMLHelper.getElementValue(child);
358: }
359:
360: return result.toString();
361: }
362:
363: /**
364: * Evaluate XPath string to an Element.
365: *
366: * @param contextNode The node to start searching from.
367: * @param exprStr A valid XPath string.
368: * @return The first element found that matches the XPath, or null.
369: */
370:
371: public static Element selectElement(Node contextNode, String exprStr)
372: throws SAXException {
373: Node node = XMLHelper.selectNode(contextNode, exprStr);
374:
375: if (node != null && node.getNodeType() == Node.ELEMENT_NODE)
376: return (Element) node;
377:
378: return null;
379: }
380:
381: /**
382: * Evaluate XPath string to a Node.
383: * XPath namespace prefixes are resolved from the contextNode.
384: *
385: * @param contextNode The node to start searching from.
386: * @param exprStr A valid XPath string.
387: * @return The first node found that matches the XPath, or null.
388: */
389:
390: public static Node selectNode(Node contextNode, String exprStr)
391: throws SAXException {
392: try {
393: return XPathAPI.selectSingleNode(contextNode, exprStr);
394: } catch (javax.xml.transform.TransformerException ex) {
395: throw new SAXException(ex.getMessage(), ex);
396: }
397: }
398:
399: /**
400: * Evaluate XPath string to a NodeIterator.
401: * XPath namespace prefixes are resolved from the contextNode.
402: *
403: * @param contextNode The node to start searching from.
404: * @param exprStr A valid XPath string.
405: * @return A NodeIterator of the matching nodes, should never be null.
406: */
407:
408: public static NodeList selectNodeList(Node contextNode,
409: String exprStr) throws SAXException {
410: try {
411: return XPathAPI.selectNodeList(contextNode, exprStr);
412: } catch (javax.xml.transform.TransformerException ex) {
413: throw new SAXException(ex.getMessage(), ex);
414: }
415: }
416:
417: public static String getNodeTypeName(short nodeType) {
418: if (nodeType == Node.ELEMENT_NODE)
419: return "ELEMENT_NODE";
420: else if (nodeType == Node.ATTRIBUTE_NODE)
421: return "ATTRIBUTE_NODE";
422: else if (nodeType == Node.TEXT_NODE)
423: return "TEXT_NODE";
424: else if (nodeType == Node.CDATA_SECTION_NODE)
425: return "CDATA_SECTION_NODE";
426: else if (nodeType == Node.ENTITY_REFERENCE_NODE)
427: return "ENTITY_REFERENCE_NODE";
428: else if (nodeType == Node.ENTITY_NODE)
429: return "ENTITY_NODE";
430: else if (nodeType == Node.COMMENT_NODE)
431: return "COMMENT_NODE";
432: else if (nodeType == Node.DOCUMENT_NODE)
433: return "DOCUMENT_NODE";
434: else if (nodeType == Node.PROCESSING_INSTRUCTION_NODE)
435: return "PROCESSING_INSTRUCTION_NODE";
436: else if (nodeType == Node.DOCUMENT_TYPE_NODE)
437: return "DOCUMENT_TYPE_NODE";
438: else if (nodeType == Node.DOCUMENT_FRAGMENT_NODE)
439: return "DOCUMENT_FRAGMENT_NODE";
440: else if (nodeType == Node.NOTATION_NODE)
441: return "NOTATION_NODE";
442: else
443: return "UNKNOWN_NODE_TYPE";
444: }
445:
446: public static String getXMLString(String prefix, Document doc)
447: throws SAXException {
448: try {
449: ByteArrayOutputStream bos = new ByteArrayOutputStream();
450:
451: XMLHelper.writeXMLDocument(bos, doc);
452:
453: return bos.toString();
454: } catch (IOException ex) {
455: throw new SAXException(ex.getMessage(), ex);
456: }
457: }
458:
459: /**
460: * Read a standard XML configuration document. We perform some
461: * additional processing here, such as stripping comments to save
462: * memory, and stripping whitespace on urls, classnames, xsl page
463: * names and other elements, where whitespace used for formatting
464: * purposes in the config file can cause incorrect element values.
465: */
466: public static Document readXMLConfiguration(String configFilePath)
467: throws SAXException {
468: return XMLHelper.readXMLConfiguration(new File(configFilePath));
469: }
470:
471: /**
472: * Read a standard XML configuration document. We perform some
473: * additional processing here, such as stripping comments to save
474: * memory, and stripping whitespace on urls, classnames, xsl page
475: * names and other elements, where whitespace used for formatting
476: * purposes in the config file can cause incorrect element values.
477: */
478: public static Document readXMLConfiguration(File configFile)
479: throws SAXException {
480: Document cfgDoc = null;
481: FileInputStream fin = null;
482: try {
483: fin = new FileInputStream(configFile);
484: cfgDoc = XMLHelper.parseDocument(fin);
485: XMLHelper.stripDocumentComments(cfgDoc);
486: return cfgDoc;
487: } catch (FileNotFoundException ex) {
488: throw new SAXException("XML file not found, '"
489: + configFile.getPath() + "', " + ex.getMessage());
490: } finally {
491: try {
492: if (fin != null)
493: fin.close();
494: } catch (IOException ex) {
495: }
496: }
497: }
498:
499: public static void writeXMLDocument(OutputStream out, Document doc)
500: throws IOException, SAXException {
501: try {
502: TransformerFactory transFactory = TransformerFactory
503: .newInstance();
504: Transformer transformer = transFactory.newTransformer();
505:
506: transformer
507: .setOutputProperty(OutputKeys.METHOD, Method.XML);
508: transformer.setOutputProperty(OutputKeys.INDENT, "yes");
509: transformer.setOutputProperty(OutputKeys.ENCODING,
510: "ISO-8859-1");
511:
512: DOMSource source = new DOMSource(doc);
513: StreamResult result = new StreamResult(out);
514: transformer.transform(source, result);
515: } catch (TransformerConfigurationException ex) {
516: throw new SAXException(ex.getMessage(), ex);
517: } catch (TransformerException ex) {
518: throw new SAXException(ex.getMessage(), ex);
519: }
520: }
521:
522: public static void writeXMLSubTree(OutputStream out, Document doc,
523: Node root) throws IOException, SAXException {
524: try {
525: TransformerFactory transFactory = TransformerFactory
526: .newInstance();
527: Transformer transformer = transFactory.newTransformer();
528:
529: transformer
530: .setOutputProperty(OutputKeys.METHOD, Method.XML);
531: transformer.setOutputProperty(OutputKeys.INDENT, "yes");
532: transformer.setOutputProperty(OutputKeys.ENCODING,
533: "ISO-8859-1");
534:
535: DOMSource source = new DOMSource(root);
536: StreamResult result = new StreamResult(out);
537: transformer.transform(source, result);
538: } catch (TransformerConfigurationException ex) {
539: throw new SAXException(ex.getMessage(), ex);
540: } catch (TransformerException ex) {
541: throw new SAXException(ex.getMessage(), ex);
542: }
543: }
544:
545: /**
546: * Removes all of the XML COMMENT nodes from the entire XML document.
547: * The COMMENT "<!-- -->" nodes that are removed should then be able
548: * to be reclaimed in GC, reducing the document's memory footprint.
549: */
550: public static void stripDocumentComments(Document xmlDoc)
551: throws SAXException {
552: Element docRoot = xmlDoc.getDocumentElement();
553: XMLHelper.stripElementComments(xmlDoc, docRoot, true);
554: }
555:
556: /**
557: * Removes all of the XML COMMENT nodes from the supplied Node.
558: */
559: public static void stripElementComments(Document xmlDoc, Node node,
560: boolean recurseFlag) throws SAXException {
561: NodeList chlist = node.getChildNodes();
562: int numNodesToRemove = 0;
563: Node[] nodesToRemove = new Node[chlist.getLength()];
564:
565: for (int i = 0, sz = chlist.getLength(); i < sz; ++i) {
566: Node chn = chlist.item(i);
567: if (chn.getNodeType() == Node.COMMENT_NODE) {
568: nodesToRemove[numNodesToRemove++] = chn;
569: } else {
570: XMLHelper
571: .stripElementComments(xmlDoc, chn, recurseFlag);
572: }
573: }
574:
575: for (int i = 0; i < numNodesToRemove; ++i) {
576: node.removeChild(nodesToRemove[i]);
577: }
578: }
579:
580: }
|