/* * Copyright (C) 2015 Red Hat, Inc. and/or its affiliates. * * Licensed 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.jboss.errai.codegen.test; import static org.junit.Assert.assertTrue; import java.util.Arrays; import org.jboss.errai.codegen.Context; import org.jboss.errai.codegen.SnapshotMaker; import org.jboss.errai.codegen.Statement; import org.jboss.errai.codegen.builder.BlockBuilder; import org.jboss.errai.codegen.builder.ClassStructureBuilder; import org.jboss.errai.codegen.builder.impl.ClassBuilder; import org.jboss.errai.codegen.exception.CyclicalObjectGraphException; import org.jboss.errai.codegen.meta.MetaMethod; import org.jboss.errai.codegen.test.AbstractCodegenTest; import org.jboss.errai.codegen.test.model.Person; import org.jboss.errai.codegen.test.model.PersonImpl; import org.jboss.errai.codegen.test.model.SnapshotInterfaceWithCollections; import org.jboss.errai.codegen.test.model.SnapshotInterfaceWithCollectionsImpl; import org.jboss.errai.codegen.util.Stmt; import org.junit.Assert; import org.junit.Test; // Note: extends from AbstractCodeGenTest to inherit overridden assertEquals(String, String) methods which are whitespace // insensitive. public class SnapshotMakerTest extends AbstractCodegenTest { @Test public void testGenerateSnapshotOfMethod() throws Exception { Person mother = new PersonImpl("mom", 30, null); Person child = new PersonImpl("kid", 5, mother); Statement snapshotStmt = SnapshotMaker.makeSnapshotAsSubclass(child, Person.class, Person.class, null, Person.class); final String generated = snapshotStmt.generate(Context.create()); final String expectedValue = "new org.jboss.errai.codegen.test.model.Person() {\n" + " public int getAge() {\n" + " return 5;\n" + " }\n" + " public org.jboss.errai.codegen.test.model.Person getMother() {\n" + " return new org.jboss.errai.codegen.test.model.Person() {\n" + " public int getAge() {\n" + " return 30;\n" + " }\n" + " public org.jboss.errai.codegen.test.model.Person getMother() {\n" + " return null;\n" + " }\n" + " public String getName() {\n" + " return \"mom\";\n" + " }\n" + " };\n" + " }\n" + " public String getName() {\n" + " return \"kid\";\n" + " }\n" + "}"; assertEquals(expectedValue, generated); } @Test public void testNoStackOverflowOnObjectCycle() { PersonImpl cycle1 = new PersonImpl("cycle1", 30, null); Person cycle2 = new PersonImpl("cycle2", 5, cycle1); cycle1.setMother(cycle2); try { Statement snapshotStmt = SnapshotMaker.makeSnapshotAsSubclass(cycle2, Person.class, Person.class, null, Person.class); snapshotStmt.generate(Context.create()); Assert.fail("Instance cycle was not detected"); } catch (CyclicalObjectGraphException e) { assertTrue(e.getObjectsInvolvedInCycle().contains(cycle1)); assertTrue(e.getObjectsInvolvedInCycle().contains(cycle2)); } } /** * This test confirms the SnapshotMaker is called automatically by the code generator's literal reification logic * for interfaces and classes explicitly market "literalizable" in the context. * * @throws Exception */ @Test public void testCollectionsLiteralizableInSnapshots() throws Exception { Person jonathan = new PersonImpl("Jonathan F.", 20, new PersonImpl("Jonathans's Parent", 50, null)); Person christian = new PersonImpl("Christian S.", 20, new PersonImpl("Christians's Parent", 50, null)); Person mike = new PersonImpl("Mike B.", 0, new PersonImpl("Mike's Parent", 50, null)); SnapshotInterfaceWithCollections personCollection = new SnapshotInterfaceWithCollectionsImpl(Arrays.asList(jonathan, christian, mike)); // create a manual context final Context ctx = Context.create(); // tell the code generator that classes that implement these interfaces are literalizable ctx.addLiteralizableClass(SnapshotInterfaceWithCollections.class); ctx.addLiteralizableClass(Person.class); // initialize a new builder with this context and try and build 'snapshot' as a code snapshot final String generated = Stmt.create(ctx).load(personCollection).toJavaString(); final String expectedValue = "new org.jboss.errai.codegen.test.model.SnapshotInterfaceWithCollections() {\n" + " public java.util.List getPersons() {\n" + " return new java.util.ArrayList() {\n" + " {\n" + " add(new org.jboss.errai.codegen.test.model.Person() {\n" + " public int getAge() {\n" + " return 20;\n" + " }\n" + " public org.jboss.errai.codegen.test.model.Person getMother() {\n" + " return new org.jboss.errai.codegen.test.model.Person() {\n" + " public int getAge() {\n" + " return 50;\n" + " }\n" + " public org.jboss.errai.codegen.test.model.Person getMother() {\n" + " return null;\n" + " }\n" + " public String getName() {\n" + " return \"Jonathans's Parent\";\n" + " }\n" + " };\n" + " }\n" + " public String getName() {\n" + " return \"Jonathan F.\";\n" + " }\n" + " });\n" + " add(new org.jboss.errai.codegen.test.model.Person() {\n" + " public int getAge() {\n" + " return 20;\n" + " }\n" + " public org.jboss.errai.codegen.test.model.Person getMother() {\n" + " return new org.jboss.errai.codegen.test.model.Person() {\n" + " public int getAge() {\n" + " return 50;\n" + " }\n" + " public org.jboss.errai.codegen.test.model.Person getMother() {\n" + " return null;\n" + " }\n" + " public String getName() {\n" + " return \"Christians's Parent\";\n" + " }\n" + " };\n" + " }\n" + " public String getName() {\n" + " return \"Christian S.\";\n" + " }\n" + " });\n" + " add(new org.jboss.errai.codegen.test.model.Person() {\n" + " public int getAge() {\n" + " return 0;\n" + " }\n" + " public org.jboss.errai.codegen.test.model.Person getMother() {\n" + " return new org.jboss.errai.codegen.test.model.Person() {\n" + " public int getAge() {\n" + " return 50;\n" + " }\n" + " public org.jboss.errai.codegen.test.model.Person getMother() {\n" + " return null;\n" + " }\n" + " public String getName() {\n" + " return \"Mike's Parent\";\n" + " }\n" + " };\n" + " }\n" + " public String getName() {\n" + " return \"Mike B.\";\n" + " }\n" + " });\n" + " }\n" + " };\n" + " }\n" + "}\n"; assertEquals(expectedValue, generated); } @Test public void testMethodBodyCallback() { Person mom = new PersonImpl("mom", 30, null); Person kid1 = new PersonImpl("Kid 1", 3, mom); Person kid2 = new PersonImpl("Kid 2", 4, mom); Person kid3 = new PersonImpl("Kid 3", 5, mom); ClassStructureBuilder<?> peopleClassBuilder = ClassBuilder.define("com.foo.People").publicScope().body(); BlockBuilder<?> method = peopleClassBuilder.publicMethod(void.class, "makePeople").body(); Statement momVar = Stmt .declareVariable(Person.class) .asFinal() .named("mom") .initializeWith(SnapshotMaker.makeSnapshotAsSubclass(mom, Person.class, Person.class, null, Person.class)); method.append(momVar); SnapshotMaker.MethodBodyCallback mbcb = new SnapshotMaker.MethodBodyCallback() { @Override public Statement generateMethodBody(MetaMethod method, Object o, ClassStructureBuilder<?> containingClass) { if (method.getName().equals("getMother")) { return Stmt.loadVariable("mom").returnValue(); } return null; } }; method.append(Stmt.declareVariable("kid1", SnapshotMaker.makeSnapshotAsSubclass(kid1, Person.class, Person.class, mbcb, Person.class))); method.append(Stmt.declareVariable("kid2", SnapshotMaker.makeSnapshotAsSubclass(kid2, Person.class, Person.class, mbcb, Person.class))); method.append(Stmt.declareVariable("kid3", SnapshotMaker.makeSnapshotAsSubclass(kid3, Person.class, Person.class, mbcb, Person.class))); method.finish(); final String generated = peopleClassBuilder.toJavaString(); String expected = "package com.foo;\n" + "\n" + "import org.jboss.errai.codegen.test.model.Person;\n" + "\n" + "public class People {\n" + " public void makePeople() {\n" + " final Person mom = new Person() {\n" + " public int getAge() {\n" + " return 30;\n" + " }\n" + " public Person getMother() {\n" + " return null;\n" + " }\n" + " public String getName() {\n" + " return \"mom\";\n" + " }\n" + " };\n" + " Person kid1 = new Person() {\n" + " public int getAge() {\n" + " return 3;\n" + " }\n" + " public Person getMother() {\n" + " return mom;\n" + " }\n" + " public String getName() {\n" + " return \"Kid 1\";\n" + " }\n" + " };\n" + " Person kid2 = new Person() {\n" + " public int getAge() {\n" + " return 4;\n" + " }\n" + " public Person getMother() {\n" + " return mom;\n" + " }\n" + " public String getName() {\n" + " return \"Kid 2\";\n" + " }\n" + " };\n" + " Person kid3 = new Person() {\n" + " public int getAge() {\n" + " return 5;\n" + " }\n" + " public Person getMother() {\n" + " return mom;\n" + " }\n" + " public String getName() {\n" + " return \"Kid 3\";\n" + " }\n" + " };\n" + " }\n" + "}\n"; assertEquals(expected, generated); } }