prefixes = this.nsUriToPrefixesMap.get(namespaceURI);
74 | if (prefixes != null && prefixes.size() > 0) {
75 | return prefixes.iterator().next();
76 | }
77 | if (this.element != null) {
78 | return this.element.lookupPrefix(namespaceURI);
79 | }
80 | return null;
81 | }
82 |
83 | // Not implemented
84 | @SuppressWarnings({ "rawtypes" })
85 | public Iterator getPrefixes(String namespaceURI) {
86 | return Collections.EMPTY_LIST.iterator();
87 | }
88 |
89 | }
90 |
--------------------------------------------------------------------------------
/src/main/java/com/jamesmurty/utils/XMLBuilder.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2008-2020 James Murty (github.com/jmurty)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | *
17 | * This code is available from the GitHub code repository at:
18 | * https://github.com/jmurty/java-xmlbuilder
19 | */
20 | package com.jamesmurty.utils;
21 |
22 | import java.io.File;
23 | import java.io.FileReader;
24 | import java.io.IOException;
25 | import java.io.StringReader;
26 |
27 | import javax.xml.namespace.NamespaceContext;
28 | import javax.xml.parsers.DocumentBuilder;
29 | import javax.xml.parsers.DocumentBuilderFactory;
30 | import javax.xml.parsers.FactoryConfigurationError;
31 | import javax.xml.parsers.ParserConfigurationException;
32 | import javax.xml.xpath.XPathExpressionException;
33 |
34 | import org.w3c.dom.Document;
35 | import org.w3c.dom.Element;
36 | import org.w3c.dom.Node;
37 | import org.xml.sax.InputSource;
38 | import org.xml.sax.SAXException;
39 |
40 | /**
41 | * XML Builder is a utility that creates simple XML documents using relatively
42 | * sparse Java code. It is intended to allow for quick and painless creation of
43 | * XML documents where you might otherwise be tempted to use concatenated
44 | * strings, rather than face the tedium and verbosity of coding with
45 | * JAXP (http://jaxp.dev.java.net/).
46 | *
47 | * Internally, XML Builder uses JAXP to build a standard W3C
48 | * {@link org.w3c.dom.Document} model (DOM) that you can easily export as a
49 | * string, or access and manipulate further if you have special requirements.
50 | *
51 | *
52 | * The XMLBuilder class serves as a wrapper of {@link org.w3c.dom.Element} nodes,
53 | * and provides a number of utility methods that make it simple to
54 | * manipulate the underlying element and the document to which it belongs.
55 | * In essence, this class performs dual roles: it represents a specific XML
56 | * node, and also allows manipulation of the entire underlying XML document.
57 | * The platform's default {@link DocumentBuilderFactory} and
58 | * {@link DocumentBuilder} classes are used to build the document.
59 | *
60 | *
61 | * @author James Murty
62 | */
63 | public final class XMLBuilder extends BaseXMLBuilder {
64 |
65 | /**
66 | * Construct a new builder object that wraps the given XML document.
67 | * This constructor is for internal use only.
68 | *
69 | * @param xmlDocument
70 | * an XML document that the builder will manage and manipulate.
71 | */
72 | protected XMLBuilder(Document xmlDocument) {
73 | super(xmlDocument);
74 | }
75 |
76 | /**
77 | * Construct a new builder object that wraps the given XML document and node.
78 | * This constructor is for internal use only.
79 | *
80 | * @param myNode
81 | * the XML node that this builder node will wrap. This node may
82 | * be part of the XML document, or it may be a new element that is to be
83 | * added to the document.
84 | * @param parentNode
85 | * If not null, the given myElement will be appended as child node of the
86 | * parentNode node.
87 | */
88 | protected XMLBuilder(Node myNode, Node parentNode) {
89 | super(myNode, parentNode);
90 | }
91 |
92 | /**
93 | * Construct a builder for new XML document with a default namespace.
94 | * The document will be created with the given root element, and the builder
95 | * returned by this method will serve as the starting-point for any further
96 | * document additions.
97 | *
98 | * @param name
99 | * the name of the document's root element.
100 | * @param namespaceURI
101 | * default namespace URI for document, ignored if null or empty.
102 | * @param enableExternalEntities
103 | * enable external entities; beware of XML External Entity (XXE) injection.
104 | * @param isNamespaceAware
105 | * enable or disable namespace awareness in the underlying
106 | * {@link DocumentBuilderFactory}
107 | * @return
108 | * a builder node that can be used to add more nodes to the XML document.
109 | *
110 | * @throws FactoryConfigurationError xyz
111 | * @throws ParserConfigurationException xyz
112 | */
113 | public static XMLBuilder create(String name, String namespaceURI,
114 | boolean enableExternalEntities, boolean isNamespaceAware)
115 | throws ParserConfigurationException, FactoryConfigurationError
116 | {
117 | return new XMLBuilder(
118 | createDocumentImpl(
119 | name, namespaceURI, enableExternalEntities, isNamespaceAware));
120 | }
121 |
122 | /**
123 | * Construct a builder for new XML document. The document will be created
124 | * with the given root element, and the builder returned by this method
125 | * will serve as the starting-point for any further document additions.
126 | *
127 | * @param name
128 | * the name of the document's root element.
129 | * @param enableExternalEntities
130 | * enable external entities; beware of XML External Entity (XXE) injection.
131 | * @param isNamespaceAware
132 | * enable or disable namespace awareness in the underlying
133 | * {@link DocumentBuilderFactory}
134 | * @return
135 | * a builder node that can be used to add more nodes to the XML document.
136 | *
137 | * @throws FactoryConfigurationError xyz
138 | * @throws ParserConfigurationException xyz
139 | */
140 | public static XMLBuilder create(String name, boolean enableExternalEntities,
141 | boolean isNamespaceAware)
142 | throws ParserConfigurationException, FactoryConfigurationError
143 | {
144 | return create(name, null, enableExternalEntities, isNamespaceAware);
145 | }
146 |
147 | /**
148 | * Construct a builder for new XML document with a default namespace.
149 | * The document will be created with the given root element, and the builder
150 | * returned by this method will serve as the starting-point for any further
151 | * document additions.
152 | *
153 | * @param name
154 | * the name of the document's root element.
155 | * @param namespaceURI
156 | * default namespace URI for document, ignored if null or empty.
157 |
158 | * @return
159 | * a builder node that can be used to add more nodes to the XML document.
160 | *
161 | * @throws FactoryConfigurationError xyz
162 | * @throws ParserConfigurationException xyz
163 | */
164 | public static XMLBuilder create(String name, String namespaceURI)
165 | throws ParserConfigurationException, FactoryConfigurationError
166 | {
167 | return create(name, namespaceURI, false, true);
168 | }
169 |
170 | /**
171 | * Construct a builder for new XML document. The document will be created
172 | * with the given root element, and the builder returned by this method
173 | * will serve as the starting-point for any further document additions.
174 | *
175 | * @param name
176 | * the name of the document's root element.
177 | * @return
178 | * a builder node that can be used to add more nodes to the XML document.
179 | *
180 | * @throws FactoryConfigurationError xyz
181 | * @throws ParserConfigurationException xyz
182 | */
183 | public static XMLBuilder create(String name)
184 | throws ParserConfigurationException, FactoryConfigurationError
185 | {
186 | return create(name, null);
187 | }
188 |
189 | /**
190 | * Construct a builder from an existing XML document. The provided XML
191 | * document will be parsed and an XMLBuilder object referencing the
192 | * document's root element will be returned.
193 | *
194 | * @param inputSource
195 | * an XML document input source that will be parsed into a DOM.
196 | * @param enableExternalEntities
197 | * enable external entities; beware of XML External Entity (XXE) injection.
198 | * @param isNamespaceAware
199 | * enable or disable namespace awareness in the underlying
200 | * {@link DocumentBuilderFactory}
201 | * @return
202 | * a builder node that can be used to add more nodes to the XML document.
203 | *
204 | * @throws ParserConfigurationException xyz
205 | * @throws FactoryConfigurationError xyz
206 | * @throws ParserConfigurationException xyz
207 | * @throws IOException xyz
208 | * @throws SAXException xyz
209 | */
210 | public static XMLBuilder parse(
211 | InputSource inputSource, boolean enableExternalEntities,
212 | boolean isNamespaceAware)
213 | throws ParserConfigurationException, SAXException, IOException
214 | {
215 | return new XMLBuilder(
216 | parseDocumentImpl(
217 | inputSource, enableExternalEntities, isNamespaceAware));
218 | }
219 |
220 | /**
221 | * Construct a builder from an existing XML document string.
222 | * The provided XML document will be parsed and an XMLBuilder
223 | * object referencing the document's root element will be returned.
224 | *
225 | * @param xmlString
226 | * an XML document string that will be parsed into a DOM.
227 | * @param enableExternalEntities
228 | * enable external entities; beware of XML External Entity (XXE) injection.
229 | * @param isNamespaceAware
230 | * enable or disable namespace awareness in the underlying
231 | * {@link DocumentBuilderFactory}
232 | * @return
233 | * a builder node that can be used to add more nodes to the XML document.
234 | *
235 | * @throws ParserConfigurationException xyz
236 | * @throws FactoryConfigurationError xyz
237 | * @throws ParserConfigurationException xyz
238 | * @throws IOException xyz
239 | * @throws SAXException xyz
240 | */
241 | public static XMLBuilder parse(
242 | String xmlString, boolean enableExternalEntities,
243 | boolean isNamespaceAware)
244 | throws ParserConfigurationException, SAXException, IOException
245 | {
246 | return XMLBuilder.parse(
247 | new InputSource(new StringReader(xmlString)),
248 | enableExternalEntities,
249 | isNamespaceAware);
250 | }
251 |
252 | /**
253 | * Construct a builder from an existing XML document file.
254 | * The provided XML document will be parsed and an XMLBuilder
255 | * object referencing the document's root element will be returned.
256 | *
257 | * @param xmlFile
258 | * an XML document file that will be parsed into a DOM.
259 | * @param enableExternalEntities
260 | * enable external entities; beware of XML External Entity (XXE) injection.
261 | * @param isNamespaceAware
262 | * enable or disable namespace awareness in the underlying
263 | * {@link DocumentBuilderFactory}
264 | * @return
265 | * a builder node that can be used to add more nodes to the XML document.
266 | *
267 | * @throws ParserConfigurationException xyz
268 | * @throws FactoryConfigurationError xyz
269 | * @throws ParserConfigurationException xyz
270 | * @throws IOException xyz
271 | * @throws SAXException xyz
272 | */
273 | public static XMLBuilder parse(File xmlFile, boolean enableExternalEntities,
274 | boolean isNamespaceAware)
275 | throws ParserConfigurationException, SAXException, IOException
276 | {
277 | return XMLBuilder.parse(
278 | new InputSource(new FileReader(xmlFile)),
279 | enableExternalEntities,
280 | isNamespaceAware);
281 | }
282 |
283 | /**
284 | * Construct a builder from an existing XML document. The provided XML
285 | * document will be parsed and an XMLBuilder object referencing the
286 | * document's root element will be returned.
287 | *
288 | * @param inputSource
289 | * an XML document input source that will be parsed into a DOM.
290 | * @return
291 | * a builder node that can be used to add more nodes to the XML document.
292 | *
293 | * @throws ParserConfigurationException xyz
294 | * @throws FactoryConfigurationError xyz
295 | * @throws ParserConfigurationException xyz
296 | * @throws IOException xyz
297 | * @throws SAXException xyz
298 | */
299 | public static XMLBuilder parse(InputSource inputSource)
300 | throws ParserConfigurationException, SAXException, IOException
301 | {
302 | return XMLBuilder.parse(inputSource, false, true);
303 | }
304 |
305 | /**
306 | * Construct a builder from an existing XML document string.
307 | * The provided XML document will be parsed and an XMLBuilder
308 | * object referencing the document's root element will be returned.
309 | *
310 | * @param xmlString
311 | * an XML document string that will be parsed into a DOM.
312 | * @return
313 | * a builder node that can be used to add more nodes to the XML document.
314 | *
315 | * @throws ParserConfigurationException xyz
316 | * @throws FactoryConfigurationError xyz
317 | * @throws ParserConfigurationException xyz
318 | * @throws IOException xyz
319 | * @throws SAXException xyz
320 | */
321 | public static XMLBuilder parse(String xmlString)
322 | throws ParserConfigurationException, SAXException, IOException
323 | {
324 | return XMLBuilder.parse(xmlString, false, true);
325 | }
326 |
327 | /**
328 | * Construct a builder from an existing XML document file.
329 | * The provided XML document will be parsed and an XMLBuilder
330 | * object referencing the document's root element will be returned.
331 | *
332 | * @param xmlFile
333 | * an XML document file that will be parsed into a DOM.
334 | * @return
335 | * a builder node that can be used to add more nodes to the XML document.
336 | *
337 | * @throws ParserConfigurationException xyz
338 | * @throws FactoryConfigurationError xyz
339 | * @throws ParserConfigurationException xyz
340 | * @throws IOException xyz
341 | * @throws SAXException xyz
342 | */
343 | public static XMLBuilder parse(File xmlFile)
344 | throws ParserConfigurationException, SAXException, IOException
345 | {
346 | return XMLBuilder.parse(xmlFile, false, true);
347 | }
348 |
349 | @Override
350 | public XMLBuilder stripWhitespaceOnlyTextNodes()
351 | throws XPathExpressionException
352 | {
353 | super.stripWhitespaceOnlyTextNodesImpl();
354 | return this;
355 | }
356 |
357 | @Override
358 | public XMLBuilder importXMLBuilder(BaseXMLBuilder builder) {
359 | super.importXMLBuilderImpl(builder);
360 | return this;
361 | }
362 |
363 | @Override
364 | public XMLBuilder root() {
365 | return new XMLBuilder(getDocument());
366 | }
367 |
368 | @Override
369 | public XMLBuilder xpathFind(String xpath, NamespaceContext nsContext)
370 | throws XPathExpressionException
371 | {
372 | Node foundNode = super.xpathFindImpl(xpath, nsContext);
373 | return new XMLBuilder(foundNode, null);
374 | }
375 |
376 | @Override
377 | public XMLBuilder xpathFind(String xpath) throws XPathExpressionException {
378 | return xpathFind(xpath, null);
379 | }
380 |
381 | @Override
382 | public XMLBuilder element(String name) {
383 | String namespaceURI = super.lookupNamespaceURIImpl(name);
384 | return element(name, namespaceURI);
385 | }
386 |
387 | @Override
388 | public XMLBuilder elem(String name) {
389 | return element(name);
390 | }
391 |
392 | @Override
393 | public XMLBuilder e(String name) {
394 | return element(name);
395 | }
396 |
397 | @Override
398 | public XMLBuilder element(String name, String namespaceURI) {
399 | Element elem = super.elementImpl(name, namespaceURI);
400 | return new XMLBuilder(elem, this.getElement());
401 | }
402 |
403 | @Override
404 | public XMLBuilder elementBefore(String name) {
405 | Element newElement = super.elementBeforeImpl(name);
406 | return new XMLBuilder(newElement, null);
407 | }
408 |
409 | @Override
410 | public XMLBuilder elementBefore(String name, String namespaceURI) {
411 | Element newElement = super.elementBeforeImpl(name, namespaceURI);
412 | return new XMLBuilder(newElement, null);
413 | }
414 |
415 | @Override
416 | public XMLBuilder attribute(String name, String value) {
417 | super.attributeImpl(name, value);
418 | return this;
419 | }
420 |
421 | @Override
422 | public XMLBuilder attr(String name, String value) {
423 | return attribute(name, value);
424 | }
425 |
426 | @Override
427 | public XMLBuilder a(String name, String value) {
428 | return attribute(name, value);
429 | }
430 |
431 |
432 | @Override
433 | public XMLBuilder text(String value, boolean replaceText) {
434 | super.textImpl(value, replaceText);
435 | return this;
436 | }
437 |
438 | @Override
439 | public XMLBuilder text(String value) {
440 | return this.text(value, false);
441 | }
442 |
443 | @Override
444 | public XMLBuilder t(String value) {
445 | return text(value);
446 | }
447 |
448 | @Override
449 | public XMLBuilder cdata(String data) {
450 | super.cdataImpl(data);
451 | return this;
452 | }
453 |
454 | @Override
455 | public XMLBuilder data(String data) {
456 | return cdata(data);
457 | }
458 |
459 | @Override
460 | public XMLBuilder d(String data) {
461 | return cdata(data);
462 | }
463 |
464 | @Override
465 | public XMLBuilder cdata(byte[] data) {
466 | super.cdataImpl(data);
467 | return this;
468 | }
469 |
470 | @Override
471 | public XMLBuilder data(byte[] data) {
472 | return cdata(data);
473 | }
474 |
475 | @Override
476 | public XMLBuilder d(byte[] data) {
477 | return cdata(data);
478 | }
479 |
480 | @Override
481 | public XMLBuilder comment(String comment) {
482 | super.commentImpl(comment);
483 | return this;
484 | }
485 |
486 | @Override
487 | public XMLBuilder cmnt(String comment) {
488 | return comment(comment);
489 | }
490 |
491 | @Override
492 | public XMLBuilder c(String comment) {
493 | return comment(comment);
494 | }
495 |
496 | @Override
497 | public XMLBuilder instruction(String target, String data) {
498 | super.instructionImpl(target, data);
499 | return this;
500 | }
501 |
502 | @Override
503 | public XMLBuilder inst(String target, String data) {
504 | return instruction(target, data);
505 | }
506 |
507 | @Override
508 | public XMLBuilder i(String target, String data) {
509 | return instruction(target, data);
510 | }
511 |
512 | @Override
513 | public XMLBuilder insertInstruction(String target, String data) {
514 | super.insertInstructionImpl(target, data);
515 | return this;
516 | }
517 |
518 | @Override
519 | public XMLBuilder reference(String name) {
520 | super.referenceImpl(name);
521 | return this;
522 | }
523 |
524 | @Override
525 | public XMLBuilder ref(String name) {
526 | return reference(name);
527 | }
528 |
529 | @Override
530 | public XMLBuilder r(String name) {
531 | return reference(name);
532 | }
533 |
534 | @Override
535 | public XMLBuilder namespace(String prefix, String namespaceURI) {
536 | super.namespaceImpl(prefix, namespaceURI);
537 | return this;
538 | }
539 |
540 | @Override
541 | public XMLBuilder ns(String prefix, String namespaceURI) {
542 | return attribute(prefix, namespaceURI);
543 | }
544 |
545 | @Override
546 | public XMLBuilder namespace(String namespaceURI) {
547 | this.namespace(null, namespaceURI);
548 | return this;
549 | }
550 |
551 | @Override
552 | public XMLBuilder ns(String namespaceURI) {
553 | return namespace(namespaceURI);
554 | }
555 |
556 | @Override
557 | public XMLBuilder up(int steps) {
558 | Node currNode = super.upImpl(steps);
559 | if (currNode instanceof Document) {
560 | return new XMLBuilder((Document) currNode);
561 | } else {
562 | return new XMLBuilder(currNode, null);
563 | }
564 | }
565 |
566 | @Override
567 | public XMLBuilder up() {
568 | return up(1);
569 | }
570 |
571 | @Override
572 | public XMLBuilder document() {
573 | return new XMLBuilder(getDocument(), null);
574 | }
575 |
576 | }
577 |
--------------------------------------------------------------------------------
/src/main/java/com/jamesmurty/utils/XMLBuilder2.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2008-2020 James Murty (github.com/jmurty)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | *
17 | * This code is available from the GitHub code repository at:
18 | * https://github.com/jmurty/java-xmlbuilder
19 | */
20 | package com.jamesmurty.utils;
21 |
22 | import java.io.File;
23 | import java.io.FileNotFoundException;
24 | import java.io.FileReader;
25 | import java.io.IOException;
26 | import java.io.StringReader;
27 | import java.io.Writer;
28 | import java.util.Properties;
29 |
30 | import javax.xml.namespace.NamespaceContext;
31 | import javax.xml.namespace.QName;
32 | import javax.xml.parsers.DocumentBuilder;
33 | import javax.xml.parsers.DocumentBuilderFactory;
34 | import javax.xml.parsers.ParserConfigurationException;
35 | import javax.xml.transform.TransformerException;
36 | import javax.xml.xpath.XPathExpressionException;
37 |
38 | import org.w3c.dom.Document;
39 | import org.w3c.dom.Element;
40 | import org.w3c.dom.Node;
41 | import org.xml.sax.InputSource;
42 | import org.xml.sax.SAXException;
43 |
44 | /**
45 | * XML Builder is a utility that creates simple XML documents using relatively
46 | * sparse Java code. It is intended to allow for quick and painless creation of
47 | * XML documents where you might otherwise be tempted to use concatenated
48 | * strings, rather than face the tedium and verbosity of coding with
49 | * JAXP (http://jaxp.dev.java.net/).
50 | *
51 | * Internally, XML Builder uses JAXP to build a standard W3C
52 | * {@link org.w3c.dom.Document} model (DOM) that you can easily export as a
53 | * string, or access and manipulate further if you have special requirements.
54 | *
55 | *
56 | * The XMLBuilder2 class serves as a wrapper of {@link org.w3c.dom.Element} nodes,
57 | * and provides a number of utility methods that make it simple to
58 | * manipulate the underlying element and the document to which it belongs.
59 | * In essence, this class performs dual roles: it represents a specific XML
60 | * node, and also allows manipulation of the entire underlying XML document.
61 | * The platform's default {@link DocumentBuilderFactory} and
62 | * {@link DocumentBuilder} classes are used to build the document.
63 | *
64 | *
65 | * XMLBuilder2 has an feature set to the original XMLBuilder, but only ever
66 | * throws runtime exceptions (as opposed to checked exceptions). Any internal
67 | * checked exceptions are caught and wrapped in an
68 | * {@link XMLBuilderRuntimeException} object.
69 | *
70 | *
71 | * @author James Murty
72 | */
73 | public final class XMLBuilder2 extends BaseXMLBuilder {
74 |
75 | /**
76 | * Construct a new builder object that wraps the given XML document.
77 | * This constructor is for internal use only.
78 | *
79 | * @param xmlDocument
80 | * an XML document that the builder will manage and manipulate.
81 | */
82 | protected XMLBuilder2(Document xmlDocument) {
83 | super(xmlDocument);
84 | }
85 |
86 | /**
87 | * Construct a new builder object that wraps the given XML document and node.
88 | * This constructor is for internal use only.
89 | *
90 | * @param myNode
91 | * the XML node that this builder node will wrap. This node may
92 | * be part of the XML document, or it may be a new element that is to be
93 | * added to the document.
94 | * @param parentNode
95 | * If not null, the given myElement will be appended as child node of the
96 | * parentNode node.
97 | */
98 | protected XMLBuilder2(Node myNode, Node parentNode) {
99 | super(myNode, parentNode);
100 | }
101 |
102 | private static RuntimeException wrapExceptionAsRuntimeException(Exception e) {
103 | // Don't wrap (or re-wrap) runtime exceptions.
104 | if (e instanceof RuntimeException) {
105 | return (RuntimeException) e;
106 | } else {
107 | return new XMLBuilderRuntimeException(e);
108 | }
109 | }
110 |
111 | /**
112 | * Construct a builder for new XML document with a default namespace.
113 | * The document will be created with the given root element, and the builder
114 | * returned by this method will serve as the starting-point for any further
115 | * document additions.
116 | *
117 | * @param name
118 | * the name of the document's root element.
119 | * @param namespaceURI
120 | * default namespace URI for document, ignored if null or empty.
121 | * @param enableExternalEntities
122 | * enable external entities; beware of XML External Entity (XXE) injection.
123 | * @param isNamespaceAware
124 | * enable or disable namespace awareness in the underlying
125 | * {@link DocumentBuilderFactory}
126 | * @return
127 | * a builder node that can be used to add more nodes to the XML document.
128 | * @throws XMLBuilderRuntimeException
129 | * to wrap {@link ParserConfigurationException}
130 | */
131 | public static XMLBuilder2 create(
132 | String name, String namespaceURI, boolean enableExternalEntities,
133 | boolean isNamespaceAware)
134 | {
135 | try {
136 | return new XMLBuilder2(
137 | createDocumentImpl(
138 | name, namespaceURI, enableExternalEntities, isNamespaceAware));
139 | } catch (ParserConfigurationException e) {
140 | throw wrapExceptionAsRuntimeException(e);
141 | }
142 | }
143 |
144 | /**
145 | * Construct a builder for new XML document. The document will be created
146 | * with the given root element, and the builder returned by this method
147 | * will serve as the starting-point for any further document additions.
148 | *
149 | * @param name
150 | * the name of the document's root element.
151 | * @param enableExternalEntities
152 | * enable external entities; beware of XML External Entity (XXE) injection.
153 | * @param isNamespaceAware
154 | * enable or disable namespace awareness in the underlying
155 | * {@link DocumentBuilderFactory}
156 | * @return
157 | * a builder node that can be used to add more nodes to the XML document.
158 | * @throws XMLBuilderRuntimeException
159 | * to wrap {@link ParserConfigurationException}
160 | */
161 | public static XMLBuilder2 create(String name,
162 | boolean enableExternalEntities, boolean isNamespaceAware)
163 | {
164 | return XMLBuilder2.create(
165 | name, null, enableExternalEntities, isNamespaceAware);
166 | }
167 |
168 | /**
169 | * Construct a builder for new XML document with a default namespace.
170 | * The document will be created with the given root element, and the builder
171 | * returned by this method will serve as the starting-point for any further
172 | * document additions.
173 | *
174 | * @param name
175 | * the name of the document's root element.
176 | * @param namespaceURI
177 | * default namespace URI for document, ignored if null or empty.
178 | * @return
179 | * a builder node that can be used to add more nodes to the XML document.
180 | * @throws XMLBuilderRuntimeException
181 | * to wrap {@link ParserConfigurationException}
182 | */
183 | public static XMLBuilder2 create(String name, String namespaceURI)
184 | {
185 | return XMLBuilder2.create(name, namespaceURI, false, true);
186 | }
187 |
188 | /**
189 | * Construct a builder for new XML document. The document will be created
190 | * with the given root element, and the builder returned by this method
191 | * will serve as the starting-point for any further document additions.
192 | *
193 | * @param name
194 | * the name of the document's root element.
195 | * @return
196 | * a builder node that can be used to add more nodes to the XML document.
197 | * @throws XMLBuilderRuntimeException
198 | * to wrap {@link ParserConfigurationException}
199 | */
200 | public static XMLBuilder2 create(String name)
201 | {
202 | return XMLBuilder2.create(name, null, false, true);
203 | }
204 |
205 | /**
206 | * Construct a builder from an existing XML document. The provided XML
207 | * document will be parsed and an XMLBuilder2 object referencing the
208 | * document's root element will be returned.
209 | *
210 | * @param inputSource
211 | * an XML document input source that will be parsed into a DOM.
212 | * @param enableExternalEntities
213 | * enable external entities; beware of XML External Entity (XXE) injection.
214 | * @param isNamespaceAware
215 | * enable or disable namespace awareness in the underlying
216 | * {@link DocumentBuilderFactory}
217 | * @return
218 | * a builder node that can be used to add more nodes to the XML document.
219 | * @throws XMLBuilderRuntimeException
220 | * to wrap {@link ParserConfigurationException}, {@link SAXException},
221 | * {@link IOException}
222 | */
223 | public static XMLBuilder2 parse(
224 | InputSource inputSource, boolean enableExternalEntities,
225 | boolean isNamespaceAware)
226 | {
227 | try {
228 | return new XMLBuilder2(
229 | parseDocumentImpl(
230 | inputSource, enableExternalEntities, isNamespaceAware));
231 | } catch (ParserConfigurationException e) {
232 | throw wrapExceptionAsRuntimeException(e);
233 | } catch (SAXException e) {
234 | throw wrapExceptionAsRuntimeException(e);
235 | } catch (IOException e) {
236 | throw wrapExceptionAsRuntimeException(e);
237 | }
238 | }
239 |
240 | /**
241 | * Construct a builder from an existing XML document string.
242 | * The provided XML document will be parsed and an XMLBuilder2
243 | * object referencing the document's root element will be returned.
244 | *
245 | * @param xmlString
246 | * an XML document string that will be parsed into a DOM.
247 | * @param enableExternalEntities
248 | * enable external entities; beware of XML External Entity (XXE) injection.
249 | * @param isNamespaceAware
250 | * enable or disable namespace awareness in the underlying
251 | * {@link DocumentBuilderFactory}
252 | * @return
253 | * a builder node that can be used to add more nodes to the XML document.
254 | */
255 | public static XMLBuilder2 parse(
256 | String xmlString, boolean enableExternalEntities, boolean isNamespaceAware)
257 | {
258 | return XMLBuilder2.parse(
259 | new InputSource(new StringReader(xmlString)),
260 | enableExternalEntities,
261 | isNamespaceAware);
262 | }
263 |
264 | /**
265 | * Construct a builder from an existing XML document file.
266 | * The provided XML document will be parsed and an XMLBuilder2
267 | * object referencing the document's root element will be returned.
268 | *
269 | * @param xmlFile
270 | * an XML document file that will be parsed into a DOM.
271 | * @param enableExternalEntities
272 | * enable external entities; beware of XML External Entity (XXE) injection.
273 | * @param isNamespaceAware
274 | * enable or disable namespace awareness in the underlying
275 | * {@link DocumentBuilderFactory}
276 | * @return
277 | * a builder node that can be used to add more nodes to the XML document.
278 | * @throws XMLBuilderRuntimeException
279 | * to wrap {@link ParserConfigurationException}, {@link SAXException},
280 | * {@link IOException}, {@link FileNotFoundException}
281 | */
282 | public static XMLBuilder2 parse(File xmlFile, boolean enableExternalEntities,
283 | boolean isNamespaceAware)
284 | {
285 | try {
286 | return XMLBuilder2.parse(
287 | new InputSource(new FileReader(xmlFile)),
288 | enableExternalEntities,
289 | isNamespaceAware);
290 | } catch (FileNotFoundException e) {
291 | throw wrapExceptionAsRuntimeException(e);
292 | }
293 | }
294 |
295 | /**
296 | * Construct a builder from an existing XML document. The provided XML
297 | * document will be parsed and an XMLBuilder2 object referencing the
298 | * document's root element will be returned.
299 | *
300 | * @param inputSource
301 | * an XML document input source that will be parsed into a DOM.
302 | * @return
303 | * a builder node that can be used to add more nodes to the XML document.
304 | * @throws XMLBuilderRuntimeException
305 | * to wrap {@link ParserConfigurationException}, {@link SAXException},
306 | * {@link IOException}
307 | */
308 | public static XMLBuilder2 parse(InputSource inputSource)
309 | {
310 | return XMLBuilder2.parse(inputSource, false, true);
311 | }
312 |
313 | /**
314 | * Construct a builder from an existing XML document string.
315 | * The provided XML document will be parsed and an XMLBuilder2
316 | * object referencing the document's root element will be returned.
317 | *
318 | * @param xmlString
319 | * an XML document string that will be parsed into a DOM.
320 | * @return
321 | * a builder node that can be used to add more nodes to the XML document.
322 | */
323 | public static XMLBuilder2 parse(String xmlString)
324 | {
325 | return XMLBuilder2.parse(xmlString, false, true);
326 | }
327 |
328 | /**
329 | * Construct a builder from an existing XML document file.
330 | * The provided XML document will be parsed and an XMLBuilder2
331 | * object referencing the document's root element will be returned.
332 | *
333 | * @param xmlFile
334 | * an XML document file that will be parsed into a DOM.
335 | * @return
336 | * a builder node that can be used to add more nodes to the XML document.
337 | * @throws XMLBuilderRuntimeException
338 | * to wrap {@link ParserConfigurationException}, {@link SAXException},
339 | * {@link IOException}, {@link FileNotFoundException}
340 | */
341 | public static XMLBuilder2 parse(File xmlFile)
342 | {
343 | return XMLBuilder2.parse(xmlFile, false, true);
344 | }
345 |
346 | /**
347 | * @throws XMLBuilderRuntimeException
348 | * to wrap {@link XPathExpressionException}
349 | */
350 | @Override
351 | public XMLBuilder2 stripWhitespaceOnlyTextNodes()
352 | {
353 | try {
354 | super.stripWhitespaceOnlyTextNodesImpl();
355 | return this;
356 | } catch (XPathExpressionException e) {
357 | throw wrapExceptionAsRuntimeException(e);
358 | }
359 | }
360 |
361 | @Override
362 | public XMLBuilder2 importXMLBuilder(BaseXMLBuilder builder) {
363 | super.importXMLBuilderImpl(builder);
364 | return this;
365 | }
366 |
367 | @Override
368 | public XMLBuilder2 root() {
369 | return new XMLBuilder2(getDocument());
370 | }
371 |
372 | /**
373 | * @throws XMLBuilderRuntimeException
374 | * to wrap {@link XPathExpressionException}
375 | */
376 | @Override
377 | public XMLBuilder2 xpathFind(String xpath, NamespaceContext nsContext)
378 | {
379 | try {
380 | Node foundNode = super.xpathFindImpl(xpath, nsContext);
381 | return new XMLBuilder2(foundNode, null);
382 | } catch (XPathExpressionException e) {
383 | throw wrapExceptionAsRuntimeException(e);
384 | }
385 | }
386 |
387 | @Override
388 | public XMLBuilder2 xpathFind(String xpath) {
389 | return xpathFind(xpath, null);
390 | }
391 |
392 | @Override
393 | public XMLBuilder2 element(String name) {
394 | String namespaceURI = super.lookupNamespaceURIImpl(name);
395 | return element(name, namespaceURI);
396 | }
397 |
398 | @Override
399 | public XMLBuilder2 elem(String name) {
400 | return element(name);
401 | }
402 |
403 | @Override
404 | public XMLBuilder2 e(String name) {
405 | return element(name);
406 | }
407 |
408 | @Override
409 | public XMLBuilder2 element(String name, String namespaceURI) {
410 | Element elem = super.elementImpl(name, namespaceURI);
411 | return new XMLBuilder2(elem, this.getElement());
412 | }
413 |
414 | @Override
415 | public XMLBuilder2 elementBefore(String name) {
416 | Element newElement = super.elementBeforeImpl(name);
417 | return new XMLBuilder2(newElement, null);
418 | }
419 |
420 | @Override
421 | public XMLBuilder2 elementBefore(String name, String namespaceURI) {
422 | Element newElement = super.elementBeforeImpl(name, namespaceURI);
423 | return new XMLBuilder2(newElement, null);
424 | }
425 |
426 | @Override
427 | public XMLBuilder2 attribute(String name, String value) {
428 | super.attributeImpl(name, value);
429 | return this;
430 | }
431 |
432 | @Override
433 | public XMLBuilder2 attr(String name, String value) {
434 | return attribute(name, value);
435 | }
436 |
437 | @Override
438 | public XMLBuilder2 a(String name, String value) {
439 | return attribute(name, value);
440 | }
441 |
442 |
443 | @Override
444 | public XMLBuilder2 text(String value, boolean replaceText) {
445 | super.textImpl(value, replaceText);
446 | return this;
447 | }
448 |
449 | @Override
450 | public XMLBuilder2 text(String value) {
451 | return this.text(value, false);
452 | }
453 |
454 | @Override
455 | public XMLBuilder2 t(String value) {
456 | return text(value);
457 | }
458 |
459 | @Override
460 | public XMLBuilder2 cdata(String data) {
461 | super.cdataImpl(data);
462 | return this;
463 | }
464 |
465 | @Override
466 | public XMLBuilder2 data(String data) {
467 | return cdata(data);
468 | }
469 |
470 | @Override
471 | public XMLBuilder2 d(String data) {
472 | return cdata(data);
473 | }
474 |
475 | @Override
476 | public XMLBuilder2 cdata(byte[] data) {
477 | super.cdataImpl(data);
478 | return this;
479 | }
480 |
481 | @Override
482 | public XMLBuilder2 data(byte[] data) {
483 | return cdata(data);
484 | }
485 |
486 | @Override
487 | public XMLBuilder2 d(byte[] data) {
488 | return cdata(data);
489 | }
490 |
491 | @Override
492 | public XMLBuilder2 comment(String comment) {
493 | super.commentImpl(comment);
494 | return this;
495 | }
496 |
497 | @Override
498 | public XMLBuilder2 cmnt(String comment) {
499 | return comment(comment);
500 | }
501 |
502 | @Override
503 | public XMLBuilder2 c(String comment) {
504 | return comment(comment);
505 | }
506 |
507 | @Override
508 | public XMLBuilder2 instruction(String target, String data) {
509 | super.instructionImpl(target, data);
510 | return this;
511 | }
512 |
513 | @Override
514 | public XMLBuilder2 inst(String target, String data) {
515 | return instruction(target, data);
516 | }
517 |
518 | @Override
519 | public XMLBuilder2 i(String target, String data) {
520 | return instruction(target, data);
521 | }
522 |
523 | @Override
524 | public XMLBuilder2 insertInstruction(String target, String data) {
525 | super.insertInstructionImpl(target, data);
526 | return this;
527 | }
528 |
529 | @Override
530 | public XMLBuilder2 reference(String name) {
531 | super.referenceImpl(name);
532 | return this;
533 | }
534 |
535 | @Override
536 | public XMLBuilder2 ref(String name) {
537 | return reference(name);
538 | }
539 |
540 | @Override
541 | public XMLBuilder2 r(String name) {
542 | return reference(name);
543 | }
544 |
545 | @Override
546 | public XMLBuilder2 namespace(String prefix, String namespaceURI) {
547 | super.namespaceImpl(prefix, namespaceURI);
548 | return this;
549 | }
550 |
551 | @Override
552 | public XMLBuilder2 ns(String prefix, String namespaceURI) {
553 | return namespace(prefix, namespaceURI);
554 | }
555 |
556 | @Override
557 | public XMLBuilder2 namespace(String namespaceURI) {
558 | this.namespace(null, namespaceURI);
559 | return this;
560 | }
561 |
562 | @Override
563 | public XMLBuilder2 ns(String namespaceURI) {
564 | return namespace(namespaceURI);
565 | }
566 |
567 | @Override
568 | public XMLBuilder2 up(int steps) {
569 | Node currNode = super.upImpl(steps);
570 | if (currNode instanceof Document) {
571 | return new XMLBuilder2((Document) currNode);
572 | } else {
573 | return new XMLBuilder2(currNode, null);
574 | }
575 | }
576 |
577 | @Override
578 | public XMLBuilder2 up() {
579 | return up(1);
580 | }
581 |
582 | @Override
583 | public XMLBuilder2 document() {
584 | return new XMLBuilder2(getDocument(), null);
585 | }
586 |
587 | /**
588 | * @throws XMLBuilderRuntimeException
589 | * to wrap {@link TransformerException}
590 | *
591 | */
592 | @Override
593 | public String asString() {
594 | try {
595 | return super.asString();
596 | } catch (TransformerException e) {
597 | throw wrapExceptionAsRuntimeException(e);
598 | }
599 | }
600 |
601 | /**
602 | * @throws XMLBuilderRuntimeException
603 | * to wrap {@link TransformerException}
604 | *
605 | */
606 | @Override
607 | public String asString(Properties properties) {
608 | try {
609 | return super.asString(properties);
610 | } catch (TransformerException e) {
611 | throw wrapExceptionAsRuntimeException(e);
612 | }
613 | }
614 |
615 | /**
616 | * @throws XMLBuilderRuntimeException
617 | * to wrap {@link TransformerException}
618 | *
619 | */
620 | @Override
621 | public String elementAsString() {
622 | try {
623 | return super.elementAsString();
624 | } catch (TransformerException e) {
625 | throw wrapExceptionAsRuntimeException(e);
626 | }
627 | }
628 |
629 | /**
630 | * @throws XMLBuilderRuntimeException
631 | * to wrap {@link TransformerException}
632 | *
633 | */
634 | @Override
635 | public String elementAsString(Properties outputProperties) {
636 | try {
637 | return super.elementAsString(outputProperties);
638 | } catch (TransformerException e) {
639 | throw wrapExceptionAsRuntimeException(e);
640 | }
641 | }
642 |
643 | /**
644 | * @throws XMLBuilderRuntimeException
645 | * to wrap {@link TransformerException}
646 | *
647 | */
648 | @Override
649 | public void toWriter(boolean wholeDocument, Writer writer, Properties outputProperties)
650 | {
651 | try {
652 | super.toWriter(wholeDocument, writer, outputProperties);
653 | } catch (TransformerException e) {
654 | throw wrapExceptionAsRuntimeException(e);
655 | }
656 | }
657 |
658 | /**
659 | * @throws XMLBuilderRuntimeException
660 | * to wrap {@link TransformerException}
661 | *
662 | */
663 | @Override
664 | public void toWriter(Writer writer, Properties outputProperties)
665 | {
666 | try {
667 | super.toWriter(writer, outputProperties);
668 | } catch (TransformerException e) {
669 | throw wrapExceptionAsRuntimeException(e);
670 | }
671 | }
672 |
673 | /**
674 | * @throws XMLBuilderRuntimeException
675 | * to wrap {@link XPathExpressionException}
676 | *
677 | */
678 | @Override
679 | public Object xpathQuery(String xpath, QName type, NamespaceContext nsContext)
680 | {
681 | try {
682 | return super.xpathQuery(xpath, type, nsContext);
683 | } catch (XPathExpressionException e) {
684 | throw wrapExceptionAsRuntimeException(e);
685 | }
686 | }
687 |
688 | /**
689 | * @throws XMLBuilderRuntimeException
690 | * to wrap {@link XPathExpressionException}
691 | *
692 | */
693 | @Override
694 | public Object xpathQuery(String xpath, QName type)
695 | {
696 | try {
697 | return super.xpathQuery(xpath, type);
698 | } catch (XPathExpressionException e) {
699 | throw wrapExceptionAsRuntimeException(e);
700 | }
701 | }
702 |
703 | }
704 |
--------------------------------------------------------------------------------
/src/main/java/com/jamesmurty/utils/XMLBuilderRuntimeException.java:
--------------------------------------------------------------------------------
1 | package com.jamesmurty.utils;
2 |
3 | /**
4 | * A runtime exception class used in {@link XMLBuilder2} to wrap any exceptions
5 | * that would otherwise lead to checked exceptions in the interface.
6 | *
7 | * @author jmurty
8 | *
9 | */
10 | public class XMLBuilderRuntimeException extends RuntimeException {
11 |
12 | private static final long serialVersionUID = -635323496745601589L;
13 |
14 | /**
15 | * @param exception
16 | * cause exception to be wrapped
17 | */
18 | public XMLBuilderRuntimeException(Exception exception) {
19 | super(exception);
20 | }
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/src/test/java/com/jamesmurty/utils/BaseXMLBuilderTests.java:
--------------------------------------------------------------------------------
1 | package com.jamesmurty.utils;
2 |
3 | import java.io.File;
4 | import java.io.FileWriter;
5 | import java.io.StringReader;
6 | import java.io.StringWriter;
7 | import java.util.Properties;
8 |
9 | import javax.xml.transform.OutputKeys;
10 | import javax.xml.xpath.XPathConstants;
11 | import javax.xml.xpath.XPathExpressionException;
12 |
13 | import junit.framework.TestCase;
14 | import net.iharder.Base64;
15 |
16 | import org.w3c.dom.Node;
17 | import org.w3c.dom.NodeList;
18 | import org.xml.sax.InputSource;
19 |
20 | public abstract class BaseXMLBuilderTests extends TestCase {
21 |
22 | public static final String EXAMPLE_XML_DOC_START =
23 | "" +
24 | "" +
25 | "http://code.google.com/p/java-xmlbuilder/" +
26 | "" +
27 | "" +
28 | "http://jets3t.s3.amazonaws.com/index.html";
29 |
30 | public static final String EXAMPLE_XML_DOC_END =
31 | "" +
32 | "";
33 |
34 | public static final String EXAMPLE_XML_DOC = EXAMPLE_XML_DOC_START + EXAMPLE_XML_DOC_END;
35 |
36 | protected abstract Class extends BaseXMLBuilder> XMLBuilderToTest() throws Exception;
37 |
38 | protected abstract boolean isRuntimeExceptionsOnly();
39 |
40 | protected BaseXMLBuilder XMLBuilder_create(String name) throws Exception {
41 | return (BaseXMLBuilder) XMLBuilderToTest().getMethod(
42 | "create", String.class).invoke(null, name);
43 | }
44 |
45 | protected BaseXMLBuilder XMLBuilder_create(String name, String nsURI) throws Exception {
46 | return (BaseXMLBuilder) XMLBuilderToTest().getMethod(
47 | "create", String.class, String.class).invoke(null, name, nsURI);
48 | }
49 |
50 | protected BaseXMLBuilder XMLBuilder_parse(InputSource source) throws Exception {
51 | return (BaseXMLBuilder) XMLBuilderToTest().getMethod(
52 | "parse", InputSource.class).invoke(null, source);
53 | }
54 |
55 | protected BaseXMLBuilder XMLBuilder_parse(
56 | String documentString, boolean enableExternalEntities,
57 | boolean isNamespaceAware) throws Exception
58 | {
59 | return (BaseXMLBuilder) XMLBuilderToTest().getMethod(
60 | "parse", String.class, boolean.class, boolean.class).invoke(
61 | null, documentString, enableExternalEntities, isNamespaceAware);
62 | }
63 |
64 | protected BaseXMLBuilder XMLBuilder_parse(String documentString) throws Exception {
65 | return (BaseXMLBuilder) XMLBuilderToTest().getMethod(
66 | "parse", String.class).invoke(null, documentString);
67 | }
68 |
69 | public void testXmlDocumentCreation() throws Exception {
70 | /* Build XML document in-place */
71 | BaseXMLBuilder builder = XMLBuilder_create("Projects")
72 | .e("java-xmlbuilder")
73 | .a("language", "Java")
74 | .a("scm","SVN")
75 | .e("Location")
76 | .a("type", "URL")
77 | .t("http://code.google.com/p/java-xmlbuilder/")
78 | .up()
79 | .up()
80 | .e("JetS3t")
81 | .a("language", "Java")
82 | .a("scm","CVS")
83 | .e("Location")
84 | .a("type", "URL")
85 | .t("http://jets3t.s3.amazonaws.com/index.html");
86 |
87 | /* Set output properties */
88 | Properties outputProperties = new Properties();
89 | // Explicitly identify the output as an XML document
90 | outputProperties.put(javax.xml.transform.OutputKeys.METHOD, "xml");
91 | // Pretty-print the XML output (doesn't work in all cases)
92 | outputProperties.put(javax.xml.transform.OutputKeys.INDENT, "no");
93 | // Omit the XML declaration, which can differ depending on the test's run context.
94 | outputProperties.put(javax.xml.transform.OutputKeys.OMIT_XML_DECLARATION, "yes");
95 |
96 | /* Serialize builder document */
97 | StringWriter writer = new StringWriter();
98 | builder.toWriter(writer, outputProperties);
99 |
100 | assertEquals(EXAMPLE_XML_DOC, writer.toString());
101 |
102 | /* Build XML document in segments*/
103 | BaseXMLBuilder projectsB = XMLBuilder_create("Projects");
104 | projectsB.e("java-xmlbuilder")
105 | .a("language", "Java")
106 | .a("scm","SVN")
107 | .e("Location")
108 | .a("type", "URL")
109 | .t("http://code.google.com/p/java-xmlbuilder/");
110 | BaseXMLBuilder jets3tB = projectsB.e("JetS3t")
111 | .a("language", "Java")
112 | .a("scm","CVS");
113 | jets3tB.e("Location")
114 | .a("type", "URL")
115 | .t("http://jets3t.s3.amazonaws.com/index.html");
116 |
117 | assertEquals(builder.asString(), projectsB.asString());
118 | }
119 |
120 | public void testParseAndXPath() throws Exception {
121 | // Parse an existing XML document
122 | BaseXMLBuilder builder = XMLBuilder_parse(
123 | new InputSource(new StringReader(EXAMPLE_XML_DOC)));
124 | assertEquals("Projects", builder.root().getElement().getNodeName());
125 | assertEquals("Invalid current element", "Projects", builder.getElement().getNodeName());
126 |
127 | // Find the first Location element
128 | builder = builder.xpathFind("//Location");
129 | assertEquals("Location", builder.getElement().getNodeName());
130 | assertEquals("http://code.google.com/p/java-xmlbuilder/",
131 | builder.getElement().getTextContent());
132 |
133 | // Find JetS3t's Location element
134 | builder = builder.xpathFind("//JetS3t/Location");
135 | assertEquals("Location", builder.getElement().getNodeName());
136 | assertEquals("http://jets3t.s3.amazonaws.com/index.html",
137 | builder.getElement().getTextContent());
138 |
139 | // Find the project with the scm attribute 'CVS' (should be JetS3t)
140 | builder = builder.xpathFind("//*[@scm = 'CVS']");
141 | assertEquals("JetS3t", builder.getElement().getNodeName());
142 |
143 | // Try an invalid XPath that does not resolve to an element
144 | try {
145 | builder.xpathFind("//@language");
146 | fail("Non-Element XPath expression should have failed");
147 | } catch (Exception e) {
148 | if (isRuntimeExceptionsOnly()) {
149 | assertEquals(XMLBuilderRuntimeException.class, e.getClass());
150 | e = (Exception) e.getCause();
151 | }
152 | assertEquals(XPathExpressionException.class, e.getClass());
153 | assertTrue(e.getMessage().contains("does not resolve to an Element"));
154 | }
155 |
156 | /* Perform full-strength XPath queries that do not have to
157 | * resolve to an Element, and do not return BaseXMLBuilder instances
158 | */
159 |
160 | // Find the Location value for the JetS3t project
161 | String location = (String) builder.xpathQuery(
162 | "//JetS3t/Location/.", XPathConstants.STRING);
163 | assertEquals("http://jets3t.s3.amazonaws.com/index.html", location);
164 |
165 | // Count the number of projects (count returned as String)
166 | String countAsString = (String) builder.xpathQuery(
167 | "count(/Projects/*)", XPathConstants.STRING);
168 | assertEquals("2", countAsString);
169 |
170 | // Count the number of projects (count returned as "Number" - actually Double)
171 | Number countAsNumber = (Number) builder.xpathQuery(
172 | "count(/Projects/*)", XPathConstants.NUMBER);
173 | assertEquals(2.0, countAsNumber);
174 |
175 | // Find all nodes under Projects
176 | NodeList nodes = (NodeList) builder.xpathQuery(
177 | "/Projects/*", XPathConstants.NODESET);
178 | assertEquals(2, nodes.getLength());
179 | assertEquals("JetS3t", nodes.item(1).getNodeName());
180 |
181 | // Returns null if nothing found when a NODE type is requested...
182 | assertNull(builder.xpathQuery("//WrongName", XPathConstants.NODE));
183 | // ... or an empty String if a STRING type is requested...
184 | assertEquals("", builder.xpathQuery("//WrongName", XPathConstants.STRING));
185 | // ... or NaN if a NUMBER type is requested...
186 | assertEquals(Double.NaN, builder.xpathQuery("//WrongName", XPathConstants.NUMBER));
187 |
188 | /* Add a new XML element at a specific XPath location in an existing document */
189 |
190 | // Use XPath to get a builder at the insert location
191 | BaseXMLBuilder xpathLocB = builder.xpathFind("//JetS3t");
192 | assertEquals("JetS3t", xpathLocB.getElement().getNodeName());
193 |
194 | // Append a new element with the location's builder
195 | BaseXMLBuilder location2B = xpathLocB.elem("Location2").attr("type", "Testing");
196 | assertEquals("Location2", location2B.getElement().getNodeName());
197 | assertEquals("JetS3t", location2B.up().getElement().getNodeName());
198 | assertEquals(xpathLocB.getElement(), location2B.up().getElement());
199 | assertEquals(builder.root(), location2B.root());
200 |
201 | // Sanity-check the entire resultant XML document
202 | Properties outputProperties = new Properties();
203 | outputProperties.put(javax.xml.transform.OutputKeys.OMIT_XML_DECLARATION, "yes");
204 | String xmlAsString = location2B.asString(outputProperties);
205 |
206 | assertFalse(EXAMPLE_XML_DOC.equals(xmlAsString));
207 | assertTrue(xmlAsString.contains(""));
208 | assertEquals(
209 | EXAMPLE_XML_DOC_START + "" + EXAMPLE_XML_DOC_END,
210 | xmlAsString);
211 | }
212 |
213 | public void testParseAndAmendDocWithWhitespaceNodes() throws Exception {
214 | // Parse example XML document and output with indenting, to add whitespace nodes
215 | Properties outputProperties = new Properties();
216 | outputProperties.put(OutputKeys.INDENT, "yes");
217 | outputProperties.put("{http://xml.apache.org/xslt}indent-amount", "2");
218 | String xmlWithWhitespaceNodes =
219 | XMLBuilder_parse(EXAMPLE_XML_DOC).asString(outputProperties);
220 |
221 | // Re-parse document that now has whitespace nodes
222 | BaseXMLBuilder builder = XMLBuilder_parse(xmlWithWhitespaceNodes);
223 |
224 | // Ensure we can add a node to the document (re issue #17)
225 | builder.xpathFind("//JetS3t")
226 | .elem("AnotherLocation").attr("type", "Testing");
227 | String xmlWithAmendments = builder.asString(outputProperties);
228 | assertTrue(xmlWithAmendments.contains(""));
229 | }
230 |
231 | public void testStripWhitespaceNodesFromDocument() throws Exception {
232 | // Parse example XML document and output with indenting, to add whitespace nodes
233 | Properties outputProperties = new Properties();
234 | outputProperties.put(OutputKeys.INDENT, "yes");
235 | outputProperties.put("{http://xml.apache.org/xslt}indent-amount", "2");
236 | String xmlWithWhitespaceNodes =
237 | XMLBuilder_parse(EXAMPLE_XML_DOC).asString(outputProperties);
238 |
239 | // Re-parse document that now has whitespace text nodes
240 | BaseXMLBuilder builder = XMLBuilder_parse(xmlWithWhitespaceNodes);
241 | assertTrue(builder.asString().contains("\n"));
242 | assertTrue(builder.asString().contains(" "));
243 |
244 | // Strip whitespace nodes
245 | builder.stripWhitespaceOnlyTextNodes();
246 | assertFalse(builder.asString().contains("\n"));
247 | assertFalse(builder.asString().contains(" "));
248 | }
249 |
250 | public void testSimpleXpath() throws Exception {
251 | String xmlDoc = "";
252 | BaseXMLBuilder builder = XMLBuilder_parse(xmlDoc);
253 | BaseXMLBuilder builderNode = builder.xpathFind("report_objects");
254 | assertTrue("report_objects".equals(builderNode.getElement().getNodeName()));
255 | assertTrue("".equals(builderNode.elementAsString()));
256 | }
257 |
258 | /**
259 | * Test for issue #11: https://code.google.com/p/java-xmlbuilder/issues/detail?id=11
260 | * @throws Exception
261 | */
262 | public void testAddElementsInLoop() throws Exception {
263 | BaseXMLBuilder builder = XMLBuilder_create("DocRoot");
264 | BaseXMLBuilder parentBuilder = builder.element("Parent");
265 |
266 | // Add set of elements to Parent using a loop...
267 | for (int i = 1; i <= 10; i++) {
268 | parentBuilder.elem("IntegerValue" + i).text("" + i);
269 | }
270 |
271 | // ...and confirm element set is within parent after a call to up()
272 | parentBuilder.up();
273 |
274 | assertEquals("Parent", parentBuilder.getElement().getNodeName());
275 | assertEquals("DocRoot", builder.getElement().getNodeName());
276 | assertEquals(1, builder.getElement().getChildNodes().getLength());
277 | assertEquals("Parent", builder.getElement().getChildNodes().item(0).getNodeName());
278 | assertEquals(10, parentBuilder.getElement().getChildNodes().getLength());
279 | assertEquals("IntegerValue1", parentBuilder.getElement().getChildNodes().item(0).getNodeName());
280 | assertEquals("1", parentBuilder.getElement().getChildNodes().item(0).getTextContent());
281 | }
282 |
283 | public void testTraversalDuringBuild() throws Exception {
284 | BaseXMLBuilder builder = XMLBuilder_create("ElemDepth1")
285 | .e("ElemDepth2")
286 | .e("ElemDepth3")
287 | .e("ElemDepth4");
288 | assertEquals("ElemDepth3", builder.up().getElement().getNodeName());
289 | assertEquals("ElemDepth1", builder.up(3).getElement().getNodeName());
290 | // Traverse too far up the node tree...
291 | assertEquals("ElemDepth1", builder.up(4).getElement().getNodeName());
292 | // Traverse way too far up the node tree...
293 | assertEquals("ElemDepth1", builder.up(100).getElement().getNodeName());
294 | }
295 |
296 | public void testImport() throws Exception {
297 | BaseXMLBuilder importer = XMLBuilder_create("Importer")
298 | .elem("Imported")
299 | .elem("Element")
300 | .elem("Goes").attr("are-we-there-yet", "almost")
301 | .elem("Here");
302 | BaseXMLBuilder importee = XMLBuilder_create("Importee")
303 | .elem("Importee").attr("awating-my", "new-home")
304 | .elem("IsEntireSubtree")
305 | .elem("Included");
306 | importer.importXMLBuilder(importee);
307 |
308 | // Ensure we're at the same point in the XML doc
309 | assertEquals("Here", importer.getElement().getNodeName());
310 |
311 | try {
312 | importer.xpathFind("//Importee");
313 | importer.xpathFind("//IsEntireSubtree");
314 | importer.xpathFind("//IsEntireSubtree");
315 | importer.xpathFind("//Included");
316 | } catch (XPathExpressionException e) {
317 | fail("XMLBuilder import failed: " + e.getMessage());
318 | }
319 |
320 | BaseXMLBuilder invalidImporter = XMLBuilder_create("InvalidImporter")
321 | .text("BadBadBad");
322 | try {
323 | invalidImporter.importXMLBuilder(importee);
324 | fail("Should not be able to import XMLBuilder into "
325 | + "an element containing text nodes");
326 | } catch (IllegalStateException e) {
327 | // Expected
328 | }
329 | }
330 |
331 | public void testCDataNodes() throws Exception {
332 | String text = "Text data -- left as it is";
333 | String textForBytes = "Byte data is automatically base64-encoded";
334 | String textEncoded = Base64.encodeBytes(textForBytes.getBytes("UTF-8"));
335 |
336 | BaseXMLBuilder builder = XMLBuilder_create("TestCDataNodes")
337 | .elem("CDataTextElem")
338 | .cdata(text)
339 | .up()
340 | .elem("CDataBytesElem")
341 | .cdata(textForBytes.getBytes("UTF-8"));
342 |
343 | Node cdataTextNode = builder.xpathFind("//CDataTextElem")
344 | .getElement().getChildNodes().item(0);
345 | assertEquals(Node.CDATA_SECTION_NODE, cdataTextNode.getNodeType());
346 | assertEquals(text, cdataTextNode.getNodeValue());
347 |
348 | Node cdataBytesNode = builder.xpathFind("//CDataBytesElem")
349 | .getElement().getChildNodes().item(0);
350 | assertEquals(Node.CDATA_SECTION_NODE, cdataBytesNode.getNodeType());
351 | assertEquals(textEncoded, cdataBytesNode.getNodeValue());
352 | String base64Decoded = new String(Base64.decode(cdataBytesNode.getNodeValue()));
353 | assertEquals(textForBytes, base64Decoded);
354 | }
355 |
356 | public void testElementAsString() throws Exception {
357 | BaseXMLBuilder builder = XMLBuilder_create("This")
358 | .elem("Is").elem("My").text("Test");
359 | // By default, entire XML document is serialized regardless of starting-point
360 | assertEquals("Test", builder.asString());
361 | assertEquals("Test", builder.xpathFind("//My").asString());
362 | // Serialize a specific Element and its descendants with elementAsString
363 | assertEquals("Test", builder.xpathFind("//My").elementAsString());
364 | }
365 |
366 | public void testNamespaces() throws Exception {
367 | BaseXMLBuilder builder = XMLBuilder_create("NamespaceTest", "urn:default")
368 | .namespace("prefix1", "urn:ns1")
369 |
370 | .element("NSDefaultImplicit").up()
371 | .element("NSDefaultExplicit", "urn:default").up()
372 |
373 | .element("NS1Explicit", "urn:ns1").up()
374 | .element("prefix1:NS1WithPrefixExplicit", "urn:ns1").up()
375 | .element("prefix1:NS1WithPrefixImplicit").up();
376 |
377 | // Build a namespace context from the builder's document
378 | NamespaceContextImpl context = builder.buildDocumentNamespaceContext();
379 |
380 | // All elements in a namespaced document inherit a namespace URI,
381 | // for namespaced document any non-namespaced XPath query will fail.
382 | try {
383 | builder.xpathFind("//:NSDefaultImplicit");
384 | fail("Namespaced xpath query without context is invalid");
385 | } catch (Exception e) {
386 | if (isRuntimeExceptionsOnly()) {
387 | assertEquals(XMLBuilderRuntimeException.class, e.getClass());
388 | e = (Exception) e.getCause();
389 | }
390 | assertEquals(XPathExpressionException.class, e.getClass());
391 | }
392 | try {
393 | builder.xpathFind("//NSDefaultImplicit", context);
394 | fail("XPath query without prefixes on namespaced docs is invalid");
395 | } catch (Exception e) {
396 | if (isRuntimeExceptionsOnly()) {
397 | assertEquals(XMLBuilderRuntimeException.class, e.getClass());
398 | e = (Exception) e.getCause();
399 | }
400 | assertEquals(XPathExpressionException.class, e.getClass());
401 | }
402 |
403 | // Find nodes with default namespace
404 | builder.xpathFind("/:NamespaceTest", context);
405 | builder.xpathFind("//:NSDefaultImplicit", context);
406 | builder.xpathFind("//:NSDefaultExplicit", context);
407 |
408 | // Must use namespace-aware xpath to find namespaced nodes
409 | try {
410 | builder.xpathFind("//NSDefaultExplicit");
411 | fail();
412 | } catch (Exception e) {
413 | if (isRuntimeExceptionsOnly()) {
414 | assertEquals(XMLBuilderRuntimeException.class, e.getClass());
415 | e = (Exception) e.getCause();
416 | }
417 | assertEquals(XPathExpressionException.class, e.getClass());
418 | }
419 | try {
420 | builder.xpathFind("//:NSDefaultExplicit");
421 | fail();
422 | } catch (Exception e) {
423 | if (isRuntimeExceptionsOnly()) {
424 | assertEquals(XMLBuilderRuntimeException.class, e.getClass());
425 | e = (Exception) e.getCause();
426 | }
427 | assertEquals(XPathExpressionException.class, e.getClass());
428 | }
429 | try {
430 | builder.xpathFind("//NSDefaultExplicit", context);
431 | fail();
432 | } catch (Exception e) {
433 | if (isRuntimeExceptionsOnly()) {
434 | assertEquals(XMLBuilderRuntimeException.class, e.getClass());
435 | e = (Exception) e.getCause();
436 | }
437 | assertEquals(XPathExpressionException.class, e.getClass());
438 | }
439 |
440 | // Find node with namespace prefix
441 | builder.xpathFind("//prefix1:NS1Explicit", context);
442 | builder.xpathFind("//prefix1:NS1WithPrefixExplicit", context);
443 | builder.xpathFind("//prefix1:NS1WithPrefixImplicit", context);
444 |
445 | // Find nodes with user-defined prefix "aliases"
446 | context.addNamespace("default-alias", "urn:default");
447 | context.addNamespace("prefix1-alias", "urn:ns1");
448 | builder.xpathFind("//default-alias:NSDefaultExplicit", context);
449 | builder.xpathFind("//prefix1-alias:NS1Explicit", context);
450 |
451 | // User can override context mappings, for better or worse
452 | context.addNamespace("", "urn:default");
453 | builder.xpathFind("//:NSDefaultExplicit", context);
454 |
455 | context.addNamespace("", "urn:wrong");
456 | try {
457 | builder.xpathFind("//:NSDefaultExplicit", context);
458 | fail();
459 | } catch (Exception e) {
460 | if (isRuntimeExceptionsOnly()) {
461 | assertEquals(XMLBuilderRuntimeException.class, e.getClass());
462 | e = (Exception) e.getCause();
463 | }
464 | assertEquals(XPathExpressionException.class, e.getClass());
465 | }
466 |
467 | // Users are not prevented from creating elements that reference
468 | // an undefined namespace prefix -- user beware
469 | builder.element("undefined-prefix:ElementName");
470 | }
471 |
472 | public void testNamespaceUnawareBuilder() throws Exception {
473 | String XML_WITH_NAMESPACES =
474 | "" +
475 | "Found me" +
476 | "";
477 |
478 | // Builder set to be unaware of namespaces can traverse DOM with
479 | // namespaces without using namespace prefixes
480 | BaseXMLBuilder result = XMLBuilder_parse(
481 | XML_WITH_NAMESPACES,
482 | false, // enableExternalEntities
483 | false // isNamespaceAware
484 | ).xpathFind("/NamespaceUnwareTest/NestedElement");
485 | assertEquals("Found me", result.getElement().getTextContent());
486 |
487 | // Builder set to be aware of namespaces (per default) cannot traverse
488 | // DOM with namespaces without using namespace prefixes
489 | try {
490 | result = XMLBuilder_parse(XML_WITH_NAMESPACES)
491 | .xpathFind("/NamespaceUnwareTest/NestedElement");
492 | } catch (Exception ex) {
493 | Throwable cause = null;
494 | if (this instanceof TestXMLBuilder2) {
495 | cause = ex.getCause(); // Exception wrapped in runtime ex
496 | } else {
497 | cause = ex;
498 | }
499 | assertEquals(
500 | cause.getClass(), XPathExpressionException.class);
501 | assertTrue(
502 | cause.getMessage().contains(
503 | "XPath expression \"/NamespaceUnwareTest/NestedElement\""
504 | + " does not resolve to an Element in context"
505 | ));
506 | }
507 | assertEquals("Found me", result.getElement().getTextContent());
508 | }
509 |
510 | public void testElementBefore() throws Exception {
511 | BaseXMLBuilder builder = XMLBuilder_create("TestDocument", "urn:default")
512 | .namespace("custom", "urn:custom")
513 | .elem("Before").up()
514 | .elem("After");
515 | NamespaceContextImpl context = builder.buildDocumentNamespaceContext();
516 |
517 | // Ensure XML structure is correct before insert
518 | assertEquals(""
519 | + "", builder.asString());
520 |
521 | // Insert an element before the "After" element, no explicit namespace (will use default)
522 | BaseXMLBuilder testDoc = XMLBuilder_parse(builder.asString())
523 | .xpathFind("/:TestDocument/:After", context);
524 | BaseXMLBuilder insertedBuilder = testDoc.elementBefore("Inserted");
525 | assertEquals("Inserted", insertedBuilder.getElement().getNodeName());
526 | assertEquals(""
527 | + "", testDoc.asString());
528 |
529 | // Insert another element, this time with a custom namespace prefix
530 | insertedBuilder = insertedBuilder.elementBefore("custom:InsertedAgain");
531 | assertEquals("custom:InsertedAgain", insertedBuilder.getElement().getNodeName());
532 | assertEquals(""
533 | + "",
534 | testDoc.asString());
535 |
536 | // Insert another element, this time with a custom namespace ref
537 | insertedBuilder = insertedBuilder.elementBefore("InsertedYetAgain", "urn:custom2");
538 | assertEquals("InsertedYetAgain", insertedBuilder.getElement().getNodeName());
539 | assertEquals(""
540 | + ""
541 | + "",
542 | testDoc.asString());
543 | }
544 |
545 | public void testTextNodes() throws Exception {
546 | BaseXMLBuilder builder = XMLBuilder_create("TestDocument")
547 | .elem("TextElement")
548 | .text("Initial");
549 |
550 | BaseXMLBuilder textElementBuilder = builder.xpathFind("//TextElement");
551 | assertEquals("Initial", textElementBuilder.getElement().getTextContent());
552 |
553 | // By default, text methods append value to existing text
554 | textElementBuilder.text("Appended");
555 | assertEquals("InitialAppended", textElementBuilder.getElement().getTextContent());
556 |
557 | // Use boolean flag to replace text nodes with a new value
558 | textElementBuilder.text("Replacement", true);
559 | assertEquals("Replacement", textElementBuilder.getElement().getTextContent());
560 |
561 | // Fail-fast if a null text value is provided.
562 | try {
563 | textElementBuilder.text(null);
564 | fail("null text value should cause IllegalArgumentException");
565 | } catch (IllegalArgumentException ex) {
566 | assertEquals("Illegal null text value", ex.getMessage());
567 | }
568 |
569 | try {
570 | textElementBuilder.text(null, true);
571 | fail("null text value should cause IllegalArgumentException");
572 | } catch (IllegalArgumentException ex) {
573 | assertEquals("Illegal null text value", ex.getMessage());
574 | }
575 |
576 | }
577 |
578 | public void testProcessingInstructionNodes() throws Exception {
579 | // Add instruction to root document element node (usual append-in-node behaviour)
580 | BaseXMLBuilder builder = XMLBuilder_create("TestDocument").instruction("test", "data");
581 | assertEquals("", builder.asString());
582 |
583 | // Add instruction after the root document element (not within it)
584 | builder = XMLBuilder_create("TestDocument3").document().instruction("test", "data");
585 | assertEquals("", builder.asString().trim());
586 |
587 | // Insert instruction as first node of the root document
588 | builder = XMLBuilder_create("TestDocument3").insertInstruction("test", "data");
589 | assertEquals(
590 | "",
591 | // Remove newlines from XML as this differs across platforms
592 | builder.asString().replace("\n", ""));
593 |
594 | // Insert instruction as first node of the root document, second example
595 | builder = XMLBuilder_create("TestDocument4").elem("ChildElem")
596 | .root().insertInstruction("test", "data");
597 | assertEquals(
598 | "",
599 | // Remove newlines from XML as this differs across platforms
600 | builder.asString().replace("\n", ""));
601 | }
602 |
603 | /**
604 | * Test for strange issue raised by user on comments form where OutputKeys.STANDALONE setting
605 | * in transformer is ignored.
606 | *
607 | * @throws Exception
608 | */
609 | public void testSetStandaloneToYes() throws Exception {
610 | String xmlDoc = "";
611 | BaseXMLBuilder builder = XMLBuilder_parse(
612 | new InputSource(new StringReader(xmlDoc)));
613 |
614 | // Basic output settings
615 | Properties outputProperties = new Properties();
616 | outputProperties.put(javax.xml.transform.OutputKeys.VERSION, "1.0");
617 | outputProperties.put(javax.xml.transform.OutputKeys.METHOD, "xml");
618 | outputProperties.put(javax.xml.transform.OutputKeys.ENCODING, "UTF-8");
619 |
620 | // Use Document@setXmlStandalone(true) to ensure OutputKeys.STANDALONE is respected.
621 | builder.getDocument().setXmlStandalone(true);
622 | outputProperties.put(javax.xml.transform.OutputKeys.STANDALONE, "yes");
623 |
624 | /* Serialize builder document */
625 | StringWriter writer = new StringWriter();
626 | builder.toWriter(writer, outputProperties);
627 |
628 | assertEquals(
629 | "" + xmlDoc,
630 | writer.toString());
631 | }
632 |
633 | /**
634 | * Test the {@link BaseXMLBuilder#asString(Properties)} method output a document
635 | * of the correct size when the document is moderately large, re issue
636 | * #1 (on GitHub).
637 | *
638 | * @throws Exception
639 | */
640 | public void testModerateDocumentSizeAsString() throws Exception {
641 | // Create a moderate document around 0.5 MB
642 | long expectedByteSize = 505021;
643 | BaseXMLBuilder builder = XMLBuilder_create("RootNode");
644 | for (int i = 0; i < 5000; i++) {
645 | builder
646 | .e("TreeRoot")
647 | .e("TreeTrunk")
648 | .e("TreeBranch")
649 | .e("TreeLeaf")
650 | .t("Some Aphids");
651 |
652 | }
653 | // Omit XML declaration, which will otherwise be included in file
654 | // via #toWriter but not in string via #asString
655 | Properties outputProperties = new Properties();
656 | outputProperties.put(
657 | javax.xml.transform.OutputKeys.OMIT_XML_DECLARATION, "yes");
658 | // Ensure XML as string has expected length...
659 | String xmlString = builder.asString(outputProperties);
660 | assertEquals(expectedByteSize, xmlString.length());
661 | // ...and matches size of XML written to file
662 | File f = File.createTempFile(
663 | "java-xmlbuilder-testmoderatedocumentsizeasstring", ".xml");
664 | builder.toWriter(new FileWriter(f), outputProperties);
665 | assertEquals(expectedByteSize, f.length());
666 | f.delete();
667 | }
668 |
669 | /**
670 | * Ensure XML Builder parse methods use a default configuration that
671 | * prevents XML External Entity (XXE) injection attacks.
672 | *
673 | * @throws Exception
674 | */
675 | public void testXMLBuilderParserImmuneToXXEAttackByDefault() throws Exception {
676 | String externalFilePath = "src/test/java/com/jamesmurty/utils/external.txt";
677 | File externalFile = new File(externalFilePath);
678 | String XML_DOC_WITH_XXE =
679 | "" +
680 | "" +
682 | " ]>" +
683 | EXAMPLE_XML_DOC_START + "&xx1;" + EXAMPLE_XML_DOC_END;
684 | // By default, builder is immune from XXE injection
685 | BaseXMLBuilder builder = XMLBuilder_parse(XML_DOC_WITH_XXE);
686 | String parsedXml = builder.asString();
687 | assertFalse(parsedXml.indexOf("Injected XXE Data") >= 0);
688 | // If you enable external entity processing, builder becomes subject to XXE injection
689 | builder = XMLBuilder_parse(XML_DOC_WITH_XXE, true, true);
690 | parsedXml = builder.asString();
691 | assertTrue(parsedXml.indexOf("Injected XXE Data") >= 0);
692 | }
693 |
694 | }
695 |
--------------------------------------------------------------------------------
/src/test/java/com/jamesmurty/utils/TestXMLBuilder.java:
--------------------------------------------------------------------------------
1 | package com.jamesmurty.utils;
2 |
3 | public class TestXMLBuilder extends BaseXMLBuilderTests {
4 |
5 | @Override
6 | public Class extends BaseXMLBuilder> XMLBuilderToTest() throws Exception {
7 | return XMLBuilder.class;
8 | }
9 |
10 | @Override
11 | protected boolean isRuntimeExceptionsOnly() {
12 | return false;
13 | }
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/src/test/java/com/jamesmurty/utils/TestXMLBuilder2.java:
--------------------------------------------------------------------------------
1 | package com.jamesmurty.utils;
2 |
3 | import javax.xml.xpath.XPathConstants;
4 | import javax.xml.xpath.XPathExpressionException;
5 |
6 | public class TestXMLBuilder2 extends BaseXMLBuilderTests {
7 |
8 | @Override
9 | public Class extends BaseXMLBuilder> XMLBuilderToTest() throws Exception {
10 | return XMLBuilder2.class;
11 | }
12 |
13 | @Override
14 | protected boolean isRuntimeExceptionsOnly() {
15 | return true;
16 | }
17 |
18 | // NOTE: No checked exceptions for API calls made in this test method
19 | public void testNoCheckedExceptions() {
20 | XMLBuilder2 builder = XMLBuilder2.create("Blah");
21 | builder = XMLBuilder2.parse(EXAMPLE_XML_DOC);
22 | builder.stripWhitespaceOnlyTextNodes();
23 | builder.asString();
24 | builder.elementAsString();
25 | builder.xpathQuery("/*", XPathConstants.NODESET);
26 | builder.xpathFind("/Projects");
27 | }
28 |
29 | public void testExceptionWrappedInXMLBuilderRuntimeException() {
30 | XMLBuilder2 builder = XMLBuilder2.parse(EXAMPLE_XML_DOC);
31 | try {
32 | builder.xpathFind("/BadPath");
33 | fail("Expected XMLBuilderRuntimeException");
34 | } catch (XMLBuilderRuntimeException e) {
35 | assertEquals(XMLBuilderRuntimeException.class, e.getClass());
36 | Throwable cause = e.getCause();
37 | assertEquals(XPathExpressionException.class, cause.getClass());
38 | assertTrue(cause.getMessage().contains("does not resolve to an Element"));
39 | }
40 | }
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/src/test/java/com/jamesmurty/utils/external.txt:
--------------------------------------------------------------------------------
1 | Injected XXE Data
--------------------------------------------------------------------------------