http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e4dfdf81/juneau-core-test/src/test/java/org/apache/juneau/a/rttests/RoundTripBeanInheritanceTest.java ---------------------------------------------------------------------- diff --git a/juneau-core-test/src/test/java/org/apache/juneau/a/rttests/RoundTripBeanInheritanceTest.java b/juneau-core-test/src/test/java/org/apache/juneau/a/rttests/RoundTripBeanInheritanceTest.java new file mode 100755 index 0000000..0c4eef6 --- /dev/null +++ b/juneau-core-test/src/test/java/org/apache/juneau/a/rttests/RoundTripBeanInheritanceTest.java @@ -0,0 +1,218 @@ +// *************************************************************************************************************************** +// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file * +// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file * +// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance * +// * with the License. You may obtain a copy of the License at * +// * * +// * http://www.apache.org/licenses/LICENSE-2.0 * +// * * +// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an * +// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * +// * specific language governing permissions and limitations under the License. * +// *************************************************************************************************************************** +package org.apache.juneau.a.rttests; + +import static org.apache.juneau.TestUtils.*; +import static org.junit.Assert.*; + +import org.apache.juneau.*; +import org.apache.juneau.parser.*; +import org.apache.juneau.serializer.*; +import org.junit.*; + +/** + * Tests designed to serialize and parse objects to make sure we end up + * with the same objects for all serializers and parsers. + */ +@SuppressWarnings("javadoc") +public class RoundTripBeanInheritanceTest extends RoundTripTest { + + public RoundTripBeanInheritanceTest(String label, Serializer s, Parser p, int flags) throws Exception { + super(label, s, p, flags); + } + + //==================================================================================================== + // testBeanInheritance + //==================================================================================================== + @Test + public void testBeanInheritance() throws Exception { + + // Skip tests that just return the same object. + if (returnOriginalObject) + return; + + A2 t1 = new A2(), t2; + t1.init(); + t2 = roundTrip(t1, A2.class); + assertEqualObjects(t1, t2); + + A3 t3 = new A3(); + t3.init(); + try { + ClassMeta<?> cm = BeanContext.DEFAULT.getClassMeta(A3.class); + assertEquals("No properties detected on bean class", cm.getNotABeanReason()); + roundTrip(t3, A3.class); + fail("Exception expected"); + } catch (ParseException e) { + } catch (SerializeException e) { + } catch (InvalidDataConversionException e) {} + } + + + public static abstract class A1 { + protected String x = null; + protected String y = null; + protected String z = null; + + public A1() { + this.x = null; + this.y = null; + this.z = null; + } + + public A1(String x, String y, String z) { + this.x = x; + this.y = y; + this.z = z; + } + + public void setX(String x) { + this.x = x; + } + + public void setY(String y) { + this.y = y; + } + + public void setZ(String z) { + this.z = z; + } + + @Override /* Object */ + public String toString() { + return ("A1(x: " + this.x + ", y: " + this.y + ", z: " + this.z + ")"); + } + + public A1 init() { + x = null; + y = ""; + z = "z"; + return this; + } + } + + public static class A2 extends A1 { + public A2() { + super(); + } + + public A2(String x, String y, String z) { + super(x, y, z); + } + + public String getX() { + return this.x; + } + + public String getY() { + return this.y; + } + + public String getZ() { + return this.z; + } + } + + // This is not supposed to be a valid bean since it has no getters defined. + public static class A3 extends A1 { + public A3() { + super(); + } + + public A3(String x, String y, String z) { + super(x, y, z); + } + + public String isX() { + throw new RuntimeException("Should not be called!"); + } + + public String isY() { + throw new RuntimeException("Should not be called!"); + } + + public String isZ() { + throw new RuntimeException("Should not be called!"); + } + } + + //==================================================================================================== + // testBeanInheritance2 + //==================================================================================================== + @Test + public void testBeanInheritance2() throws Exception { + B1 t1 = new B1().init(), t2; + t2 = roundTrip(t1, B1.class); + assertEqualObjects(t1, t2); + } + + public static class B1 extends B2 { + private A2 f4; + + public A2 getF4() { + return this.f4; + } + + public void setF4(A2 f4) { + this.f4 = f4; + } + + @Override /* Object */ + public String toString() { + return super.toString() + " / " + this.f4; + } + + public B1 init() { + setF1("A1"); + setF2(101); + setF3(false); + setF4((A2)new A2().init()); + return this; + } + } + + public static class B2 { + private String f1 = null; + private int f2 = -1; + private boolean f3 = false; + + public String getF1() { + return this.f1; + } + + public void setF1(String f1) { + this.f1 = f1; + } + + public int getF2() { + return this.f2; + } + + public void setF2(int f2) { + this.f2 = f2; + } + + public boolean isF3() { + return this.f3; + } + + public void setF3(boolean f3) { + this.f3 = f3; + } + + @Override /* Object */ + public String toString() { + return ("B2(f1: " + this.getF1() + ", f2: " + this.getF2() + ")"); + } + } +}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e4dfdf81/juneau-core-test/src/test/java/org/apache/juneau/a/rttests/RoundTripBeanMapsTest.java ---------------------------------------------------------------------- diff --git a/juneau-core-test/src/test/java/org/apache/juneau/a/rttests/RoundTripBeanMapsTest.java b/juneau-core-test/src/test/java/org/apache/juneau/a/rttests/RoundTripBeanMapsTest.java new file mode 100755 index 0000000..4ab6325 --- /dev/null +++ b/juneau-core-test/src/test/java/org/apache/juneau/a/rttests/RoundTripBeanMapsTest.java @@ -0,0 +1,1014 @@ +// *************************************************************************************************************************** +// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file * +// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file * +// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance * +// * with the License. You may obtain a copy of the License at * +// * * +// * http://www.apache.org/licenses/LICENSE-2.0 * +// * * +// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an * +// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * +// * specific language governing permissions and limitations under the License. * +// *************************************************************************************************************************** +package org.apache.juneau.a.rttests; + +import static org.apache.juneau.TestUtils.*; +import static org.junit.Assert.*; + +import java.util.*; + +import javax.xml.datatype.*; + +import org.apache.juneau.*; +import org.apache.juneau.annotation.*; +import org.apache.juneau.json.*; +import org.apache.juneau.json.annotation.*; +import org.apache.juneau.parser.*; +import org.apache.juneau.serializer.*; +import org.apache.juneau.transform.*; +import org.apache.juneau.transforms.*; +import org.junit.*; + +/** + * Tests designed to serialize and parse objects to make sure we end up + * with the same objects for all serializers and parsers. + */ +@SuppressWarnings({"unchecked","serial","javadoc"}) +public class RoundTripBeanMapsTest extends RoundTripTest { + + public RoundTripBeanMapsTest(String label, Serializer s, Parser p, int flags) throws Exception { + super(label, s, p, flags); + } + + @Override /* RoundTripTest */ + public Map<Class<?>,Class<?>> getImplClasses() { + Map<Class<?>,Class<?>> m = new HashMap<Class<?>,Class<?>>(); + m.put(IBean.class, CBean.class); + return m; + } + + //==================================================================================================== + // IBean/ABean/Bean + //==================================================================================================== + @Test + public void testImplClasses() throws Exception { + IBean bean = new CBean(); + + bean.setF1("bar"); + bean = roundTrip(bean, IBean.class); + assertEquals("bar", bean.getF1()); + + bean.setF1("baz"); + bean = roundTrip(bean, ABean.class); + assertEquals("baz", bean.getF1()); + + bean.setF1("bing"); + bean = roundTrip(bean, CBean.class); + assertEquals("bing", bean.getF1()); + } + + //==================================================================================================== + // IBean[]/ABean[]/Bean[] + //==================================================================================================== + @Test + public void testImplArrayClasses() throws Exception { + IBean[] bean = new CBean[]{new CBean()}; + + bean[0].setF1("bar"); + bean = roundTrip(bean, IBean[].class); + assertEquals("bar", bean[0].getF1()); + + bean[0].setF1("baz"); + bean = roundTrip(bean, ABean[].class); + assertEquals("baz", bean[0].getF1()); + + bean[0].setF1("bing"); + bean = roundTrip(bean, CBean[].class); + assertEquals("bing", bean[0].getF1()); + } + + //==================================================================================================== + // List<IBean/ABean/Bean> + //==================================================================================================== + @Test + public void testImplListClasses() throws Exception { + List<IBean> l = new LinkedList<IBean>() {{ + add(new CBean()); + }}; + + l.get(0).setF1("bar"); + l = roundTripCollection(l, List.class, IBean.class); + assertEquals("bar", l.get(0).getF1()); + l = roundTripCollection(l, LinkedList.class, IBean.class); + assertEquals("bar", l.get(0).getF1()); + + l.get(0).setF1("baz"); + l = roundTripCollection(l, List.class, ABean.class); + assertEquals("baz", l.get(0).getF1()); + l = roundTripCollection(l, LinkedList.class, ABean.class); + assertEquals("baz", l.get(0).getF1()); + + l.get(0).setF1("bing"); + l = roundTripCollection(l, List.class, CBean.class); + assertEquals("bing", l.get(0).getF1()); + l = roundTripCollection(l, LinkedList.class, CBean.class); + assertEquals("bing", l.get(0).getF1()); + } + + //==================================================================================================== + // Map<String,IBean/ABean/Bean> + //==================================================================================================== + @Test + public void testImplMap() throws Exception { + Map<String,IBean> l = new LinkedHashMap<String,IBean>() {{ + put("foo", new CBean()); + }}; + + l.get("foo").setF1("bar"); + l = roundTripMap(l, Map.class, String.class, IBean.class); + assertEquals("bar", l.get("foo").getF1()); + l = roundTripMap(l, LinkedHashMap.class, String.class, IBean.class); + assertEquals("bar", l.get("foo").getF1()); + + l.get("foo").setF1("baz"); + l = roundTripMap(l, Map.class, String.class, ABean.class); + assertEquals("baz", l.get("foo").getF1()); + l = roundTripMap(l, LinkedHashMap.class, String.class, ABean.class); + assertEquals("baz", l.get("foo").getF1()); + + l.get("foo").setF1("bing"); + l = roundTripMap(l, Map.class, String.class, CBean.class); + assertEquals("bing", l.get("foo").getF1()); + l = roundTripMap(l, LinkedHashMap.class, String.class, CBean.class); + assertEquals("bing", l.get("foo").getF1()); + } + + //==================================================================================================== + // Map<String,IBean/ABean/Bean> + //==================================================================================================== + @Test + public void testImplMap2() throws Exception { + A b = new A(1); + b = roundTrip(b); + if (returnOriginalObject || p == null) + return; + assertEquals(0, b.f1); + assertEquals(0, b.f2); + assertEquals(1, b.f3); + assertEquals(1, b.f4); + assertEquals(0, b.getF5()); + assertEquals(1, b.getF6()); + } + + public static interface IBean { + public String getF1(); + public void setF1(String f1); + } + + public static abstract class ABean implements IBean { + @Override /* IBean */ + public abstract String getF1(); + @Override /* IBean */ + public abstract void setF1(String f1); + } + + public static class CBean extends ABean { + private String f1 = "foo"; + @Override /* IBean */ + public String getF1() { + return f1; + } + @Override /* IBean */ + public void setF1(String f1) { + this.f1 = f1; + } + } + + public static class A { + + @BeanIgnore + public int f1, f2; + public int f3, f4; + + private int f5, f6; + + @BeanIgnore + public int getF5() { + return f5; + } + public void setF5(int f5) { + this.f5 = f5; + } + + public int getF6() { + return f6; + } + public void setF6(int f6) { + this.f6 = f6; + } + + public A() {} + + public A(int v) { + f1 = f2 = f3 = f4 = f5 = f6 = v; + } + } + + //==================================================================================================== + // Test @Bean(subTypes=xxx) + //==================================================================================================== + @Test + public void testSubTypesUsingAnnotation() throws Exception { + JsonSerializer js = JsonSerializer.DEFAULT_LAX.clone().addPojoSwaps(XMLGregorianCalendarSwap.class); + + // Skip validation-only tests + if (isValidationOnly()) + return; + + Serializer s = getSerializer().clone().addPojoSwaps(XMLGregorianCalendarSwap.class); + Parser p = getParser().clone().addPojoSwaps(XMLGregorianCalendarSwap.class); + + B1 b1 = B1.create(); + Object r = s.serialize(b1); + B b = p.parse(r, B.class); + assertTrue(b instanceof B1); + assertObjectEquals("{subType:'B1',f0:'f0',f1:'f1'}", b, js); + + B2 b2 = B2.create(); + r = s.serialize(b2); + b = p.parse(r, B.class); + assertTrue(b instanceof B2); + assertObjectEquals("{subType:'B2',f0:'f0',f2:1}", b, js); + + B3 b3 = B3.create(); + r = s.serialize(b3); + b = p.parse(r, B.class); + assertTrue(b instanceof B3); + assertObjectEquals("{subType:'B3',f0:'f0',f3:'2001-01-01T12:34:56.789Z'}", b, js); +} + + @Bean( + subTypeProperty="subType", + subTypes={B1.class,B2.class,B3.class} + ) + public abstract static class B { + public String f0 = "f0"; + } + + @Bean(typeName="B1") + public static class B1 extends B { + public String f1; + public static B1 create() { + B1 b = new B1(); + b.f0 = "f0"; + b.f1 = "f1"; + return b; + } + } + + @Bean(typeName="B2") + public static class B2 extends B { + public int f2; + public static B2 create() { + B2 b = new B2(); + b.f0 = "f0"; + b.f2 = 1; + return b; + } + } + + @Bean(typeName="B3") + public static class B3 extends B { + public XMLGregorianCalendar f3; + public static B3 create() throws Exception { + B3 b = new B3(); + b.f0 = "f0"; + b.f3 = DatatypeFactory.newInstance().newXMLGregorianCalendar("2001-01-01T12:34:56.789Z"); + return b; + } + } + + //==================================================================================================== + // Test @Bean(subTypes=xxx) using BeanFilter + //==================================================================================================== + @Test + public void testSubTypesUsingBeanFilter() throws Exception { + JsonSerializer js = JsonSerializer.DEFAULT_LAX.clone().addPojoSwaps(XMLGregorianCalendarSwap.class); + + // Skip validation-only tests + if (isValidationOnly()) + return; + + Serializer s = getSerializer().clone().addBeanFilters(CFilter.class).addPojoSwaps(XMLGregorianCalendarSwap.class); + Parser p = getParser().clone().addBeanFilters(CFilter.class).addPojoSwaps(XMLGregorianCalendarSwap.class); + + C1 c1 = C1.create(); + Object r = s.serialize(c1); + C c = p.parse(r, C.class); + assertTrue(c instanceof C1); + assertObjectEquals("{f0:'f0',f1:'f1'}", c, js); + + C2 c2 = C2.create(); + r = s.serialize(c2); + c = p.parse(r, C.class); + assertTrue(c instanceof C2); + assertObjectEquals("{f0:'f0',f2:1}", c, js); + + C3 c3 = C3.create(); + r = s.serialize(c3); + c = p.parse(r, C.class); + assertTrue(c instanceof C3); + assertObjectEquals("{f0:'f0',f3:'2001-01-01T12:34:56.789Z'}", c, js); + } + + public abstract static class C { + public String f0; + } + + public static class C1 extends C { + public String f1; + public static C1 create() { + C1 c = new C1(); + c.f0 = "f0"; + c.f1 = "f1"; + return c; + } + } + + public static class C2 extends C { + public int f2; + public static C2 create() { + C2 c = new C2(); + c.f0 = "f0"; + c.f2 = 1; + return c; + } + } + + public static class C3 extends C { + public XMLGregorianCalendar f3; + public static C3 create() throws Exception { + C3 c = new C3(); + c.f0 = "f0"; + c.f3 = DatatypeFactory.newInstance().newXMLGregorianCalendar("2001-01-01T12:34:56.789Z"); + return c; + } + } + + public static class CFilter extends BeanFilterBuilder { + public CFilter() { + super(C.class); + setSubTypeProperty("subType"); + addSubType("C1", C1.class); + addSubType("C2", C2.class); + addSubType("C3", C3.class); + } + } + + //==================================================================================================== + // Test @Bean(subTypeProperty=xxx) with real bean property + //==================================================================================================== + @Test + public void testSubTypePropertyWithRealPropertyUsingAnnotation() throws Exception { + // Skip validation-only tests + if (isValidationOnly()) + return; + + Serializer s = getSerializer(); + Parser p = getParser(); + + BA1 ba1 = BA1.create(); + Object r = s.serialize(ba1); + BA b = p.parse(r, BA.class); + assertTrue(b instanceof BA1); + assertEquals("BA1", b.subType); + assertObjectEquals("{subType:'BA1',f0a:'f0a',f0b:'f0b',f1:'f1'}", b); + } + + @Bean( + subTypeProperty="subType", + subTypes={BA1.class,BA2.class} + ) + public abstract static class BA { + public String f0a, subType, f0b; + } + + @Bean(typeName="BA1") + public static class BA1 extends BA { + public String f1; + public static BA1 create() { + BA1 b = new BA1(); + b.f0a = "f0a"; + b.f0b = "f0b"; + b.f1 = "f1"; + b.subType = "xxx";// Should be ignored. + return b; + } + } + + @Bean(typeName="BA2") + public static class BA2 extends BA { + public String f2; + } + + + //==================================================================================================== + // Test @Bean(subTypes=xxx) with real bean property using BeanFilter + //==================================================================================================== + @Test + public void testSubTypePropertyWithRealPropertyUsingBeanFilter() throws Exception { + // Skip validation-only tests + if (isValidationOnly()) + return; + + Serializer s = getSerializer().clone().addBeanFilters(CAFilter.class); + Parser p = getParser().clone().addBeanFilters(CAFilter.class); + + CA1 c1 = CA1.create(); + Object r = s.serialize(c1); + CA c = p.parse(r, CA.class); + assertTrue(c instanceof CA1); + assertEquals("CA1", c.subType); + assertObjectEquals("{f0a:'f0a',subType:'CA1',f0b:'f0b',f1:'f1'}", c); + } + + public abstract static class CA { + public String f0a, subType, f0b; + } + + public static class CA1 extends CA { + public String f1; + public static CA1 create() { + CA1 c = new CA1(); + c.f0a = "f0a"; + c.f0b = "f0b"; + c.f1 = "f1"; + c.subType = "xxx";// Should be ignored. + return c; + } + } + + public static class CA2 extends CA { + public String f2; + } + + public static class CAFilter extends BeanFilterBuilder { + public CAFilter() { + super(CA.class); + setSubTypeProperty("subType"); + addSubType("CA1", CA1.class); + addSubType("CA2", CA2.class); + } + } + + //==================================================================================================== + // Test @Bean(properties=xxx) + //==================================================================================================== + @Test + public void testPropertiesUsingAnnotation() throws Exception { + // Skip validation-only tests + if (isValidationOnly()) + return; + + Serializer s = getSerializer(); + Parser p = getParser(); + + D1 d = new D1().init(); + Object r = s.serialize(d); + d = p.parse(r, D1.class); + assertNull(d.f1); + assertObjectEquals("{f3:'f3',f2:'f2'}", d); + } + + @Bean(properties="f3,f2") + public static class D1 { + public String f1, f2, f3; + public D1 init() { + f1 = "f1"; + f2 = "f2"; + f3 = "f3"; + return this; + } + } + + //==================================================================================================== + // Test @Bean(properties=xxx) using BeanFilter + //==================================================================================================== + @Test + public void testPropertiesUsingBeanFilter() throws Exception { + JsonSerializer js = JsonSerializer.DEFAULT_LAX.clone().addBeanFilters(D2Filter.class); + + // Skip validation-only tests + if (isValidationOnly()) + return; + + Serializer s = getSerializer().clone().addBeanFilters(D2Filter.class); + Parser p = getParser().clone().addBeanFilters(D2Filter.class); + + D2 d = new D2().init(); + Object r = s.serialize(d); + d = p.parse(r, D2.class); + assertNull(d.f1); + assertObjectEquals("{f3:'f3',f2:'f2'}", d, js); + } + + public static class D2 { + public String f1, f2, f3; + public D2 init() { + f1 = "f1"; + f2 = "f2"; + f3 = "f3"; + return this; + } + } + public static class D2Filter extends BeanFilterBuilder { + public D2Filter() { + super(D2.class); + setProperties("f3,f2"); + } + } + + //==================================================================================================== + // Test @Bean(excludeProperties=xxx) + //==================================================================================================== + @Test + public void testExcludePropertiesUsingAnnotation() throws Exception { + // Skip validation-only tests + if (isValidationOnly()) + return; + + Serializer s = getSerializer(); + Parser p = getParser(); + + E1 e = new E1().init(); + Object r = s.serialize(e); + e = p.parse(r, E1.class); + assertObjectEquals("{f1:'f1',f3:'f3'}", e); + } + + @Bean(excludeProperties="f2") + public static class E1 { + public String f1, f2, f3; + public E1 init() { + f1 = "f1"; + f2 = "f2"; + f3 = "f3"; + return this; + } + } + + //==================================================================================================== + // Test @Bean(excludeProperties=xxx) using BeanFilter + //==================================================================================================== + @Test + public void testExcludePropertiesUsingBeanFilter() throws Exception { + // Skip validation-only tests + if (isValidationOnly()) + return; + + Serializer s = getSerializer().clone().addBeanFilters(E2Filter.class); + Parser p = getParser().clone().addBeanFilters(E2Filter.class); + + E2 e = new E2().init(); + Object r = s.serialize(e); + e = p.parse(r, E2.class); + assertObjectEquals("{f1:'f1',f3:'f3'}", e); + } + + public static class E2 { + public String f1, f2, f3; + public E2 init() { + f1 = "f1"; + f2 = "f2"; + f3 = "f3"; + return this; + } + } + public static class E2Filter extends BeanFilterBuilder { + public E2Filter() { + super(E2.class); + setExcludeProperties("f2"); + } + } + + //==================================================================================================== + // Test @Bean(interfaceClass=xxx) + //==================================================================================================== + @Test + public void testInterfaceClassUsingAnnotation() throws Exception { + // Skip validation-only tests + if (isValidationOnly()) + return; + + Serializer s = getSerializer(); + Parser p = getParser(); + + FA2 t = new FA2().init(); + Object r = s.serialize(t); + t = p.parse(r, FA2.class); + assertObjectEquals("{f1:'f1'}", t); + } + + @Bean(interfaceClass=FA1.class) + public static class FA1 { + public String f1; + } + + public static class FA2 extends FA1 { + public String f2; + public FA2 init() { + f1 = "f1"; + f2 = "f2"; + return this; + } + } + + //==================================================================================================== + // Test @Bean(interfaceClass=xxx) using BeanFilter + //==================================================================================================== + @Test + public void testInterfaceClassUsingBeanFilter() throws Exception { + Serializer s; + Parser p; + FB2 t; + Object r; + + // Skip validation-only tests + if (isValidationOnly()) + return; + + // --- BeanFilter defined on parent class --- + s = getSerializer().clone().addBeanFilters(FB1Filter.class); + p = getParser().clone().addBeanFilters(FB1Filter.class); + + t = new FB2().init(); + r = s.serialize(t); + t = p.parse(r, FB2.class); + assertObjectEquals("{f1:'f1'}", t); + + // --- BeanFilter defined on child class class --- + s = getSerializer().clone().addBeanFilters(FB2Filter.class); + p = getParser().clone().addBeanFilters(FB2Filter.class); + + t = new FB2().init(); + r = s.serialize(t); + t = p.parse(r, FB2.class); + assertObjectEquals("{f1:'f1'}", t); + + // --- BeanFilter defined as plain class --- + s = getSerializer().clone().addBeanFilters(FB1.class); + p = getParser().clone().addBeanFilters(FB1.class); + + t = new FB2().init(); + r = s.serialize(t); + t = p.parse(r, FB2.class); + assertObjectEquals("{f1:'f1'}", t); + } + + public static class FB1 { + public String f1; + } + + public static class FB2 extends FB1 { + public String f2; + public FB2 init() { + f1 = "f1"; + f2 = "f2"; + return this; + } + } + public static class FB1Filter extends InterfaceBeanFilterBuilder { + public FB1Filter() { + super(FB1.class); + } + } + public static class FB2Filter extends InterfaceBeanFilterBuilder { + public FB2Filter() { + super(FB1.class); + } + } + + //==================================================================================================== + // testMemberClass + //==================================================================================================== + @Test + public void testMemberClass() throws Exception { + G t = G.create(); + t = roundTrip(t, G.class); + } + + public static class G { + public int a1; + public G1 g1; + + public static G create() { + G g = new G(); + g.a1 = 1; + g.g1.a2 = 2; + g.g1.g2.a3 = 3; + return g; + } + + public G() { + g1 = new G1(); + } + + public class G1 { + public int a2; + public G2 g2; + + public G1() { + g2 = new G2(); + } + + public class G2 { + public int a3; + } + } + } + + //==================================================================================================== + // testMemberClassWithMapClass + //==================================================================================================== + @Test + public void testMemberClassWithMapClass() throws Exception { + H t = H.create(); + t = roundTrip(t, H.class); + } + + public static class H extends LinkedHashMap<String,H.H1> { + + static H create() { + H h = new H(); + h.add("foo", 1, 2); + return h; + } + + H add(String key, int a2, int a3) { + H1 h1 = new H1(); + h1.a2 = a2; + h1.h2.a3 = a3; + put(key, h1); + return this; + } + + public class H1 { + public int a2; + public H2 h2; + + public H1() { + h2 = new H2(); + } + + public class H2 { + public int a3; + } + } + } + + //==================================================================================================== + // testMemberClassWithListClass + //==================================================================================================== + @Test + public void testMemberClassWithListClass() throws Exception { + I t = I.create(); + t = roundTrip(t, I.class); + } + + public static class I extends LinkedList<I.I1> { + + static I create() { + I i = new I(); + i.add(1, 2); + return i; + } + + I add(int a2, int a3) { + I1 i1 = new I1(); + i1.a2 = a2; + i1.i2.a3 = a3; + super.add(i1); + return this; + } + + public class I1 { + public int a2; + public I2 i2; + + public I1() { + i2 = new I2(); + } + + public class I2 { + public int a3; + } + } + } + + //==================================================================================================== + // testMemberClassWithStringConstructor + //==================================================================================================== + @Test + public void testMemberClassWithStringConstructor() throws Exception { + J t = J.create(); + t = roundTrip(t, J.class); + } + + public static class J { + public J2 j2; + + static J create() { + J j = new J(); + j.init(); + return j; + } + + private void init() { + j2 = new J2("2"); + } + + public class J2 { + int a2; + + public J2(String arg) { + this.a2 = Integer.parseInt(arg); + } + + @Override /* Object */ + public String toString() { + return String.valueOf(a2); + } + } + } + + //==================================================================================================== + // testBeanPropertyPrecedence + //==================================================================================================== + @Test + public void testBeanPropertyPrecedence() throws Exception { + K t = K.create(); + t = roundTrip(t, K.class); + } + public static enum KEnum { FOO, BAR, BAZ } + + public static class K { + private KEnum a, b, c; + + static K create() { + K t = new K(); + t.a = KEnum.FOO; + t.b = KEnum.BAR; + t.c = KEnum.BAZ; + return t; + } + + @BeanIgnore + public KEnum getA() { + return KEnum.FOO; + } + + @BeanProperty(name="a") + public String getA2() { + return a.toString(); + } + + // This method should not be interpreted as the setter for this + // property because it doesn't match the getter return type above. + public void setA(KEnum a) { + throw new RuntimeException("Should not be called!"); + } + + public void setA(String a) { + this.a = KEnum.valueOf(a); + } + + public KEnum getB() { + return b; + } + + public void setB(String b) { + throw new RuntimeException("Should not be called!"); + } + + public void setB(Object b) { + throw new RuntimeException("Should not be called!"); + } + + public void setB(KEnum b) { + this.b = b; + } + + public KEnum getC() { + return c; + } + + public void setC(KEnum c) { + this.c = c; + } + + public void setC(String c) { + throw new RuntimeException("Should not be called!"); + } + + public void setC(Object c) { + throw new RuntimeException("Should not be called!"); + } + } + + //==================================================================================================== + // testWrapperAttrAnnotationOnBean + //==================================================================================================== + @Test + public void testWrapperAttrAnnotationOnBean() throws Exception { + L t = L.create(); + t = roundTrip(t, L.class); + + Map<String,L> m = new LinkedHashMap<String,L>(); + m.put("bar", L.create()); + roundTripMap(m, LinkedHashMap.class, String.class, L.class); + } + + @Json(wrapperAttr="foo") + public static class L { + public int f1; + + static L create() { + L l = new L(); + l.f1 = 1; + return l; + } + } + + //==================================================================================================== + // testWrapperAttrAnnotationOnNonBean + //==================================================================================================== + @Test + public void testWrapperAttrAnnotationOnNonBean() throws Exception { + M t = M.create(); + t = roundTrip(t, M.class); + + Map<String,M> m = new LinkedHashMap<String,M>(); + m.put("bar", M.create()); + roundTripMap(m, LinkedHashMap.class, String.class, M.class); + } + + @Json(wrapperAttr="foo") + public static class M { + int f1; + + static M create() { + M m = new M(); + m.f1 = 1; + return m; + } + + @Override /* Object */ + public String toString() { + return String.valueOf(f1); + } + + public static M valueOf(String s) { + M m = new M(); + m.f1 = Integer.parseInt(s); + return m; + } + } + + //==================================================================================================== + // Test parsing into top-level non-static inner classes with outer context. + //==================================================================================================== + @Test + public void testParsingIntoTopLevelNonStaticInnerClasses() throws Exception { + N n = new N(1); + + if (returnOriginalObject) + return; + + Serializer s = getSerializer(); + Parser p = getParser(); + + Object r = s.serialize(n.n2); + n = new N(2); + ParserSession session = p.createSession(r, null, null, n, null, null); + + N.N2 n2 = p.parse(session, BeanContext.DEFAULT.getClassMeta(N.N2.class)); + + // The inner N2.f1 field should be the value of the outer object passed in through the context. + assertEquals(2, n2.f1); + } + + public static class N { + public int f1; + public N2 n2; + + public N(int f1) { + this.f1 = f1; + n2 = new N2(); + } + public class N2 { + private int f1 = N.this.f1; + public int dummy = 1; + } + } + +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e4dfdf81/juneau-core-test/src/test/java/org/apache/juneau/a/rttests/RoundTripClassesTest.java ---------------------------------------------------------------------- diff --git a/juneau-core-test/src/test/java/org/apache/juneau/a/rttests/RoundTripClassesTest.java b/juneau-core-test/src/test/java/org/apache/juneau/a/rttests/RoundTripClassesTest.java new file mode 100644 index 0000000..654bbb4 --- /dev/null +++ b/juneau-core-test/src/test/java/org/apache/juneau/a/rttests/RoundTripClassesTest.java @@ -0,0 +1,54 @@ +// *************************************************************************************************************************** +// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file * +// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file * +// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance * +// * with the License. You may obtain a copy of the License at * +// * * +// * http://www.apache.org/licenses/LICENSE-2.0 * +// * * +// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an * +// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * +// * specific language governing permissions and limitations under the License. * +// *************************************************************************************************************************** +package org.apache.juneau.a.rttests; + +import static org.apache.juneau.TestUtils.*; +import static org.junit.Assert.*; + +import java.util.*; + +import org.apache.juneau.parser.*; +import org.apache.juneau.serializer.*; +import org.junit.*; + +/** + * Tests designed to serialize and parse objects to make sure we end up + * with the same objects for all serializers and parsers. + */ +@SuppressWarnings("javadoc") +public class RoundTripClassesTest extends RoundTripTest { + + public RoundTripClassesTest(String label, Serializer s, Parser p, int flags) throws Exception { + super(label, s, p, flags); + } + + @SuppressWarnings("serial") + @Test + public void classObjects() throws Exception { + Object o = String.class; + o = roundTrip(o); + assertTrue(o == String.class); + + o = new Class[]{String.class}; + o = roundTrip(o); + assertObjectEquals("['java.lang.String']", o); + + o = new LinkedList<Class<?>>(){{add(String.class);add(Integer.class);}}; + o = roundTrip(o); + assertObjectEquals("['java.lang.String','java.lang.Integer']", o); + + o = new LinkedHashMap<Class<?>,Class<?>>(){{put(String.class,String.class);}}; + o = roundTrip(o); + assertObjectEquals("{'java.lang.String':'java.lang.String'}", o); + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e4dfdf81/juneau-core-test/src/test/java/org/apache/juneau/a/rttests/RoundTripDTOsTest.java ---------------------------------------------------------------------- diff --git a/juneau-core-test/src/test/java/org/apache/juneau/a/rttests/RoundTripDTOsTest.java b/juneau-core-test/src/test/java/org/apache/juneau/a/rttests/RoundTripDTOsTest.java new file mode 100755 index 0000000..2c41ce2 --- /dev/null +++ b/juneau-core-test/src/test/java/org/apache/juneau/a/rttests/RoundTripDTOsTest.java @@ -0,0 +1,49 @@ +// *************************************************************************************************************************** +// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file * +// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file * +// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance * +// * with the License. You may obtain a copy of the License at * +// * * +// * http://www.apache.org/licenses/LICENSE-2.0 * +// * * +// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an * +// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * +// * specific language governing permissions and limitations under the License. * +// *************************************************************************************************************************** +package org.apache.juneau.a.rttests; + +import static org.apache.juneau.TestUtils.*; + +import org.apache.juneau.dto.jsonschema.*; +import org.apache.juneau.parser.*; +import org.apache.juneau.serializer.*; +import org.junit.*; + +/** + * Tests designed to serialize and parse objects to make sure we end up + * with the same objects for all serializers and parsers. + */ +@SuppressWarnings({"javadoc"}) +public class RoundTripDTOsTest extends RoundTripTest { + + public RoundTripDTOsTest(String label, Serializer s, Parser p, int flags) throws Exception { + super(label, s, p, flags); + } + + //==================================================================================================== + // org.apache.juneau.test.dto.jsonschema + //==================================================================================================== + @Test + public void testJsonSchema1() throws Exception { + Schema s = JsonSchemaTest.getTest1(); + Schema s2 = roundTrip(s, Schema.class); + assertEqualObjects(s, s2); + } + + @Test + public void testJsonSchema2() throws Exception { + Schema s = JsonSchemaTest.getTest2(); + Schema s2 = roundTrip(s, Schema.class); + assertEqualObjects(s, s2); + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e4dfdf81/juneau-core-test/src/test/java/org/apache/juneau/a/rttests/RoundTripEnumTest.java ---------------------------------------------------------------------- diff --git a/juneau-core-test/src/test/java/org/apache/juneau/a/rttests/RoundTripEnumTest.java b/juneau-core-test/src/test/java/org/apache/juneau/a/rttests/RoundTripEnumTest.java new file mode 100755 index 0000000..972a786 --- /dev/null +++ b/juneau-core-test/src/test/java/org/apache/juneau/a/rttests/RoundTripEnumTest.java @@ -0,0 +1,246 @@ +// *************************************************************************************************************************** +// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file * +// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file * +// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance * +// * with the License. You may obtain a copy of the License at * +// * * +// * http://www.apache.org/licenses/LICENSE-2.0 * +// * * +// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an * +// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * +// * specific language governing permissions and limitations under the License. * +// *************************************************************************************************************************** +package org.apache.juneau.a.rttests; + +import static org.apache.juneau.TestUtils.*; +import static org.junit.Assert.*; + +import java.util.*; + +import org.apache.juneau.json.*; +import org.apache.juneau.parser.*; +import org.apache.juneau.serializer.*; +import org.junit.*; + +/** + * Tests designed to serialize and parse objects to make sure we end up + * with the same objects for all serializers and parsers. + */ +@SuppressWarnings({"serial","javadoc"}) +public class RoundTripEnumTest extends RoundTripTest { + + public RoundTripEnumTest(String label, Serializer s, Parser p, int flags) throws Exception { + super(label, s, p, flags); + } + + //==================================================================================================== + // Enum object + //==================================================================================================== + @Test + public void testEnumA() throws Exception { + AEnum t = AEnum.FOO; + assertObjectEquals("'FOO'", t); + t = roundTrip(t, AEnum.class); + assertEquals(AEnum.FOO, t); + } + + @Test + public void testEnumB() throws Exception { + WriterSerializer s = new JsonSerializer.Simple().addBeanFilters(getBeanFilters()).addPojoSwaps(getPojoSwaps()); + BEnum t = BEnum.FOO; + assertEquals("'xfoo'", s.serialize(t)); + t = roundTrip(t, BEnum.class); + assertEquals(BEnum.FOO, t); + } + + //==================================================================================================== + // Enum[] object + //==================================================================================================== + @Test + public void testEnumArrayA() throws Exception { + AEnum[] t = {AEnum.FOO,AEnum.BAR,null}; + assertObjectEquals("['FOO','BAR',null]", t); + t = roundTrip(t, AEnum[].class); + assertEquals(AEnum.FOO, t[0]); + assertEquals(AEnum.BAR, t[1]); + assertNull(t[2]); + } + + @Test + public void testEnumArrayB() throws Exception { + BEnum[] t = {BEnum.FOO,BEnum.BAR,null}; + assertObjectEquals("['xfoo','xbar',null]", t); + t = roundTrip(t, BEnum[].class); + assertEquals(BEnum.FOO, t[0]); + assertEquals(BEnum.BAR, t[1]); + assertNull(t[2]); + } + + //==================================================================================================== + // Enum[][] object + //==================================================================================================== + @Test + public void testEnum2dArrayA() throws Exception { + AEnum[][] t = {{AEnum.FOO,AEnum.BAR,null},null}; + assertObjectEquals("[['FOO','BAR',null],null]", t); + t = roundTrip(t, AEnum[][].class); + assertEquals(AEnum.FOO, t[0][0]); + assertEquals(AEnum.BAR, t[0][1]); + assertNull(t[0][2]); + assertNull(t[1]); + } + + @Test + public void testEnum2dArrayB() throws Exception { + BEnum[][] t = {{BEnum.FOO,BEnum.BAR,null},null}; + assertObjectEquals("[['xfoo','xbar',null],null]", t); + t = roundTrip(t, BEnum[][].class); + assertEquals(BEnum.FOO, t[0][0]); + assertEquals(BEnum.BAR, t[0][1]); + assertNull(t[0][2]); + assertNull(t[1]); + } + + //==================================================================================================== + // Bean with Enum fields + //==================================================================================================== + @Test + public void testBeansWithEnumA() throws Exception { + A t1 = new A().init(), t2; + t2 = roundTrip(t1, A.class); + assertEqualObjects(t1, t2); + assertEquals(AEnum.FOO, t2.f3[0]); + assertNull(t2.f3[1]); + assertEquals(AEnum.FOO, t2.f4[0][0]); + assertNull(t2.f4[0][1]); + assertNull(t2.f4[1]); + } + + @Test + public void testBeansWithEnumB() throws Exception { + B t1 = new B().init(), t2; + t2 = roundTrip(t1, B.class); + assertEqualObjects(t1, t2); + assertEquals(BEnum.FOO, t2.f3[0]); + assertNull(t2.f3[1]); + assertEquals(BEnum.FOO, t2.f4[0][0]); + assertNull(t2.f4[0][1]); + assertNull(t2.f4[1]); + } + + + /** Normal Enum */ + public enum AEnum { + FOO,BAR,BAZ + } + + /** Enum with custom serialized values */ + public enum BEnum { + FOO("xfoo"), BAR("xbar"), BAZ("xbaz"); + + private String val; + + private BEnum(String val) { + this.val = val; + } + + @Override /* Object */ + public String toString() { + return val; + } + + public static BEnum fromString(String val) { + if (val.equals("xfoo")) + return FOO; + if (val.equals("xbar")) + return BAR; + if (val.equals("xbaz")) + return BAZ; + return null; + } + } + + public static class A { + + // Should have 'enum' attribute. + public AEnum f1; + + private AEnum f2; + public AEnum getF2() {return f2;} + public void setF2(AEnum f2) {this.f2 = f2;} + + public AEnum[] f3; + public AEnum[][] f4; + + // Should not have 'uniqueSet' attribute. + public List<AEnum> f5 = new LinkedList<AEnum>(); + + private List<AEnum> f6 = new LinkedList<AEnum>(); + public List<AEnum> getF6() {return f6;} + public void setF6(List<AEnum> f6) {this.f6 = f6;} + + // Should have 'uniqueSet' attribute. + public Set<AEnum> f7 = new HashSet<AEnum>(); + + private Set<AEnum> f8 = new HashSet<AEnum>(); + public Set<AEnum> getF8() {return f8;} + public void setF8(Set<AEnum> f8) {this.f8 = f8;} + + public Map<AEnum,AEnum> f9 = new LinkedHashMap<AEnum,AEnum>(); + + public A init() { + f1 = AEnum.FOO; + f2 = AEnum.BAR; + f3 = new AEnum[]{AEnum.FOO,null}; + f4 = new AEnum[][]{{AEnum.FOO,null},null}; + f5 = new ArrayList<AEnum>(){{add(AEnum.FOO);}}; + f6 = new ArrayList<AEnum>(){{add(AEnum.FOO);}}; + f7 = new HashSet<AEnum>(){{add(AEnum.FOO);}}; + f8 = new HashSet<AEnum>(){{add(AEnum.FOO);}}; + + return this; + } + } + + public static class B { + + // Should have 'enum' attribute. + public BEnum f1; + + private BEnum f2; + public BEnum getF2() {return f2;} + public void setF2(BEnum f2) {this.f2 = f2;} + + public BEnum[] f3; + public BEnum[][] f4; + + // Should not have 'uniqueSet' attribute. + public List<BEnum> f5 = new LinkedList<BEnum>(); + + private List<BEnum> f6 = new LinkedList<BEnum>(); + public List<BEnum> getF6() {return f6;} + public void setF6(List<BEnum> f6) {this.f6 = f6;} + + // Should have 'uniqueSet' attribute. + public Set<BEnum> f7 = new HashSet<BEnum>(); + + private Set<BEnum> f8 = new HashSet<BEnum>(); + public Set<BEnum> getF8() {return f8;} + public void setF8(Set<BEnum> f8) {this.f8 = f8;} + + public Map<BEnum,BEnum> f9 = new LinkedHashMap<BEnum,BEnum>(); + + public B init() { + f1 = BEnum.FOO; + f2 = BEnum.BAR; + f3 = new BEnum[]{BEnum.FOO,null}; + f4 = new BEnum[][]{{BEnum.FOO,null},null}; + f5 = new ArrayList<BEnum>(){{add(BEnum.FOO);}}; + f6 = new ArrayList<BEnum>(){{add(BEnum.FOO);}}; + f7 = new HashSet<BEnum>(){{add(BEnum.FOO);}}; + f8 = new HashSet<BEnum>(){{add(BEnum.FOO);}}; + + return this; + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e4dfdf81/juneau-core-test/src/test/java/org/apache/juneau/a/rttests/RoundTripGenericsTest.java ---------------------------------------------------------------------- diff --git a/juneau-core-test/src/test/java/org/apache/juneau/a/rttests/RoundTripGenericsTest.java b/juneau-core-test/src/test/java/org/apache/juneau/a/rttests/RoundTripGenericsTest.java new file mode 100755 index 0000000..9177596 --- /dev/null +++ b/juneau-core-test/src/test/java/org/apache/juneau/a/rttests/RoundTripGenericsTest.java @@ -0,0 +1,98 @@ +// *************************************************************************************************************************** +// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file * +// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file * +// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance * +// * with the License. You may obtain a copy of the License at * +// * * +// * http://www.apache.org/licenses/LICENSE-2.0 * +// * * +// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an * +// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * +// * specific language governing permissions and limitations under the License. * +// *************************************************************************************************************************** +package org.apache.juneau.a.rttests; + +import static org.apache.juneau.TestUtils.*; +import static org.junit.Assert.*; + +import org.apache.juneau.annotation.*; +import org.apache.juneau.parser.*; +import org.apache.juneau.serializer.*; +import org.junit.*; + +/** + * Tests designed to serialize and parse objects to make sure we end up + * with the same objects for all serializers and parsers. + */ +@SuppressWarnings("javadoc") +public class RoundTripGenericsTest extends RoundTripTest { + + public RoundTripGenericsTest(String label, Serializer s, Parser p, int flags) throws Exception { + super(label, s, p, flags); + } + + //==================================================================================================== + // testBeansWithUnboundTypeVars + //==================================================================================================== + @SuppressWarnings("rawtypes") + @Test + public void testBeansWithUnboundTypeVars() throws Exception { + + if (returnOriginalObject) + return; + + // Unbound type variables should be interpreted as Object. + // During parsing, these become ObjectMaps. + Pair pair = new Pair<Source,Target>(new Source().init(), new Target().init()); + pair = roundTrip(pair); + assertSortedObjectEquals("{s:{s1:'a1'},t:{t1:'b1'}}", pair); + assertEquals("ObjectMap", pair.getS().getClass().getSimpleName()); + assertEquals("ObjectMap", pair.getT().getClass().getSimpleName()); + + // If you specify a concrete class, the type variables become bound and + // the property types correctly resolve. + pair = roundTrip(pair, RealPair.class); + assertSortedObjectEquals("{s:{s1:'a1'},t:{t1:'b1'}}", pair); + assertEquals("Source", pair.getS().getClass().getSimpleName()); + assertEquals("Target", pair.getT().getClass().getSimpleName()); + } + + // Class with unbound type variables. + @Bean(properties="s,t") + public static class Pair<S,T> { + private S s; + private T t; + + public Pair() {} + + public Pair(S s, T t) { + this.s = s; + this.t = t; + } + + // Getters/setters + public S getS() { return s; } + public void setS(S s) { this.s = s; } + public T getT() { return t; } + public void setT(T t) { this.t = t; } + } + + // Sublcass with bound type variables. + public static class RealPair extends Pair<Source,Target> {} + + public static class Source { + public String s1; + public Source init() { + this.s1 = "a1"; + return this; + } + } + + public static class Target { + public String t1; + public Target init() { + this.t1 = "b1"; + return this; + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e4dfdf81/juneau-core-test/src/test/java/org/apache/juneau/a/rttests/RoundTripLargeObjectsTest.java ---------------------------------------------------------------------- diff --git a/juneau-core-test/src/test/java/org/apache/juneau/a/rttests/RoundTripLargeObjectsTest.java b/juneau-core-test/src/test/java/org/apache/juneau/a/rttests/RoundTripLargeObjectsTest.java new file mode 100755 index 0000000..47d56ed --- /dev/null +++ b/juneau-core-test/src/test/java/org/apache/juneau/a/rttests/RoundTripLargeObjectsTest.java @@ -0,0 +1,193 @@ +// *************************************************************************************************************************** +// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file * +// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file * +// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance * +// * with the License. You may obtain a copy of the License at * +// * * +// * http://www.apache.org/licenses/LICENSE-2.0 * +// * * +// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an * +// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * +// * specific language governing permissions and limitations under the License. * +// *************************************************************************************************************************** +package org.apache.juneau.a.rttests; + +import static org.apache.juneau.a.rttests.RoundTripTest.Flags.*; +import static org.apache.juneau.serializer.SerializerContext.*; +import static org.apache.juneau.urlencoding.UonSerializerContext.*; +import static org.apache.juneau.xml.XmlSerializerContext.*; + +import java.text.*; +import java.util.*; + +import org.apache.juneau.html.*; +import org.apache.juneau.json.*; +import org.apache.juneau.msgpack.*; +import org.apache.juneau.parser.*; +import org.apache.juneau.serializer.*; +import org.apache.juneau.urlencoding.*; +import org.apache.juneau.xml.*; +import org.junit.*; +import org.junit.runners.*; + +/** + * Tests designed to serialize and parse objects to make sure we end up + * with the same objects for all serializers and parsers. + */ +@Ignore +@SuppressWarnings({"serial","javadoc"}) +public class RoundTripLargeObjectsTest extends RoundTripTest { + + private static final int NUM_RUNS = 10; + private static final int SIZE_PARAM = 20000; + + public RoundTripLargeObjectsTest(String label, Serializer s, Parser p, int flags) throws Exception { + super(label, s, p, flags); + } + + @Parameterized.Parameters + public static Collection<Object[]> getPairs() { + return Arrays.asList(new Object[][] { + // Full round-trip testing + { /* 0 */ + "Json DEFAULT", + new JsonSerializer().setProperty(SERIALIZER_trimNullProperties, false), + JsonParser.DEFAULT, + 0 + }, + { /* 1 */ + "Json DEFAULT_LAX", + new JsonSerializer.Simple().setProperty(SERIALIZER_trimNullProperties, false), + JsonParser.DEFAULT, + 0 + }, + { /* 2 */ + "Json DEFAULT_SQ", + new JsonSerializer.Simple().setProperty(SERIALIZER_trimNullProperties, false), + JsonParser.DEFAULT, + 0 + }, + { /* 3 */ + "Xml DEFAULT w/namespaces,validation", + new XmlSerializer.NsSq().setProperty(SERIALIZER_trimNullProperties, false).setProperty(XML_addNamespaceUrisToRoot, true).setProperty(SERIALIZER_useIndentation, true), + XmlParser.DEFAULT, + CHECK_XML_WHITESPACE | VALIDATE_XML + }, + { /* 4 */ + "Xml DEFAULT wo/namespaces,validation", + new XmlSerializer.Sq().setProperty(SERIALIZER_trimNullProperties, false), + XmlParser.DEFAULT, + CHECK_XML_WHITESPACE + }, + { /* 5 */ + "Html", + new HtmlSerializer().setProperty(SERIALIZER_trimNullProperties, false), + HtmlParser.DEFAULT, + CHECK_XML_WHITESPACE + }, + { /* 6 */ + "UrlEncoding", + new UrlEncodingSerializer().setProperty(SERIALIZER_trimNullProperties, false).setProperty(UON_simpleMode, false), + UrlEncodingParser.DEFAULT, + 0 + }, + { /* 7 */ + "Uon", + new UonSerializer().setProperty(SERIALIZER_trimNullProperties, false).setProperty(UON_simpleMode, false), + UonParser.DEFAULT, + 0 + }, + { /* 8 */ + "MsgPack", + new MsgPackSerializer().setProperty(SERIALIZER_trimNullProperties, false).setProperty(UON_simpleMode, false), + MsgPackParser.DEFAULT, + 0 + }, +// { /* 9 */ +// "Rdf.Xml", +// new RdfSerializer.Xml().setProperty(SERIALIZER_trimNullProperties, false).setProperty(RDF_addLiteralTypes, true), +// RdfParser.DEFAULT_XML, +// 0 +// }, +// { /* 10 */ +// "Rdf.XmlAbbrev", +// new RdfSerializer.XmlAbbrev().setProperty(SERIALIZER_trimNullProperties, false).setProperty(RDF_addLiteralTypes, true), +// RdfParser.DEFAULT_XML, +// 0 +// }, +// { /* 11 */ +// "Rdf.Turtle", +// new RdfSerializer.Turtle().setProperty(SERIALIZER_trimNullProperties, false).setProperty(RDF_addLiteralTypes, true), +// RdfParser.DEFAULT_TURTLE, +// 0 +// }, +// { /* 12 */ +// "Rdf.NTriple", +// new RdfSerializer.NTriple().setProperty(SERIALIZER_trimNullProperties, false).setProperty(RDF_addLiteralTypes, true), +// RdfParser.DEFAULT_NTRIPLE, +// 0 +// }, +// { /* 13 */ +// "Rdf.N3", +// new RdfSerializer.N3().setProperty(SERIALIZER_trimNullProperties, false).setProperty(RDF_addLiteralTypes, true), +// RdfParser.DEFAULT_N3, +// 0 +// }, + }); + } + + //==================================================================================================== + // test + //==================================================================================================== + @Test + public void testLargeMap() throws Exception { + long startTime; + int numRuns = NUM_RUNS; + + A a = A.create(); + Serializer s = getSerializer(); + Parser p = getParser(); + System.err.println("\n---Speed test on " + label + "---"); + Object r = ""; + + // Initialization run. + r = s.serialize(a); + System.err.println(MessageFormat.format("Serialized size: {0,number} ", (r instanceof String ? r.toString().length() : ((byte[])r).length))); + p.parse(r, A.class); + + startTime = System.currentTimeMillis(); + for (int i = 0; i < numRuns; i++) + r = s.serialize(a); + System.err.println(MessageFormat.format("Average serialize time: {0,number}ms", (System.currentTimeMillis()-startTime)/numRuns)); + startTime = System.currentTimeMillis(); + for (int i = 0; i < numRuns; i++) + a = p.parse(r, A.class); + System.err.println(MessageFormat.format("Average parsed time: {0,number}ms", (System.currentTimeMillis()-startTime)/numRuns)); + } + + public static class A { + public A1Map a1Map; + public A1List a1List; + public A1[] a1Array; + + static A create() { + A a = new A(); + a.a1Map = new A1Map(); + a.a1List = new A1List(); + for (int i = 0; i < SIZE_PARAM; i++) { + a.a1Map.put(String.valueOf(i), new A1()); + a.a1List.add(new A1()); + } + a.a1Array = a.a1List.toArray(new A1[0]); + return a; + } + } + + public static class A1 { + public String f1 = "a123456789b123456789c123456789d123456789e123456789f123456789g123456789h123456789i123456789j123456789"; + } + + public static class A1Map extends LinkedHashMap<String,A1> {} + + public static class A1List extends LinkedList<A1> {} +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e4dfdf81/juneau-core-test/src/test/java/org/apache/juneau/a/rttests/RoundTripMapsTest.java ---------------------------------------------------------------------- diff --git a/juneau-core-test/src/test/java/org/apache/juneau/a/rttests/RoundTripMapsTest.java b/juneau-core-test/src/test/java/org/apache/juneau/a/rttests/RoundTripMapsTest.java new file mode 100755 index 0000000..abd4c72 --- /dev/null +++ b/juneau-core-test/src/test/java/org/apache/juneau/a/rttests/RoundTripMapsTest.java @@ -0,0 +1,214 @@ +// *************************************************************************************************************************** +// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file * +// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file * +// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance * +// * with the License. You may obtain a copy of the License at * +// * * +// * http://www.apache.org/licenses/LICENSE-2.0 * +// * * +// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an * +// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * +// * specific language governing permissions and limitations under the License. * +// *************************************************************************************************************************** +package org.apache.juneau.a.rttests; + +import static org.apache.juneau.serializer.SerializerContext.*; +import static org.apache.juneau.html.HtmlSerializerContext.*; +import static org.junit.Assert.*; + +import java.util.*; + +import org.apache.juneau.html.*; +import org.apache.juneau.json.*; +import org.apache.juneau.parser.*; +import org.apache.juneau.serializer.*; +import org.apache.juneau.transforms.*; +import org.apache.juneau.urlencoding.*; +import org.apache.juneau.xml.*; +import org.junit.*; + +/** + * Tests designed to serialize and parse objects to make sure we end up + * with the same objects for all serializers and parsers. + */ +@SuppressWarnings({"unchecked","deprecation","javadoc"}) +public class RoundTripMapsTest extends RoundTripTest { + + public RoundTripMapsTest(String label, Serializer s, Parser p, int flags) throws Exception { + super(label, s, p, flags); + } + + @Override /* RoundTripTest */ + public Class<?>[] getPojoSwaps() { + return new Class<?>[]{ + ByteArrayBase64Swap.class, + DateSwap.ISO8601DTZ.class, + CalendarLongSwap.class, + }; + } + + //==================================================================================================== + // Map<Integer,String> test + //==================================================================================================== + @Test + public void testMapIntegerString() throws Exception { + Map<Integer,String> t = new TreeMap<Integer,String>(); + t.put(1, "a"); + t.put(2, null); + t = roundTripMap(t, TreeMap.class, Integer.class, String.class); + assertEquals("a", t.get(1)); + assertNull(null, t.get(2)); + + t = new HashMap<Integer,String>(); + t.put(1, "a"); + t.put(2, null); + t.put(null, "b"); + t = roundTripMap(t, HashMap.class, Integer.class, String.class); + assertEquals("a", t.get(1)); + assertNull(t.get(2)); + assertEquals("b", t.get(null)); + } + + //==================================================================================================== + // Map<Boolean,String> test + //==================================================================================================== + @Test + public void testMapBooleanString() throws Exception { + Map<Boolean,String> t = new TreeMap<Boolean,String>(); + t.put(true, "a"); + t.put(false, null); + t = roundTripMap(t, TreeMap.class, Boolean.class, String.class); + assertEquals("a", t.get(true)); + assertNull(null, t.get(false)); + + t = new HashMap<Boolean,String>(); + t.put(true, "a"); + t.put(false, null); + t.put(null, "b"); + t = roundTripMap(t, HashMap.class, Boolean.class, String.class); + assertEquals("a", t.get(true)); + assertNull(t.get(false)); + assertEquals("b", t.get(null)); + } + + //==================================================================================================== + // Map<byte[],String> test + //==================================================================================================== + @Test + public void testMapByteArrayString() throws Exception { + + // Note, you cannot really test maps with byte[] keys since byte[] does not test for equality. + // So just test serialization. + String e; + Object r; + + Map<byte[],String> t = new LinkedHashMap<byte[],String>(); + t.put(new byte[]{1,2,3}, "a"); + t.put(new byte[]{4,5,6}, null); + t.put(null, "b"); + + s = new JsonSerializer.Simple().addPojoSwaps(getPojoSwaps()).setProperty(SERIALIZER_trimNullProperties, false); + e = "{AQID:'a',BAUG:null,null:'b'}"; + r = s.serialize(t); + assertEquals(e, r); + + s = new XmlSerializer.NsSq().addPojoSwaps(getPojoSwaps()).setProperty(SERIALIZER_trimNullProperties, false); + e = "<object><AQID>a</AQID><BAUG _type='null'/><_x0000_>b</_x0000_></object>"; + r = s.serialize(t); + assertEquals(e, r); + + s = new HtmlSerializer.Sq().addPojoSwaps(getPojoSwaps()).setProperty(SERIALIZER_trimNullProperties, false).setProperty(HTML_addKeyValueTableHeaders, true); + e = "<table><tr><th>key</th><th>value</th></tr><tr><td>AQID</td><td>a</td></tr><tr><td>BAUG</td><td><null/></td></tr><tr><td><null/></td><td>b</td></tr></table>"; + r = s.serialize(t); + assertEquals(e, r); + + s = new UonSerializer.Encoding().addPojoSwaps(getPojoSwaps()).setProperty(SERIALIZER_trimNullProperties, false); + e = "$o(AQID=a,BAUG=%00,%00=b)"; + r = s.serialize(t); + assertEquals(e, r); + + s = new UrlEncodingSerializer().addPojoSwaps(getPojoSwaps()).setProperty(SERIALIZER_trimNullProperties, false); + e = "AQID=a&BAUG=%00&%00=b"; + r = s.serialize(t); + assertEquals(e, r); + } + + //==================================================================================================== + // Map<Date,String> test + //==================================================================================================== + @Test + public void testMapDateString() throws Exception { + Date td1 = new Date(1,2,3,4,5,6); + Date td2 = new Date(2,3,4,5,6,7); + + Map<Date,String> t = new TreeMap<Date,String>(); + t.put(td1, "a"); + t.put(td2, null); + t = roundTripMap(t, TreeMap.class, Date.class, String.class); + assertEquals("a", t.get(td1)); + assertNull(null, t.get(td2)); + + t = new HashMap<Date,String>(); + t.put(td1, "a"); + t.put(td2, null); + t.put(null, "b"); + t = roundTripMap(t, HashMap.class, Date.class, String.class); + assertEquals("a", t.get(td1)); + assertNull(t.get(td2)); + assertEquals("b", t.get(null)); + } + + //==================================================================================================== + // Map<Calendar,String> test + //==================================================================================================== + @Test + public void testMapCalendarString() throws Exception { + Calendar td1 = new GregorianCalendar(); + td1.setTime(new Date(1,2,3,4,5,6)); + Calendar td2 = new GregorianCalendar(); + td2.setTime(new Date(2,3,4,5,6,7)); + + Map<Calendar,String> t = new TreeMap<Calendar,String>(); + t.put(td1, "a"); + t.put(td2, null); + t = roundTripMap(t, TreeMap.class, GregorianCalendar.class, String.class); + assertEquals("a", t.get(td1)); + assertNull(null, t.get(td2)); + + t = new HashMap<Calendar,String>(); + t.put(td1, "a"); + t.put(td2, null); + t.put(null, "b"); + t = roundTripMap(t, HashMap.class, GregorianCalendar.class, String.class); + assertEquals("a", t.get(td1)); + assertNull(t.get(td2)); + assertEquals("b", t.get(null)); + } + + //==================================================================================================== + // Map<Enum,String> test + //==================================================================================================== + @Test + public void testMapEnumString() throws Exception { + + Map<TestEnum,String> t = new TreeMap<TestEnum,String>(); + t.put(TestEnum.FOO, "a"); + t.put(TestEnum.BAR, null); + t = roundTripMap(t, TreeMap.class, TestEnum.class, String.class); + assertEquals("a", t.get(TestEnum.FOO)); + assertNull(null, t.get(TestEnum.BAR)); + + t = new HashMap<TestEnum,String>(); + t.put(TestEnum.FOO, "a"); + t.put(TestEnum.BAR, null); + t.put(null, "b"); + t = roundTripMap(t, HashMap.class, TestEnum.class, String.class); + assertEquals("a", t.get(TestEnum.FOO)); + assertNull(t.get(TestEnum.BAR)); + assertEquals("b", t.get(null)); + } + + public enum TestEnum { + FOO,BAR,BAZ + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e4dfdf81/juneau-core-test/src/test/java/org/apache/juneau/a/rttests/RoundTripNumericConstructorsTest.java ---------------------------------------------------------------------- diff --git a/juneau-core-test/src/test/java/org/apache/juneau/a/rttests/RoundTripNumericConstructorsTest.java b/juneau-core-test/src/test/java/org/apache/juneau/a/rttests/RoundTripNumericConstructorsTest.java new file mode 100644 index 0000000..e398451 --- /dev/null +++ b/juneau-core-test/src/test/java/org/apache/juneau/a/rttests/RoundTripNumericConstructorsTest.java @@ -0,0 +1,49 @@ +// *************************************************************************************************************************** +// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file * +// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file * +// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance * +// * with the License. You may obtain a copy of the License at * +// * * +// * http://www.apache.org/licenses/LICENSE-2.0 * +// * * +// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an * +// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * +// * specific language governing permissions and limitations under the License. * +// *************************************************************************************************************************** +package org.apache.juneau.a.rttests; + +import static org.junit.Assert.*; + +import java.util.*; +import org.apache.juneau.parser.*; +import org.apache.juneau.serializer.*; +import org.junit.*; + +/** + * Tests designed to serialize and parse objects to make sure we end up + * with the same objects for all serializers and parsers. + */ +@SuppressWarnings({"javadoc","deprecation"}) +public class RoundTripNumericConstructorsTest extends RoundTripTest { + + public RoundTripNumericConstructorsTest(String label, Serializer s, Parser p, int flags) throws Exception { + super(label, s, p, flags); + } + + //==================================================================================================== + // Test parsing numbers to dates. + //==================================================================================================== + @Test + public void testParseNumberToDate() throws Exception { + if (isValidationOnly()) + return; + + Serializer s = getSerializer(); + Parser p = getParser(); + Date d = new Date(100, 1, 1); + + Object r = s.serialize(d.getTime()); + Date d2 = p.parse(r, Date.class); + assertEquals(d.getTime(), d2.getTime()); + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e4dfdf81/juneau-core-test/src/test/java/org/apache/juneau/a/rttests/RoundTripObjectsAsStringsTest.java ---------------------------------------------------------------------- diff --git a/juneau-core-test/src/test/java/org/apache/juneau/a/rttests/RoundTripObjectsAsStringsTest.java b/juneau-core-test/src/test/java/org/apache/juneau/a/rttests/RoundTripObjectsAsStringsTest.java new file mode 100755 index 0000000..80d0bde --- /dev/null +++ b/juneau-core-test/src/test/java/org/apache/juneau/a/rttests/RoundTripObjectsAsStringsTest.java @@ -0,0 +1,272 @@ +// *************************************************************************************************************************** +// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file * +// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file * +// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance * +// * with the License. You may obtain a copy of the License at * +// * * +// * http://www.apache.org/licenses/LICENSE-2.0 * +// * * +// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an * +// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * +// * specific language governing permissions and limitations under the License. * +// *************************************************************************************************************************** +package org.apache.juneau.a.rttests; + +import static org.apache.juneau.TestUtils.*; +import static org.junit.Assert.*; + +import org.apache.juneau.*; +import org.apache.juneau.parser.*; +import org.apache.juneau.serializer.*; +import org.junit.*; + +/** + * Tests to ensure the valueOf(String), fromString(String), parse(String), and parseString(String) methods + * are used correctly by parsers. + */ +@SuppressWarnings({"unused","javadoc"}) +public class RoundTripObjectsAsStringsTest extends RoundTripTest { + + public RoundTripObjectsAsStringsTest(String label, Serializer s, Parser p, int flags) throws Exception { + super(label, s, p, flags); + } + + //==================================================================================================== + // testBasic + //==================================================================================================== + @Test + public void testBasic() throws Exception { + A t = new A().init(); + t = roundTrip(t); + assertObjectEquals("{a1:{f:'1'},a2:{f:'2'},a3:{f:'3'},a4:{f:'4'}}", t); + } + + public static class A { + public A1 a1; + public A2 a2; + public A3 a3; + public A4 a4; + + public A init() { + a1 = new A1(); + a1.f = "1"; + a2 = new A2(); + a2.f = "2"; + a3 = new A3(); + a3.f = "3"; + a4 = new A4(); + a4.f = "4"; + return this; + } + } + + public static class A1 { + public String f; + public static A1 fromString(String s) { + A1 x = new A1(); + x.f = s.substring(3); + return x; + } + @Override /* Object */ + public String toString() { + return "A1-" + f; + } + } + + public static class A2 { + public String f; + public static A2 valueOf(String s) { + A2 x = new A2(); + x.f = s.substring(3); + return x; + } + @Override /* Object */ + public String toString() { + return "A2-" + f; + } + } + + public static class A3 { + public String f; + public static A3 parse(String s) { + A3 x = new A3(); + x.f = s.substring(3); + return x; + } + @Override /* Object */ + public String toString() { + return "A3-" + f; + } + } + + public static class A4 { + public String f; + public static A4 parseString(String s) { + A4 x = new A4(); + x.f = s.substring(3); + return x; + } + @Override /* Object */ + public String toString() { + return "A4-" + f; + } + } + + //==================================================================================================== + // testEnumWithOverriddenStringValue + // The B1 enum should serialize as "X1" but the B2 enum should serialize as "X-1". + //==================================================================================================== + @Test + public void testEnumWithOverriddenStringValue() throws Exception { + B t = new B().init(); + if (! returnOriginalObject) { + Object r = getSerializer().serialize(t); + assertTrue(TestUtils.toString(r).contains("X-2")); + } + t = roundTrip(t); + assertObjectEquals("{b1:'X1',b2:'X-2'}", t); + } + + public static class B { + public B1 b1; + public B2 b2; + + public B init() { + b1 = B1.X1; + b2 = B2.X2; + return this; + } + + } + + public static enum B1 { + X1(1), + X2(2), + X3(3); + + private int i; + B1(int i) { + this.i = i; + } + } + + public static enum B2 { + X1(1), + X2(2), + X3(3); + + private int i; + B2(int i) { + this.i = i; + } + + @Override /* Object */ + public String toString() { + return "X-" + i; + } + + public static B2 fromString(String s) { + return valueOf("X" + s.substring(2)); + } + } + + //==================================================================================================== + // testMethodOrdering + //==================================================================================================== + @Test + public void testOrdering() throws Exception { + C t = new C().init(); + t = roundTrip(t); + assertObjectEquals("{c1:{f:'1'},c2:{f:'2'},c3:{f:'3'},c4:{f:'4'}}", t); + } + + public static class C { + public C1 c1; + public C2 c2; + public C3 c3; + public C4 c4; + + public C init() { + c1 = new C1(); + c1.f = "1"; + c2 = new C2(); + c2.f = "2"; + c3 = new C3(); + c3.f = "3"; + c4 = new C4(); + c4.f = "4"; + return this; + } + } + + public static class C1 { + public String f; + public static C2 valueOf(String s) { + throw new RuntimeException("Shouldn't be called!"); + } + public static C2 parse(String s) { + throw new RuntimeException("Shouldn't be called!"); + } + public static C2 parseString(String s) { + throw new RuntimeException("Shouldn't be called!"); + } + public static C1 fromString(String s) { + C1 x = new C1(); + x.f = s.substring(3); + return x; + } + + @Override /* Object */ + public String toString() { + return "C1-" + f; + } + } + + public static class C2 { + public String f; + public static C2 parse(String s) { + throw new RuntimeException("Shouldn't be called!"); + } + public static C2 parseString(String s) { + throw new RuntimeException("Shouldn't be called!"); + } + public static C2 valueOf(String s) { + C2 x = new C2(); + x.f = s.substring(3); + return x; + } + @Override /* Object */ + public String toString() { + return "C2-" + f; + } + } + + public static class C3 { + public String f; + public static C2 parseString(String s) { + throw new RuntimeException("Shouldn't be called!"); + } + public static C3 parse(String s) { + C3 x = new C3(); + x.f = s.substring(3); + return x; + } + @Override /* Object */ + public String toString() { + return "C3-" + f; + } + } + + public static class C4 { + public String f; + public static C4 parseString(String s) { + C4 x = new C4(); + x.f = s.substring(3); + return x; + } + @Override /* Object */ + public String toString() { + return "C4" + f; + } + } +}