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    *     http://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      /** Constant for the name of an element. */
61      private static final String ELEMENT = "testElementName";
62  
63      /** Constant for the name of the test XML file. */
64      private static final String TEST_FILE = "testcombine1.xml";
65  
66      /**
67       * Serializes the specified document to a string.
68       *
69       * @param document the document
70       * @return the document serialized to a string
71       * @throws ConfigurationException if an error occurs
72       */
73      private static String documentToString(final Document document) throws ConfigurationException {
74          final Transformer transformer = XMLDocumentHelper.createTransformer();
75          final StringWriter writer = new StringWriter();
76          final Result result = new StreamResult(writer);
77          XMLDocumentHelper.transform(transformer, new DOMSource(document.getDocumentElement()), result);
78          return writer.toString();
79      }
80  
81      /**
82       * Serializes the document wrapped by the given helper to a string.
83       *
84       * @param helper the document helper
85       * @return the document serialized to a string
86       * @throws ConfigurationException if an error occurs
87       */
88      private static String documentToString(final XMLDocumentHelper helper) throws ConfigurationException {
89          return documentToString(helper.getDocument());
90      }
91  
92      /**
93       * Obtains all text elements contained in the given document.
94       *
95       * @param document the document
96       * @return a collection with all text elements
97       */
98      private static Collection<Node> findTextElements(final Document document) {
99          final Collection<Node> texts = new HashSet<>();
100         findTextElementsForNode(document.getDocumentElement(), texts);
101         return texts;
102     }
103 
104     /**
105      * Recursively obtains all text elements for the given node.
106      *
107      * @param node the node
108      * @param texts the collection with text elements
109      */
110     private static void findTextElementsForNode(final Node node, final Collection<Node> texts) {
111         if (node instanceof Text) {
112             texts.add(node);
113         }
114         final NodeList childNodes = node.getChildNodes();
115         for (int i = 0; i < childNodes.getLength(); i++) {
116             findTextElementsForNode(childNodes.item(i), texts);
117         }
118     }
119 
120     /**
121      * Loads a test XML document.
122      *
123      * @return the test document
124      */
125     private static Document loadDocument() throws ParserConfigurationException, IOException, SAXException {
126         return loadDocument(TEST_FILE);
127     }
128 
129     /**
130      * Loads the test document with the given name.
131      *
132      * @param name the name of the test document
133      * @return the parsed document
134      */
135     private static Document loadDocument(final String name) throws IOException, SAXException, ParserConfigurationException {
136         final DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
137         return builder.parse(ConfigurationAssert.getTestFile(name));
138     }
139 
140     /**
141      * Helper method for testing the element mapping of a copied document.
142      *
143      * @param file the name of the test file
144      */
145     private void checkCopyElementMapping(final String file) throws Exception {
146         final XMLDocumentHelper helper = XMLDocumentHelper.forSourceDocument(loadDocument(file));
147         final XMLDocumentHelper copy = helper.createCopy();
148         final Collection<Node> texts = findTextElements(helper.getDocument());
149         assertFalse(texts.isEmpty());
150         for (final Node n : texts) {
151             final Text txtSrc = (Text) n;
152             final Text txtCopy = (Text) copy.getElementMapping().get(n);
153             assertNotNull(txtCopy, "No matching element for " + n);
154             assertEquals(txtSrc.getData(), txtCopy.getData());
155         }
156     }
157 
158     /**
159      * Tests whether a document can be copied.
160      */
161     @Test
162     public void testCopyDocument() throws Exception {
163         final XMLDocumentHelper helper = XMLDocumentHelper.forSourceDocument(loadDocument());
164         final XMLDocumentHelper copy = helper.createCopy();
165         assertNotSame(helper.getDocument(), copy.getDocument());
166         final String doc1 = documentToString(helper);
167         final String doc2 = documentToString(copy);
168         assertEquals(doc1, doc2);
169     }
170 
171     /**
172      * Tests the element mapping of a copied document.
173      */
174     @Test
175     public void testCopyElementMapping() throws Exception {
176         checkCopyElementMapping(TEST_FILE);
177     }
178 
179     /**
180      * Tests whether the element is correctly constructed for a more complex document.
181      */
182     @Test
183     public void testCopyElementMappingForComplexDocument() throws Exception {
184         checkCopyElementMapping("test.xml");
185     }
186 
187     /**
188      * Tests whether an exception thrown by a document builder factory is handled correctly.
189      */
190     @Test
191     public void testCreateDocumentBuilderFromFactoryException() throws ParserConfigurationException {
192         final DocumentBuilderFactory factory = mock(DocumentBuilderFactory.class);
193         final ParserConfigurationException pcex = new ParserConfigurationException();
194 
195         when(factory.newDocumentBuilder()).thenThrow(pcex);
196 
197         final ConfigurationException cex = assertThrows(ConfigurationException.class, () -> XMLDocumentHelper.createDocumentBuilder(factory));
198         assertEquals(pcex, cex.getCause());
199     }
200 
201     /**
202      * Tests whether a correct transformer factory can be created.
203      */
204     @Test
205     public void testCreateTransformerFactory() {
206         assertNotNull(XMLDocumentHelper.createTransformerFactory());
207     }
208 
209     /**
210      * Tests whether exceptions while creating transformers are correctly handled.
211      */
212     @Test
213     public void testCreateTransformerFactoryException() throws TransformerConfigurationException {
214         final TransformerFactory factory = mock(TransformerFactory.class);
215         final TransformerConfigurationException cause = new TransformerConfigurationException();
216 
217         when(factory.newTransformer()).thenThrow(cause);
218 
219         final ConfigurationException cex = assertThrows(ConfigurationException.class, () -> XMLDocumentHelper.createTransformer(factory));
220         assertEquals(cause, cex.getCause());
221     }
222 
223     /**
224      * Tests the content of the element mapping for a newly created document.
225      */
226     @Test
227     public void testElementMappingForNewDocument() throws ConfigurationException {
228         final XMLDocumentHelper helper = XMLDocumentHelper.forNewDocument(ELEMENT);
229         assertTrue(helper.getElementMapping().isEmpty());
230     }
231 
232     /**
233      * Tests the content of the element mapping for a source document.
234      */
235     @Test
236     public void testElementMappingForSourceDocument() throws Exception {
237         final Document doc = loadDocument();
238         final XMLDocumentHelper helper = XMLDocumentHelper.forSourceDocument(doc);
239         assertTrue(helper.getElementMapping().isEmpty());
240     }
241 
242     /**
243      * Tests whether an instance can be created wrapping a new document.
244      */
245     @Test
246     public void testInitForNewDocument() throws ConfigurationException {
247         final XMLDocumentHelper helper = XMLDocumentHelper.forNewDocument(ELEMENT);
248         final Document doc = helper.getDocument();
249         final Element rootElement = doc.getDocumentElement();
250         assertEquals(ELEMENT, rootElement.getNodeName());
251         final NodeList childNodes = rootElement.getChildNodes();
252         assertEquals(0, childNodes.getLength());
253         assertNull(helper.getSourcePublicID());
254         assertNull(helper.getSourceSystemID());
255     }
256 
257     /**
258      * Tests whether an instance can be created based on a source document.
259      */
260     @Test
261     public void testInitForSourceDocument() throws Exception {
262         final Document doc = loadDocument();
263         final XMLDocumentHelper helper = XMLDocumentHelper.forSourceDocument(doc);
264         assertNotSame(doc, helper.getDocument());
265         assertEquals(documentToString(doc), documentToString(helper));
266     }
267 
268     /**
269      * Tests whether transform() handles a TransformerException.
270      */
271     @Test
272     public void testTransformException() throws TransformerException {
273         final Transformer transformer = mock(Transformer.class);
274         final Source src = mock(Source.class);
275         final Result res = mock(Result.class);
276         final TransformerException tex = new TransformerException("Test Exception");
277 
278         doThrow(tex).when(transformer).transform(src, res);
279 
280         final ConfigurationException cex = assertThrows(ConfigurationException.class, () -> XMLDocumentHelper.transform(transformer, src, res));
281         assertEquals(tex, cex.getCause());
282     }
283 }