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  
18  package org.apache.commons.betwixt.recursion;
19  
20  import java.io.StringReader;
21  import java.io.StringWriter;
22  import java.io.Writer;
23  import java.util.List;
24  
25  import junit.framework.Test;
26  import junit.framework.TestSuite;
27  
28  import org.apache.commons.betwixt.AbstractTestCase;
29  import org.apache.commons.betwixt.XMLIntrospector;
30  import org.apache.commons.betwixt.io.BeanReader;
31  import org.apache.commons.betwixt.io.BeanWriter;
32  import org.apache.commons.betwixt.io.CyclicReferenceException;
33  
34  /**
35   * This will test the recursive behaviour of betwixt.
36   * 
37   * @author <a href="mailto:martin@mvdb.net">Martin van den Bemt </a>
38   * @version $Id: TestRecursion.java 438373 2006-08-30 05:17:21Z bayard $
39   */
40  public class TestRecursion extends AbstractTestCase {
41  
42      public TestRecursion(String testName) {
43          super(testName);
44      }
45  
46      public static Test suite() {
47          return new TestSuite(TestRecursion.class);
48      }
49  
50      /**
51       * This will test reading a simple recursive xml file
52       *  
53       */
54      public void testReadwithCollectionsInElementRoundTrip() throws Exception {
55          //SimpleLog log = new
56          // SimpleLog("[testReadwithCollectionsInElementRoundTrip:XMLIntrospectorHelper]");
57          //log.setLevel(SimpleLog.LOG_LEVEL_TRACE);
58          //XMLIntrospectorHelper.setLog(log);
59  
60          //log = new
61          // SimpleLog("[testReadwithCollectionsInElementRoundTrip:XMLIntrospector]");
62          //log.setLevel(SimpleLog.LOG_LEVEL_TRACE);
63  
64          XMLIntrospector intro = createXMLIntrospector();
65          //intro.setLog(log);
66          intro.getConfiguration().setWrapCollectionsInElement(true);
67  
68          //log = new
69          // SimpleLog("[testReadwithCollectionsInElementRoundTrip:BeanReader]");
70          //log.setLevel(SimpleLog.LOG_LEVEL_TRACE);
71  
72          BeanReader reader = new BeanReader();
73          reader.setXMLIntrospector(intro);
74          //reader.setLog(log);
75          reader.registerBeanClass(ElementBean.class);
76  
77          ElementBean bean = (ElementBean) reader
78                  .parse(getTestFileURL("src/test/org/apache/commons/betwixt/recursion/recursion.xml"));
79  
80          List elements = bean.getElements();
81          assertEquals("Root elements size", 2, elements.size());
82          Element elementOne = (Element) elements.get(0);
83          assertEquals("Element one name", "element1", elementOne.getName());
84          Element elementTwo = (Element) elements.get(1);
85          assertEquals("Element two name", "element2", elementTwo.getName());
86          assertEquals("Element two children", 0, elementTwo.getElements().size());
87          elements = elementOne.getElements();
88          assertEquals("Element one children", 2, elements.size());
89          Element elementOneOne = (Element) elements.get(0);
90          assertEquals("Element one one name", "element11", elementOneOne
91                  .getName());
92          Element elementOneTwo = (Element) elements.get(1);
93          assertEquals("Element one two name", "element12", elementOneTwo
94                  .getName());
95          assertEquals("Element one two children", 0, elementOneTwo.getElements()
96                  .size());
97          elements = elementOneOne.getElements();
98          assertEquals("Element one one children", 2, elements.size());
99          Element elementOneOneOne = (Element) elements.get(0);
100         assertEquals("Element one one one name", "element111", elementOneOneOne
101                 .getName());
102         Element elementOneOneTwo = (Element) elements.get(1);
103         assertEquals("Element one one two name", "element112", elementOneOneTwo
104                 .getName());
105 
106         StringWriter buffer = new StringWriter();
107         write(bean, buffer, true);
108 
109         String xml = "<?xml version='1.0'?><ElementBean><elements><element name='element1'>"
110                 + "<elements><element name='element11'><elements><element name='element111'>"
111                 + "<elements/></element><element name='element112'><elements/></element>"
112                 + "</elements></element><element name='element12'><elements/></element>"
113                 + "</elements></element><element name='element2'><elements/>"
114                 + "</element></elements></ElementBean>";
115 
116         xmlAssertIsomorphic(parseString(xml), parseString(buffer.getBuffer()
117                 .toString()), true);
118     }
119 
120     /**
121      * This will test reading a simple recursive xml file
122      */
123     public void testReadWithoutCollectionsInElementRoundTrip() throws Exception {
124         //        SimpleLog log = new
125         // SimpleLog("[testReadWithoutCollectionsInElementRoundTrip:BeanRuleSet]");
126         //        log.setLevel(SimpleLog.LOG_LEVEL_TRACE);
127         //        BeanRuleSet.setLog(log);
128 
129         //	log = new
130         // SimpleLog("[testReadWithoutCollectionsInElementRoundTrip:XMLIntrospector]");
131         //        log.setLevel(SimpleLog.LOG_LEVEL_TRACE);
132 
133         XMLIntrospector intro = createXMLIntrospector();
134         intro.getConfiguration().setWrapCollectionsInElement(false);
135         //        intro.setLog(log);
136         //        log = new
137         // SimpleLog("[testReadWithoutCollectionsInElementRoundTrip:XMLIntrospectorHelper]");
138         //        log.setLevel(SimpleLog.LOG_LEVEL_TRACE);
139         //        XMLIntrospectorHelper.setLog(log);
140         BeanReader reader = new BeanReader();
141         //        log = new
142         // SimpleLog("[testReadWithoutCollectionsInElementRoundTrip:BeanReader]");
143         //        log.setLevel(SimpleLog.LOG_LEVEL_TRACE);
144         //        reader.setLog(log);
145         reader.setXMLIntrospector(intro);
146         reader.registerBeanClass(ElementBean.class);
147         ElementBean bean = (ElementBean) reader
148                 .parse(getTestFileURL("src/test/org/apache/commons/betwixt/recursion/recursion2.xml"));
149         List elements = bean.getElements();
150         assertEquals("Number of elements in root bean", 2, elements.size());
151         Element elementOne = (Element) bean.elements.get(0);
152         assertEquals("First element name", "element1", elementOne.getName());
153         Element elementTwo = (Element) bean.elements.get(1);
154         assertEquals("Second element name", "element2", elementTwo.getName());
155 
156         elements = elementOne.getElements();
157         assertEquals("Number of child elements in first element", 2, elements
158                 .size());
159         Element elementOneOne = (Element) elements.get(0);
160         assertEquals("11 element name", "element11", elementOneOne.getName());
161         Element elementOneTwo = (Element) elements.get(1);
162         assertEquals("12 element name", "element12", elementOneTwo.getName());
163 
164         elements = elementOneOne.getElements();
165         assertEquals("Number of child elements in element 11", 2, elements
166                 .size());
167         Element elementOneOneOne = (Element) elements.get(0);
168         assertEquals("111 element name", "element111", elementOneOneOne
169                 .getName());
170 
171         assertEquals("111 child elements ", 0, elementOneOneOne.getElements()
172                 .size());
173 
174         Element elementOneOneTwo = (Element) elements.get(1);
175         assertEquals("112 element name", "element112", elementOneOneTwo
176                 .getName());
177         assertEquals("112 child elements ", 0, elementOneOneTwo.getElements()
178                 .size());
179 
180         elements = elementOneTwo.getElements();
181         assertEquals("Number of child elements in element 12", 0, elements
182                 .size());
183 
184         elements = elementTwo.getElements();
185         assertEquals("Number of child elements in element 2", 0, elements
186                 .size());
187 
188         StringWriter buffer = new StringWriter();
189         buffer.write("<?xml version='1.0'?>");
190         write(bean, buffer, false);
191 
192         String xml = "<ElementBean><element name='element1'><element name='element11'><element name='element111' />"
193                 + "<element name='element112' /> </element><element name='element12' /> </element>"
194                 + "<element name='element2' /> </ElementBean>";
195 
196         xmlAssertIsomorphic(parseString(xml), parseString(buffer.getBuffer()
197                 .toString()), true);
198 
199     }
200 
201     /**
202      * Opens a writer and writes an object model according to the retrieved bean
203      */
204     private void write(Object bean, Writer out, boolean wrapIt)
205             throws Exception {
206         BeanWriter writer = new BeanWriter(out);
207         writer.setWriteEmptyElements(true);
208         writer.setXMLIntrospector(createXMLIntrospector());
209         // specifies weather to use collection elements or not.
210         writer.getXMLIntrospector().getConfiguration()
211                 .setWrapCollectionsInElement(wrapIt);
212         // we don't want to write Id attributes to every element
213         // we just want our opbject model written nothing more..
214         writer.getBindingConfiguration().setMapIDs(false);
215         // the source has 2 spaces indention and \n as line seperator.
216         writer.setIndent("  ");
217         writer.setEndOfLine("\n");
218         writer.write(bean);
219     }
220 
221     /**
222      * Set up the XMLIntroSpector
223      */
224     protected XMLIntrospector createXMLIntrospector() {
225         XMLIntrospector introspector = new XMLIntrospector();
226 
227         // set elements for attributes to true
228         introspector.getConfiguration().setAttributesForPrimitives(true);
229         introspector.getConfiguration().setWrapCollectionsInElement(false);
230 
231         return introspector;
232     }
233 
234     /**
235      */
236     public void testBeanWithIdProperty() throws Exception {
237         IdBean bean = new IdBean("Hello, World");
238         bean.setNotId("Not ID");
239         StringWriter out = new StringWriter();
240         out.write("<?xml version='1.0'?>");
241         BeanWriter writer = new BeanWriter(out);
242         writer.setWriteEmptyElements(true);
243         writer.getXMLIntrospector().getConfiguration()
244                 .setAttributesForPrimitives(true);
245         writer.getBindingConfiguration().setMapIDs(true);
246         writer.write(bean);
247 
248         String xml = "<?xml version='1.0'?><IdBean notId='Not ID' id='Hello, World'/>";
249 
250         xmlAssertIsomorphic(parseString(xml), parseString(out.getBuffer()
251                 .toString()), true);
252     }
253 
254     /**
255      * Check that a cyclic reference exception is not thrown in this case
256      */
257     public void testCyclicReferenceStack1() throws Exception {
258         Element alpha = new Element("Alpha");
259         Element beta = new Element("Beta");
260         Element gamma = new Element("Gamma");
261         Element epsilon = new Element("Epsilon");
262 
263         alpha.addElement(beta);
264         beta.addElement(gamma);
265         gamma.addElement(epsilon);
266         alpha.addElement(epsilon);
267 
268         StringWriter stringWriter = new StringWriter();
269         BeanWriter writer = new BeanWriter(stringWriter);
270         writer.setWriteEmptyElements(true);
271         writer.getBindingConfiguration().setMapIDs(false);
272         writer.write(alpha);
273 
274         String xml = "<?xml version='1.0'?><Element><name>Alpha</name><elements><element>"
275                 + "<name>Beta</name><elements><element><name>Gamma</name><elements>"
276                 + "<element><name>Epsilon</name><elements/></element></elements>"
277                 + "</element></elements></element><element><name>Epsilon</name>"
278                 + "<elements/></element></elements></Element>";
279 
280         xmlAssertIsomorphic(parseString(xml), parseString(stringWriter
281                 .getBuffer().toString()), true);
282     }
283 
284     /**
285      * This should throw a cyclic reference
286      */
287     public void testCyclicReferenceStack2() throws Exception {
288         Element alpha = new Element("Alpha");
289         Element beta = new Element("Beta");
290         Element gamma = new Element("Gamma");
291         Element epsilon = new Element("Epsilon");
292 
293         alpha.addElement(beta);
294         beta.addElement(gamma);
295         gamma.addElement(epsilon);
296         epsilon.addElement(beta);
297 
298         StringWriter stringWriter = new StringWriter();
299         BeanWriter writer = new BeanWriter(stringWriter);
300         writer.setWriteEmptyElements(true);
301         writer.getBindingConfiguration().setMapIDs(false);
302 
303         //SimpleLog log = new
304         // SimpleLog("[testCyclicReferenceStack2:BeanWriter]");
305         //log.setLevel(SimpleLog.LOG_LEVEL_TRACE);
306         //writer.setLog(log);
307 
308         //log = new SimpleLog("[testCyclicReferenceStack2:BeanWriter]");
309         //log.setLevel(SimpleLog.LOG_LEVEL_TRACE);
310         //writer.setAbstractBeanWriterLog(log);
311 
312         try {
313             writer.write(alpha);
314             fail("Cycle was not detected!");
315 
316         } catch (CyclicReferenceException e) {
317             // that's what we expected!
318         }
319     }
320 
321     /** Tests for a stack overflow bug */
322     public void testRegisterOverflow() throws Exception {
323         BeanReader reader = new BeanReader();
324         try {
325             reader.registerBeanClass(NorthWind.class);
326         } catch (StackOverflowError e) {
327             e.printStackTrace();
328             fail("Expected registration to succeed");
329         }
330     }
331 
332     public void testRegisterOverflow2() throws Exception {
333         BeanReader beanReader = new BeanReader();
334         try {
335             beanReader.registerBeanClass(PersonTest.class);
336         } catch (StackOverflowError e) {
337             e.printStackTrace();
338             fail("Expected registration to succeed");
339         }
340     }
341 
342     public void testCycleReferences() throws Exception {
343         PersonTest person = new PersonTest();
344         person.setName("John Doe");
345         AddressTest address = new AddressTest();
346         address.setStreetAddress("1221 Washington Street");
347         person.setAddress(address);
348         ReferenceTest reference = new ReferenceTest();
349         reference.setPerson(person);
350         address.setReference(reference);
351 
352         StringWriter outputWriter = new StringWriter();
353 
354         outputWriter.write("<?xml version='1.0' ?>\n");
355         BeanWriter beanWriter = new BeanWriter(outputWriter);
356         beanWriter.setEndOfLine("\n");
357         beanWriter.enablePrettyPrint();
358         beanWriter.getBindingConfiguration().setMapIDs(true);
359         beanWriter.write(person);
360 
361         BeanReader beanReader = new BeanReader();
362         beanReader.getBindingConfiguration().setMapIDs(true);
363 
364         // Configure the reader
365         beanReader.registerBeanClass(PersonTest.class);
366         beanReader.registerBeanClass(AddressTest.class);
367         beanReader.registerBeanClass(ReferenceTest.class);
368 
369         String out = outputWriter.toString();
370         StringReader xmlReader = new StringReader(out);
371 
372         //Parse the xml
373         PersonTest result = (PersonTest) beanReader.parse(xmlReader);
374         assertSame("Cycle did not result in the same reference", result, result
375                 .getAddress().getReference().getPerson());
376 
377     }
378 
379 }
380