5959import javax .xml .xpath .XPathConstants ;
6060import javax .xml .xpath .XPathExpressionException ;
6161import javax .xml .xpath .XPathFactory ;
62+ import net .sf .saxon .xpath .XPathFactoryImpl ;
6263import org .w3c .dom .Document ;
6364import org .w3c .dom .Node ;
6465import org .w3c .dom .NodeList ;
8081 }
8182 )
8283public final class XMLDocument implements XML {
83-
84- /**
85- * XPath factory.
86- */
87- private static final XPathFactory XFACTORY =
88- XPathFactory .newInstance ();
89-
90- /**
91- * Transformer factory.
92- */
93- private static final TransformerFactory TFACTORY =
94- TransformerFactory .newInstance ();
95-
96- /**
97- * DOM document builder factory.
98- */
99- private static final DocumentBuilderFactory DFACTORY =
100- DocumentBuilderFactory .newInstance ();
101-
10284 /**
10385 * Namespace context to use for {@link #xpath(String)}
10486 * and {@link #nodes(String)} methods.
@@ -115,25 +97,6 @@ public final class XMLDocument implements XML {
11597 */
11698 private final transient Node cache ;
11799
118- /**
119- * Transformer factory to use for {@link #toString()}.
120- */
121- private final transient TransformerFactory tfactory ;
122-
123- static {
124- if (XMLDocument .DFACTORY .getClass ().getName ().contains ("xerces" )) {
125- try {
126- XMLDocument .DFACTORY .setFeature (
127- "http://apache.org/xml/features/nonvalidating/load-external-dtd" ,
128- false
129- );
130- } catch (final ParserConfigurationException ex ) {
131- throw new IllegalStateException (ex );
132- }
133- }
134- XMLDocument .DFACTORY .setNamespaceAware (true );
135- }
136-
137100 /**
138101 * Public ctor, from XML as a text.
139102 *
@@ -154,40 +117,12 @@ public final class XMLDocument implements XML {
154117 */
155118 public XMLDocument (final String text ) {
156119 this (
157- new DomParser (XMLDocument .DFACTORY , text ).document (),
120+ new DomParser (XMLDocument .configuredDFactory () , text ).document (),
158121 new XPathContext (),
159122 false
160123 );
161124 }
162125
163- /**
164- * Public ctor, from XML as a text.
165- *
166- * <p>The object is created with a default implementation of
167- * {@link NamespaceContext}, which already defines a
168- * number of namespaces, for convenience, including:
169- *
170- * <pre> xhtml: http://www.w3.org/1999/xhtml
171- * xs: http://www.w3.org/2001/XMLSchema
172- * xsi: http://www.w3.org/2001/XMLSchema-instance
173- * xsl: http://www.w3.org/1999/XSL/Transform
174- * svg: http://www.w3.org/2000/svg</pre>
175- *
176- * <p>In future versions we will add more namespaces (submit a ticket if
177- * you need more of them defined here).
178- *
179- * @param text XML document body
180- * @param factory Transformer factory
181- */
182- public XMLDocument (final String text , final TransformerFactory factory ) {
183- this (
184- new DomParser (XMLDocument .DFACTORY , text ).document (),
185- new XPathContext (),
186- false ,
187- factory
188- );
189- }
190-
191126 /**
192127 * Public ctor, from XML as a text.
193128 *
@@ -208,7 +143,7 @@ public XMLDocument(final String text, final TransformerFactory factory) {
208143 */
209144 public XMLDocument (final byte [] data ) {
210145 this (
211- new DomParser (XMLDocument .DFACTORY , data ).document (),
146+ new DomParser (XMLDocument .configuredDFactory () , data ).document (),
212147 new XPathContext (),
213148 false
214149 );
@@ -334,43 +269,26 @@ public XMLDocument(final InputStream stream) throws IOException {
334269 stream .close ();
335270 }
336271
337- /**
338- * Private ctor.
339- * @param node The source
340- * @param ctx Namespace context
341- * @param lfe Is it a leaf node?
342- */
343- private XMLDocument (
344- final Node node ,
345- final XPathContext ctx ,
346- final boolean lfe
347- ) {
348- this (node , ctx , lfe , XMLDocument .TFACTORY );
349- }
350-
351272 /**
352273 * Private ctor.
353274 * @param cache The source
354275 * @param context Namespace context
355276 * @param leaf Is it a leaf node?
356- * @param tfactory Transformer factory
357277 * @checkstyle ParameterNumberCheck (5 lines)
358278 */
359- public XMLDocument (
279+ private XMLDocument (
360280 final Node cache ,
361281 final XPathContext context ,
362- final boolean leaf ,
363- final TransformerFactory tfactory
282+ final boolean leaf
364283 ) {
365284 this .context = context ;
366285 this .leaf = leaf ;
367286 this .cache = cache ;
368- this .tfactory = tfactory ;
369287 }
370288
371289 @ Override
372290 public String toString () {
373- return this .asString (this .cache );
291+ return XMLDocument .asString (this .cache );
374292 }
375293
376294 @ Override
@@ -438,7 +356,7 @@ public List<String> xpath(final String query) {
438356 throw new IllegalArgumentException (
439357 String .format (
440358 "Invalid XPath query '%s' at %s: %s" ,
441- query , XMLDocument . XFACTORY . getClass () .getName (),
359+ query , XPathFactoryImpl . class .getName (),
442360 ex .getLocalizedMessage ()
443361 ),
444362 exp
@@ -475,7 +393,7 @@ public List<XML> nodes(final String query) {
475393 throw new IllegalArgumentException (
476394 String .format (
477395 "Invalid XPath query '%s' by %s" ,
478- query , XMLDocument . XFACTORY . getClass () .getName ()
396+ query , XPathFactoryImpl . class .getName ()
479397 ), ex
480398 );
481399 }
@@ -497,14 +415,15 @@ public XML merge(final NamespaceContext ctx) {
497415 * @return A cloned node imported in a dedicated document.
498416 */
499417 private static Node createImportedNode (final Node node ) {
418+ final DocumentBuilderFactory factory = XMLDocument .configuredDFactory ();
500419 final DocumentBuilder builder ;
501420 try {
502- builder = XMLDocument . DFACTORY .newDocumentBuilder ();
421+ builder = factory .newDocumentBuilder ();
503422 } catch (final ParserConfigurationException ex ) {
504423 throw new IllegalArgumentException (
505424 String .format (
506425 "Failed to create document builder by %s" ,
507- XMLDocument . DFACTORY .getClass ().getName ()
426+ factory .getClass ().getName ()
508427 ),
509428 ex
510429 );
@@ -530,12 +449,9 @@ private static Node createImportedNode(final Node node) {
530449 * @throws XPathExpressionException If an error occurs when evaluating XPath
531450 */
532451 @ SuppressWarnings ("unchecked" )
533- private <T > T fetch (final String query , final Class <T > type )
534- throws XPathExpressionException {
535- final XPath xpath ;
536- synchronized (XMLDocument .class ) {
537- xpath = XMLDocument .XFACTORY .newXPath ();
538- }
452+ private <T > T fetch (final String query , final Class <T > type ) throws XPathExpressionException {
453+ final XPathFactory factory = XPathFactory .newInstance ();
454+ final XPath xpath = factory .newXPath ();
539455 xpath .setNamespaceContext (this .context );
540456 final QName qname ;
541457 if (type .equals (String .class )) {
@@ -559,18 +475,16 @@ private <T> T fetch(final String query, final Class<T> type)
559475 * @param node The DOM node.
560476 * @return String representation
561477 */
562- private String asString (final Node node ) {
563- final StringWriter writer = new StringWriter ();
478+ private static String asString (final Node node ) {
479+ final TransformerFactory factory = TransformerFactory . newInstance ();
564480 final Transformer trans ;
565481 try {
566- synchronized (XMLDocument .class ) {
567- trans = this .tfactory .newTransformer ();
568- }
482+ trans = factory .newTransformer ();
569483 } catch (final TransformerConfigurationException ex ) {
570484 throw new IllegalArgumentException (
571485 String .format (
572486 "Failed to create transformer by %s" ,
573- this . tfactory . getClass () .getName ()
487+ XPathFactoryImpl . class .getName ()
574488 ),
575489 ex
576490 );
@@ -581,11 +495,10 @@ private String asString(final Node node) {
581495 trans .setOutputProperty (OutputKeys .OMIT_XML_DECLARATION , "yes" );
582496 }
583497 final Source source = new DOMSource (node );
498+ final StringWriter writer = new StringWriter ();
584499 final Result result = new StreamResult (writer );
585500 try {
586- synchronized (node ) {
587- trans .transform (source , result );
588- }
501+ trans .transform (source , result );
589502 } catch (final TransformerException ex ) {
590503 throw new IllegalArgumentException (
591504 String .format (
@@ -606,11 +519,9 @@ private String asString(final Node node) {
606519 */
607520 private static Node transform (final Source source ) {
608521 final DOMResult result = new DOMResult ();
522+ final TransformerFactory factory = TransformerFactory .newInstance ();
609523 try {
610- final Transformer trans ;
611- synchronized (XMLDocument .class ) {
612- trans = XMLDocument .TFACTORY .newTransformer ();
613- }
524+ final Transformer trans = factory .newTransformer ();
614525 trans .transform (source , result );
615526 } catch (final TransformerException ex ) {
616527 throw new IllegalArgumentException (
@@ -625,4 +536,23 @@ private static Node transform(final Source source) {
625536 return result .getNode ();
626537 }
627538
539+ /**
540+ * Create new {@link DocumentBuilderFactory} and configure it.
541+ * @return Configured factory
542+ */
543+ private static DocumentBuilderFactory configuredDFactory () {
544+ final DocumentBuilderFactory factory = DocumentBuilderFactory .newInstance ();
545+ if (factory .getClass ().getName ().contains ("xerces" )) {
546+ try {
547+ factory .setFeature (
548+ "http://apache.org/xml/features/nonvalidating/load-external-dtd" ,
549+ false
550+ );
551+ } catch (final ParserConfigurationException ex ) {
552+ throw new IllegalStateException (ex );
553+ }
554+ }
555+ factory .setNamespaceAware (true );
556+ return factory ;
557+ }
628558}
0 commit comments