View Javadoc

1   package com.netflix.api.utils;
2   
3   import java.io.BufferedReader;
4   import java.io.ByteArrayInputStream;
5   import java.io.IOException;
6   import java.io.InputStream;
7   import java.io.InputStreamReader;
8   import java.io.OutputStream;
9   import java.io.StringReader;
10  import java.io.StringWriter;
11  import java.io.UnsupportedEncodingException;
12  import java.util.List;
13  import java.util.StringTokenizer;
14  
15  import javax.xml.transform.Transformer;
16  import javax.xml.transform.TransformerFactory;
17  import javax.xml.transform.stream.StreamResult;
18  import javax.xml.transform.stream.StreamSource;
19  import javax.xml.xpath.XPathConstants;
20  import javax.xml.xpath.XPathExpression;
21  import javax.xml.xpath.XPathFactory;
22  
23  import org.apache.commons.io.IOUtils;
24  import org.jaxen.JaxenException;
25  import org.jdom.Attribute;
26  import org.jdom.Document;
27  import org.jdom.Element;
28  import org.jdom.IllegalDataException;
29  import org.jdom.IllegalNameException;
30  import org.jdom.JDOMException;
31  import org.jdom.Parent;
32  import org.jdom.input.SAXBuilder;
33  import org.jdom.output.Format;
34  import org.jdom.output.XMLOutputter;
35  import org.jdom.xpath.XPath;
36  import org.slf4j.LoggerFactory;
37  import org.slf4j.Logger;
38  import org.xml.sax.InputSource;
39  
40  /**
41   * Because Java is what it is when it comes to XML. Sigh...
42   * 
43   * @author jharen
44   */
45  public class XMLUtils
46  {
47  	private static final Logger logger = LoggerFactory.getLogger(XMLUtils.class);
48  
49  	private static final String DEFAULT_ENCODING = "UTF-8";
50  	
51  	private static XPathFactory xPathFactory;
52  	
53  	private static javax.xml.xpath.XPath xPathInstance;
54  	
55  	static
56  	{
57  		xPathFactory = XPathFactory.newInstance();
58  		try
59  		{
60  			xPathInstance = xPathFactory.newXPath();
61  		} 
62  		catch (Exception e)
63  		{
64  			logger.error("Could not instantiate XPath due to exception", e);
65  		}
66  	}
67  
68  	/**
69  	 * Applies an XSL transformation to <code>xmlString</code> using
70  	 * <code>xsltStr</code> as the stylesheet and returns the transformed
71  	 * string.
72  	 * 
73  	 * @param xmlString
74  	 *            The XML to transform.
75  	 * @param xsltStr
76  	 *            The stylesheet as an XML string (not a file).
77  	 * @return Transformed string.
78  	 * @throws Exception
79  	 */
80  	public static String applyStylesheetAsString(String xmlString, String xsltStr) throws Exception
81  	{
82  		// Use the static TransformerFactory.newInstance() method to instantiate
83  		// a TransformerFactory. The javax.xml.transform.TransformerFactory
84  		// system property setting determines the actual class to instantiate --
85  		// org.apache.xalan.transformer.TransformerImpl.
86  
87  		TransformerFactory tFactory = TransformerFactory.newInstance();
88  
89  		// Use the TransformerFactory to instantiate a Transformer that will work with
90  		// the stylesheet you specify. This method call also processes the
91  		// stylesheet into a compiled Templates object.
92  
93  		Transformer transformer = tFactory.newTransformer(new StreamSource(new ByteArrayInputStream(xsltStr.getBytes())));
94  
95  		// Use the Transformer to apply the associated Templates object to an
96  		// XML document (foo.xml) and write the output to a file (foo.out).
97  
98  		StringWriter swriter = new StringWriter();
99  		StreamResult sresult = new StreamResult(swriter);
100 		transformer.transform(new StreamSource(new ByteArrayInputStream(xmlString.getBytes("UTF-8"))), sresult);
101 		return swriter.toString();
102 	}
103 
104 	/**
105 	 * Applies an XSL transformation to <code>xmlString</code> using
106 	 * <code>xslFileName</code> as the stylesheet and returns the transformed
107 	 * string.
108 	 * 
109 	 * @param xmlString
110 	 * @param xslFileName
111 	 * @return Transfromed string.
112 	 */
113 	public static String applyStylesheetAsFile(String xmlString, String xslFileName) throws Exception
114 	{
115 		logger.debug("XMLUtils: applyStylesheetAsFile() - fileName: " + xslFileName);
116 
117 		try
118 		{
119 			InputStream is = XMLUtils.class.getResourceAsStream(xslFileName);
120 			byte[] xslBytes = IOUtils.toByteArray(is);
121 			String xslString = new String(xslBytes);
122 			xmlString = applyStylesheetAsString(xmlString, xslString);
123 		} catch (Exception e)
124 		{
125 			logger.error("Exception caught in applyStylesheetAsFile " + e.getMessage());
126 			throw e;
127 		}
128 		return xmlString;
129 	}
130 
131 	/**
132 	 * Given an Element and an <code>attributeName</code> returns the attribute
133 	 * value
134 	 * 
135 	 * @param elem
136 	 * @param attributeName
137 	 * @return The attribute value for <code>attributeName</code>
138 	 * @throws Exception
139 	 */
140 	public static String getAttribute(Element elem, String attributeName) throws Exception
141 	{
142 		String value = elem.getAttributeValue(attributeName);
143 		return value;
144 	}
145 
146 	/**
147 	 * Given an XML String, gets the root attribute value for the
148 	 * <code>attributeName</code>.
149 	 * 
150 	 * @param xmlString
151 	 * @param attributeName
152 	 *            name of the attribute whose value is returned
153 	 * @return The value for the attribute.
154 	 * @throws Exception
155 	 */
156 	public static String RootAttribute(String xmlString, String attributeName) throws Exception
157 	{
158 		Document doc = createDocumentFromString(xmlString);
159 		String value = getRootAttribute(doc, attributeName);
160 		return value;
161 	}
162 
163 	/**
164 	 * Builds the Document object from the <code>xmlString</code>. Encodes the
165 	 * string to UTF-8 format before building the document.
166 	 * 
167 	 * @param xmlString
168 	 * @return Document object
169 	 * @throws JDOMException
170 	 * @throws IOException
171 	 */
172 	public static Document createDocumentFromString(String xmlString) throws JDOMException, IOException
173 	{
174 		Document schemaDoc = null;
175 		SAXBuilder builder = new SAXBuilder(false);
176 		byte[] xmlBytes = null;
177 		try
178 		{
179 			// get the UTF 8 encoded bytes
180 			xmlBytes = xmlString.getBytes(DEFAULT_ENCODING);
181 			schemaDoc = builder.build(new ByteArrayInputStream(xmlBytes));
182 		} catch (UnsupportedEncodingException usee)
183 		{
184 			schemaDoc = builder.build(new StringReader(xmlString));
185 		}
186 		return schemaDoc;
187 	}
188 
189 	/**
190 	 * @param xmlString
191 	 * @param encoding
192 	 * @return
193 	 * @throws JDOMException
194 	 * @throws IOException
195 	 */
196 	public static Document createDocumentFromString(String xmlString, String encoding) throws JDOMException, IOException
197 	{
198 		Document schemaDoc = null;
199 		SAXBuilder builder = new SAXBuilder(false);
200 		byte[] xmlBytes = null;
201 		try
202 		{
203 			xmlBytes = xmlString.getBytes(encoding);
204 			schemaDoc = builder.build(new ByteArrayInputStream(xmlBytes));
205 		} catch (UnsupportedEncodingException usee)
206 		{
207 			schemaDoc = builder.build(new StringReader(xmlString));
208 		}
209 		return schemaDoc;
210 	}
211 
212 	/**
213 	 * Given an <code>inputStream</code>, ceates a Document fromt he stream data
214 	 * and returns the Document
215 	 * 
216 	 * @param inputStream
217 	 * @return Document
218 	 * @throws UnsupportedEncodingException
219 	 * @throws JDOMException
220 	 */
221 	public static Document createDocumentFromStream(InputStream inputStream) throws UnsupportedEncodingException, JDOMException, IOException
222 	{
223 		Document xmlDoc = null;
224 		SAXBuilder builder = new SAXBuilder(false);
225 		BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, DEFAULT_ENCODING));
226 		xmlDoc = builder.build(new InputSource(reader));
227 		return xmlDoc;
228 	}
229 
230 	/**
231 	 * Tests a string. If it is null OR blank returns false else true.
232 	 * 
233 	 * @param s
234 	 *            String to test.
235 	 * @return true if not null and not blank.
236 	 */
237 	public static boolean isSet(String s)
238 	{
239 		return ((s != null) && (!s.equals("")));
240 	}
241 
242 	/**
243 	 * Adds an attribute to the element identified by <code>xPath</code> in the
244 	 * <code>xmlDoc</code>. Results are undetermined if there is more than one
245 	 * element that matches <code>xpath</code>.
246 	 * 
247 	 * @param xmlDoc
248 	 *            The Document to check.
249 	 * @param xPath
250 	 *            The XPath to look for. The first element that matches is
251 	 *            returned. Order is indeterminate if more that one element with
252 	 *            the same XPath exist in the Document.
253 	 * @param name
254 	 *            The attribute name to add
255 	 * @param value
256 	 *            The attribute value to add
257 	 * @throws Exception
258 	 */
259 	public static void addAttribute(Document xmlDoc, String xPath, String name, String value) throws Exception
260 	{
261 		Element e;
262 		XPath xpath1;
263 		xpath1 = XPath.newInstance(xPath);
264 		e = (Element) xpath1.selectSingleNode(xmlDoc);
265 		if (e != null)
266 		{
267 			e.setAttribute(name, value);
268 		}
269 	}
270 
271 	/**
272 	 * Given an <code>xPath</code>, adds the Element defined by
273 	 * <code>xPath</code> to the <code>xmlDoc </code> and returns the added
274 	 * Element
275 	 * 
276 	 * @param xmlDoc
277 	 * @param xPath
278 	 * @return The newly added Element. Results are undetermined if the element
279 	 *         already exists. It will be added but its position in the tree is
280 	 *         indeterminate.
281 	 */
282 	public static Element addElement(Document xmlDoc, String xPath)
283 	{
284 		Element root = null;
285 		Element currentElem = null;
286 		Element tmpElem = null;
287 		root = xmlDoc.getRootElement();
288 
289 		// break the XPath into tokens, each representing a node
290 		StringTokenizer tok = new StringTokenizer(xPath, "/");
291 		// assume the root node exists so decrement count by 1
292 		int numNodes = tok.countTokens() - 1;
293 		if (numNodes < 0)
294 			numNodes = 0;
295 
296 		// set the root xpath
297 		currentElem = root;
298 		for (int i = 0; i < numNodes; i++)
299 		{
300 			String parentTagName = (String) tok.nextToken();
301 			tmpElem = currentElem.getChild(parentTagName);
302 			if (tmpElem == null)
303 			{
304 				// no such element, need to add
305 				tmpElem = new Element(parentTagName);
306 				currentElem.addContent(tmpElem);
307 			}
308 			currentElem = tmpElem;
309 		}
310 		return tmpElem;
311 	}
312 
313 	/**
314 	 * Adds text (value) to the <code>element</code>
315 	 * 
316 	 * @param element
317 	 *            The Element whose content will be modified.
318 	 * @param value
319 	 *            The text content to add.
320 	 * @throws JaxenException
321 	 * @throws Exception
322 	 */
323 	public static void addElementValue(Element element, String value)
324 	{
325 		if (element == null)
326 			return;
327 		element.setText(value);
328 	}
329 
330 	/**
331 	 * Adds text (value) to the element identified by the <code>xPath</code>.
332 	 * Assumes only one element with
333 	 * <code>xPath</cdoe> exists. If the element does
334 	 * not exist, calls <code>addElement</code> to add the element to the root
335 	 * node of the
336 	 * document, then sets the content for the newly added element.
337 	 * 
338 	 * @param xmlDoc
339 	 * @param xPath
340 	 * @param value
341 	 *            element content to add
342 	 * @throws Exception
343 	 */
344 	public static void addElementValue(Document xmlDoc, String xPath, String value) throws Exception
345 	{
346 		Element e;
347 		XPath xpath1;
348 		xpath1 = XPath.newInstance(xPath);
349 		e = (Element) xpath1.selectSingleNode(xmlDoc);
350 		if (e != null)
351 		{
352 			e.setText(value);
353 		}
354 		else
355 		{
356 			addElement(xmlDoc, xPath);
357 			e = (Element) xpath1.selectSingleNode(xmlDoc);
358 			if (e == null)
359 			{
360 				logger.error("XMLUtils: addElementValue. "
361 					+ " Unable to add element " + xPath
362 					+ " to document.");
363 				return;
364 			}
365 			e.setText(value);
366 		}
367 	}
368 
369 	/**
370 	 * Adds an attribute to the root node of the <code>xmlDoc</code>
371 	 * 
372 	 * @param xmlDoc
373 	 * @param name
374 	 * @param value
375 	 * @throws IllegalNameException
376 	 * @throws IllegalDataException
377 	 */
378 	public static void addRootAttribute(Document xmlDoc, String name, String value) throws Exception
379 	{
380 		Element root = xmlDoc.getRootElement();
381 		if (root != null)
382 			root.setAttribute(name, value);
383 	}
384 
385 	/**
386 	 * Adds a namespace to the root element of <code>xmlDoc</code>.
387 	 * 
388 	 * @param xmlDoc
389 	 * @param namespace
390 	 *            The namespace to add
391 	 */
392 	public void addRootNamespace(Document xmlDoc, String namespace)
393 	{
394 		Element root = xmlDoc.getRootElement();
395 		if (root != null)
396 			root.setNamespace(org.jdom.Namespace.getNamespace(namespace));
397 	}
398 
399 	/**
400 	 * Given and <code>xPath</code>, deletes the first Element found with
401 	 * that <code>xPath</code>.
402 	 * 
403 	 * @param xmlDoc
404 	 * @param xPath
405 	 *            The xPath of the element to delete. Assumes there is only one
406 	 *            such element. Results are indeterminate if there is more than
407 	 *            one Element with the same XPath.
408 	 * @throws Exception
409 	 */
410 	public static void deleteElementGivenXPath(Document xmlDoc, String xPath) throws JDOMException
411 	{
412 		Element e;
413 		XPath xpath1;
414 		xpath1 = XPath.newInstance(xPath);
415 		e = (Element) xpath1.selectSingleNode(xmlDoc);
416 		if (e != null)
417 		{
418 			Parent parentElement = e.getParent();
419 			if (parentElement != null)
420 			{
421 				parentElement.removeContent(e);
422 			}
423 		}
424 		else
425 		{
426 			logger.warn("XMLUtils:deleteElementGivenXPath.  There is no such element for: " + xPath);
427 		}
428 	}
429 
430 	/**
431 	 * Given a the name of an element to search for (start) and a string to
432 	 * search in, returns the content of that element. This gets around the
433 	 * inability to use xpath when only a default namespace is defined for a
434 	 * document.
435 	 * 
436 	 * @param docString
437 	 *            The string to search in.
438 	 * @param start
439 	 *            The element name whose content you want.
440 	 * @return The element's text content.
441 	 * @throws Exception
442 	 */
443 	public static String getElementTextFromString(String docString, String start)
444 	{
445 		String tmp = "<" + start + ">";
446 		int begin = docString.indexOf(tmp);
447 		if (begin == -1)
448 		{
449 			logger.error("XMLUtils:getElementTextFromString: "
450 				+ " Unable to find <" + start + "> in string.");
451 			return null;
452 		}
453 
454 		int end = docString.indexOf("</", begin);
455 		if (end == -1)
456 		{
457 			logger.error("XMLUtils:getElementTextFromString: Unable to find </" + start + ">.");
458 			return null;
459 		}
460 
461 		String val = docString.substring(begin + tmp.length(), end);
462 		return val;
463 	}
464 
465 	/**
466 	 * Given an <code>inputStream</code>, creates a Document and returns the
467 	 * specified attribute of the root element of the document.
468 	 * 
469 	 * @param inputStream
470 	 * @param attribute
471 	 * @return the specified attribute of the root element of the document
472 	 * @throws JDOMException
473 	 * @throws IOException
474 	 */
475 	@SuppressWarnings("unused")
476 	private static String getRootAttribute(InputStream inputStream, String attribute) throws JDOMException, IOException, Exception
477 	{
478 		Document doc = null;
479 		doc = createDocumentFromStream(inputStream);
480 		if (doc == null)
481 		{
482 			logger.error("XMLUtils: " + " getRootAttribute failed. Null document.");
483 			throw new Exception("XMLUtils: getRootAttribute.  Unable to create Document from stream.");
484 		}
485 		Element root = doc.getRootElement();
486 		String attrVal = root.getAttributeValue(attribute);
487 		return attrVal;
488 	}
489 
490 	/**
491 	 * Given a Document <code>xmlDoc</code>, returns the attribute value of
492 	 * the specified <code>attribute</code> of the root element of the
493 	 * document.
494 	 * 
495 	 * @param xmlDoc
496 	 * @param attributeName
497 	 *            The name of the attribute whose value is returned.
498 	 * @return the specified attribute of the root element of the document
499 	 * @throws JDOMException
500 	 * @throws IOException
501 	 */
502 	private static String getRootAttribute(Document xmlDoc, String attributeName) throws JDOMException, IOException, Exception
503 	{
504 		if (xmlDoc == null)
505 		{
506 			logger.error("XMLUtils:  getRootAttribute failed. Null document.");
507 			throw new Exception("XMLUtils: getRootAttribute.  Unable to get attribute. Null Document.");
508 		}
509 		Element root = xmlDoc.getRootElement();
510 		String attrVal = root.getAttributeValue(attributeName);
511 		return attrVal;
512 	}
513 
514 	/**
515 	 * Given an XML string, creates a Document and returns attribute value of
516 	 * the specified attributeName of the root element of the Document.
517 	 * 
518 	 * @param xml
519 	 * @param attributeName
520 	 *            The name of the attribute whose value is returned.
521 	 * @return The attribute value of <code>attributeName</code> of the root
522 	 *         element of the document
523 	 * @throws JDOMException
524 	 * @throws IOException
525 	 */
526 	@SuppressWarnings("unused")
527 	private static String getRootAttributeFromString(String xml, String attributeName) throws JDOMException, IOException, Exception
528 	{
529 		Document xmlDoc = createDocumentFromString(xml);
530 		if (xmlDoc == null)
531 		{
532 			logger.error("XMLUtils: getRootAttributeFromString failed. Null document.");
533 			throw new Exception("XMLUtils: getRootAttributeFromString.  Unable to get attribute. Null Document.");
534 		}
535 		return getRootAttribute(xmlDoc, attributeName);
536 	}
537 
538 	/**
539 	 * Given a Document, returns the Document content as a string
540 	 * @param doc
541 	 * @return the content of the Document as a string
542 	 */
543 	public static String getXMLStringFromDoc(Document doc)
544 	{
545 		String resultingXML = null;
546 		Format format = Format.getPrettyFormat();
547 		format.setEncoding(DEFAULT_ENCODING);
548 		XMLOutputter xmlOut = new XMLOutputter(format);
549 		resultingXML = xmlOut.outputString(doc);
550 		return resultingXML;
551 	}
552 	
553 	/**
554 	 * Given a string of XML, returns a pretty-printed,
555 	 * indented copy.
556 	 * @param xml
557 	 * @return
558 	 * @throws Exception
559 	 */
560 	public static String prettyPrint(String xml) throws Exception
561 	{
562 		Document doc = createDocumentFromString(xml);
563 		return getXMLStringFromDoc(doc);
564 	}
565 
566 	/**
567 	 * @param xmlString
568 	 * @param xPath
569 	 * @return
570 	 * @throws Exception
571 	 */
572 	public static Element getXPathElementFromString(String xmlString, String xPath) throws Exception
573 	{
574 		Document xmlDoc = createDocumentFromString(xmlString);
575 		return getXPathElementFromDoc(xmlDoc, xPath);
576 	}
577 	
578 	/**
579 	 * @param xmlString
580 	 * @param xPath
581 	 * @return String
582 	 * @throws Exception
583 	 */
584 	public static String getXPathElementTextFromString(String xmlString, String xPath) throws Exception
585 	{
586 		Document xmlDoc = createDocumentFromString(xmlString);
587 		Element elem = getXPathElementFromDoc(xmlDoc, xPath);
588 		if (elem != null)
589 			return elem.getText();
590 		else return null;
591 	}
592 	
593 	/**
594 	 * @param doc
595 	 * @param xPath
596 	 * @return
597 	 * @throws Exception
598 	 */
599 	public static String getXPathElementTextFromDoc(Document doc, String xPath) throws Exception
600 	{
601 		Element elem = getXPathElementFromDoc(doc, xPath);
602 		if (elem != null)
603 			return elem.getText();
604 		else return null;
605 	}
606 	
607 	/**
608 	 * @param xmlString
609 	 * @param xPath
610 	 * @return String
611 	 * @throws Exception
612 	 */
613 	public static String getXPathAttributeTextFromString(String xmlString, String xPath) throws Exception
614 	{
615 		Document xmlDoc = createDocumentFromString(xmlString);
616 		Attribute att = getXPathAttributeFromDoc(xmlDoc, xPath);
617 		if (att != null)
618 			return att.getValue();
619 		else return null;
620 	}
621 
622 	/**
623 	 * Given a Document and an <code>xPath</code> returns the Element
624 	 * represented by the <code>xPath</code> from the <code>xmlDoc</code>.
625 	 * Assumes there is only one Element with this <code>xPath</code>.
626 	 * Results are indeterminate if more than one Element with
627 	 * <code>xPath</code> exists.
628 	 * 
629 	 * @param xmlDoc
630 	 * @param xPath
631 	 * @return the Element for the xpath. Assumes only one such element exists.
632 	 *         Results are undetermined if more than one Element exists with the
633 	 *         same xPath.
634 	 * @throws Exception
635 	 */
636 	public static Element getXPathElementFromDoc(Document xmlDoc, String xPath) throws Exception
637 	{
638 		XPath xpath1;
639 		if (xmlDoc == null)
640 		{
641 			throw new Exception("getXPathElementFromDoc. Null document passed.");
642 		}
643 
644 		if (isSet(xPath) && xPath.endsWith("/"))
645 		{
646 			xPath = xPath.substring(0, xPath.lastIndexOf("/"));
647 		}
648 
649 		xpath1 = XPath.newInstance(xPath);
650 		return (Element) xpath1.selectSingleNode(xmlDoc);
651 	}
652 	
653 	public static Attribute getXPathAttributeFromDoc(Document xmlDoc, String xPath) throws Exception
654 	{
655 		XPath xpath1;
656 		if (xmlDoc == null)
657 		{
658 			throw new Exception("getXPathElementFromDoc. Null document passed.");
659 		}
660 
661 		if (isSet(xPath) && xPath.endsWith("/"))
662 		{
663 			xPath = xPath.substring(0, xPath.lastIndexOf("/"));
664 		}
665 
666 		xpath1 = XPath.newInstance(xPath);
667 		return (Attribute) xpath1.selectSingleNode(xmlDoc);
668 	}
669 	
670 	/**
671 	 * Given a Document and an <code>xPath</code> returns the Element
672 	 * represented by the <code>xPath</code> from the <code>xmlDoc</code>.
673 	 * 
674 	 * @param xmlDoc
675 	 * @param xPath
676 	 * @return the Element for the xpath.
677 	 * @throws Exception
678 	 */
679 	@SuppressWarnings("unchecked")
680 	public static List<Element> getXPathElementListFromDoc(Document xmlDoc, String xPathExpression) throws Exception
681 	{
682 		XPath xpath;
683 		if (xmlDoc == null)
684 		{
685 			throw new Exception("getXPathElementFromDoc. Null document passed.");
686 		}
687 
688 		if (isSet(xPathExpression) && xPathExpression.endsWith("/"))
689 		{
690 			xPathExpression = xPathExpression.substring(0, xPathExpression.lastIndexOf("/"));
691 		}
692 
693 		xpath = XPath.newInstance(xPathExpression);
694 		return xpath.selectNodes(xmlDoc);
695 	}
696 	
697 	public static List<Element> getXPathElementListFromString(String xmlString, String xPathExpression) throws Exception
698 	{
699 		Document doc = createDocumentFromString(xmlString);
700 		return getXPathElementListFromDoc(doc, xPathExpression);
701 	}
702 
703 	/**
704 	 * Retrieves the text of an
705 	 * element represented by the <code>xPath</code> from the Document.
706 	 * Assumes there is only one element with <code>xPath</code>. Results are
707 	 * undetermined if there is more than one Element with <code>xPath</code>.
708 	 * @param xmlString
709 	 * @param xPath
710 	 * @return
711 	 * @throws Exception
712 	 */
713 	public static String getXPathValueFromString(String xmlString, String xPath)
714 	{
715 		try
716 		{
717 			Document doc = createDocumentFromString(xmlString);
718 			return getXPathValueFromDoc(doc, xPath);
719 		}
720 		catch (Exception e)
721 		{
722 			return null;
723 		}
724 	}
725 
726 	/**
727 	 * Given a Document, <code>xmlDoc</code>, retrieves the text of an
728 	 * element represented by the <code>xPath</code> from the Document.
729 	 * Assumes there is only one element with <code>xPath</code>. Results are
730 	 * undetermined if there is more than one Element with <code>xPath</code>.
731 	 * 
732 	 * @param xmlDoc
733 	 * @param xPath
734 	 * @return the text of the element represented by the xpath
735 	 * @throws Exception
736 	 */
737 	public static String getXPathValueFromDoc(Document xmlDoc, String xPath) throws Exception
738 	{
739 		Element e;
740 		String xmlValue = null;
741 		XPathExpression xpe;
742 		if (xmlDoc == null)
743 		{
744 			throw new Exception("XMLUtils:getXPathValueFromDoc. Null document passed.");
745 		}
746 
747 		if (isSet(xPath) && xPath.endsWith("/"))
748 		{
749 			xPath = xPath.substring(0, xPath.lastIndexOf("/"));
750 		}
751 		
752 		xpe = xPathInstance.compile(xPath);
753 		e = (Element) xpe.evaluate(xmlDoc, XPathConstants.NODE);
754 		if (e != null)
755 		{
756 			if (e.getTextTrim() != null)
757 			{
758 				xmlValue = e.getTextTrim();
759 			}
760 		}
761 		else
762 		{
763 			logger.error("XMLUtils:getXPathValueFromDoc.  No value found for " + xPath + ".");
764 			return null;
765 		}
766 		return xmlValue;
767 	}
768 
769 	/**
770 	 * Given an <code>inputStream</code>, creates a Document and retrieves
771 	 * the text of an element represented by the <code>xPath</code> from the
772 	 * Document. Assumes there is only one element with <code>xPath</code>.
773 	 * Results are undetermined if there is more than one Element with
774 	 * <code>xPath</code>.
775 	 * 
776 	 * @param inputStream
777 	 *            An input stream used to create a Document.
778 	 * @param xPath
779 	 * @return The text content of the element.
780 	 * @throws JaxenException
781 	 * @throws JDOMException
782 	 * @throws IOException
783 	 */
784 	public static String getXPathValueFromStream(InputStream inputStream, String xPath) throws Exception
785 	{
786 		Document doc = createDocumentFromStream(inputStream);
787 		return getXPathValueFromDoc(doc, xPath);
788 	}
789 
790 	/**
791 	 * Given a Document, <code>xmlDoc</code> writes the content of the
792 	 * document to the logger for this class. Exceptions are caught;an error in
793 	 * this functionality does not stop the processing.
794 	 * @param xmlDoc the Document to log
795 	 */
796 	public static void writeMetadataToLog(Document xmlDoc)
797 	{
798 		try
799 		{
800 			Format format = Format.getPrettyFormat();
801 			format.setEncoding(DEFAULT_ENCODING);
802 			XMLOutputter xmlOut = new XMLOutputter(format);
803 			logger.debug("\n" + xmlOut.outputString(xmlDoc));
804 		}
805 		catch (Exception e)
806 		{
807 			logger.error("IOException in XMLUtils: writeMetadataToLog with document. No action taken", e);
808 		}
809 	}
810 
811 	/**
812 	 * Given an input Stream, writes the content of the stream to the logger
813 	 * for this class. Exceptions are caught;an error in this functionality does not
814 	 * stop the processing.
815 	 * @param inputStream
816 	 */
817 	public static void writeMetadataToLog(InputStream inputStream)
818 	{
819 		if (logger.isDebugEnabled())
820 		{
821 			try
822 			{
823 				Document doc = createDocumentFromStream(inputStream);
824 				Format format = Format.getPrettyFormat();
825 				format.setEncoding(DEFAULT_ENCODING);
826 				XMLOutputter xmlOut = new XMLOutputter(format);
827 				xmlOut.outputString(doc);
828 			}
829 			catch (Exception ex)
830 			{
831 				logger.error("Exception in XMLUtils with document. No action taken", ex);
832 			}
833 			finally
834 			{
835 				try
836 				{
837 					inputStream.close();
838 				}
839 				catch (IOException ioex)
840 				{
841 					logger.error("Unable to close stream. No action taken.");
842 				}
843 			}
844 		}
845 	}
846 
847 	/**
848 	 * Given a Document, <code>newDoc</code> and a
849 	 * <code>metadataOutputStream</code>, writes the content of the Document
850 	 * to the stream. Exceptions are caught; an error in this functionality does
851 	 * not stop processing.
852 	 * @param newDoc
853 	 *            the metadata as a Document
854 	 * @param metadataOutputStream
855 	 *            an OutputStream to which the contents of the Document are
856 	 *            written.
857 	 */
858 	public static void writeMetadataToOutputStream(Document newDoc, OutputStream metadataOutputStream)
859 	{
860 		try
861 		{
862 			Format format = Format.getPrettyFormat();
863 			format.setEncoding(DEFAULT_ENCODING);
864 			XMLOutputter xmlOut = new XMLOutputter(format);
865 			xmlOut.output(newDoc, metadataOutputStream);
866 		}
867 		catch (IOException ioex)
868 		{
869 			logger.error("IOException in XMLUtils: No action taken.", ioex);
870 		}
871 	}
872 
873 }