View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *     https://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.configuration2;
18  
19  import static org.junit.jupiter.api.Assertions.assertEquals;
20  import static org.junit.jupiter.api.Assertions.assertFalse;
21  import static org.junit.jupiter.api.Assertions.assertNotNull;
22  import static org.junit.jupiter.api.Assertions.assertNotSame;
23  import static org.junit.jupiter.api.Assertions.assertNull;
24  import static org.junit.jupiter.api.Assertions.assertThrows;
25  import static org.junit.jupiter.api.Assertions.assertTrue;
26  import static org.mockito.Mockito.doThrow;
27  import static org.mockito.Mockito.mock;
28  import static org.mockito.Mockito.when;
29  
30  import java.io.IOException;
31  import java.io.StringWriter;
32  import java.util.Collection;
33  import java.util.HashSet;
34  
35  import javax.xml.parsers.DocumentBuilder;
36  import javax.xml.parsers.DocumentBuilderFactory;
37  import javax.xml.parsers.ParserConfigurationException;
38  import javax.xml.transform.Result;
39  import javax.xml.transform.Source;
40  import javax.xml.transform.Transformer;
41  import javax.xml.transform.TransformerConfigurationException;
42  import javax.xml.transform.TransformerException;
43  import javax.xml.transform.TransformerFactory;
44  import javax.xml.transform.dom.DOMSource;
45  import javax.xml.transform.stream.StreamResult;
46  
47  import org.apache.commons.configuration2.ex.ConfigurationException;
48  import org.junit.jupiter.api.Test;
49  import org.w3c.dom.Document;
50  import org.w3c.dom.Element;
51  import org.w3c.dom.Node;
52  import org.w3c.dom.NodeList;
53  import org.w3c.dom.Text;
54  import org.xml.sax.SAXException;
55  
56  /**
57   * Test class for {@code XMLDocumentHelper}.
58   */
59  public class TestXMLDocumentHelper {
60  
61      /** Constant for the name of an element. */
62      private static final String ELEMENT = "testElementName";
63  
64      /** Constant for the name of the test XML file. */
65      private static final String TEST_FILE = "testcombine1.xml";
66  
67      /**
68       * Serializes the specified document to a string.
69       *
70       * @param document the document
71       * @return the document serialized to a string
72       * @throws ConfigurationException if an error occurs
73       */
74      private static String documentToString(final Document document) throws ConfigurationException {
75          final Transformer transformer = XMLDocumentHelper.createTransformer();
76          final StringWriter writer = new StringWriter();
77          final Result result = new StreamResult(writer);
78          XMLDocumentHelper.transform(transformer, new DOMSource(document.getDocumentElement()), result);
79          return writer.toString();
80      }
81  
82      /**
83       * Serializes the document wrapped by the given helper to a string.
84       *
85       * @param helper the document helper
86       * @return the document serialized to a string
87       * @throws ConfigurationException if an error occurs
88       */
89      private static String documentToString(final XMLDocumentHelper helper) throws ConfigurationException {
90          return documentToString(helper.getDocument());
91      }
92  
93      /**
94       * Obtains all text elements contained in the given document.
95       *
96       * @param document the document
97       * @return a collection with all text elements
98       */
99      private static Collection<Node> findTextElements(final Document document) {
100         final Collection<Node> texts = new HashSet<>();
101         findTextElementsForNode(document.getDocumentElement(), texts);
102         return texts;
103     }
104 
105     /**
106      * Recursively obtains all text elements for the given node.
107      *
108      * @param node the node
109      * @param texts the collection with text elements
110      */
111     private static void findTextElementsForNode(final Node node, final Collection<Node> texts) {
112         if (node instanceof Text) {
113             texts.add(node);
114         }
115         final NodeList childNodes = node.getChildNodes();
116         for (int i = 0; i < childNodes.getLength(); i++) {
117             findTextElementsForNode(childNodes.item(i), texts);
118         }
119     }
120 
121     /**
122      * Loads a test XML document.
123      *
124      * @return the test document
125      */
126     private static Document loadDocument() throws ParserConfigurationException, IOException, SAXException {
127         return loadDocument(TEST_FILE);
128     }
129 
130     /**
131      * Loads the test document with the given name.
132      *
133      * @param name the name of the test document
134      * @return the parsed document
135      */
136     private static Document loadDocument(final String name) throws IOException, SAXException, ParserConfigurationException {
137         final DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
138         return builder.parse(ConfigurationAssert.getTestFile(name));
139     }
140 
141     /**
142      * Helper method for testing the element mapping of a copied document.
143      *
144      * @param file the name of the test file
145      */
146     private void checkCopyElementMapping(final String file) throws Exception {
147         final XMLDocumentHelper helper = XMLDocumentHelper.forSourceDocument(loadDocument(file));
148         final XMLDocumentHelper copy = helper.createCopy();
149         final Collection<Node> texts = findTextElements(helper.getDocument());
150         assertFalse(texts.isEmpty());
151         for (final Node n : texts) {
152             final Text txtSrc = (Text) n;
153             final Text txtCopy = (Text) copy.getElementMapping().get(n);
154             assertNotNull(txtCopy, "No matching element for " + n);
155             assertEquals(txtSrc.getData(), txtCopy.getData());
156         }
157     }
158 
159     /**
160      * Tests whether a document can be copied.
161      */
162     @Test
163     void testCopyDocument() throws Exception {
164         final XMLDocumentHelper helper = XMLDocumentHelper.forSourceDocument(loadDocument());
165         final XMLDocumentHelper copy = helper.createCopy();
166         assertNotSame(helper.getDocument(), copy.getDocument());
167         final String doc1 = documentToString(helper);
168         final String doc2 = documentToString(copy);
169         assertEquals(doc1, doc2);
170     }
171 
172     /**
173      * Tests the element mapping of a copied document.
174      */
175     @Test
176     void testCopyElementMapping() throws Exception {
177         checkCopyElementMapping(TEST_FILE);
178     }
179 
180     /**
181      * Tests whether the element is correctly constructed for a more complex document.
182      */
183     @Test
184     void testCopyElementMappingForComplexDocument() throws Exception {
185         checkCopyElementMapping("test.xml");
186     }
187 
188     /**
189      * Tests whether an exception thrown by a document builder factory is handled correctly.
190      */
191     @Test
192     void testCreateDocumentBuilderFromFactoryException() throws ParserConfigurationException {
193         final DocumentBuilderFactory factory = mock(DocumentBuilderFactory.class);
194         final ParserConfigurationException pcex = new ParserConfigurationException();
195 
196         when(factory.newDocumentBuilder()).thenThrow(pcex);
197 
198         final ConfigurationException cex = assertThrows(ConfigurationException.class, () -> XMLDocumentHelper.createDocumentBuilder(factory));
199         assertEquals(pcex, cex.getCause());
200     }
201 
202     /**
203      * Tests whether a correct transformer factory can be created.
204      */
205     @Test
206     void testCreateTransformerFactory() {
207         assertNotNull(XMLDocumentHelper.createTransformerFactory());
208     }
209 
210     /**
211      * Tests whether exceptions while creating transformers are correctly handled.
212      */
213     @Test
214     void testCreateTransformerFactoryException() throws TransformerConfigurationException {
215         final TransformerFactory factory = mock(TransformerFactory.class);
216         final TransformerConfigurationException cause = new TransformerConfigurationException();
217 
218         when(factory.newTransformer()).thenThrow(cause);
219 
220         final ConfigurationException cex = assertThrows(ConfigurationException.class, () -> XMLDocumentHelper.createTransformer(factory));
221         assertEquals(cause, cex.getCause());
222     }
223 
224     /**
225      * Tests the content of the element mapping for a newly created document.
226      */
227     @Test
228     void testElementMappingForNewDocument() throws ConfigurationException {
229         final XMLDocumentHelper helper = XMLDocumentHelper.forNewDocument(ELEMENT);
230         assertTrue(helper.getElementMapping().isEmpty());
231     }
232 
233     /**
234      * Tests the content of the element mapping for a source document.
235      */
236     @Test
237     void testElementMappingForSourceDocument() throws Exception {
238         final Document doc = loadDocument();
239         final XMLDocumentHelper helper = XMLDocumentHelper.forSourceDocument(doc);
240         assertTrue(helper.getElementMapping().isEmpty());
241     }
242 
243     /**
244      * Tests whether an instance can be created wrapping a new document.
245      */
246     @Test
247     void testInitForNewDocument() throws ConfigurationException {
248         final XMLDocumentHelper helper = XMLDocumentHelper.forNewDocument(ELEMENT);
249         final Document doc = helper.getDocument();
250         final Element rootElement = doc.getDocumentElement();
251         assertEquals(ELEMENT, rootElement.getNodeName());
252         final NodeList childNodes = rootElement.getChildNodes();
253         assertEquals(0, childNodes.getLength());
254         assertNull(helper.getSourcePublicID());
255         assertNull(helper.getSourceSystemID());
256     }
257 
258     /**
259      * Tests whether an instance can be created based on a source document.
260      */
261     @Test
262     void testInitForSourceDocument() throws Exception {
263         final Document doc = loadDocument();
264         final XMLDocumentHelper helper = XMLDocumentHelper.forSourceDocument(doc);
265         assertNotSame(doc, helper.getDocument());
266         assertEquals(documentToString(doc), documentToString(helper));
267     }
268 
269     /**
270      * Tests whether transform() handles a TransformerException.
271      */
272     @Test
273     void testTransformException() throws TransformerException {
274         final Transformer transformer = mock(Transformer.class);
275         final Source src = mock(Source.class);
276         final Result res = mock(Result.class);
277         final TransformerException tex = new TransformerException("Test Exception");
278 
279         doThrow(tex).when(transformer).transform(src, res);
280 
281         final ConfigurationException cex = assertThrows(ConfigurationException.class, () -> XMLDocumentHelper.transform(transformer, src, res));
282         assertEquals(tex, cex.getCause());
283     }
284 }