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