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