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