001 /* 002 @license.text@ 003 */ 004 005 package biz.hammurapi.xml.dom; 006 007 import java.io.File; 008 import java.io.FileInputStream; 009 import java.io.FileOutputStream; 010 import java.io.IOException; 011 import java.io.InputStream; 012 import java.io.OutputStream; 013 import java.io.Reader; 014 import java.io.StringWriter; 015 import java.io.Writer; 016 import java.util.Iterator; 017 import java.util.Map; 018 import java.util.Properties; 019 020 import javax.xml.namespace.QName; 021 import javax.xml.parsers.DocumentBuilderFactory; 022 import javax.xml.parsers.FactoryConfigurationError; 023 import javax.xml.parsers.ParserConfigurationException; 024 import javax.xml.transform.Result; 025 import javax.xml.transform.Transformer; 026 import javax.xml.transform.TransformerException; 027 import javax.xml.transform.TransformerFactory; 028 import javax.xml.transform.dom.DOMSource; 029 import javax.xml.transform.stream.StreamResult; 030 import javax.xml.transform.stream.StreamSource; 031 import javax.xml.xpath.XPath; 032 import javax.xml.xpath.XPathConstants; 033 import javax.xml.xpath.XPathExpressionException; 034 import javax.xml.xpath.XPathFactory; 035 036 import org.w3c.dom.Document; 037 import org.w3c.dom.Element; 038 import org.w3c.dom.NamedNodeMap; 039 import org.w3c.dom.Node; 040 import org.w3c.dom.NodeList; 041 import org.xml.sax.InputSource; 042 import org.xml.sax.SAXException; 043 044 import biz.hammurapi.config.ConfigurationException; 045 046 /** 047 * Utility class for querying DOM. 048 * 049 * @author Pavel Vlasov 050 */ 051 public class DOMUtils { 052 053 /** Creates a new instance of DOMUtils */ 054 private DOMUtils() { 055 // Utility class 056 } 057 058 /** 059 * Ensures that thre is one and only one element and returns it. 060 * @throws XPathExpressionException 061 */ 062 public static Element getSingleElement(Element e, String elementName) throws ConfigurationException, XPathExpressionException { 063 NodeList nl = selectNodeList(e, elementName); 064 if (nl.getLength() == 0) { 065 throw new ConfigurationException("Element <" + elementName 066 + "> not found"); 067 } 068 if (nl.getLength() > 1) { 069 throw new ConfigurationException("Duplicate element <" 070 + elementName + ">"); 071 } 072 073 return (Element) nl.item(0); 074 } 075 076 /** 077 * Ensures that there is only one element and returns its text 078 * @throws TransformerException 079 * @throws XPathExpressionException 080 * @throws TransformerException 081 */ 082 public static String getSingleElementText(Element e, String elementName) throws ConfigurationException, XPathExpressionException { 083 return getElementText(getSingleElement(e, elementName)); 084 } 085 086 public static String getSingleNonBlankElementText(Element e, String elementName) throws ConfigurationException, XPathExpressionException { 087 return getNonBlankElementText(getSingleElement(e, elementName)); 088 } 089 090 public static String getElementText(Element e) throws XPathExpressionException { 091 return eval(e, "text()").toString(); 092 } 093 094 public static String getNonBlankElementText(Element e) throws ConfigurationException, XPathExpressionException { 095 String res = getElementText(e); 096 if (res.trim().length() == 0) { 097 throw new ConfigurationException("Element <" + e.getNodeName() + "> is blank"); 098 } 099 return res; 100 } 101 102 public static Properties dom2Properties(Element e, String prefix) throws XPathExpressionException { 103 String root = prefix == null ? e.getNodeName() : prefix + "." 104 + e.getNodeName(); 105 Properties res = new Properties(); 106 String text = getElementText(e); 107 if (text.trim().length() != 0) { 108 res.setProperty(root, text); 109 } 110 111 NamedNodeMap attributes = e.getAttributes(); 112 for (int i=0, l=attributes.getLength(); i<l; i++) { 113 Node attribute=attributes.item(i); 114 res.setProperty(root+"("+attribute.getNodeName()+")", attribute.getNodeValue()); 115 } 116 117 118 NodeList nl = e.getChildNodes(); 119 for (int i = 0; i < nl.getLength(); i++) { 120 Node n = nl.item(i); 121 if (n.getNodeType() == Node.ELEMENT_NODE) { 122 res.putAll(dom2Properties((Element) n, root)); 123 } 124 } 125 126 return res; 127 } 128 129 /** 130 * Reads elements <property name="...">...</property> into 131 * properties 132 * @throws XPathExpressionException 133 */ 134 public static void readProperties(Element holder, Properties properties) throws ConfigurationException, XPathExpressionException { 135 NodeList nl = selectNodeList(holder, "property"); 136 for (int i=0, l=nl.getLength(); i<l; ++i) { 137 Element el = (Element) nl.item(i); 138 if (!el.hasAttribute("name")) { 139 throw new ConfigurationException("Unnamed property"); 140 } 141 if (properties.containsKey(el.getAttribute("name"))) { 142 throw new ConfigurationException("Property already set: " 143 + el.getAttribute("name")); 144 } 145 146 properties.setProperty(el.getAttribute("name"), DOMUtils.getNonBlankElementText(el)); 147 } 148 } 149 150 public static void serialize(Object o, String root, Result result) throws ParserConfigurationException, FactoryConfigurationError, TransformerException { 151 Document doc=DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument(); 152 Element rootElement = AbstractDomObject.addElement(doc, root); 153 toDom(o, rootElement); 154 TransformerFactory 155 .newInstance() 156 .newTransformer() 157 .transform(new DOMSource(doc), result); 158 } 159 160 public static void serialize(Object o, String root, File out) throws ParserConfigurationException, FactoryConfigurationError, IOException, TransformerException { 161 Document doc=DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument(); 162 Element rootElement = AbstractDomObject.addElement(doc, root); 163 toDom(o, rootElement); 164 FileOutputStream os = new FileOutputStream(out); 165 serialize(doc, os); 166 os.close(); 167 } 168 169 public static void serialize(Node node, File out) throws IOException, TransformerException { 170 FileOutputStream os = new FileOutputStream(out); 171 serialize(node, os); 172 os.close(); 173 } 174 175 public static void serialize(Node node, OutputStream os) throws TransformerException { 176 TransformerFactory 177 .newInstance() 178 .newTransformer() 179 .transform(new DOMSource(node), new StreamResult(os)); 180 } 181 182 public static void serialize(Node node, Writer w) throws TransformerException { 183 TransformerFactory 184 .newInstance() 185 .newTransformer() 186 .transform(new DOMSource(node), new StreamResult(w)); 187 } 188 189 public static String toString(Node node) throws IOException, TransformerException { 190 Transformer transformer = TransformerFactory.newInstance().newTransformer(); 191 192 //transformer.setOutputProperty("method", "xml"); 193 transformer.setOutputProperty("indent", "yes"); 194 transformer.setOutputProperty("omit-xml-declaration", "yes"); 195 196 StringWriter sw=new StringWriter(); 197 transformer.transform(new DOMSource(node), new StreamResult(sw)); 198 sw.close(); 199 return sw.toString(); 200 } 201 202 public static String toXmlString(Object o, String root) throws IOException, TransformerException, ParserConfigurationException, FactoryConfigurationError { 203 Document doc=DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument(); 204 Element rootElement = AbstractDomObject.addElement(doc, root); 205 toDom(o, rootElement); 206 return toString(rootElement); 207 } 208 209 public static String toXmlString(Object o, String root, CompositeDomSerializer domSerializer) throws IOException, TransformerException, ParserConfigurationException, FactoryConfigurationError { 210 Document doc=DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument(); 211 Element rootElement = AbstractDomObject.addElement(doc, root); 212 toDom(o, rootElement, domSerializer); 213 return toString(rootElement); 214 } 215 216 public static void style(Object o, String root, OutputStream os, InputStream style, Map parameters) throws ParserConfigurationException, FactoryConfigurationError, TransformerException { 217 Document doc=DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument(); 218 Element rootElement = AbstractDomObject.addElement(doc, root); 219 toDom(o, rootElement); 220 style(doc, os, style, parameters); 221 } 222 223 public static void style(Object o, String root, File out, InputStream style, Map parameters) throws ParserConfigurationException, FactoryConfigurationError, IOException, TransformerException { 224 Document doc=DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument(); 225 Element rootElement = AbstractDomObject.addElement(doc, root); 226 toDom(o, rootElement); 227 FileOutputStream os = new FileOutputStream(out); 228 style(doc, os, style, parameters); 229 os.close(); 230 } 231 232 public static void style(Document doc, File out, InputStream style, Map parameters) throws IOException, TransformerException { 233 FileOutputStream os = new FileOutputStream(out); 234 style(doc, os, style, parameters); 235 os.close(); 236 } 237 238 public static void style(Document doc, OutputStream os, InputStream style, Map parameters) throws TransformerException { 239 TransformerFactory factory=TransformerFactory.newInstance(); 240 Transformer transformer = style==null ? factory.newTransformer() : factory.newTransformer(new StreamSource(style)); 241 if (parameters!=null) { 242 Iterator it=parameters.entrySet().iterator(); 243 while (it.hasNext()) { 244 Map.Entry entry=(Map.Entry) it.next(); 245 transformer.setParameter((String) entry.getKey(), entry.getValue()); 246 } 247 } 248 transformer.transform(new DOMSource(doc), new StreamResult(os)); 249 } 250 251 public static Document parse(File file) throws SAXException, IOException, ParserConfigurationException, FactoryConfigurationError { 252 InputStream is=new FileInputStream(file); 253 try { 254 return parse(is); 255 } finally { 256 is.close(); 257 } 258 } 259 260 /** 261 * @param is 262 * @return 263 * @throws FactoryConfigurationError 264 * @throws ParserConfigurationException 265 * @throws IOException 266 * @throws SAXException 267 */ 268 public static Document parse(InputStream is) throws SAXException, IOException, ParserConfigurationException, FactoryConfigurationError { 269 return DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(is); 270 } 271 272 /** 273 * @param is 274 * @return 275 * @throws FactoryConfigurationError 276 * @throws ParserConfigurationException 277 * @throws IOException 278 * @throws SAXException 279 */ 280 public static Document parse(Reader is) throws SAXException, IOException, ParserConfigurationException, FactoryConfigurationError { 281 return DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new InputSource(is)); 282 } 283 284 /** 285 * Serializes object to XML. Specially treats collections, 286 * maps, arrays and DomSerializable objects. 287 * @param o 288 * @param holder 289 */ 290 public static void toDom(Object o, Element holder) { 291 if (o!=null) { 292 DomSerializable ds = CompositeDomSerializer.getThreadInstance().toDomSerializable(o); 293 if (ds!=null) { 294 ds.toDom(holder); 295 } 296 } 297 } 298 299 /** 300 * Serializes object to XML. Specially treats collections, 301 * maps, arrays and DomSerializable objects. 302 * @param o 303 * @param name Element name 304 * @param parent parent element 305 */ 306 public static void toDom(Object o, String name, Element parent) { 307 if (o!=null) { 308 DomSerializable ds = CompositeDomSerializer.getThreadInstance().toDomSerializable(o); 309 if (ds!=null) { 310 Element holder=parent.getOwnerDocument().createElement(name); 311 parent.appendChild(holder); 312 ds.toDom(holder); 313 } 314 } 315 } 316 317 public static Document toDom(Object o) throws ParserConfigurationException, FactoryConfigurationError { 318 Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument(); 319 Element root=document.createElement("root"); 320 document.appendChild(root); 321 toDom(o, root); 322 return document; 323 } 324 325 /** 326 * Serializes object to XML. Specially treats collections, 327 * maps, arrays and DomSerializable objects. 328 * @param o 329 * @param holder 330 */ 331 public static void toDom(Object o, Element holder, CompositeDomSerializer domSerializer) { 332 if (o!=null) { 333 DomSerializable ds = domSerializer.toDomSerializable(o); 334 if (ds!=null) { 335 ds.toDom(holder); 336 } 337 } 338 } 339 340 /** 341 * Serializes object to XML. Specially treats collections, 342 * maps, arrays and DomSerializable objects. 343 * @param o 344 * @param name Element name 345 * @param parent parent element 346 */ 347 public static void toDom(Object o, String name, Element parent, CompositeDomSerializer domSerializer) { 348 if (o!=null) { 349 DomSerializable ds = domSerializer.toDomSerializable(o); 350 if (ds!=null) { 351 Element holder=parent.getOwnerDocument().createElement(name); 352 parent.appendChild(holder); 353 ds.toDom(holder); 354 } 355 } 356 } 357 358 public static Document toDom(Object o, CompositeDomSerializer domSerializer) throws ParserConfigurationException, FactoryConfigurationError { 359 Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument(); 360 Element root=document.createElement("root"); 361 document.appendChild(root); 362 toDom(o, root, domSerializer); 363 return document; 364 } 365 366 public static String eval(Object item, String str) throws XPathExpressionException { 367 return getXPath().evaluate(str, item); 368 } 369 370 public static Object eval(Object item, String str, QName returnType) throws XPathExpressionException { 371 return getXPath().evaluate(str, item, returnType); 372 } 373 374 private static XPath getXPath() { 375 XPathFactory xPathFactory = XPathFactory.newInstance(); 376 XPath xpath = xPathFactory.newXPath(); 377 return xpath; 378 } 379 380 public static NodeList selectNodeList(Node contextNode, String str) throws XPathExpressionException { 381 return (NodeList) eval(contextNode, str, XPathConstants.NODESET); 382 } 383 384 public static Node selectSingleNode(Node contextNode, String str) throws XPathExpressionException { 385 NodeList nl = selectNodeList(contextNode, str); 386 return nl.getLength()==0 ? null : nl.item(0); 387 } 388 389 }