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 &lt;property name="..."&gt;...&lt/property&gt; 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    }