001    /*
002     * Common usable utilities
003     *
004     * Copyright (c) 2006
005     *   Petr Hadraba <hadrabap@bluetone.cz>
006     *
007     * Author: Petr Hadraba
008     *
009     * --
010     *
011     * XML Utilities
012     */
013    
014    package global.sandbox.xmlutilities;
015    
016    import java.io.BufferedInputStream;
017    import java.io.File;
018    import java.io.FileInputStream;
019    import java.io.FileNotFoundException;
020    import java.io.IOException;
021    import java.io.InputStream;
022    import java.io.StringWriter;
023    import java.util.Iterator;
024    import java.util.Map;
025    
026    import javax.xml.XMLConstants;
027    import javax.xml.namespace.NamespaceContext;
028    import javax.xml.parsers.DocumentBuilder;
029    import javax.xml.parsers.DocumentBuilderFactory;
030    import javax.xml.parsers.ParserConfigurationException;
031    import javax.xml.transform.Source;
032    import javax.xml.transform.Templates;
033    import javax.xml.transform.Transformer;
034    import javax.xml.transform.TransformerConfigurationException;
035    import javax.xml.transform.TransformerException;
036    import javax.xml.transform.TransformerFactory;
037    import javax.xml.transform.stream.StreamResult;
038    import javax.xml.validation.SchemaFactory;
039    import javax.xml.validation.Validator;
040    import javax.xml.xpath.XPath;
041    import javax.xml.xpath.XPathConstants;
042    import javax.xml.xpath.XPathExpressionException;
043    import javax.xml.xpath.XPathFactory;
044    import javax.xml.xpath.XPathFactoryConfigurationException;
045    
046    import org.w3c.dom.Document;
047    import org.w3c.dom.Node;
048    import org.w3c.dom.NodeList;
049    import org.xml.sax.SAXException;
050    
051    /**
052     * This class provides simple XML utilities.
053     * 
054     * @author Petr Hadraba
055     * 
056     * @version 1.1
057     */
058    public class XMLUtilities {
059        
060        /**
061         * stores schema factory
062         */
063        private final SchemaFactory schemaFactory;
064        
065        /**
066         * stores document builder factory
067         */
068        private final DocumentBuilderFactory documentBuilderFactory;
069        
070        /**
071         * stores document builder
072         */
073        private final DocumentBuilder documentBuilder;
074        
075        /**
076         * stores localTransformer factory
077         */
078        private final TransformerFactory transformerFactory;
079        
080        /**
081         * stores localTransformer
082         */
083        private final Transformer transformer;
084        
085        /**
086         * stores XPath factory
087         */
088        private final XPathFactory xpathFactory;
089        
090        /**
091         * stores xpath evaluator
092         */
093        private final XPath xpath;
094        
095        /**
096         * stores the default NamespaceContext
097         */
098        private final NamespaceContext defaultNamespaceContext;
099        
100        /**
101         * initializes XMLUtilities
102         * 
103         * Creates new factories and new instanties
104         * 
105         * @throws ParserConfigurationException
106         * @throws TransformerConfigurationException
107         * @throws XPathFactoryConfigurationException
108         */
109        public XMLUtilities() throws ParserConfigurationException,
110                TransformerConfigurationException,
111                XPathFactoryConfigurationException {
112            this(null, null, null, null, null, null, null, null);
113        }
114        
115        /**
116         * initializes XMLUtilities
117         * 
118         * Creates new factories and new instaties according to custom objects.
119         * 
120         * @param documentBuilderFactory
121         *            custom DocumentBuilderFactory
122         * @param documentBuilder
123         *            custom DocumentBuilder
124         * @param schemaFactory
125         *            custom SchemaFactory
126         * @param transformerFactory
127         *            custom TransformerFactory
128         * @param transformer
129         *            custom Transformer
130         * @param xpathFactory
131         *            custom XPathFactory
132         * @param xpath
133         *            custom XPath
134         * @param defaultNamespaceContext
135         *            custom namespaces
136         * 
137         * @throws ParserConfigurationException
138         * @throws TransformerConfigurationException
139         * @throws XPathFactoryConfigurationException
140         */
141        public XMLUtilities(final DocumentBuilderFactory documentBuilderFactory,
142                final DocumentBuilder documentBuilder,
143                final SchemaFactory schemaFactory,
144                final TransformerFactory transformerFactory,
145                final Transformer transformer, final XPathFactory xpathFactory,
146                final XPath xpath, final NamespaceContext defaultNamespaceContext)
147                throws ParserConfigurationException,
148                TransformerConfigurationException,
149                XPathFactoryConfigurationException {
150            super();
151            
152            if (documentBuilderFactory == null) {
153                this.documentBuilderFactory = DocumentBuilderFactory.newInstance();
154                this.documentBuilderFactory.setNamespaceAware(true);
155            } else {
156                this.documentBuilderFactory = documentBuilderFactory;
157            }
158            
159            if (documentBuilder == null) {
160                this.documentBuilder = this.documentBuilderFactory
161                        .newDocumentBuilder();
162            } else {
163                this.documentBuilder = documentBuilder;
164            }
165            
166            if (schemaFactory == null) {
167                this.schemaFactory = SchemaFactory
168                        .newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
169            } else {
170                this.schemaFactory = schemaFactory;
171            }
172            
173            if (transformerFactory == null) {
174                this.transformerFactory = TransformerFactory.newInstance();
175            } else {
176                this.transformerFactory = transformerFactory;
177            }
178            
179            if (transformer == null) {
180                this.transformer = this.transformerFactory.newTransformer();
181            } else {
182                this.transformer = transformer;
183            }
184            
185            if (xpathFactory == null) {
186                this.xpathFactory = XPathFactory
187                        .newInstance(XPathFactory.DEFAULT_OBJECT_MODEL_URI);
188            } else {
189                this.xpathFactory = xpathFactory;
190            }
191            
192            if (xpath == null) {
193                this.xpath = this.xpathFactory.newXPath();
194            } else {
195                this.xpath = xpath;
196            }
197            
198            if (defaultNamespaceContext == null) {
199                this.defaultNamespaceContext = new NamespaceContextImpl();
200            } else {
201                this.defaultNamespaceContext = defaultNamespaceContext;
202            }
203        }
204        
205        /**
206         * obtains SchemaFactory object internaly used by the XMLUtilities
207         * 
208         * @return SchemaFactory object
209         */
210        public SchemaFactory getSchemaFactory() {
211            return schemaFactory;
212        }
213        
214        /**
215         * obtains DocumentBuilderFactory object internaly used by the XMLUtilities
216         * 
217         * @return DocumentBuilderFactory object
218         */
219        public DocumentBuilderFactory getDocumentBuilderFactory() {
220            return documentBuilderFactory;
221        }
222        
223        /**
224         * obtains DocumentBuilder object internaly used by the XMLUtilities
225         * 
226         * @return DocumentBuilder object
227         */
228        public DocumentBuilder getDocumentBuilder() {
229            return documentBuilder;
230        }
231        
232        /**
233         * obtains TransformerFactory object internaly used by the XMLUtilities
234         * 
235         * @return TransformerFactory object
236         */
237        public TransformerFactory getTransformerFactory() {
238            return transformerFactory;
239        }
240        
241        /**
242         * obtains Transformer object internaly used by the XMLUtilities
243         * 
244         * @return Transformer object
245         */
246        public Transformer getTransformer() {
247            return transformer;
248        }
249        
250        /**
251         * obtains XPathFactory object internaly used by the XMLUtilities
252         * 
253         * @return XPathFactory object
254         */
255        public XPathFactory getXPathFactory() {
256            return xpathFactory;
257        }
258        
259        /**
260         * obtains XPath object internaly used by the XMLUtilities
261         * 
262         * @return XPath object
263         */
264        public XPath getXPath() {
265            return xpath;
266        }
267        
268        /**
269         * obtains default NamespaceContext used by the XMLUtilities
270         *
271         * @return default NamespaceContenxt object
272         */
273        public NamespaceContext getDefaultNamespaceContext() {
274            return defaultNamespaceContext;
275        }
276        
277        /**
278         * validates XML file using XML schema
279         * 
280         * @param xmlDocument
281         *            document to validate
282         * @param xmlSchema
283         *            XMLSchema
284         * 
285         * @return null if successful, string with error otherwise
286         * 
287         * @throws IOException
288         */
289        public String validateXmlUsingSchema(final Source xmlDocument,
290                final Source xmlSchema) throws IOException {
291            final Validator validator;
292            
293            try {
294                validator = schemaFactory.newSchema(xmlSchema).newValidator();
295                
296                validator.validate(xmlDocument);
297            } catch (SAXException e) {
298                return e.getMessage();
299            }
300            
301            return null;
302        }
303        
304        /**
305         * loads XML from file specified with file name
306         * 
307         * @param fileName
308         *            file to load
309         * 
310         * @return Document object
311         * 
312         * @throws SAXException
313         * @throws IOException
314         * @throws FileNotFoundException
315         */
316        public Document loadDocumentFromFile(final String fileName)
317                throws FileNotFoundException, IOException, SAXException {
318            return loadDocumentFromFile(new File(fileName));
319        }
320        
321        /**
322         * loads XML from file specified with file name
323         * 
324         * @param fileName
325         *            file to load
326         * @param documentBuilder
327         *            custom document builder
328         * 
329         * @return Document object
330         * 
331         * @throws SAXException
332         * @throws IOException
333         * @throws FileNotFoundException
334         */
335        public Document loadDocumentFromFile(final String fileName,
336                final DocumentBuilder documentBuilder)
337                throws FileNotFoundException, IOException, SAXException {
338            return loadDocumentFromFile(new File(fileName), documentBuilder);
339        }
340        
341        /**
342         * loads XML from file specified with the File object
343         * 
344         * @param file
345         *            file to load
346         * 
347         * @return Document object
348         * 
349         * @throws SAXException
350         * @throws IOException
351         * @throws FileNotFoundException
352         */
353        public Document loadDocumentFromFile(final File file)
354                throws FileNotFoundException, IOException, SAXException {
355            return loadDocumentFromFile(file, documentBuilder);
356        }
357        
358        /**
359         * loads XML from file specified with the file object
360         * 
361         * @param file
362         *            file to load
363         * @param documentBuilder
364         *            custom document builder
365         * 
366         * @return Document object
367         * 
368         * @throws FileNotFoundException
369         * @throws IOException
370         * @throws SAXException
371         */
372        public Document loadDocumentFromFile(final File file,
373                final DocumentBuilder documentBuilder)
374                throws FileNotFoundException, IOException, SAXException {
375            final InputStream inputStream = new FileInputStream(file);
376            
377            final Document document;
378            try {
379                document = loadDocumentFromStream(inputStream, documentBuilder);
380            } finally {
381                inputStream.close();
382            }
383            
384            return document;
385        }
386        
387        /**
388         * loads XML from specified input stream
389         * 
390         * @param is
391         *            input stream
392         * 
393         * @return Document object
394         * 
395         * @throws IOException
396         * @throws SAXException
397         */
398        public Document loadDocumentFromStream(final InputStream is)
399                throws IOException, SAXException {
400            return loadDocumentFromStream(is, documentBuilder);
401        }
402        
403        /**
404         * loads XML from specified input stream
405         * 
406         * @param is
407         *            input stream
408         * @param documentBuilder
409         *            custom Document Builder
410         * 
411         * @return Document object
412         * 
413         * @throws IOException
414         * @throws SAXException
415         */
416        public Document loadDocumentFromStream(final InputStream is,
417                final DocumentBuilder documentBuilder) throws IOException,
418                SAXException {
419            final BufferedInputStream bufferedInputStream
420                    = new BufferedInputStream(is);
421            
422            final Document document = documentBuilder.parse(bufferedInputStream);
423            
424            bufferedInputStream.close();
425            
426            return document;
427        }
428        
429        /**
430         * converts XML Document into String
431         * 
432         * @param source
433         *            Document to convert
434         * 
435         * @return XML in the String
436         * 
437         * @throws TransformerException
438         * @throws IOException
439         */
440        public String documentToString(final Document source)
441                throws TransformerException, IOException {
442            return sourceToString(XMLTools.documentToDOMSource(source));
443        }
444        
445        /**
446         * converts XML Source into String
447         * 
448         * @param source
449         *            Document to convert
450         * 
451         * @return XML in the String
452         * 
453         * @throws TransformerException
454         * @throws IOException
455         */
456        public String sourceToString(final Source source)
457                throws TransformerException, IOException {
458            final StringWriter stringWriter = new StringWriter();
459            final StreamResult streamResult = new StreamResult(stringWriter);
460            
461            transformer.setParameter("", "");
462            transformer.clearParameters();
463            //localTransformer.reset();
464            
465            transformer.transform(source, streamResult);
466            
467            final String result = stringWriter.toString();
468            
469            stringWriter.close();
470            
471            return result;
472        }
473        
474        /**
475         * evaluates specified XPath expression on specified contenxt node
476         * 
477         * @param query
478         *            XPath expression
479         * @param context
480         *            context node
481         * 
482         * @return NodeList or null if no matches
483         * 
484         * @throws XPathExpressionException
485         */
486        public NodeList evaluateXPath(final String query, final Node context)
487                throws XPathExpressionException {
488            return evaluateXPath(query, context, null);
489        }
490        
491        /**
492         * evaluates specified XPath expression on specified contenxt node
493         * 
494         * @param query
495         *            XPath expression
496         * @param context
497         *            context node
498         * @param namespaces
499         *            namespace context
500         * 
501         * @return NodeList or null if no matches
502         * 
503         * @throws XPathExpressionException
504         */
505        public NodeList evaluateXPath(final String query, final Node context,
506                final NamespaceContext namespaces) throws XPathExpressionException {
507            xpath.reset();
508            
509            if (namespaces == null) {
510                xpath.setNamespaceContext(defaultNamespaceContext);
511            } else {
512                xpath.setNamespaceContext(namespaces);
513            }
514            
515            return (NodeList) xpath
516                    .evaluate(query, context, XPathConstants.NODESET);
517        }
518        
519        /**
520         * evaluates specified XPath expression and returnes first Node if XPath has
521         * matches
522         * 
523         * @param query
524         *            XPath expression
525         * @param context
526         *            context node
527         * 
528         * @return first node or null
529         * 
530         * @throws XPathExpressionException
531         */
532        public Node getFirstNodeForXPath(final String query, final Node context)
533                throws XPathExpressionException {
534            return getFirstNodeForXPath(query, context, null);
535        }
536        
537        /**
538         * evaluates specified XPath expression and returnes first Node if XPath has
539         * matches
540         * 
541         * @param query
542         *            XPath expression
543         * @param context
544         *            context node
545         * @param namespaces
546         *            namespace context
547         * 
548         * @return first node or null
549         * 
550         * @throws XPathExpressionException
551         */
552        public Node getFirstNodeForXPath(final String query, final Node context,
553                NamespaceContext namespaces) throws XPathExpressionException {
554            final NodeList nodes = evaluateXPath(query, context, namespaces);
555            
556            if (nodes == null) {
557                return null;
558            }
559            
560            if (nodes.getLength() > 0) {
561                return nodes.item(0);
562            }
563            
564            return null;
565        }
566        
567        /**
568         * transformes specified XML source using specified XSLT template
569         * 
570         * @param xsltTemplate
571         *            template to use
572         * @param document
573         *            source XML document
574         * @param parameters
575         *            parameters to propagate to the localTransformer
576         * 
577         * @return resulting XML in the <code>String</code>
578         * 
579         * @throws TransformerConfigurationException
580         * @throws TransformerException
581         */
582        public String transformToString(final Source xsltTemplate,
583                final Source document, final Map<String, Object> parameters)
584                throws TransformerConfigurationException, TransformerException {
585            final Transformer localTransformer = transformerFactory
586                    .newTransformer(xsltTemplate);
587            
588            if (parameters != null) {
589                final Iterator<String> it = parameters.keySet().iterator();
590                
591                while (it.hasNext()) {
592                    final String parameterName = it.next();
593                    
594                    localTransformer.setParameter(parameterName, parameters
595                            .get(parameterName));
596                }
597            }
598            
599            final StringWriter sw = new StringWriter();
600            final StreamResult sr = new StreamResult(sw);
601            
602            localTransformer.transform(document, sr);
603            
604            final String result = sw.toString();
605            
606            try {
607                sw.close();
608            } catch (IOException e) {
609                return null;
610            }
611            
612            return result;
613        }
614        
615        /**
616         * transforms specified XML source using specified XSLT (compiled) template
617         * 
618         * @param xsltTemplate
619         *            template to use
620         * @param document
621         *            source XML document to transform
622         * @param parameters
623         *            parameters to propagate to the localTransformer
624         * 
625         * @return resulting XML in the <code>String</code>
626         * 
627         * @throws TransformerException
628         */
629        public String transformToString(final Templates xsltTemplate,
630                final Source document, final Map<String, Object> parameters)
631                throws TransformerException {
632            final Transformer localTransformer = xsltTemplate.newTransformer();
633            
634            if (parameters != null) {
635                final Iterator<String> it = parameters.keySet().iterator();
636                
637                while (it.hasNext()) {
638                    final String parameterName = it.next();
639                    
640                    localTransformer.setParameter(parameterName, parameters
641                            .get(parameterName));
642                }
643            }
644            
645            final StringWriter sw = new StringWriter();
646            final StreamResult sr = new StreamResult(sw);
647            
648            localTransformer.transform(document, sr);
649            
650            final String result = sw.toString();
651            
652            try {
653                sw.close();
654            } catch (IOException e) {
655                return null;
656            }
657            
658            return result;
659        }
660        
661        /**
662         * transforms specified XML source using specified XSLT (compiled) template
663         * 
664         * @param xsltTemplate
665         *            template to use
666         * @param document
667         *            source XML document to transform
668         * 
669         * @return resulting XML in the <code>String</code>
670         * 
671         * @throws TransformerException
672         */
673        public String transformToString(final Templates xsltTemplate,
674                final Source document) throws TransformerException {
675            return transformToString(xsltTemplate, document, null);
676        }
677        
678        /**
679         * transformes specified XML document using specified XSLT template
680         * 
681         * @param xsltTemplate
682         *            XSLT template
683         * @param document
684         *            source XML document
685         * 
686         * @return resulting XML document in the <code>String</code>
687         * 
688         * @throws TransformerConfigurationException
689         * @throws TransformerException
690         */
691        public String transformToString(final Source xsltTemplate,
692                final Source document) throws TransformerConfigurationException,
693                TransformerException {
694            return transformToString(xsltTemplate, document, null);
695        }
696        
697        /**
698         * transformes specified XML document using specified XSLT template
699         * 
700         * @param xsltTemplate
701         *            XSLT template
702         * @param document
703         *            source XML document
704         * @return resulting XML document in the <code>Document</code>
705         * 
706         * @throws TransformerConfigurationException
707         * @throws XPathFactoryConfigurationException
708         * @throws ParserConfigurationException
709         * @throws SAXException
710         * @throws IOException
711         * @throws TransformerException
712         */
713        public Document transformToDocument(final Source xsltTemplate,
714                final Source document) throws TransformerConfigurationException,
715                XPathFactoryConfigurationException, ParserConfigurationException,
716                SAXException, IOException, TransformerException {
717            final XMLUtilities xmlUtils = new XMLUtilities();
718            
719            return XMLTools.loadDocumentFromString(transformToString(xsltTemplate,
720                    document, null), xmlUtils);
721        }
722        
723        /**
724         * transforms specified XML document using XSLT (compiled) template
725         * 
726         * @param xsltTemplate
727         *            template to use
728         * @param document
729         *            document to transform
730         * 
731         * @return resulting XML document in the <code>Document</code>
732         * 
733         * @throws TransformerConfigurationException
734         * @throws XPathFactoryConfigurationException
735         * @throws ParserConfigurationException
736         * @throws SAXException
737         * @throws IOException
738         * @throws TransformerException
739         */
740        public Document transformToDocument(final Templates xsltTemplate,
741                final Source document) throws TransformerConfigurationException,
742                XPathFactoryConfigurationException, ParserConfigurationException,
743                SAXException, IOException, TransformerException {
744            final XMLUtilities xmlUtils = new XMLUtilities();
745            
746            return XMLTools.loadDocumentFromString(transformToString(xsltTemplate,
747                    document, null), xmlUtils);
748        }
749        
750        /**
751         * transformes specified XML document using specified XSLT template
752         * 
753         * @param xsltTemplate
754         *            XSLT template
755         * @param document
756         *            source XML document
757         * @param parameters
758         *            parameters for the template
759         * @return resulting XML document in the <code>Document</code>
760         * 
761         * @throws TransformerConfigurationException
762         * @throws XPathFactoryConfigurationException
763         * @throws ParserConfigurationException
764         * @throws SAXException
765         * @throws IOException
766         * @throws TransformerException
767         */
768        public Document transformToDocument(final Source xsltTemplate,
769                final Source document, final Map<String, Object> parameters)
770                throws TransformerConfigurationException,
771                XPathFactoryConfigurationException, ParserConfigurationException,
772                SAXException, IOException, TransformerException {
773            final XMLUtilities xmlUtils = new XMLUtilities();
774            
775            return XMLTools.loadDocumentFromString(transformToString(xsltTemplate,
776                    document, parameters), xmlUtils);
777        }
778        
779        /**
780         * transforms sepecified XML using XSLT (compiled) template
781         * 
782         * @param xsltTemplate
783         *            template to use
784         * @param document
785         *            XML document to transform
786         * @param parameters
787         *            parameters for the transformation
788         * 
789         * @return resulting XML document in the <code>Document</code>
790         * 
791         * @throws TransformerConfigurationException
792         * @throws XPathFactoryConfigurationException
793         * @throws ParserConfigurationException
794         * @throws SAXException
795         * @throws IOException
796         * @throws TransformerException
797         */
798        public Document transformToDocument(final Templates xsltTemplate,
799                final Source document, final Map<String, Object> parameters)
800                throws TransformerConfigurationException,
801                XPathFactoryConfigurationException, ParserConfigurationException,
802                SAXException, IOException, TransformerException {
803            final XMLUtilities xmlUtils = new XMLUtilities();
804            
805            return XMLTools.loadDocumentFromString(transformToString(xsltTemplate,
806                    document, parameters), xmlUtils);
807        }
808        
809    }