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