001    /*
002     * Common usable utilities
003     *
004     * Copyright (c) 2006 Petr Hadraba <hadrabap@gmail.com>
005     *
006     * Author: Petr Hadraba
007     *
008     * --
009     *
010     * XML Utilities
011     */
012    
013    package global.sandbox.xmlutilities;
014    
015    import java.io.BufferedInputStream;
016    import java.io.BufferedOutputStream;
017    import java.io.BufferedReader;
018    import java.io.File;
019    import java.io.FileInputStream;
020    import java.io.FileNotFoundException;
021    import java.io.FileOutputStream;
022    import java.io.IOException;
023    import java.io.InputStream;
024    import java.io.OutputStream;
025    import java.io.StringReader;
026    import java.io.StringWriter;
027    import java.util.Map;
028    import java.util.Properties;
029    import java.util.logging.Level;
030    import java.util.logging.Logger;
031    import javax.xml.XMLConstants;
032    import javax.xml.namespace.NamespaceContext;
033    import javax.xml.parsers.DocumentBuilder;
034    import javax.xml.parsers.DocumentBuilderFactory;
035    import javax.xml.parsers.ParserConfigurationException;
036    import javax.xml.transform.Source;
037    import javax.xml.transform.Templates;
038    import javax.xml.transform.Transformer;
039    import javax.xml.transform.TransformerConfigurationException;
040    import javax.xml.transform.TransformerException;
041    import javax.xml.transform.TransformerFactory;
042    import javax.xml.transform.TransformerFactoryConfigurationError;
043    import javax.xml.transform.dom.DOMSource;
044    import javax.xml.transform.stream.StreamResult;
045    import javax.xml.validation.SchemaFactory;
046    import javax.xml.validation.Validator;
047    import javax.xml.xpath.XPath;
048    import javax.xml.xpath.XPathConstants;
049    import javax.xml.xpath.XPathExpressionException;
050    import javax.xml.xpath.XPathFactory;
051    import javax.xml.xpath.XPathFactoryConfigurationException;
052    import org.w3c.dom.Document;
053    import org.w3c.dom.Node;
054    import org.w3c.dom.NodeList;
055    import org.xml.sax.InputSource;
056    import org.xml.sax.SAXException;
057    
058    /**
059     * This class provides simple XML utilities.
060     *
061     * @author Petr Hadraba
062     *
063     * @version 1.2
064     */
065    public class XmlUtilities {
066    
067        /**
068         * Name of internal logger.
069         */
070        static final String XMLUTILITIES_LOGGER_NAME;
071    
072        static {
073            XMLUTILITIES_LOGGER_NAME = XmlUtilities.class.getName();
074        }
075    
076        /**
077         * Internal logger.
078         */
079        private static final Logger LOGGER = Logger.getLogger(XMLUTILITIES_LOGGER_NAME);
080    
081        /**
082         * Stores schema factory.
083         */
084        private final SchemaFactory schemaFactory;
085    
086        /**
087         * Stores document builder factory.
088         */
089        private final DocumentBuilderFactory documentBuilderFactory;
090    
091        /**
092         * Stores document builder.
093         */
094        private final DocumentBuilder documentBuilder;
095    
096        /**
097         * Stores localTransformer factory.
098         */
099        private final TransformerFactory transformerFactory;
100    
101        /**
102         * Stores localTransformer.
103         */
104        private final Transformer transformer;
105    
106        /**
107         * Stores XPath factory.
108         */
109        private final XPathFactory xpathFactory;
110    
111        /**
112         * Stores XPath evaluator.
113         */
114        private final XPath xpath;
115    
116        /**
117         * Stores the default NamespaceContext.
118         */
119        private final NamespaceContext defaultNamespaceContext;
120    
121        /**
122         * Initializes XML Utilities.
123         *
124         * Creates new factories and new default instances.
125         *
126         * @throws XmlUtilitiesException on error
127         */
128        public XmlUtilities() throws XmlUtilitiesException {
129            this(null, null, null, null, null, null, null, null);
130        }
131    
132        /**
133         * Initializes XML Utilities.
134         *
135         * Creates new factories and new instances according to custom objects.
136         *
137         * @param documentBuilderFactory custom DocumentBuilderFactory
138         * @param documentBuilder custom DocumentBuilder
139         * @param schemaFactory custom SchemaFactory
140         * @param transformerFactory custom TransformerFactory
141         * @param transformer custom Transformer
142         * @param xpathFactory custom XPathFactory
143         * @param xpath custom XPath
144         * @param defaultNamespaceContext custom name spaces
145         *
146         * @throws XmlUtilitiesException on error
147         */
148        public XmlUtilities(
149                final DocumentBuilderFactory documentBuilderFactory,
150                final DocumentBuilder documentBuilder,
151                final SchemaFactory schemaFactory,
152                final TransformerFactory transformerFactory,
153                final Transformer transformer,
154                final XPathFactory xpathFactory,
155                final XPath xpath,
156                final NamespaceContext defaultNamespaceContext)
157                throws XmlUtilitiesException {
158            try {
159                if (documentBuilderFactory == null) {
160                    LOGGER.log(Level.FINEST, "Using default Name Space aware DocumentBuilderFactory.");
161                    this.documentBuilderFactory = DocumentBuilderFactory.newInstance();
162                    this.documentBuilderFactory.setNamespaceAware(true);
163                } else {
164                    LOGGER.log(Level.FINEST, "Using provided DocumentBuilderFactory.");
165                    this.documentBuilderFactory = documentBuilderFactory;
166                }
167    
168                if (documentBuilder == null) {
169                    LOGGER.log(Level.FINEST, "Using default DocumentBuilder.");
170                    this.documentBuilder = this.documentBuilderFactory.newDocumentBuilder();
171                } else {
172                    LOGGER.log(Level.FINEST, "Using provided DocumentBuilder.");
173                    this.documentBuilder = documentBuilder;
174                }
175    
176                if (schemaFactory == null) {
177                    LOGGER.log(Level.FINEST, "Using default SchemaFactory.");
178                    this.schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
179                } else {
180                    LOGGER.log(Level.FINEST, "Using provided SchemaFactory.");
181                    this.schemaFactory = schemaFactory;
182                }
183    
184                if (transformerFactory == null) {
185                    LOGGER.log(Level.FINEST, "Using default TransformerFactory.");
186                    this.transformerFactory = TransformerFactory.newInstance();
187                } else {
188                    LOGGER.log(Level.FINEST, "Using provided TransformerFactory.");
189                    this.transformerFactory = transformerFactory;
190                }
191    
192                if (transformer == null) {
193                    LOGGER.log(Level.FINEST, "Using default Transformer.");
194                    this.transformer = this.transformerFactory.newTransformer();
195                } else {
196                    LOGGER.log(Level.FINEST, "Using provided Transformer.");
197                    this.transformer = transformer;
198                }
199    
200                if (xpathFactory == null) {
201                    LOGGER.log(Level.FINEST, "Using default XPathFactory.");
202                    this.xpathFactory = XPathFactory.newInstance(XPathFactory.DEFAULT_OBJECT_MODEL_URI);
203                } else {
204                    LOGGER.log(Level.FINEST, "Using provided XPathFactory.");
205                    this.xpathFactory = xpathFactory;
206                }
207    
208                if (xpath == null) {
209                    LOGGER.log(Level.FINEST, "Using default XPath.");
210                    this.xpath = this.xpathFactory.newXPath();
211                } else {
212                    LOGGER.log(Level.FINEST, "Using provided XPath.");
213                    this.xpath = xpath;
214                }
215    
216                if (defaultNamespaceContext == null) {
217                    LOGGER.log(Level.FINEST, "Using default NamespaceContextImpl.");
218                    this.defaultNamespaceContext = NamespaceContextImpl.namespaceContextWithDefaults();
219                } else {
220                    LOGGER.log(Level.FINEST, "Using provided NamespaceContext.");
221                    this.defaultNamespaceContext = defaultNamespaceContext;
222                }
223            } catch (ParserConfigurationException ex) {
224                final String message = "Failed to initialize XML Utilities.";
225                LOGGER.log(Level.SEVERE, message, ex);
226                throw new XmlUtilitiesException(message, ex);
227            } catch (TransformerFactoryConfigurationError ex) {
228                final String message = "Failed to initialize XML Utilities.";
229                LOGGER.log(Level.SEVERE, message, ex);
230                throw new XmlUtilitiesException(message, ex);
231            } catch (TransformerConfigurationException ex) {
232                final String message = "Failed to initialize XML Utilities.";
233                LOGGER.log(Level.SEVERE, message, ex);
234                throw new XmlUtilitiesException(message, ex);
235            } catch (XPathFactoryConfigurationException ex) {
236                final String message = "Failed to initialize XML Utilities.";
237                LOGGER.log(Level.SEVERE, message, ex);
238                throw new XmlUtilitiesException(message, ex);
239            }
240        }
241    
242        /**
243         * Returns SchemaFactory object internally used by the XML Utilities.
244         *
245         * @return SchemaFactory object
246         */
247        public SchemaFactory getSchemaFactory() {
248            return schemaFactory;
249        }
250    
251        /**
252         * Returns DocumentBuilderFactory object internally used by the XML Utilities.
253         *
254         * @return DocumentBuilderFactory object
255         */
256        public DocumentBuilderFactory getDocumentBuilderFactory() {
257            return documentBuilderFactory;
258        }
259    
260        /**
261         * Returns DocumentBuilder object internally used by the XML Utilities.
262         *
263         * @return DocumentBuilder object
264         */
265        public DocumentBuilder getDocumentBuilder() {
266            return documentBuilder;
267        }
268    
269        /**
270         * Returns TransformerFactory object internally used by the XML Utilities.
271         *
272         * @return TransformerFactory object
273         */
274        public TransformerFactory getTransformerFactory() {
275            return transformerFactory;
276        }
277    
278        /**
279         * Returns Transformer object internally used by the XML Utilities.
280         *
281         * @return Transformer object
282         */
283        public Transformer getTransformer() {
284            return transformer;
285        }
286    
287        /**
288         * Returns XPathFactory object internally used by the XML Utilities.
289         *
290         * @return XPathFactory object
291         */
292        public XPathFactory getXPathFactory() {
293            return xpathFactory;
294        }
295    
296        /**
297         * Returns XPath object internally used by the XML Utilities.
298         *
299         * @return XPath object
300         */
301        public XPath getXPath() {
302            return xpath;
303        }
304    
305        /**
306         * Returns default NamespaceContext used by the XML Utilities.
307         *
308         * @return default NamespaceContenxt object
309         */
310        public NamespaceContext getDefaultNamespaceContext() {
311            return defaultNamespaceContext;
312        }
313    
314        /**
315         * Validates XML Document against XML Schema.
316         *
317         * @param xmlDocument document to validate
318         * @param xmlSchema XMLSchema
319         *
320         * @return {@code null} if successful, string with error otherwise
321         *
322         * @throws XmlUtilitiesException on error
323         */
324        public String validateXmlUsingSchema(final Source xmlDocument, final Source xmlSchema) throws XmlUtilitiesException {
325            final Validator validator;
326    
327            try {
328                validator = schemaFactory.newSchema(xmlSchema).newValidator();
329    
330                validator.validate(xmlDocument);
331            } catch (SAXException ex) {
332                return ex.getMessage();
333            } catch (IOException ex) {
334                final String message = "Failed to validate XML Document against XML Schema.";
335                LOGGER.log(Level.SEVERE, message, ex);
336                throw new XmlUtilitiesException(message, ex);
337            }
338    
339            return null;
340        }
341    
342        /**
343         * Loads XML from file specified with file name.
344         *
345         * @param fileName file to load
346         *
347         * @return Document object
348         *
349         * @throws XmlUtilitiesException on error
350         */
351        public Document loadDocumentFromFile(final String fileName) throws XmlUtilitiesException {
352            return loadDocumentFromFile(new File(fileName));
353        }
354    
355        /**
356         * Loads XML from file specified with file name.
357         *
358         * @param fileName file to load
359         * @param documentBuilder custom document builder
360         *
361         * @return Document object
362         *
363         * @throws XmlUtilitiesException on error
364         */
365        public Document loadDocumentFromFile(final String fileName, final DocumentBuilder documentBuilder) throws XmlUtilitiesException {
366            return loadDocumentFromFile(new File(fileName), documentBuilder);
367        }
368    
369        /**
370         * Loads XML from file specified with the File object.
371         *
372         * @param file file to load
373         *
374         * @return Document object
375         *
376         * @throws XmlUtilitiesException on error
377         */
378        public Document loadDocumentFromFile(final File file) throws XmlUtilitiesException {
379            return loadDocumentFromFile(file, documentBuilder);
380        }
381    
382        /**
383         * Loads XML from file specified with the file object.
384         *
385         * @param file file to load
386         * @param documentBuilder custom document builder
387         *
388         * @return Document object
389         *
390         * @throws XmlUtilitiesException on error
391         */
392        public Document loadDocumentFromFile(final File file, final DocumentBuilder documentBuilder) throws XmlUtilitiesException {
393            final InputStream inputStream;
394            try {
395                inputStream = new FileInputStream(file);
396            } catch (FileNotFoundException ex) {
397                final String message = String.format("The specified file '%s' does not exist.", file);
398                LOGGER.log(Level.SEVERE, message, ex);
399                throw new XmlUtilitiesException(message, ex);
400            }
401    
402            final Document document = loadDocumentFromStreamAndClose(inputStream, documentBuilder, true);
403    
404            return document;
405        }
406    
407        /**
408         * loads XML from specified input stream. The stream is not being closed.
409         *
410         * @param is input stream
411         *
412         * @return Document object
413         *
414         * @throws XmlUtilitiesException on error
415         */
416        public Document loadDocumentFromStream(final InputStream is) throws XmlUtilitiesException {
417            return loadDocumentFromStream(is, documentBuilder);
418        }
419    
420        /**
421         * Loads XML from specified input stream. The stream is not being closed.
422         *
423         * @param is input stream
424         * @param documentBuilder custom Document Builder
425         *
426         * @return Document object
427         *
428         * @throws XmlUtilitiesException on error
429         */
430        public Document loadDocumentFromStream(final InputStream is, final DocumentBuilder documentBuilder) throws XmlUtilitiesException {
431            final BufferedInputStream bufferedInputStream = new BufferedInputStream(is);
432    
433            try {
434                final Document document = documentBuilder.parse(bufferedInputStream);
435    
436                return document;
437            } catch (IOException ex) {
438                final String message = "Failed to read data from stream.";
439                LOGGER.log(Level.SEVERE, message, ex);
440                throw new XmlUtilitiesException(message, ex);
441            } catch (SAXException ex) {
442                final String message = "Failed to parse XML from stream.";
443                LOGGER.log(Level.SEVERE, message, ex);
444                throw new XmlUtilitiesException(message, ex);
445            }
446        }
447    
448        /**
449         * Loads XML from specified input stream and closes it if specified.
450         *
451         * @param is input stream
452         * @param close {@code true} to close input stream, {@code false} to leave it unclosed
453         *
454         * @return Document object
455         *
456         * @throws XmlUtilitiesException on error
457         */
458        public Document loadDocumentFromStreamAndClose(final InputStream is, final boolean close) throws XmlUtilitiesException {
459            return loadDocumentFromStreamAndClose(is, documentBuilder, close);
460        }
461    
462        /**
463         * Loads XML from specified input stream and closes it if specified.
464         *
465         * @param is input stream
466         * @param documentBuilder custom Document Builder
467         * @param close {@code true} to close input stream, {@code false} to leave it unclosed
468         *
469         * @return Document object
470         *
471         * @throws XmlUtilitiesException on error
472         */
473        public Document loadDocumentFromStreamAndClose(final InputStream is, final DocumentBuilder documentBuilder, final boolean close) throws XmlUtilitiesException {
474            final BufferedInputStream bufferedInputStream = new BufferedInputStream(is);
475    
476            Document document = null;
477    
478            XmlUtilitiesException exception = null;
479            try {
480                try {
481                    document = documentBuilder.parse(bufferedInputStream);
482                } catch (IOException ex) {
483                    final String message = "Failed to read data from stream.";
484                    LOGGER.log(Level.SEVERE, message, ex);
485                    exception = new XmlUtilitiesException(message, ex);
486                } catch (SAXException ex) {
487                    final String message = "Failed to parse XML from stream.";
488                    LOGGER.log(Level.SEVERE, message, ex);
489                    exception = new XmlUtilitiesException(message, ex);
490                }
491            } finally {
492                if (close) {
493                    try {
494                        bufferedInputStream.close();
495                    } catch (IOException ex) {
496                        final String message = "Failed to close stream.";
497                        if (exception != null) {
498                            LOGGER.log(Level.WARNING, message, ex);
499                            exception.addConsequent(ex);
500                        } else {
501                            LOGGER.log(Level.SEVERE, message, ex);
502                            exception = new XmlUtilitiesException(message, ex);
503                        }
504                    }
505                }
506    
507                if (exception != null) {
508                    throw exception;
509                }
510            }
511    
512            return document;
513        }
514    
515        /**
516         * Loads document from ClassLoaders. The first of {@code ContextClassLoader}, {@code fallBackClazz#getClassLoader()}
517         * or {@code SystemClassLoader} is used whichever is find first.
518         *
519         * @param resource resource name to load from
520         * @param fallbackClazz ClassLoader to use if {@code ContextClassLoader} does not exist
521         *
522         * @return Document object
523         *
524         * @throws XmlUtilitiesException on error
525         */
526        public Document loadDocumentFromClassLoader(final String resource, final Class<?> fallbackClazz) throws XmlUtilitiesException {
527            return loadDocumentFromClassLoader(resource, documentBuilder, fallbackClazz);
528        }
529    
530        /**
531         * Loads document from ClassLoaders. The first of {@code ContextClassLoader}, {@code fallBackClazz#getClassLoader()}
532         * or {@code SystemClassLoader} is used whichever is find first.
533         *
534         * @param resource resource name to load from
535         * @param documentBuilder custom DocumentBuilder
536         * @param fallbackClazz ClassLoader to use if {@code ContextClassLoader} does not exist
537         *
538         * @return Document object
539         *
540         * @throws XmlUtilitiesException on error
541         */
542        public Document loadDocumentFromClassLoader(final String resource, final DocumentBuilder documentBuilder, final Class<?> fallbackClazz) throws XmlUtilitiesException {
543            ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
544    
545            if (classLoader == null) {
546                if (fallbackClazz != null) {
547                    LOGGER.log(Level.FINEST, "ContextClassLoader is null.");
548                    LOGGER.log(Level.FINEST, "About to use ClassLoader from provided fallback Class.");
549                    classLoader = fallbackClazz.getClassLoader();
550                }
551            }
552    
553            if (classLoader == null) {
554                classLoader = ClassLoader.getSystemClassLoader();
555                if (classLoader != null) {
556                    LOGGER.log(Level.FINEST, "About to use SystemClassLoader.");
557                }
558            }
559    
560            if (classLoader == null) {
561                final String message = "Failed to retrieve ContextClassLoader, ClassLoader or SystemClassLoader.";
562                LOGGER.log(Level.SEVERE, message);
563                throw new XmlUtilitiesException(message);
564            }
565    
566            InputStream is = classLoader.getResourceAsStream(resource);
567    
568            if (is == null) {
569                final String message = String.format("The specified resource '%s' has not been found using ClassLoader %s.", resource, classLoader.toString());
570                LOGGER.log(Level.SEVERE, message);
571                throw new XmlUtilitiesException(message);
572            }
573    
574            return loadDocumentFromStreamAndClose(is, documentBuilder, true);
575        }
576    
577        /**
578         * Loads Document from resource from given class.
579         *
580         * @param resource resource to load
581         * @param clazz class to use
582         *
583         * @return Document object
584         *
585         * @throws XmlUtilitiesException on error
586         */
587        public Document loadDocumentFromResource(final String resource, final Class<?> clazz) throws XmlUtilitiesException {
588            return loadDocumentFromResource(resource, documentBuilder, clazz);
589        }
590    
591        /**
592         * Loads Document from resource from given class.
593         *
594         * @param resource resource to load
595         * @param documentBuilder custom DocumentBuilder
596         * @param clazz class to use
597         *
598         * @return Document object
599         *
600         * @throws XmlUtilitiesException on error
601         */
602        public Document loadDocumentFromResource(final String resource, final DocumentBuilder documentBuilder, final Class<?> clazz) throws XmlUtilitiesException {
603            InputStream is = clazz.getResourceAsStream(resource);
604    
605            if (is == null) {
606                final String message = String.format("The resource '%s' not found.", resource);
607                LOGGER.log(Level.SEVERE, message);
608                throw new XmlUtilitiesException(message);
609            }
610    
611            return loadDocumentFromStreamAndClose(is, documentBuilder, true);
612        }
613    
614        /**
615         * Loads document from resource using specified ClassLoader.
616         *
617         * @param resource resource to load
618         * @param classLoader ClassLoader to use
619         *
620         * @return Document object
621         *
622         * @throws XmlUtilitiesException on error
623         */
624        public Document loadDocumentFromResource(final String resource, final ClassLoader classLoader) throws XmlUtilitiesException {
625            return loadDocumentFromResource(resource, documentBuilder, classLoader);
626        }
627    
628        /**
629         * Loads document from resource using specified ClassLoader.
630         *
631         * @param resource resource to load
632         * @param documentBuilder custom DocumentBuilder
633         * @param classLoader ClassLoader to use
634         *
635         * @return Document object
636         *
637         * @throws XmlUtilitiesException on error
638         */
639        public Document loadDocumentFromResource(final String resource, final DocumentBuilder documentBuilder, final ClassLoader classLoader) throws XmlUtilitiesException {
640            InputStream is = classLoader.getResourceAsStream(resource);
641    
642            if (is == null) {
643                final String message = String.format("The resource '%s' not found.", resource);
644                LOGGER.log(Level.SEVERE, message);
645                throw new XmlUtilitiesException(message);
646            }
647    
648            return loadDocumentFromStreamAndClose(is, documentBuilder, true);
649        }
650    
651        /**
652         * Loads document from String into the {@code Document}.
653         *
654         * @param source source XML
655         *
656         * @return resulting XML
657         *
658         * @throws XmlUtilitiesException on error
659         */
660        public Document loadDocumentFromString(final String source) throws XmlUtilitiesException {
661            return loadDocumentFromString(source, documentBuilder);
662        }
663    
664        /**
665         * Loads document from String into the {@code Document}.
666         *
667         * @param source source XML
668         * @param documentBuilder custom document builder
669         *
670         * @return resulting XML
671         *
672         * @throws XmlUtilitiesException on error
673         */
674        public Document loadDocumentFromString(final String source, final DocumentBuilder documentBuilder) throws XmlUtilitiesException {
675            final StringReader sr = new StringReader(source);
676            final BufferedReader br = new BufferedReader(sr);
677            final InputSource is = new InputSource(br);
678    
679            Document result = null;
680    
681            XmlUtilitiesException exception = null;
682            try {
683                try {
684                    result = documentBuilder.parse(is);
685                } catch (SAXException ex) {
686                    final String message = "Failed to parse document from String.";
687                    LOGGER.log(Level.SEVERE, message, ex);
688                    exception = new XmlUtilitiesException(message, ex);
689                } catch (IOException ex) {
690                    final String message = "Failed to parse document from String (I/O).";
691                    LOGGER.log(Level.SEVERE, message, ex);
692                    exception = new XmlUtilitiesException(message, ex);
693                }
694            } finally {
695                try {
696                    br.close();
697                    sr.close();
698                } catch (IOException ex) {
699                    final String message = "Failed to close().";
700                    if (exception != null) {
701                        LOGGER.log(Level.WARNING, message, ex);
702                        exception.addConsequent(ex);
703                    } else {
704                        LOGGER.log(Level.SEVERE, message, ex);
705                        exception = new XmlUtilitiesException(message, ex);
706                    }
707                }
708    
709                if (exception != null) {
710                    throw exception;
711                }
712            }
713    
714            return result;
715        }
716    
717        /**
718         * Converts XML Document into String.
719         *
720         * @param source Document to convert
721         *
722         * @return XML in the String
723         *
724         * @throws XmlUtilitiesException on error
725         */
726        public String documentToString(final Document source) throws XmlUtilitiesException {
727            return sourceToString(XmlTools.documentToDomSource(source));
728        }
729    
730        /**
731         * Converts XML Document into String.
732         *
733         * @param source Document to convert
734         * @param outputFormat output format configuration
735         *
736         * @return XML in the String
737         *
738         * @throws XmlUtilitiesException on error
739         */
740        public String documentToString(final Document source, final OutputFormat outputFormat) throws XmlUtilitiesException {
741            return sourceToString(XmlTools.documentToDomSource(source), outputFormat);
742        }
743    
744        /**
745         * Converts XML Document into String.
746         *
747         * @param source Document to convert
748         * @param outputProperties output properties (see {@link OutputFormat OutputFormat})
749         *
750         * @return XML in the String
751         *
752         * @throws XmlUtilitiesException on error
753         */
754        public String documentToString(final Document source, final Properties outputProperties) throws XmlUtilitiesException {
755            return sourceToString(XmlTools.documentToDomSource(source), outputProperties);
756        }
757    
758        /**
759         * Converts XML Source into String.
760         *
761         * @param source Document to convert
762         *
763         * @return XML in the String
764         *
765         * @throws XmlUtilitiesException on error
766         */
767        public String sourceToString(final Source source) throws XmlUtilitiesException {
768            return sourceToString(source, (Properties) null);
769        }
770    
771        /**
772         * Converts XML Source into String.
773         *
774         * @param source Document to convert
775         * @param outputFormat output format configuration
776         *
777         * @return XML in the String
778         *
779         * @throws XmlUtilitiesException on error
780         */
781        public String sourceToString(final Source source, final OutputFormat outputFormat) throws XmlUtilitiesException {
782            Properties outputProperties = null;
783            if (outputFormat != null) {
784                outputProperties = outputFormat.toTransformerOutputProperties();
785            }
786            return sourceToString(source, outputProperties);
787        }
788    
789        /**
790         * Converts XML Source into String.
791         *
792         * @param source Document to convert
793         * @param outputProperties output properties (see {@link OutputFormat OutputFormat})
794         *
795         * @return XML in the String
796         *
797         * @throws XmlUtilitiesException on error
798         */
799        public String sourceToString(final Source source, final Properties outputProperties) throws XmlUtilitiesException {
800            final StringWriter stringWriter = new StringWriter();
801            final StreamResult streamResult = new StreamResult(stringWriter);
802    
803            transformer.setParameter("", "");
804            transformer.clearParameters();
805            transformer.setOutputProperties(null);
806            if (outputProperties != null) {
807                transformer.setOutputProperties(outputProperties);
808            }
809            try {
810                transformer.transform(source, streamResult);
811            } catch (TransformerException ex) {
812                final String message = "Failed to convert Source to String.";
813                LOGGER.log(Level.SEVERE, message, ex);
814                throw new XmlUtilitiesException(message, ex);
815            }
816    
817            final String result = stringWriter.toString();
818    
819            try {
820                stringWriter.close();
821            } catch (IOException ex) {
822                final String message = "Failed to close() StringWriter.";
823                LOGGER.log(Level.SEVERE, message, ex);
824                throw new XmlUtilitiesException(message, ex);
825            }
826    
827            return result;
828        }
829    
830        /**
831         * Evaluates specified XPath expression on specified context node.
832         *
833         * @param query XPath expression
834         * @param context context node
835         *
836         * @return {@link NodeList NodeList} or {@code null} if no matches
837         *
838         * @throws XmlUtilitiesException on error
839         */
840        public NodeList evaluateXPath(final String query, final Node context) throws XmlUtilitiesException {
841            return evaluateXPath(query, context, (NamespaceContext) null);
842        }
843    
844        /**
845         * Evaluates specified XPath expression on specified context node.
846         *
847         * @param query XPath expression
848         * @param context context node
849         * @param namespaces name space context
850         *
851         * @return {@link NodeList NodeList} or {@code null} if no matches
852         *
853         * @throws XmlUtilitiesException on error
854         */
855        public NodeList evaluateXPath(final String query, final Node context, final NamespaceContext namespaces) throws XmlUtilitiesException {
856            xpath.reset();
857    
858            if (namespaces == null) {
859                xpath.setNamespaceContext(defaultNamespaceContext);
860            } else {
861                xpath.setNamespaceContext(namespaces);
862            }
863    
864            try {
865                return (NodeList) xpath.evaluate(query, context, XPathConstants.NODESET);
866            } catch (XPathExpressionException ex) {
867                final String message = String.format("Failed to evaluate XPath '%s'.", query);
868                LOGGER.log(Level.SEVERE, message, ex);
869                throw new XmlUtilitiesException(message, ex);
870            }
871        }
872    
873        /**
874         * Evaluates specified XPath expression on specified context node and returns results as
875         * {@link NodeListCollection NodeListCollection} of specified element types. This method never returns {@code null}.
876         *
877         * @param <E> type of element
878         * @param query XPath expression
879         * @param context context node
880         * @param elementType class type of element
881         *
882         * @return {@link NodeListCollection NodeListCollection} of element type or empty list if no matches.
883         *
884         * @throws XmlUtilitiesException on error
885         */
886        @SuppressWarnings("unchecked")
887        public <E extends Node> NodeListCollection<E> evaluateXPath(final String query, final Node context, final Class<E> elementType) throws XmlUtilitiesException {
888            return evaluateXPath(query, context, null, elementType);
889        }
890    
891        /**
892         * Evaluates specified XPath expression on specified context node and returns results as
893         * {@link NodeListCollection NodeListCollection} of specified element types. This method never returns {@code null}.
894         *
895         * @param <E> type of element
896         * @param query XPath expression
897         * @param context context node
898         * @param namespaces name space context
899         * @param elementType class type of element
900         *
901         * @return {@link NodeListCollection NodeListCollection} of element type or empty list if no matches.
902         *
903         * @throws XmlUtilitiesException on error
904         */
905        @SuppressWarnings("unchecked")
906        public <E extends Node> NodeListCollection<E> evaluateXPath(final String query, final Node context, final NamespaceContext namespaces, final Class<E> elementType)
907                throws XmlUtilitiesException {
908            NodeList nodes = evaluateXPath(query, context, namespaces);
909    
910            NodeListCollection<E> result = new NodeListCollection<E>(nodes, elementType);
911    
912            return result;
913        }
914    
915        /**
916         * Evaluates specified XPath expression and returns first Node if XPath has matches.
917         *
918         * @param query XPath expression
919         * @param context context node
920         *
921         * @return first node or {@code null}
922         *
923         * @throws XmlUtilitiesException on error
924         */
925        public Node getFirstNodeForXPath(final String query, final Node context) throws XmlUtilitiesException {
926            return getFirstNodeForXPath(query, context, (NamespaceContext) null);
927        }
928    
929        /**
930         * Evaluates specified XPath expression and returns first Node if XPath has matches.
931         *
932         * @param query XPath expression
933         * @param context context node
934         * @param namespaces name space context
935         *
936         * @return first node or {@code null}
937         *
938         * @throws XmlUtilitiesException on error
939         */
940        public Node getFirstNodeForXPath(final String query, final Node context, final NamespaceContext namespaces) throws XmlUtilitiesException {
941            final NodeList nodes = evaluateXPath(query, context, namespaces);
942    
943            if (nodes == null) {
944                return null;
945            }
946    
947            if (nodes.getLength() > 0) {
948                return nodes.item(0);
949            }
950    
951            return null;
952        }
953    
954        /**
955         * Evaluates specified XPath expression and returns first Node as specified element type if XPath has matches.
956         *
957         * @param <E> type of element
958         * @param query XPath expression
959         * @param context context node
960         * @param elementType class type of element
961         *
962         * @return first node as specified type or {@code null}
963         *
964         * @throws XmlUtilitiesException on error
965         */
966        public <E extends Node> E getFirstNodeForXPath(final String query, final Node context, final Class<E> elementType) throws XmlUtilitiesException {
967            return getFirstNodeForXPath(query, context, null, elementType);
968        }
969    
970        /**
971         * Evaluates specified XPath expression and returns first Node as specified element type if XPath has matches.
972         *
973         * @param <E> type of element
974         * @param query XPath expression
975         * @param context context node
976         * @param namespaces name space context
977         * @param elementType class type of element
978         *
979         * @return first node as specified type or {@code null}
980         *
981         * @throws XmlUtilitiesException on error
982         */
983        public <E extends Node> E getFirstNodeForXPath(final String query, final Node context, final NamespaceContext namespaces, final Class<E> elementType)
984                throws XmlUtilitiesException {
985            Node node = getFirstNodeForXPath(query, context, namespaces);
986    
987            if (node == null) {
988                return null;
989            }
990    
991            final E element;
992            try {
993                element = elementType.cast(node);
994            } catch (ClassCastException ex) {
995                final String message = String.format("Failed to cast node from %s to %s.", node.getClass(), elementType);
996                LOGGER.log(Level.SEVERE, message, ex);
997                throw new XmlUtilitiesException(message, ex);
998            }
999    
1000            return element;
1001        }
1002    
1003        /**
1004         * Transforms specified XML source using specified XSLT template.
1005         *
1006         * @param xsltTemplate template to use
1007         * @param document source XML document
1008         * @param parameters parameters to propagate to the localTransformer
1009         *
1010         * @return resulting XML in the String
1011         *
1012         * @throws XmlUtilitiesException on error
1013         */
1014        public String transformToString(final Source xsltTemplate, final Source document, final Map<String, Object> parameters) throws XmlUtilitiesException {
1015            try {
1016                final Transformer localTransformer = transformerFactory.newTransformer(xsltTemplate);
1017    
1018                if (parameters != null) {
1019                    for (Map.Entry<String, Object> parameter : parameters.entrySet()) {
1020                        localTransformer.setParameter(parameter.getKey(), parameter.getValue());
1021                    }
1022                }
1023    
1024                final StringWriter sw = new StringWriter();
1025                final StreamResult sr = new StreamResult(sw);
1026    
1027                localTransformer.transform(document, sr);
1028    
1029                final String result = sw.toString();
1030    
1031                try {
1032                    sw.close();
1033                } catch (IOException ex) {
1034                    return null;
1035                }
1036    
1037                return result;
1038            } catch (TransformerConfigurationException ex) {
1039                final String message = "Failed to transform Document using XSLT template and parameters.";
1040                LOGGER.log(Level.SEVERE, message, ex);
1041                throw new XmlUtilitiesException(message, ex);
1042            } catch (TransformerException ex) {
1043                final String message = "Failed to transform Document using XSLT template and parameters.";
1044                LOGGER.log(Level.SEVERE, message, ex);
1045                throw new XmlUtilitiesException(message, ex);
1046            }
1047        }
1048    
1049        /**
1050         * Transforms specified XML source using specified XSLT (compiled) template.
1051         *
1052         * @param xsltTemplate template to use
1053         * @param document source XML document to transform
1054         * @param parameters parameters to propagate to the localTransformer
1055         *
1056         * @return resulting XML in the String
1057         *
1058         * @throws XmlUtilitiesException on error
1059         */
1060        public String transformToString(final Templates xsltTemplate, final Source document, final Map<String, Object> parameters) throws XmlUtilitiesException {
1061            try {
1062                final Transformer localTransformer = xsltTemplate.newTransformer();
1063    
1064                if (parameters != null) {
1065                    for (Map.Entry<String, Object> parameter : parameters.entrySet()) {
1066                        localTransformer.setParameter(parameter.getKey(), parameter.getValue());
1067                    }
1068                }
1069    
1070                final StringWriter sw = new StringWriter();
1071                final StreamResult sr = new StreamResult(sw);
1072    
1073                localTransformer.transform(document, sr);
1074    
1075                final String result = sw.toString();
1076    
1077                try {
1078                    sw.close();
1079                } catch (IOException e) {
1080                    return null;
1081                }
1082    
1083                return result;
1084            } catch (TransformerConfigurationException ex) {
1085                final String message = "Failed to transform Document using XSLT template and parameters.";
1086                LOGGER.log(Level.SEVERE, message, ex);
1087                throw new XmlUtilitiesException(message, ex);
1088            } catch (TransformerException ex) {
1089                final String message = "Failed to transform Document using XSLT template and parameters.";
1090                LOGGER.log(Level.SEVERE, message, ex);
1091                throw new XmlUtilitiesException(message, ex);
1092            }
1093        }
1094    
1095        /**
1096         * Transforms specified XML source using specified XSLT (compiled) template.
1097         *
1098         * @param xsltTemplate template to use
1099         * @param document source XML document to transform
1100         *
1101         * @return resulting XML in the String
1102         *
1103         * @throws XmlUtilitiesException on error
1104         */
1105        public String transformToString(final Templates xsltTemplate, final Source document) throws XmlUtilitiesException {
1106            return transformToString(xsltTemplate, document, null);
1107        }
1108    
1109        /**
1110         * Transforms specified XML document using specified XSLT template.
1111         *
1112         * @param xsltTemplate XSLT template
1113         * @param document source XML document
1114         *
1115         * @return resulting XML document in the String
1116         *
1117         * @throws XmlUtilitiesException on error
1118         */
1119        public String transformToString(final Source xsltTemplate, final Source document) throws XmlUtilitiesException {
1120            return transformToString(xsltTemplate, document, null);
1121        }
1122    
1123        /**
1124         * Transforms specified XML document using specified XSLT template.
1125         *
1126         * @param xsltTemplate XSLT template
1127         * @param document source XML document
1128         *
1129         * @return resulting XML document in the {@link Document Document}
1130         *
1131         * @throws XmlUtilitiesException on error
1132         */
1133        public Document transformToDocument(final Source xsltTemplate, final Source document) throws XmlUtilitiesException {
1134            final XmlUtilities xmlUtils = new XmlUtilities();
1135    
1136            return XmlTools.loadDocumentFromString(transformToString(xsltTemplate, document, null), xmlUtils);
1137        }
1138    
1139        /**
1140         * Transforms specified XML document using XSLT (compiled) template.
1141         *
1142         * @param xsltTemplate template to use
1143         * @param document document to transform
1144         *
1145         * @return resulting XML document in the {@link Document Document}
1146         *
1147         * @throws XmlUtilitiesException on error
1148         */
1149        public Document transformToDocument(final Templates xsltTemplate, final Source document) throws XmlUtilitiesException {
1150            final XmlUtilities xmlUtils = new XmlUtilities();
1151    
1152            return XmlTools.loadDocumentFromString(transformToString(xsltTemplate, document, null), xmlUtils);
1153        }
1154    
1155        /**
1156         * Transforms specified XML document using specified XSLT template.
1157         *
1158         * @param xsltTemplate XSLT template
1159         * @param document source XML document
1160         * @param parameters parameters for the template
1161         *
1162         * @return resulting XML document in the {@link Document Document}
1163         *
1164         * @throws XmlUtilitiesException on error
1165         */
1166        public Document transformToDocument(final Source xsltTemplate, final Source document, final Map<String, Object> parameters) throws XmlUtilitiesException {
1167            final XmlUtilities xmlUtils = new XmlUtilities();
1168    
1169            return XmlTools.loadDocumentFromString(transformToString(xsltTemplate, document, parameters), xmlUtils);
1170        }
1171    
1172        /**
1173         * Transforms specified XML using XSLT (compiled) template.
1174         *
1175         * @param xsltTemplate template to use
1176         * @param document XML document to transform
1177         * @param parameters parameters for the transformation
1178         *
1179         * @return resulting XML document in the {@link Document Document}
1180         *
1181         * @throws XmlUtilitiesException on error
1182         */
1183        public Document transformToDocument(final Templates xsltTemplate, final Source document, final Map<String, Object> parameters) throws XmlUtilitiesException {
1184            final XmlUtilities xmlUtils = new XmlUtilities();
1185    
1186            return XmlTools.loadDocumentFromString(transformToString(xsltTemplate, document, parameters), xmlUtils);
1187        }
1188    
1189        /**
1190         * Writes specified document into specified output stream.
1191         *
1192         * @param doc document
1193         * @param os output stream
1194         *
1195         * @throws XmlUtilitiesException on error
1196         */
1197        public void writeDocumentToStream(final Document doc, final OutputStream os) throws XmlUtilitiesException {
1198            writeDocumentToStreamAndClose(doc, os, false, (Properties) null);
1199        }
1200    
1201        /**
1202         * Writes specified document into specified stream and specified output format.
1203         *
1204         * @param doc document
1205         * @param os output stream
1206         * @param outputFormat output format
1207         *
1208         * @throws XmlUtilitiesException on error
1209         */
1210        public void writeDocumentToStream(final Document doc, final OutputStream os, final OutputFormat outputFormat) throws XmlUtilitiesException {
1211            writeDocumentToStreamAndClose(doc, os, false, outputFormat);
1212        }
1213    
1214        /**
1215         * Writes specified document into specified stream and specified output format properties.
1216         *
1217         * @param doc document
1218         * @param os output stream
1219         * @param outputProperties output format properties
1220         *
1221         * @throws XmlUtilitiesException on error
1222         */
1223        public void writeDocumentToStream(final Document doc, final OutputStream os, final Properties outputProperties) throws XmlUtilitiesException {
1224            writeDocumentToStreamAndClose(doc, os, false, outputProperties);
1225        }
1226    
1227        /**
1228         * Writes specified document into specified stream and specified {@link Transformer Transformer}.
1229         *
1230         * @param doc document
1231         * @param os output stream
1232         * @param transformer Transformer to use
1233         *
1234         * @throws XmlUtilitiesException on error
1235         */
1236        public void writeDocumentToStream(final Document doc, final OutputStream os, final Transformer transformer) throws XmlUtilitiesException {
1237            writeDocumentToStreamAndClose(doc, os, false, transformer);
1238        }
1239    
1240        /**
1241         * Writes specified document into specified output stream.
1242         *
1243         * @param doc document
1244         * @param os output stream
1245         * @param close tells if stream should be closed
1246         *
1247         * @throws XmlUtilitiesException on error
1248         */
1249        public void writeDocumentToStreamAndClose(final Document doc, final OutputStream os, final boolean close) throws XmlUtilitiesException {
1250            writeDocumentToStreamAndClose(doc, os, close, (Properties) null);
1251        }
1252    
1253        /**
1254         * Writes specified document into specified stream and specified output format.
1255         *
1256         * @param doc document
1257         * @param os output stream
1258         * @param close tells if stream should be closed
1259         * @param outputFormat output format
1260         *
1261         * @throws XmlUtilitiesException on error
1262         */
1263        public void writeDocumentToStreamAndClose(final Document doc, final OutputStream os, final boolean close, final OutputFormat outputFormat) throws XmlUtilitiesException {
1264            Properties outputProperties = null;
1265            if (outputFormat != null) {
1266                outputProperties = outputFormat.toTransformerOutputProperties();
1267            }
1268            writeDocumentToStreamAndClose(doc, os, close, outputProperties);
1269        }
1270    
1271        /**
1272         * Writes specified document into specified stream and specified output format properties.
1273         *
1274         * @param doc document
1275         * @param os output stream
1276         * @param close tells if stream should be closed
1277         * @param outputProperties output format properties
1278         *
1279         * @throws XmlUtilitiesException on error
1280         */
1281        public void writeDocumentToStreamAndClose(final Document doc, final OutputStream os, final boolean close, final Properties outputProperties) throws XmlUtilitiesException {
1282            DOMSource domSource = new DOMSource(doc);
1283            StreamResult streamResult = new StreamResult(os);
1284    
1285            XmlUtilitiesException exception = null;
1286            try {
1287                try {
1288                    transformer.setParameter("", "");
1289                    transformer.clearParameters();
1290                    transformer.setOutputProperties(null);
1291                    if (outputProperties != null) {
1292                        transformer.setOutputProperties(outputProperties);
1293                    }
1294                    transformer.transform(domSource, streamResult);
1295                } catch (TransformerException ex) {
1296                    final String message = "Failed to write document to stream.";
1297                    LOGGER.log(Level.SEVERE, message, ex);
1298                    exception = new XmlUtilitiesException(message, ex);
1299                }
1300            } finally {
1301                if (close) {
1302                    try {
1303                        os.close();
1304                    } catch (IOException ex) {
1305                        final String message = "Failed to close stream.";
1306                        if (exception != null) {
1307                            LOGGER.log(Level.WARNING, message, ex);
1308                            exception.addConsequent(ex);
1309                        } else {
1310                            LOGGER.log(Level.SEVERE, message, ex);
1311                            exception = new XmlUtilitiesException(message, ex);
1312                        }
1313                    }
1314                }
1315    
1316                if (exception != null) {
1317                    throw exception;
1318                }
1319            }
1320        }
1321    
1322        /**
1323         * Writes specified document into specified stream and specified {@link Transformer Transformer}.
1324         *
1325         * @param doc document
1326         * @param os output stream
1327         * @param close tells if stream should be closed
1328         * @param transformer Transformer to use
1329         *
1330         * @throws XmlUtilitiesException on error
1331         */
1332        public void writeDocumentToStreamAndClose(final Document doc, final OutputStream os, final boolean close, final Transformer transformer) throws XmlUtilitiesException {
1333            DOMSource domSource = new DOMSource(doc);
1334            StreamResult streamResult = new StreamResult(os);
1335    
1336            XmlUtilitiesException exception = null;
1337            try {
1338                try {
1339                    transformer.transform(domSource, streamResult);
1340                } catch (TransformerException ex) {
1341                    final String message = "Failed to write document to stream.";
1342                    LOGGER.log(Level.SEVERE, message, ex);
1343                    exception = new XmlUtilitiesException(message, ex);
1344                }
1345            } finally {
1346                if (close) {
1347                    try {
1348                        os.close();
1349                    } catch (IOException ex) {
1350                        final String message = "Failed to close stream.";
1351                        if (exception != null) {
1352                            LOGGER.log(Level.WARNING, message, ex);
1353                            exception.addConsequent(ex);
1354                        } else {
1355                            LOGGER.log(Level.SEVERE, message, ex);
1356                            exception = new XmlUtilitiesException(message, ex);
1357                        }
1358                    }
1359                }
1360    
1361                if (exception != null) {
1362                    throw exception;
1363                }
1364            }
1365        }
1366    
1367        /**
1368         * Writes Document to stream using internal buffer and closes the stream.
1369         *
1370         * @param doc Document to write
1371         * @param os target stream
1372         * @param bufferSize specified buffer size, {@code null} for default
1373         *
1374         * @throws XmlUtilitiesException on error
1375         */
1376        public void writeDocumentToBufferedStreamAndClose(final Document doc, final OutputStream os, final Integer bufferSize) throws XmlUtilitiesException {
1377            final OutputStream output;
1378            if (bufferSize == null) {
1379                LOGGER.log(Level.FINEST, "Using default buffer size.");
1380                output = new BufferedOutputStream(os);
1381            } else {
1382                LOGGER.log(Level.FINEST, "Using buffer size {0}.", bufferSize);
1383                output = new BufferedOutputStream(os, bufferSize);
1384            }
1385            writeDocumentToStreamAndClose(doc, output, true);
1386        }
1387    
1388        /**
1389         * Writes Document to stream using internal buffer and closes the stream.
1390         *
1391         * @param doc Document to write
1392         * @param os target stream
1393         * @param bufferSize specified buffer size, {@code null} for default
1394         * @param outputFormat output format
1395         *
1396         * @throws XmlUtilitiesException on error
1397         */
1398        public void writeDocumentToBufferedStreamAndClose(final Document doc, final OutputStream os, final Integer bufferSize, final OutputFormat outputFormat)
1399                throws XmlUtilitiesException {
1400            final OutputStream output;
1401            if (bufferSize == null) {
1402                LOGGER.log(Level.FINEST, "Using default buffer size.");
1403                output = new BufferedOutputStream(os);
1404            } else {
1405                LOGGER.log(Level.FINEST, "Using buffer size {0}.", bufferSize);
1406                output = new BufferedOutputStream(os, bufferSize);
1407            }
1408            writeDocumentToStreamAndClose(doc, output, true, outputFormat);
1409        }
1410    
1411        /**
1412         * Writes Document to stream using internal buffer and closes the stream.
1413         *
1414         * @param doc Document to write
1415         * @param os target stream
1416         * @param bufferSize specified buffer size, {@code null} for default
1417         * @param outputProperties output format properties
1418         *
1419         * @throws XmlUtilitiesException on error
1420         */
1421        public void writeDocumentToBufferedStreamAndClose(final Document doc, final OutputStream os, final Integer bufferSize, final Properties outputProperties)
1422                throws XmlUtilitiesException {
1423            final OutputStream output;
1424            if (bufferSize == null) {
1425                LOGGER.log(Level.FINEST, "Using default buffer size.");
1426                output = new BufferedOutputStream(os);
1427            } else {
1428                LOGGER.log(Level.FINEST, "Using buffer size {0}.", bufferSize);
1429                output = new BufferedOutputStream(os, bufferSize);
1430            }
1431            writeDocumentToStreamAndClose(doc, output, true, outputProperties);
1432        }
1433    
1434        /**
1435         * Writes Document to stream using internal buffer and closes the stream.
1436         *
1437         * @param doc Document to write
1438         * @param os target stream
1439         * @param bufferSize specified buffer size, {@code null} for default
1440         * @param transformer transformer
1441         *
1442         * @throws XmlUtilitiesException on error
1443         */
1444        public void writeDocumentToBufferedStreamAndClose(final Document doc, final OutputStream os, final Integer bufferSize, final Transformer transformer)
1445                throws XmlUtilitiesException {
1446            final OutputStream output;
1447            if (bufferSize == null) {
1448                LOGGER.log(Level.FINEST, "Using default buffer size.");
1449                output = new BufferedOutputStream(os);
1450            } else {
1451                LOGGER.log(Level.FINEST, "Using buffer size {0}.", bufferSize);
1452                output = new BufferedOutputStream(os, bufferSize);
1453            }
1454            writeDocumentToStreamAndClose(doc, output, true, transformer);
1455        }
1456    
1457        /**
1458         * Writes Document to specified file.
1459         *
1460         * @param doc document
1461         * @param file target file
1462         *
1463         * @throws XmlUtilitiesException on error
1464         */
1465        public void writeDocumentToFile(final Document doc, final String file) throws XmlUtilitiesException {
1466            writeDocumentToFile(doc, new File(file), (Integer) null);
1467        }
1468    
1469        /**
1470         * Writes Document to specified file.
1471         *
1472         * @param doc document
1473         * @param file target file
1474         * @param outputFormat output format
1475         *
1476         * @throws XmlUtilitiesException on error
1477         */
1478        public void writeDocumentToFile(final Document doc, final String file, final OutputFormat outputFormat) throws XmlUtilitiesException {
1479            writeDocumentToFile(doc, new File(file), null, outputFormat);
1480        }
1481    
1482        /**
1483         * Writes Document to specified file.
1484         *
1485         * @param doc document
1486         * @param file target file
1487         * @param outputProperties output format properties
1488         *
1489         * @throws XmlUtilitiesException on error
1490         */
1491        public void writeDocumentToFile(final Document doc, final String file, final Properties outputProperties) throws XmlUtilitiesException {
1492            writeDocumentToFile(doc, new File(file), null, outputProperties);
1493        }
1494    
1495        /**
1496         * Writes Document to specified file.
1497         *
1498         * @param doc document
1499         * @param file target file
1500         * @param transformer transformer
1501         *
1502         * @throws XmlUtilitiesException on error
1503         */
1504        public void writeDocumentToFile(final Document doc, final String file, final Transformer transformer) throws XmlUtilitiesException {
1505            writeDocumentToFile(doc, new File(file), null, transformer);
1506        }
1507    
1508        /**
1509         * Writes Document to specified file using internal buffer.
1510         *
1511         * @param doc document
1512         * @param file target file
1513         * @param bufferSize buffer size or {@code null} for default
1514         *
1515         * @throws XmlUtilitiesException on error
1516         */
1517        public void writeDocumentToFile(final Document doc, final String file, final Integer bufferSize) throws XmlUtilitiesException {
1518            writeDocumentToFile(doc, new File(file), bufferSize);
1519        }
1520    
1521        /**
1522         * Writes Document to specified file using internal buffer.
1523         *
1524         * @param doc document
1525         * @param file target file
1526         * @param bufferSize buffer size or {@code null} for default
1527         * @param outputFormat output format
1528         *
1529         * @throws XmlUtilitiesException on error
1530         */
1531        public void writeDocumentToFile(final Document doc, final String file, final Integer bufferSize, final OutputFormat outputFormat) throws XmlUtilitiesException {
1532            writeDocumentToFile(doc, new File(file), bufferSize, outputFormat);
1533        }
1534    
1535        /**
1536         * Writes Document to specified file using internal buffer.
1537         *
1538         * @param doc document
1539         * @param file target file
1540         * @param bufferSize buffer size or {@code null} for default
1541         * @param outputProperties output format properties
1542         *
1543         * @throws XmlUtilitiesException on error
1544         */
1545        public void writeDocumentToFile(final Document doc, final String file, final Integer bufferSize, final Properties outputProperties) throws XmlUtilitiesException {
1546            writeDocumentToFile(doc, new File(file), bufferSize, outputProperties);
1547        }
1548    
1549        /**
1550         * Writes Document to specified file using internal buffer.
1551         *
1552         * @param doc document
1553         * @param file target file
1554         * @param bufferSize buffer size or {@code null} for default
1555         * @param transformer transformer
1556         *
1557         * @throws XmlUtilitiesException on error
1558         */
1559        public void writeDocumentToFile(final Document doc, final String file, final Integer bufferSize, final Transformer transformer) throws XmlUtilitiesException {
1560            writeDocumentToFile(doc, new File(file), bufferSize, transformer);
1561        }
1562    
1563        /**
1564         * Writes Document to specified file.
1565         *
1566         * @param doc document
1567         * @param file target file
1568         *
1569         * @throws XmlUtilitiesException on error
1570         */
1571        public void writeDocumentToFile(final Document doc, final File file) throws XmlUtilitiesException {
1572            writeDocumentToFile(doc, file, (Integer) null);
1573        }
1574    
1575        /**
1576         * Writes Document to specified file.
1577         *
1578         * @param doc document
1579         * @param file target file
1580         * @param outputFormat output format
1581         *
1582         * @throws XmlUtilitiesException on error
1583         */
1584        public void writeDocumentToFile(final Document doc, final File file, final OutputFormat outputFormat) throws XmlUtilitiesException {
1585            writeDocumentToFile(doc, file, null, outputFormat);
1586        }
1587    
1588        /**
1589         * Writes Document to specified file.
1590         *
1591         * @param doc document
1592         * @param file target file
1593         * @param outputProperties output format properties
1594         *
1595         * @throws XmlUtilitiesException on error
1596         */
1597        public void writeDocumentToFile(final Document doc, final File file, final Properties outputProperties) throws XmlUtilitiesException {
1598            writeDocumentToFile(doc, file, null, outputProperties);
1599        }
1600    
1601        /**
1602         * Writes Document to specified file.
1603         *
1604         * @param doc document
1605         * @param file target file
1606         * @param transformer transformer
1607         *
1608         * @throws XmlUtilitiesException on error
1609         */
1610        public void writeDocumentToFile(final Document doc, final File file, final Transformer transformer) throws XmlUtilitiesException {
1611            writeDocumentToFile(doc, file, null, transformer);
1612        }
1613    
1614        /**
1615         * Writes Document to specified file using internal buffer.
1616         *
1617         * @param doc document
1618         * @param file target file
1619         * @param bufferSize buffer size or {@code null} for default
1620         *
1621         * @throws XmlUtilitiesException on error
1622         */
1623        public void writeDocumentToFile(final Document doc, final File file, final Integer bufferSize) throws XmlUtilitiesException {
1624            if (file == null) {
1625                throw new IllegalArgumentException("file can't be null.");
1626            }
1627    
1628            final OutputStream os;
1629            try {
1630                os = new FileOutputStream(file);
1631            } catch (FileNotFoundException ex) {
1632                final String message = String.format("Failed to create output file '%s'.", file);
1633                LOGGER.log(Level.SEVERE, message, ex);
1634                throw new XmlUtilitiesException(message, ex);
1635            }
1636            writeDocumentToBufferedStreamAndClose(doc, os, bufferSize);
1637        }
1638    
1639        /**
1640         * Writes Document to specified file using internal buffer.
1641         *
1642         * @param doc document
1643         * @param file target file
1644         * @param bufferSize buffer size or {@code null} for default
1645         * @param outputFormat output format
1646         *
1647         * @throws XmlUtilitiesException on error
1648         */
1649        public void writeDocumentToFile(final Document doc, final File file, final Integer bufferSize, final OutputFormat outputFormat) throws XmlUtilitiesException {
1650            if (file == null) {
1651                throw new IllegalArgumentException("file can't be null.");
1652            }
1653    
1654            final OutputStream os;
1655            try {
1656                os = new FileOutputStream(file);
1657            } catch (FileNotFoundException ex) {
1658                final String message = String.format("Failed to create output file '%s'.", file);
1659                LOGGER.log(Level.SEVERE, message, ex);
1660                throw new XmlUtilitiesException(message, ex);
1661            }
1662            writeDocumentToBufferedStreamAndClose(doc, os, bufferSize, outputFormat);
1663        }
1664    
1665        /**
1666         * Writes Document to specified file using internal buffer.
1667         *
1668         * @param doc document
1669         * @param file target file
1670         * @param bufferSize buffer size or {@code null} for default
1671         * @param outputProperties output format properties
1672         *
1673         * @throws XmlUtilitiesException on error
1674         */
1675        public void writeDocumentToFile(final Document doc, final File file, final Integer bufferSize, final Properties outputProperties) throws XmlUtilitiesException {
1676            if (file == null) {
1677                throw new IllegalArgumentException("file can't be null.");
1678            }
1679    
1680            final OutputStream os;
1681            try {
1682                os = new FileOutputStream(file);
1683            } catch (FileNotFoundException ex) {
1684                final String message = String.format("Failed to create output file '%s'.", file);
1685                LOGGER.log(Level.SEVERE, message, ex);
1686                throw new XmlUtilitiesException(message, ex);
1687            }
1688            writeDocumentToBufferedStreamAndClose(doc, os, bufferSize, outputProperties);
1689        }
1690    
1691        /**
1692         * Writes Document to specified file using internal buffer.
1693         *
1694         * @param doc document
1695         * @param file target file
1696         * @param bufferSize buffer size or {@code null} for default
1697         * @param transformer transformer
1698         *
1699         * @throws XmlUtilitiesException on error
1700         */
1701        public void writeDocumentToFile(final Document doc, final File file, final Integer bufferSize, final Transformer transformer) throws XmlUtilitiesException {
1702            if (file == null) {
1703                throw new IllegalArgumentException("file can't be null.");
1704            }
1705    
1706            final OutputStream os;
1707            try {
1708                os = new FileOutputStream(file);
1709            } catch (FileNotFoundException ex) {
1710                final String message = String.format("Failed to create output file '%s'.", file);
1711                LOGGER.log(Level.SEVERE, message, ex);
1712                throw new XmlUtilitiesException(message, ex);
1713            }
1714            writeDocumentToBufferedStreamAndClose(doc, os, bufferSize, transformer);
1715        }
1716    
1717        /**
1718         * Disables logging of {@code XmlTools} and {@code XmlUtilities}.
1719         */
1720        public static void disableLogging() {
1721            disableLogging0();
1722            XmlTools.disableLogging0();
1723        }
1724    
1725        /**
1726         * Disables logging.
1727         */
1728        static void disableLogging0() {
1729            setLoggingLevel0(Level.OFF);
1730        }
1731    
1732        /**
1733         * Sets new logging level of {@code XmlTools} and {@code XmlUtilities}.
1734         *
1735         * @param newLevel level to set
1736         */
1737        public static void setLoggingLevel(final Level newLevel) {
1738            setLoggingLevel0(newLevel);
1739            XmlTools.setLoggingLevel0(newLevel);
1740        }
1741    
1742        /**
1743         * Sets new logging level.
1744         *
1745         * @param newLevel level to set
1746         */
1747        static void setLoggingLevel0(final Level newLevel) {
1748            LOGGER.setLevel(newLevel);
1749        }
1750    
1751    }