diff --git a/cdlang/build.gradle b/cdlang/build.gradle index 1f117f0c6..986959e93 100644 --- a/cdlang/build.gradle +++ b/cdlang/build.gradle @@ -225,8 +225,13 @@ sourceSets { cdGenIntTest { // Include target/cdGenOutTest/** as src dirs (which are generated by the CDGenTests) java.srcDirs(() -> project.layout.buildDirectory.dir("cdGenOutTest").get().asFile.listFiles()) + // Include src/cdGenIntTestHwc/java as a src dir (to test the TOP mechanism) + java.srcDirs(file('src/cdGenIntTestHwc/java')) } } +tasks.named('generateCdGenIntTestMCGrammars') { + dependsOn tasks.named('test') +} dependencies { cdGenIntTestImplementation "org.junit.jupiter:junit-jupiter:$junit_version" cdGenIntTestRuntimeOnly 'org.junit.platform:junit-platform-launcher' diff --git a/cdlang/src/cdGenIntTest/java/builder/BuilderDecoratorResultTest.java b/cdlang/src/cdGenIntTest/java/builder/BuilderDecoratorResultTest.java new file mode 100644 index 000000000..307456354 --- /dev/null +++ b/cdlang/src/cdGenIntTest/java/builder/BuilderDecoratorResultTest.java @@ -0,0 +1,678 @@ +/* (c) https://github.com/MontiCore/monticore */ +package builder; + +import TestBuilder.*; +import de.se_rwth.commons.logging.Log; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.math.BigInteger; +import java.util.Set; + +/** + * Test the result of the Builder Decorator. + */ +public class BuilderDecoratorResultTest { + + @Test + public void test() throws Exception { + checkClassAndMethodExistence(); + + //we need to disable the fail quick mode, otherwise the test will be skipped + // Afterward we will test for error messages + Log.enableFailQuick(false); + + testBuild(); + testUnsafeBuild(); + testConstructorModificationsAndCreations(); + testInheritanceBuilder(); + Log.clearFindings(); + } + + @Test + public void testBuild() { + //we need to disable the fail quick mode, otherwise the test will be skipped + // Afterward we will test for error messages + Log.enableFailQuick(false); + + Set manyBTest = Set.of(new B(), new B()); + B optBTest = new B(); + B oneBTest = new B(); + Level2class level2class = new Level2class(); + level2class.myInt = 1; + TestEnum testEnum = TestEnum.ERROR; + + //build with all parameters set + TestBuilderWithoutSetter objWithoutPojoSetters = new TestBuilderWithoutSetterBuilder().setManyB( + manyBTest).setOneB(oneBTest).setOptB(optBTest).setMyBool(true).setMyInt(1).setMyLevel1( + level2class).setMyTestEnum(testEnum).build(); + Assertions.assertSame(objWithoutPojoSetters.getManyB(), manyBTest); + Assertions.assertSame(objWithoutPojoSetters.getOneB(), oneBTest); + Assertions.assertSame(objWithoutPojoSetters.getOptB(), optBTest); + Assertions.assertTrue(objWithoutPojoSetters.isMyBool()); + Assertions.assertSame(objWithoutPojoSetters.getMyLevel1(), level2class); + Assertions.assertSame(objWithoutPojoSetters.getMyTestEnum(), testEnum); + Assertions.assertEquals(1, objWithoutPojoSetters.getMyInt()); + + TestBuilderWithSetter objWithPojoSetters = new TestBuilderWithSetterBuilder().setManyB( + manyBTest).setOneB(oneBTest).setOptB(optBTest).setMyBool(true).setMyInt(1).setMyLevel1( + level2class).setMyTestEnum(testEnum).build(); + Assertions.assertSame(objWithPojoSetters.getManyB(), manyBTest); + Assertions.assertSame(objWithPojoSetters.getOneB(), oneBTest); + Assertions.assertSame(objWithPojoSetters.getOptB(), optBTest); + Assertions.assertTrue(objWithPojoSetters.isMyBool()); + Assertions.assertSame(objWithPojoSetters.getMyLevel1(), level2class); + Assertions.assertSame(objWithPojoSetters.getMyTestEnum(), testEnum); + Assertions.assertEquals(1, objWithPojoSetters.getMyInt()); + + //build with ManyB set to null -> an empty list should be created + TestBuilderWithSetter objWithPojoSettersManyBNull = new TestBuilderWithSetterBuilder().setManyB( + null).setOneB(oneBTest).setOptB(optBTest).setMyBool(false).setMyInt(1).setMyLevel1( + level2class).setMyTestEnum(testEnum).build(); + Assertions.assertEquals(0, objWithPojoSettersManyBNull.getManyB().size()); + Assertions.assertSame(objWithPojoSettersManyBNull.getOneB(), oneBTest); + Assertions.assertSame(objWithPojoSettersManyBNull.getOptB(), optBTest); + Assertions.assertFalse(objWithPojoSettersManyBNull.isMyBool()); + Assertions.assertEquals(1, objWithPojoSettersManyBNull.getMyInt()); + + TestBuilderWithoutSetter objWithoutPojoSettersManyBNull = new TestBuilderWithoutSetterBuilder() + .setManyB(null).setOneB(oneBTest).setOptB(optBTest).setMyBool(false).setMyInt(1) + .setMyLevel1(level2class).setMyTestEnum(testEnum).build(); + Assertions.assertEquals(0, objWithoutPojoSettersManyBNull.getManyB().size()); + Assertions.assertSame(objWithoutPojoSettersManyBNull.getOneB(), oneBTest); + Assertions.assertSame(objWithoutPojoSettersManyBNull.getOptB(), optBTest); + Assertions.assertFalse(objWithoutPojoSettersManyBNull.isMyBool()); + Assertions.assertEquals(1, objWithoutPojoSettersManyBNull.getMyInt()); + + //build with Opt set to null -> an error will occur + TestBuilderWithSetter objWithPojoSettersOptNull = new TestBuilderWithSetterBuilder().setManyB( + manyBTest).setOneB(oneBTest).setOptB(null).setMyBool(false).setMyInt(1).setMyLevel1( + level2class).setMyTestEnum(testEnum).build(); + Log.clearFindings(); + Assertions.assertSame(objWithPojoSettersOptNull.getManyB(), manyBTest); + Assertions.assertSame(objWithPojoSettersOptNull.getOneB(), oneBTest); + Assertions.assertThrows(IllegalStateException.class, objWithPojoSettersOptNull::getOptB); + Assertions.assertEquals(1, Log.getFindings().size()); + Assertions.assertTrue(Log.getFindings().get(0).getMsg().contains( + "get for OptB can't return a value. Attribute is empty.")); + Log.clearFindings(); + Assertions.assertEquals(1, objWithPojoSettersOptNull.getMyInt()); + Assertions.assertFalse(objWithPojoSettersOptNull.isMyBool()); + + TestBuilderWithoutSetter objWithoutPojoSettersOptNull = new TestBuilderWithoutSetterBuilder() + .setManyB(manyBTest).setOneB(oneBTest).setOptB(null).setMyBool(false).setMyInt(1) + .setMyLevel1(level2class).setMyTestEnum(testEnum).build(); + Log.clearFindings(); + Assertions.assertSame(objWithoutPojoSettersOptNull.getManyB(), manyBTest); + Assertions.assertSame(objWithoutPojoSettersOptNull.getOneB(), oneBTest); + Assertions.assertThrows(IllegalStateException.class, objWithoutPojoSettersOptNull::getOptB); + Assertions.assertEquals(1, Log.getFindings().size()); + Assertions.assertTrue(Log.getFindings().get(0).getMsg().contains( + "get for OptB can't return a value. Attribute is empty.")); + Log.clearFindings(); + Assertions.assertEquals(1, objWithoutPojoSettersOptNull.getMyInt()); + Assertions.assertFalse(objWithoutPojoSettersOptNull.isMyBool()); + + //build with OneB set to null -> the build should not work + Assertions.assertThrows(IllegalStateException.class, () -> new TestBuilderWithSetterBuilder() + .setManyB(manyBTest).setOneB(null).setOptB(optBTest).setMyBool(false).setMyInt(1) + .setMyLevel1(level2class).setMyTestEnum(testEnum).build()); + Assertions.assertSame(1, Log.getFindings().size()); + Assertions.assertTrue(Log.getFindings().get(0).getMsg().contains( + "oneB of type TestBuilder.B must not be null")); + Log.clearFindings(); + + Assertions.assertThrows(IllegalStateException.class, () -> new TestBuilderWithoutSetterBuilder() + .setManyB(manyBTest).setOneB(null).setOptB(optBTest).setMyBool(false).setMyInt(1) + .setMyLevel1(level2class).setMyTestEnum(testEnum).build()); + Assertions.assertSame(1, Log.getFindings().size()); + Assertions.assertTrue(Log.getFindings().get(0).getMsg().contains( + "oneB of type TestBuilder.B must not be null")); + Log.clearFindings(); + + //build with manyB not set -> an empty list should be created + TestBuilderWithSetter objWithPojoSettersManyBNotSet = new TestBuilderWithSetterBuilder() + //.setManyB(manyBTest) + .setOneB(oneBTest).setOptB(optBTest).setMyBool(false).setMyInt(1).setMyLevel1(level2class) + .setMyTestEnum(testEnum).build(); + Assertions.assertEquals(0, objWithPojoSettersManyBNotSet.getManyB().size()); + + TestBuilderWithoutSetter objWithoutPojoSettersManyBNotSet = + new TestBuilderWithoutSetterBuilder() + //.setManyB(manyBTest) + .setOneB(oneBTest).setOptB(optBTest).setMyBool(false).setMyInt(1).setMyLevel1( + level2class).setMyTestEnum(testEnum).build(); + Assertions.assertEquals(0, objWithoutPojoSettersManyBNotSet.getManyB().size()); + + //build with oneB not set -> an error will occur + Assertions.assertThrows(IllegalStateException.class, () -> new TestBuilderWithSetterBuilder() + .setManyB(manyBTest) + //.setOneB(oneBTest) + .setOptB(optBTest).setMyBool(false).setMyInt(1).setMyLevel1(level2class).setMyTestEnum( + testEnum).build()); + Assertions.assertSame(1, Log.getFindings().size()); + Assertions.assertTrue(Log.getFindings().get(0).getMsg().contains( + "oneB of type TestBuilder.B must not be null")); + Log.clearFindings(); + + Assertions.assertThrows(IllegalStateException.class, () -> new TestBuilderWithoutSetterBuilder() + .setManyB(manyBTest) + //.setOneB(oneBTest) + .setOptB(optBTest).setMyBool(false).setMyInt(1).setMyLevel1(level2class).setMyTestEnum( + testEnum).build()); + Assertions.assertSame(1, Log.getFindings().size()); + Assertions.assertTrue(Log.getFindings().get(0).getMsg().contains( + "oneB of type TestBuilder.B must not be null")); + Log.clearFindings(); + + //build with optB not set + TestBuilderWithSetter objWithPojoSettersOptBNotSet = new TestBuilderWithSetterBuilder() + .setManyB(manyBTest).setOneB(oneBTest) + //.setOptB(optBTest) + .setMyBool(false).setMyInt(1).setMyLevel1(level2class).setMyTestEnum(testEnum).build(); + Assertions.assertSame(objWithPojoSettersOptBNotSet.getManyB(), manyBTest); + Assertions.assertSame(objWithPojoSettersOptBNotSet.getOneB(), oneBTest); + Assertions.assertThrows(IllegalStateException.class, objWithPojoSettersOptBNotSet::getOptB); + Assertions.assertEquals(1, Log.getFindings().size()); + Assertions.assertTrue(Log.getFindings().get(0).getMsg().contains( + "get for OptB can't return a value. Attribute is empty.")); + Log.clearFindings(); + Assertions.assertFalse(objWithPojoSettersOptBNotSet.isMyBool()); + Assertions.assertEquals(1, objWithPojoSettersOptBNotSet.getMyInt()); + + TestBuilderWithoutSetter objWithoutPojoSettersOptBNotSet = new TestBuilderWithoutSetterBuilder() + .setManyB(manyBTest).setOneB(oneBTest) + //.setOptB(optBTest) + .setMyBool(false).setMyInt(1).setMyLevel1(level2class).setMyTestEnum(testEnum).build(); + Assertions.assertSame(objWithoutPojoSettersOptBNotSet.getManyB(), manyBTest); + Assertions.assertSame(objWithoutPojoSettersOptBNotSet.getOneB(), oneBTest); + Assertions.assertThrows(IllegalStateException.class, objWithoutPojoSettersOptBNotSet::getOptB); + Assertions.assertEquals(1, Log.getFindings().size()); + Assertions.assertTrue(Log.getFindings().get(0).getMsg().contains( + "get for OptB can't return a value. Attribute is empty.")); + Log.clearFindings(); + Assertions.assertFalse(objWithoutPojoSettersOptBNotSet.isMyBool()); + Assertions.assertEquals(1, objWithoutPojoSettersOptBNotSet.getMyInt()); + + //build with interface not set + Assertions.assertThrows(IllegalStateException.class, () -> new TestBuilderWithSetterBuilder() + .setManyB(manyBTest).setOneB(oneBTest).setOptB(optBTest).setMyBool(false).setMyInt(1) + //.setMyLevel1(level2class) + .setMyTestEnum(testEnum).build()); + Assertions.assertSame(1, Log.getFindings().size()); + Assertions.assertTrue(Log.getFindings().get(0).getMsg().contains( + "myLevel1 of type Level1Interface must not be null")); + Log.clearFindings(); + + Assertions.assertThrows(IllegalStateException.class, () -> new TestBuilderWithoutSetterBuilder() + .setManyB(manyBTest).setOneB(oneBTest).setOptB(optBTest).setMyBool(false).setMyInt(1) + //.setMyLevel1(level2class) + .setMyTestEnum(testEnum).build()); + Assertions.assertSame(1, Log.getFindings().size()); + Assertions.assertTrue(Log.getFindings().get(0).getMsg().contains( + "myLevel1 of type Level1Interface must not be null")); + Log.clearFindings(); + + //build with enum not set + Assertions.assertThrows(IllegalStateException.class, () -> new TestBuilderWithoutSetterBuilder() + .setManyB(manyBTest).setOneB(oneBTest).setOptB(optBTest).setMyBool(false).setMyInt(1) + .setMyLevel1(level2class) + //.setMyTestEnum(testEnum) + .build()); + Assertions.assertSame(1, Log.getFindings().size()); + Assertions.assertTrue(Log.getFindings().get(0).getMsg().contains( + "myTestEnum of type TestEnum must not be null")); + Log.clearFindings(); + + Assertions.assertThrows(IllegalStateException.class, () -> new TestBuilderWithoutSetterBuilder() + .setManyB(manyBTest).setOneB(oneBTest).setOptB(optBTest).setMyBool(false).setMyInt(1) + .setMyLevel1(level2class) + //.setMyTestEnum(testEnum) + .build()); + Assertions.assertSame(1, Log.getFindings().size()); + Assertions.assertTrue(Log.getFindings().get(0).getMsg().contains( + "myTestEnum of type TestEnum must not be null")); + Log.clearFindings(); + + } + + @Test + public void testUnsafeBuild() { + //we need to disable the fail quick mode, otherwise the test will be skipped + // Afterward we will test for error messages + Log.enableFailQuick(false); + + Set manyBTest = Set.of(new B(), new B()); + B optBTest = new B(); + B oneBTest = new B(); + Level2class level2class = new Level2class(); + level2class.myInt = 1; + TestEnum testEnum = TestEnum.ERROR; + + //unsafeBuild with all parameters set + TestBuilderWithoutSetter unsafeBuildObjWithoutPojoSetters = + new TestBuilderWithoutSetterBuilder().setManyB(manyBTest).setOneB(oneBTest).setOptB( + optBTest).setMyBool(true).setMyInt(1).setMyLevel1(level2class).setMyTestEnum(testEnum) + .unsafeBuild(); + Assertions.assertSame(unsafeBuildObjWithoutPojoSetters.getManyB(), manyBTest); + Assertions.assertSame(unsafeBuildObjWithoutPojoSetters.getOneB(), oneBTest); + Assertions.assertSame(unsafeBuildObjWithoutPojoSetters.getOptB(), optBTest); + Assertions.assertTrue(unsafeBuildObjWithoutPojoSetters.isMyBool()); + Assertions.assertEquals(1, unsafeBuildObjWithoutPojoSetters.getMyInt()); + Assertions.assertSame(unsafeBuildObjWithoutPojoSetters.getMyLevel1(), level2class); + Assertions.assertSame(unsafeBuildObjWithoutPojoSetters.getMyTestEnum(), testEnum); + + TestBuilderWithSetter unsafeBuildObjWithPojoSetters = new TestBuilderWithSetterBuilder() + .setManyB(manyBTest).setOneB(oneBTest).setOptB(optBTest).setMyBool(true).setMyInt(1) + .setMyLevel1(level2class).setMyTestEnum(testEnum).unsafeBuild(); + Assertions.assertSame(unsafeBuildObjWithPojoSetters.getManyB(), manyBTest); + Assertions.assertSame(unsafeBuildObjWithPojoSetters.getOneB(), oneBTest); + Assertions.assertSame(unsafeBuildObjWithPojoSetters.getOptB(), optBTest); + Assertions.assertTrue(unsafeBuildObjWithPojoSetters.isMyBool()); + Assertions.assertEquals(1, unsafeBuildObjWithPojoSetters.getMyInt()); + Assertions.assertSame(unsafeBuildObjWithPojoSetters.getMyLevel1(), level2class); + Assertions.assertSame(unsafeBuildObjWithPojoSetters.getMyTestEnum(), testEnum); + + //unsafeBuild with ManyB set to null -> the list is set to an empty set + TestBuilderWithSetter unsafeBuildObjWithPojoSettersManyBNull = + new TestBuilderWithSetterBuilder().setManyB(null).setOneB(oneBTest).setOptB(optBTest) + .setMyBool(false).setMyInt(1).setMyLevel1(level2class).setMyTestEnum(testEnum) + .unsafeBuild(); + Assertions.assertEquals(0, unsafeBuildObjWithPojoSettersManyBNull.getManyB().size()); + Assertions.assertSame(unsafeBuildObjWithPojoSettersManyBNull.getOneB(), oneBTest); + Assertions.assertSame(unsafeBuildObjWithPojoSettersManyBNull.getOptB(), optBTest); + Assertions.assertFalse(unsafeBuildObjWithPojoSettersManyBNull.isMyBool()); + Assertions.assertEquals(1, unsafeBuildObjWithPojoSettersManyBNull.getMyInt()); + Assertions.assertSame(unsafeBuildObjWithPojoSettersManyBNull.getMyLevel1(), level2class); + Assertions.assertSame(unsafeBuildObjWithPojoSettersManyBNull.getMyTestEnum(), testEnum); + + TestBuilderWithoutSetter unsafeBuildObjWithoutPojoSettersManyBNull = + new TestBuilderWithoutSetterBuilder().setManyB(null).setOneB(oneBTest).setOptB(optBTest) + .setMyBool(false).setMyInt(1).setMyLevel1(level2class).setMyTestEnum(testEnum) + .unsafeBuild(); + Assertions.assertEquals(0, unsafeBuildObjWithoutPojoSettersManyBNull.getManyB().size()); + Assertions.assertSame(unsafeBuildObjWithoutPojoSettersManyBNull.getOneB(), oneBTest); + Assertions.assertSame(unsafeBuildObjWithoutPojoSettersManyBNull.getOptB(), optBTest); + Assertions.assertFalse(unsafeBuildObjWithoutPojoSettersManyBNull.isMyBool()); + Assertions.assertEquals(1, unsafeBuildObjWithoutPojoSettersManyBNull.getMyInt()); + Assertions.assertSame(unsafeBuildObjWithoutPojoSettersManyBNull.getMyLevel1(), level2class); + Assertions.assertSame(unsafeBuildObjWithoutPojoSettersManyBNull.getMyTestEnum(), testEnum); + + //unsafeBuild with Opt set to null -> an error will occur + TestBuilderWithSetter unsafeBuildObjWithPojoSettersOptNull = new TestBuilderWithSetterBuilder() + .setManyB(manyBTest).setOneB(oneBTest).setOptB(null).setMyBool(false).setMyInt(1) + .setMyLevel1(level2class).setMyTestEnum(testEnum).unsafeBuild(); + Log.clearFindings(); + Assertions.assertSame(unsafeBuildObjWithPojoSettersOptNull.getManyB(), manyBTest); + Assertions.assertSame(unsafeBuildObjWithPojoSettersOptNull.getOneB(), oneBTest); + Assertions.assertThrows(IllegalStateException.class, + unsafeBuildObjWithPojoSettersOptNull::getOptB); + Assertions.assertEquals(1, Log.getFindings().size()); + Assertions.assertTrue(Log.getFindings().get(0).getMsg().contains( + "get for OptB can't return a value. Attribute is empty.")); + Log.clearFindings(); + Assertions.assertEquals(1, unsafeBuildObjWithPojoSettersOptNull.getMyInt()); + Assertions.assertFalse(unsafeBuildObjWithPojoSettersOptNull.isMyBool()); + Assertions.assertSame(unsafeBuildObjWithPojoSettersOptNull.getMyLevel1(), level2class); + Assertions.assertSame(unsafeBuildObjWithPojoSettersOptNull.getMyTestEnum(), testEnum); + + TestBuilderWithoutSetter unsafeBuildObjWithoutPojoSettersOptNull = + new TestBuilderWithoutSetterBuilder().setManyB(manyBTest).setOneB(oneBTest).setOptB(null) + .setMyBool(false).setMyInt(1).setMyLevel1(level2class).setMyTestEnum(testEnum) + .unsafeBuild(); + Log.clearFindings(); + Assertions.assertSame(unsafeBuildObjWithoutPojoSettersOptNull.getManyB(), manyBTest); + Assertions.assertSame(unsafeBuildObjWithoutPojoSettersOptNull.getOneB(), oneBTest); + Assertions.assertThrows(IllegalStateException.class, + unsafeBuildObjWithoutPojoSettersOptNull::getOptB); + Assertions.assertEquals(1, Log.getFindings().size()); + Assertions.assertTrue(Log.getFindings().get(0).getMsg().contains( + "get for OptB can't return a value. Attribute is empty.")); + Log.clearFindings(); + Assertions.assertEquals(1, unsafeBuildObjWithoutPojoSettersOptNull.getMyInt()); + Assertions.assertFalse(unsafeBuildObjWithoutPojoSettersOptNull.isMyBool()); + Assertions.assertSame(unsafeBuildObjWithoutPojoSettersOptNull.getMyLevel1(), level2class); + Assertions.assertSame(unsafeBuildObjWithoutPojoSettersOptNull.getMyTestEnum(), testEnum); + + //unsafeBuild with OneB set to null -> set it to null + TestBuilderWithSetter unsafeBuildObjWithPojoSetterOneBNull = new TestBuilderWithSetterBuilder() + .setManyB(manyBTest).setOneB(null).setOptB(optBTest).setMyBool(false).setMyInt(1) + .setMyLevel1(level2class).setMyTestEnum(testEnum).unsafeBuild(); + Assertions.assertNull(unsafeBuildObjWithPojoSetterOneBNull.getOneB()); + Assertions.assertSame(unsafeBuildObjWithPojoSetterOneBNull.getManyB(), manyBTest); + Assertions.assertSame(unsafeBuildObjWithPojoSetterOneBNull.getOptB(), optBTest); + Assertions.assertFalse(unsafeBuildObjWithPojoSetterOneBNull.isMyBool()); + Assertions.assertEquals(1, unsafeBuildObjWithPojoSetterOneBNull.getMyInt()); + Assertions.assertSame(unsafeBuildObjWithPojoSetterOneBNull.getMyLevel1(), level2class); + Assertions.assertSame(unsafeBuildObjWithPojoSetterOneBNull.getMyTestEnum(), testEnum); + + TestBuilderWithoutSetter unsafeBuildObjWithoutPojoSetterOneBNull = + new TestBuilderWithoutSetterBuilder().setManyB(manyBTest).setOneB(null).setOptB(optBTest) + .setMyBool(false).setMyInt(1).setMyLevel1(level2class).setMyTestEnum(testEnum) + .unsafeBuild(); + Assertions.assertNull(unsafeBuildObjWithoutPojoSetterOneBNull.getOneB()); + Assertions.assertSame(unsafeBuildObjWithoutPojoSetterOneBNull.getManyB(), manyBTest); + Assertions.assertSame(unsafeBuildObjWithoutPojoSetterOneBNull.getOptB(), optBTest); + Assertions.assertFalse(unsafeBuildObjWithoutPojoSetterOneBNull.isMyBool()); + Assertions.assertEquals(1, unsafeBuildObjWithoutPojoSetterOneBNull.getMyInt()); + Assertions.assertSame(unsafeBuildObjWithoutPojoSetterOneBNull.getMyLevel1(), level2class); + Assertions.assertSame(unsafeBuildObjWithoutPojoSetterOneBNull.getMyTestEnum(), testEnum); + + //unsafeBuild with manyB not set -> set to an empty set + TestBuilderWithSetter unsafeBuildObjWithPojoSettersManyBNotSet = + new TestBuilderWithSetterBuilder() + //.setManyB(manyBTest) + .setOneB(oneBTest).setOptB(optBTest).setMyBool(false).setMyInt(1).setMyLevel1( + level2class).setMyTestEnum(testEnum).unsafeBuild(); + Assertions.assertEquals(0, unsafeBuildObjWithPojoSettersManyBNotSet.getManyB().size()); + Assertions.assertSame(unsafeBuildObjWithPojoSettersManyBNotSet.getOneB(), oneBTest); + Assertions.assertSame(unsafeBuildObjWithPojoSettersManyBNotSet.getOptB(), optBTest); + Assertions.assertFalse(unsafeBuildObjWithPojoSettersManyBNotSet.isMyBool()); + Assertions.assertEquals(1, unsafeBuildObjWithPojoSettersManyBNotSet.getMyInt()); + Assertions.assertSame(unsafeBuildObjWithPojoSettersManyBNotSet.getMyLevel1(), level2class); + Assertions.assertSame(unsafeBuildObjWithPojoSettersManyBNotSet.getMyTestEnum(), testEnum); + + TestBuilderWithoutSetter unsafeBuildObjWithoutPojoSettersManyBNotSet = + new TestBuilderWithoutSetterBuilder() + //.setManyB(manyBTest) + .setOneB(oneBTest).setOptB(optBTest).setMyBool(false).setMyInt(1).setMyLevel1( + level2class).setMyTestEnum(testEnum).unsafeBuild(); + Assertions.assertEquals(0, unsafeBuildObjWithoutPojoSettersManyBNotSet.getManyB().size()); + Assertions.assertSame(unsafeBuildObjWithoutPojoSettersManyBNotSet.getOneB(), oneBTest); + Assertions.assertSame(unsafeBuildObjWithoutPojoSettersManyBNotSet.getOptB(), optBTest); + Assertions.assertFalse(unsafeBuildObjWithoutPojoSettersManyBNotSet.isMyBool()); + Assertions.assertEquals(1, unsafeBuildObjWithoutPojoSettersManyBNotSet.getMyInt()); + Assertions.assertSame(unsafeBuildObjWithoutPojoSettersManyBNotSet.getMyLevel1(), level2class); + Assertions.assertSame(unsafeBuildObjWithoutPojoSettersManyBNotSet.getMyTestEnum(), testEnum); + + //unsafeBuild with oneB not set -> set to an empty set + TestBuilderWithSetter unsafeBuildObjWithPojoSettersOneBNotSet = + new TestBuilderWithSetterBuilder().setManyB(manyBTest) + //.setOneB(oneBTest) + .setOptB(optBTest).setMyBool(false).setMyInt(1).setMyLevel1(level2class).setMyTestEnum( + testEnum).unsafeBuild(); + Assertions.assertNull(unsafeBuildObjWithPojoSettersOneBNotSet.getOneB()); + Assertions.assertSame(unsafeBuildObjWithPojoSettersOneBNotSet.getManyB(), manyBTest); + Assertions.assertSame(unsafeBuildObjWithPojoSettersOneBNotSet.getOptB(), optBTest); + Assertions.assertFalse(unsafeBuildObjWithPojoSettersOneBNotSet.isMyBool()); + Assertions.assertEquals(1, unsafeBuildObjWithPojoSettersOneBNotSet.getMyInt()); + Assertions.assertSame(unsafeBuildObjWithPojoSettersOneBNotSet.getMyLevel1(), level2class); + Assertions.assertSame(unsafeBuildObjWithPojoSettersOneBNotSet.getMyTestEnum(), testEnum); + + TestBuilderWithoutSetter unsafeBuildObjWithoutPojoSettersOneBNotSet = + new TestBuilderWithoutSetterBuilder().setManyB(manyBTest) + //.setOneB(oneBTest) + .setOptB(optBTest).setMyBool(false).setMyInt(1).setMyLevel1(level2class).setMyTestEnum( + testEnum).unsafeBuild(); + Assertions.assertNull(unsafeBuildObjWithoutPojoSettersOneBNotSet.getOneB()); + Assertions.assertSame(unsafeBuildObjWithoutPojoSettersOneBNotSet.getManyB(), manyBTest); + Assertions.assertSame(unsafeBuildObjWithoutPojoSettersOneBNotSet.getOptB(), optBTest); + Assertions.assertFalse(unsafeBuildObjWithoutPojoSettersOneBNotSet.isMyBool()); + Assertions.assertEquals(1, unsafeBuildObjWithoutPojoSettersOneBNotSet.getMyInt()); + Assertions.assertSame(unsafeBuildObjWithoutPojoSettersOneBNotSet.getMyLevel1(), level2class); + Assertions.assertSame(unsafeBuildObjWithoutPojoSettersOneBNotSet.getMyTestEnum(), testEnum); + + //unsafeBuild with optB not set + TestBuilderWithSetter unsafeBuildObjWithPojoSettersOptBNotSet = + new TestBuilderWithSetterBuilder().setManyB(manyBTest).setOneB(oneBTest) + //.setOptB(optBTest) + .setMyBool(false).setMyInt(1).setMyLevel1(level2class).setMyTestEnum(testEnum) + .unsafeBuild(); + Assertions.assertSame(unsafeBuildObjWithPojoSettersOptBNotSet.getManyB(), manyBTest); + Assertions.assertSame(unsafeBuildObjWithPojoSettersOptBNotSet.getOneB(), oneBTest); + Assertions.assertThrows(IllegalStateException.class, + unsafeBuildObjWithPojoSettersOptBNotSet::getOptB); + Assertions.assertEquals(1, Log.getFindings().size()); + Assertions.assertTrue(Log.getFindings().get(0).getMsg().contains( + "get for OptB can't return a value. Attribute is empty.")); + Log.clearFindings(); + Assertions.assertFalse(unsafeBuildObjWithPojoSettersOptBNotSet.isMyBool()); + Assertions.assertEquals(1, unsafeBuildObjWithPojoSettersOptBNotSet.getMyInt()); + Assertions.assertSame(unsafeBuildObjWithPojoSettersOptBNotSet.getMyLevel1(), level2class); + Assertions.assertSame(unsafeBuildObjWithPojoSettersOptBNotSet.getMyTestEnum(), testEnum); + + TestBuilderWithoutSetter unsafeBuildObjWithoutPojoSettersOptBNotSet = + new TestBuilderWithoutSetterBuilder().setManyB(manyBTest).setOneB(oneBTest) + //.setOptB(optBTest) + .setMyBool(false).setMyInt(1).setMyLevel1(level2class).setMyTestEnum(testEnum) + .unsafeBuild(); + Assertions.assertSame(unsafeBuildObjWithoutPojoSettersOptBNotSet.getManyB(), manyBTest); + Assertions.assertSame(unsafeBuildObjWithoutPojoSettersOptBNotSet.getOneB(), oneBTest); + Assertions.assertThrows(IllegalStateException.class, + unsafeBuildObjWithoutPojoSettersOptBNotSet::getOptB); + Assertions.assertEquals(1, Log.getFindings().size()); + Assertions.assertTrue(Log.getFindings().get(0).getMsg().contains( + "get for OptB can't return a value. Attribute is empty.")); + Log.clearFindings(); + Assertions.assertFalse(unsafeBuildObjWithoutPojoSettersOptBNotSet.isMyBool()); + Assertions.assertEquals(1, unsafeBuildObjWithoutPojoSettersOptBNotSet.getMyInt()); + Assertions.assertSame(unsafeBuildObjWithoutPojoSettersOptBNotSet.getMyLevel1(), level2class); + Assertions.assertSame(unsafeBuildObjWithoutPojoSettersOptBNotSet.getMyTestEnum(), testEnum); + + //unsafeBuild with interface not set + TestBuilderWithSetter unsafeBuildObjWithPojoSetterMyLevel1NotSet = + new TestBuilderWithSetterBuilder().setManyB(manyBTest).setOneB(oneBTest).setOptB(optBTest) + .setMyBool(false).setMyInt(1) + //.setMyLevel1(level2class) + .setMyTestEnum(testEnum).unsafeBuild(); + Assertions.assertSame(unsafeBuildObjWithPojoSetterMyLevel1NotSet.getManyB(), manyBTest); + Assertions.assertSame(unsafeBuildObjWithPojoSetterMyLevel1NotSet.getOneB(), oneBTest); + Assertions.assertSame(unsafeBuildObjWithPojoSetterMyLevel1NotSet.getOneB(), oneBTest); + Assertions.assertFalse(unsafeBuildObjWithPojoSetterMyLevel1NotSet.isMyBool()); + Assertions.assertEquals(1, unsafeBuildObjWithPojoSetterMyLevel1NotSet.getMyInt()); + Assertions.assertNull(unsafeBuildObjWithPojoSetterMyLevel1NotSet.getMyLevel1()); + Assertions.assertEquals(unsafeBuildObjWithPojoSetterMyLevel1NotSet.getMyTestEnum(), testEnum); + + TestBuilderWithoutSetter unsafeBuildObjWithoutPojoSetterMyLevel1NotSet = + new TestBuilderWithoutSetterBuilder().setManyB(manyBTest).setOneB(oneBTest).setOptB( + optBTest).setMyBool(false).setMyInt(1) + //.setMyLevel1(level2class) + .setMyTestEnum(testEnum).unsafeBuild(); + Assertions.assertSame(unsafeBuildObjWithoutPojoSetterMyLevel1NotSet.getManyB(), manyBTest); + Assertions.assertSame(unsafeBuildObjWithoutPojoSetterMyLevel1NotSet.getOneB(), oneBTest); + Assertions.assertSame(unsafeBuildObjWithoutPojoSetterMyLevel1NotSet.getOneB(), oneBTest); + Assertions.assertFalse(unsafeBuildObjWithoutPojoSetterMyLevel1NotSet.isMyBool()); + Assertions.assertEquals(1, unsafeBuildObjWithoutPojoSetterMyLevel1NotSet.getMyInt()); + Assertions.assertNull(unsafeBuildObjWithoutPojoSetterMyLevel1NotSet.getMyLevel1()); + Assertions.assertEquals(unsafeBuildObjWithoutPojoSetterMyLevel1NotSet.getMyTestEnum(), + testEnum); + + //unsafeBuild with enum not set + TestBuilderWithSetter unsafeBuildObjWithPojoSettersMyTestEnumNotSet = + new TestBuilderWithSetterBuilder().setManyB(manyBTest).setOneB(oneBTest).setOptB(optBTest) + .setMyBool(false).setMyInt(1).setMyLevel1(level2class) + //.setMyTestEnum(testEnum) + .unsafeBuild(); + Assertions.assertSame(unsafeBuildObjWithPojoSettersMyTestEnumNotSet.getManyB(), manyBTest); + Assertions.assertSame(unsafeBuildObjWithPojoSettersMyTestEnumNotSet.getOneB(), oneBTest); + Assertions.assertSame(unsafeBuildObjWithPojoSettersMyTestEnumNotSet.getOptB(), optBTest); + Assertions.assertFalse(unsafeBuildObjWithPojoSettersMyTestEnumNotSet.isMyBool()); + Assertions.assertEquals(1, unsafeBuildObjWithPojoSettersMyTestEnumNotSet.getMyInt()); + Assertions.assertSame(unsafeBuildObjWithPojoSettersMyTestEnumNotSet.getMyLevel1(), level2class); + Assertions.assertNull(unsafeBuildObjWithPojoSettersMyTestEnumNotSet.getMyTestEnum()); + + TestBuilderWithoutSetter unsafeBuildObjWithoutPojoSettersMyTestEnumNotSet = + new TestBuilderWithoutSetterBuilder().setManyB(manyBTest).setOneB(oneBTest).setOptB( + optBTest).setMyBool(false).setMyInt(1).setMyLevel1(level2class) + //.setMyTestEnum(testEnum) + .unsafeBuild(); + Assertions.assertSame(unsafeBuildObjWithoutPojoSettersMyTestEnumNotSet.getManyB(), manyBTest); + Assertions.assertSame(unsafeBuildObjWithoutPojoSettersMyTestEnumNotSet.getOneB(), oneBTest); + Assertions.assertSame(unsafeBuildObjWithoutPojoSettersMyTestEnumNotSet.getOptB(), optBTest); + Assertions.assertFalse(unsafeBuildObjWithoutPojoSettersMyTestEnumNotSet.isMyBool()); + Assertions.assertEquals(1, unsafeBuildObjWithoutPojoSettersMyTestEnumNotSet.getMyInt()); + Assertions.assertSame(unsafeBuildObjWithoutPojoSettersMyTestEnumNotSet.getMyLevel1(), + level2class); + Assertions.assertNull(unsafeBuildObjWithoutPojoSettersMyTestEnumNotSet.getMyTestEnum()); + + //unsafeBuild with no arguments + TestBuilderWithoutSetter unsafeBuildEmpty = new TestBuilderWithoutSetterBuilder().unsafeBuild(); + Assertions.assertSame(0, unsafeBuildEmpty.getManyB().size()); + Assertions.assertSame(0, Log.getFindings().size()); + } + + @Test + public void testConstructorModificationsAndCreations() { + PrivateDefaultConstructor privateDefaultConstructor = new PrivateDefaultConstructorBuilder() + .unsafeBuild(); + NoDefaultConstructor noDefaultConstructorBuilder = new NoDefaultConstructorBuilder() + .unsafeBuild(); + } + + @Test + public void testInheritanceBuilder() { + TestBuilderWithSuperClass testBuilderWithSuperClass = new TestBuilderWithSuperClassBuilder() + .setManyBAbsent().setOptBAbsent().setMyTestEnum(TestEnum.ERROR).setMyBool(true).setMyLevel1( + new Level2class()).setMyInt(1).setOneB(new B()).build(); + + Assertions.assertSame(TestEnum.ERROR, testBuilderWithSuperClass.getMyTestEnum()); + TestBuilderWithSuperClass testBuilderWithSuperClass2 = new TestBuilderWithSuperClassBuilder() + .setMyBool(false).unsafeBuild(); + Assertions.assertSame(false, testBuilderWithSuperClass2.isMyBool()); + } + + @Test + public void checkClassAndMethodExistence() throws Exception { + //constructor methods + Constructor constructorWithSetter = + TestBuilderWithSetterBuilder.class.getDeclaredConstructor(); + BigInteger constructorModifier = BigInteger.valueOf(constructorWithSetter.getModifiers()); + Assertions.assertEquals(Modifier.PUBLIC, constructorModifier.intValue()); + + Constructor constructorWithoutSetter = + TestBuilderWithoutSetterBuilder.class.getDeclaredConstructor(); + BigInteger constructorWithoutSetterModifier = BigInteger.valueOf(constructorWithoutSetter + .getModifiers()); + Assertions.assertEquals(Modifier.PUBLIC, constructorWithoutSetterModifier.intValue()); + + //build methods + Method buildWithSetter = TestBuilderWithSetterBuilderTOP.class.getDeclaredMethod("build"); + BigInteger modifier = BigInteger.valueOf(buildWithSetter.getModifiers()); + Assertions.assertEquals(Modifier.PUBLIC, modifier.intValue()); + + Method buildWithoutSetter = TestBuilderWithoutSetterBuilder.class.getDeclaredMethod("build"); + BigInteger modifierWithoutSetter = BigInteger.valueOf(buildWithoutSetter.getModifiers()); + Assertions.assertEquals(Modifier.PUBLIC, modifierWithoutSetter.intValue()); + + //unsafeBuild methods + Method unsafeBuildWithSetter = TestBuilderWithSetterBuilderTOP.class.getDeclaredMethod( + "unsafeBuild"); + BigInteger unsafeModifier = BigInteger.valueOf(unsafeBuildWithSetter.getModifiers()); + Assertions.assertEquals(Modifier.PUBLIC, unsafeModifier.intValue()); + + Method unsafeBuildWithoutSetter = TestBuilderWithoutSetterBuilder.class.getDeclaredMethod( + "unsafeBuild"); + BigInteger unsafeModifierWithoutSetter = BigInteger.valueOf(unsafeBuildWithoutSetter + .getModifiers()); + Assertions.assertEquals(Modifier.PUBLIC, unsafeModifierWithoutSetter.intValue()); + + //isValid methods + Method isValidWithSetter = TestBuilderWithSetterBuilderTOP.class.getDeclaredMethod("isValid"); + BigInteger isValidModifier = BigInteger.valueOf(isValidWithSetter.getModifiers()); + Assertions.assertEquals(Modifier.PRIVATE, isValidModifier.intValue()); + + Method isValidWithoutSetter = TestBuilderWithoutSetterBuilder.class.getDeclaredMethod( + "isValid"); + BigInteger isValidModifierWithoutSetter = BigInteger.valueOf(isValidWithoutSetter + .getModifiers()); + Assertions.assertEquals(Modifier.PRIVATE, isValidModifierWithoutSetter.intValue()); + + //set methods + Method setManyBWithSetter = TestBuilderWithSetterBuilderTOP.class.getDeclaredMethod("setManyB", + Set.class); + Assertions.assertEquals(Modifier.PUBLIC, setManyBWithSetter.getModifiers()); + + Method setManyBWithoutSetter = TestBuilderWithoutSetterBuilder.class.getDeclaredMethod( + "setManyB", Set.class); + Assertions.assertEquals(Modifier.PUBLIC, setManyBWithoutSetter.getModifiers()); + + Method setOptBWithSetter = TestBuilderWithSetterBuilderTOP.class.getDeclaredMethod("setOptB", + B.class); + Assertions.assertEquals(Modifier.PUBLIC, setOptBWithSetter.getModifiers()); + + Method setOptBWithoutSetter = TestBuilderWithoutSetterBuilder.class.getDeclaredMethod("setOptB", + B.class); + Assertions.assertEquals(Modifier.PUBLIC, setOptBWithoutSetter.getModifiers()); + + Method setOneBWithSetter = TestBuilderWithSetterBuilderTOP.class.getDeclaredMethod("setOneB", + B.class); + Assertions.assertEquals(Modifier.PUBLIC, setOneBWithSetter.getModifiers()); + + Method setOneBWithoutSetter = TestBuilderWithoutSetterBuilder.class.getDeclaredMethod("setOneB", + B.class); + Assertions.assertEquals(Modifier.PUBLIC, setOneBWithoutSetter.getModifiers()); + + Method setMyIntWithSetter = TestBuilderWithSetterBuilderTOP.class.getDeclaredMethod("setMyInt", + int.class); + Assertions.assertEquals(Modifier.PUBLIC, setMyIntWithSetter.getModifiers()); + + Method setMyIntWithoutSetter = TestBuilderWithoutSetterBuilder.class.getDeclaredMethod( + "setMyInt", int.class); + Assertions.assertEquals(Modifier.PUBLIC, setMyIntWithoutSetter.getModifiers()); + + Method setMyBoolWithSetter = TestBuilderWithSetterBuilderTOP.class.getDeclaredMethod( + "setMyBool", boolean.class); + Assertions.assertEquals(Modifier.PUBLIC, setMyBoolWithSetter.getModifiers()); + + Method setMyBoolWithoutSetter = TestBuilderWithoutSetterBuilder.class.getDeclaredMethod( + "setMyBool", boolean.class); + + Method setMyLevel1WithSetter = TestBuilderWithSetterBuilderTOP.class.getDeclaredMethod( + "setMyLevel1", Level1Interface.class); + Assertions.assertEquals(Modifier.PUBLIC, setMyLevel1WithSetter.getModifiers()); + + Method setMyLevel1WithoutSetter = TestBuilderWithoutSetterBuilder.class.getDeclaredMethod( + "setMyLevel1", Level1Interface.class); + Assertions.assertEquals(Modifier.PUBLIC, setMyLevel1WithoutSetter.getModifiers()); + + Method setMyTestEnumWithSetter = TestBuilderWithSetterBuilderTOP.class.getDeclaredMethod( + "setMyTestEnum", TestEnum.class); + Assertions.assertEquals(Modifier.PUBLIC, setMyTestEnumWithSetter.getModifiers()); + + Method setMyTestEnumWithoutSetter = TestBuilderWithoutSetterBuilder.class.getDeclaredMethod( + "setMyTestEnum", TestEnum.class); + Assertions.assertEquals(Modifier.PUBLIC, setMyTestEnumWithoutSetter.getModifiers()); + + //setAbsent methods + Method setManyBAbsentWithSetter = TestBuilderWithSetterBuilderTOP.class.getDeclaredMethod( + "setManyBAbsent"); + Assertions.assertEquals(Modifier.PUBLIC, setManyBAbsentWithSetter.getModifiers()); + + Method setManyBAbsentWithoutSetter = TestBuilderWithoutSetterBuilder.class.getDeclaredMethod( + "setManyBAbsent"); + Assertions.assertEquals(Modifier.PUBLIC, setManyBAbsentWithoutSetter.getModifiers()); + + Method setOptBAbsentWithSetter = TestBuilderWithSetterBuilderTOP.class.getDeclaredMethod( + "setOptBAbsent"); + Assertions.assertEquals(Modifier.PUBLIC, setOptBAbsentWithSetter.getModifiers()); + + Method setOptBAbsentWithoutSetter = TestBuilderWithoutSetterBuilder.class.getDeclaredMethod( + "setOptBAbsent"); + Assertions.assertEquals(Modifier.PUBLIC, setOptBAbsentWithoutSetter.getModifiers()); + + //setAbsent should not exist for cardinality of 1 + Assertions.assertThrows(NoSuchMethodException.class, () -> TestBuilderWithSetterBuilder.class + .getDeclaredMethod("setOneBAbsent")); + Assertions.assertThrows(NoSuchMethodException.class, () -> TestBuilderWithoutSetterBuilder.class + .getDeclaredMethod("setOneBAbsent")); + Assertions.assertThrows(NoSuchMethodException.class, () -> TestBuilderWithSetterBuilder.class + .getDeclaredMethod("setMyIntAbsent")); + Assertions.assertThrows(NoSuchMethodException.class, () -> TestBuilderWithoutSetterBuilder.class + .getDeclaredMethod("setMyIntAbsent")); + Assertions.assertThrows(NoSuchMethodException.class, () -> TestBuilderWithSetterBuilder.class + .getDeclaredMethod("setMyBoolAbsent")); + Assertions.assertThrows(NoSuchMethodException.class, () -> TestBuilderWithoutSetterBuilder.class + .getDeclaredMethod("setMyBoolAbsent")); + Assertions.assertThrows(NoSuchMethodException.class, () -> TestBuilderWithSetterBuilder.class + .getDeclaredMethod("setMyLevel1Absent")); + Assertions.assertThrows(NoSuchMethodException.class, () -> TestBuilderWithoutSetterBuilder.class + .getDeclaredMethod("setMyLevel1Absent")); + Assertions.assertThrows(NoSuchMethodException.class, () -> TestBuilderWithSetterBuilder.class + .getDeclaredMethod("setMyTestEnumAbsent")); + Assertions.assertThrows(NoSuchMethodException.class, () -> TestBuilderWithoutSetterBuilder.class + .getDeclaredMethod("setMyTestEnumAbsent")); + } + +} diff --git a/cdlang/src/cdGenIntTest/java/deepCopyAnddeepEquals/DeepCloneAndDeepEqualsDecoratorResultTest.java b/cdlang/src/cdGenIntTest/java/deepCopyAnddeepEquals/DeepCloneAndDeepEqualsDecoratorResultTest.java new file mode 100644 index 000000000..6e15b00b1 --- /dev/null +++ b/cdlang/src/cdGenIntTest/java/deepCopyAnddeepEquals/DeepCloneAndDeepEqualsDecoratorResultTest.java @@ -0,0 +1,1493 @@ +/* (c) https://github.com/MontiCore/monticore */ +package deepCopyAnddeepEquals; + +import TestDeepCloneAndDeepEquals.*; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.io.File; +import java.io.FileNotFoundException; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Test the result of the DeepCloneAndDeepEquals Decorator. + */ +public class DeepCloneAndDeepEqualsDecoratorResultTest { + + @Test + public void test() { + //TODO should deepEquals always be be symmetric? a=b implies b=a? + // this is only the case when forceSameOrder is true + // in this case we can call the method always with a.equals(b) and b.equals(a) + // remark: this is not the case for deepEquals in MontiCore + testDeepEquals(); + + testDeepClone(); + + //check the construction of the default constructor if not present + ClassWithNoDefaultConstructor classWithNoDefaultConstructor = new ClassWithNoDefaultConstructor( + 1); + ClassWithNoDefaultConstructor classWithNoDefaultConstructor2 = classWithNoDefaultConstructor + .deepClone(); + Assertions.assertTrue(classWithNoDefaultConstructor.deepEquals(classWithNoDefaultConstructor2)); + } + + @Test + public void testDeepEquals() { + testDeepEqualsPrimitiveTypes(); + testDeepEqualsStringTypes(); + testDeepEqualsArrayTypes(); + testDeepEqualsPojoTypes(); + testDeepEqualsListTypes(); + testDeepEqualsSetTypes(); + testDeepEqualsOptionalTypes(); + testDeepEqualsMapTypes(); + testDeepEqualsAssociationTypes(); + testDeepEqualsCompositionTypes(); + testDeepEqualsCircularRelations(); + testDeepEqualsUnequalCircularRelations(); + testDeepEqualsInterfaceTypes(); + testDeepEqualsEnumTypes(); + testDeepEqualsAllTogether(); + + } + + @Test + public void testDeepClone() { + testDeepClonePrimitiveType(); + testDeepCloneStringType(); + testDeepCloneArrayType(); + testDeepClonePojoType(); + testDeepCloneListType(); + testDeepCloneSetType(); + testDeepCloneOptionalType(); + testDeepCloneMapType(); + testDeepCloneAssociationType(); + testDeepCloneCompositionType(); + testDeepCloneCircularRelations(); + testDeepCloneMultipleTypesAndDimensions(); + testDeepCloneInterfaceTypes(); + testDeepCloneEnumTypes(); + testDeepCloneWithBuilder(); + } + + @Test + public void testDeepEqualsPrimitiveTypes() { + ClassWithPrimitiveType de1 = new ClassWithPrimitiveType(); + ClassWithPrimitiveType de2 = new ClassWithPrimitiveType(); + de1.myInt = 1; + de2.myInt = 1; + Assertions.assertTrue(de1.deepEquals(de2)); + de1.myInt = 2; + de2.myInt = 1; + Assertions.assertFalse(de1.deepEquals(de2)); + } + + @Test + public void testDeepEqualsStringTypes() { + ClassWithString deString1 = new ClassWithString(); + ClassWithString deString2 = new ClassWithString(); + deString1.myString = "test"; + deString2.myString = "test"; + Assertions.assertTrue(deString1.deepEquals(deString2)); + deString1.myString = "test1"; + deString2.myString = "test"; + Assertions.assertFalse(deString1.deepEquals(deString2)); + //null check + deString1.myString = null; + Assertions.assertFalse(deString1.deepEquals(deString2)); + deString2.myString = null; + Assertions.assertTrue(deString1.deepEquals(deString2)); + } + + @Test + public void testDeepEqualsArrayTypes() { + ClassWithArray deArray1 = new ClassWithArray(); + ClassWithArray deArray2 = new ClassWithArray(); + deArray1.arrayOfString = new ClassWithPrimitiveType[2]; + deArray2.arrayOfString = new ClassWithPrimitiveType[2]; + deArray1.arrayOfString[0] = new ClassWithPrimitiveType(); + deArray1.arrayOfString[1] = new ClassWithPrimitiveType(); + deArray2.arrayOfString[0] = new ClassWithPrimitiveType(); + deArray2.arrayOfString[1] = new ClassWithPrimitiveType(); + Assertions.assertTrue(deArray1.deepEquals(deArray2)); + Assertions.assertTrue(deArray1.deepEquals(deArray1, true)); + Assertions.assertTrue(deArray1.deepEquals(deArray1, false)); + deArray2.arrayOfString[1].myInt = 2; + Assertions.assertFalse(deArray1.deepEquals(deArray2)); + Assertions.assertFalse(deArray1.deepEquals(deArray2, true)); + Assertions.assertTrue(deArray1.deepEquals(deArray2, false)); + //TODO Nico note here that deArray1.deepEquals(deArray2) is true but deArray2.deepEquals(deArray1) is not + Assertions.assertFalse(deArray2.deepEquals(deArray1)); + Assertions.assertFalse(deArray2.deepEquals(deArray1, true)); + Assertions.assertFalse(deArray2.deepEquals(deArray1, false)); + //null check + deArray1.arrayOfString = null; + Assertions.assertFalse(deArray1.deepEquals(deArray2)); + Assertions.assertFalse(deArray1.deepEquals(deArray2, true)); + Assertions.assertFalse(deArray1.deepEquals(deArray2, false)); + deArray2.arrayOfString = null; + Assertions.assertTrue(deArray1.deepEquals(deArray2)); + Assertions.assertTrue(deArray1.deepEquals(deArray2, true)); + Assertions.assertTrue(deArray1.deepEquals(deArray2, false)); + + //test multidimensional arrays + ClassWith3DArray deArray3 = new ClassWith3DArray(); + ClassWith3DArray deArray4 = new ClassWith3DArray(); + deArray3.threeDimArrayOfString = new ClassWithPrimitiveType[2][2][2]; + deArray4.threeDimArrayOfString = new ClassWithPrimitiveType[2][2][2]; + deArray3.threeDimArrayOfString[0][0][0] = new ClassWithPrimitiveType(); + deArray3.threeDimArrayOfString[0][0][1] = new ClassWithPrimitiveType(); + deArray3.threeDimArrayOfString[0][1][0] = new ClassWithPrimitiveType(); + deArray3.threeDimArrayOfString[0][1][1] = new ClassWithPrimitiveType(); + deArray3.threeDimArrayOfString[1][0][0] = new ClassWithPrimitiveType(); + deArray3.threeDimArrayOfString[1][0][1] = new ClassWithPrimitiveType(); + deArray3.threeDimArrayOfString[1][1][0] = new ClassWithPrimitiveType(); + deArray3.threeDimArrayOfString[1][1][1] = new ClassWithPrimitiveType(); + deArray4.threeDimArrayOfString[0][0][0] = new ClassWithPrimitiveType(); + deArray4.threeDimArrayOfString[0][0][1] = new ClassWithPrimitiveType(); + deArray4.threeDimArrayOfString[0][1][0] = new ClassWithPrimitiveType(); + deArray4.threeDimArrayOfString[0][1][1] = new ClassWithPrimitiveType(); + deArray4.threeDimArrayOfString[1][0][0] = new ClassWithPrimitiveType(); + deArray4.threeDimArrayOfString[1][0][1] = new ClassWithPrimitiveType(); + deArray4.threeDimArrayOfString[1][1][0] = new ClassWithPrimitiveType(); + deArray4.threeDimArrayOfString[1][1][1] = new ClassWithPrimitiveType(); + Assertions.assertTrue(deArray3.deepEquals(deArray4)); + Assertions.assertTrue(deArray3.deepEquals(deArray3, true)); + Assertions.assertTrue(deArray3.deepEquals(deArray3, false)); + deArray4.threeDimArrayOfString[0][0][0].myInt = 1; + Assertions.assertFalse(deArray3.deepEquals(deArray4)); + Assertions.assertFalse(deArray3.deepEquals(deArray4, true)); + Assertions.assertTrue(deArray3.deepEquals(deArray4, false)); + //TODO Nico note here that deArray1.deepEquals(deArray2) is true but deArray2.deepEquals(deArray1) is not + Assertions.assertFalse(deArray4.deepEquals(deArray3)); + Assertions.assertFalse(deArray4.deepEquals(deArray3, true)); + Assertions.assertFalse(deArray4.deepEquals(deArray3, false)); + //null check + deArray3.threeDimArrayOfString = null; + Assertions.assertFalse(deArray3.deepEquals(deArray4)); + Assertions.assertFalse(deArray3.deepEquals(deArray4, true)); + Assertions.assertFalse(deArray3.deepEquals(deArray4, false)); + deArray4.threeDimArrayOfString = null; + Assertions.assertTrue(deArray3.deepEquals(deArray4)); + Assertions.assertTrue(deArray3.deepEquals(deArray4, true)); + Assertions.assertTrue(deArray3.deepEquals(deArray4, false)); + } + + @Test + public void testDeepEqualsPojoTypes() { + ClassWithPrimitiveType dePrimitiveType1 = new ClassWithPrimitiveType(); + ClassWithPrimitiveType dePrimitiveType2 = new ClassWithPrimitiveType(); + dePrimitiveType1.myInt = 1; + dePrimitiveType2.myInt = 1; + ClassWithPojoClassType de3 = new ClassWithPojoClassType(); + ClassWithPojoClassType de4 = new ClassWithPojoClassType(); + de3.pojoType = dePrimitiveType1; + de4.pojoType = dePrimitiveType2; + Assertions.assertTrue(de3.deepEquals(de4)); + dePrimitiveType1.myInt = 2; + dePrimitiveType2.myInt = 1; + Assertions.assertFalse(de3.deepEquals(de4)); + //null check + dePrimitiveType1.myInt = 2; + dePrimitiveType2.myInt = 1; + de3.pojoType = null; + de4.pojoType = null; + Assertions.assertTrue(de3.deepEquals(de4)); + } + + @Test + public void testDeepEqualsListTypes() { + List listAbsent1 = new ArrayList<>(); + List listAbsent2 = new ArrayList<>(); + List listDescent1 = new ArrayList<>(); + List listUnequal = new ArrayList<>(); + for (int i = 0; i <= 10; i++) { + Integer absent1 = i; + Integer absent2 = i; + Integer descent = 10 - i; + Random rand = new Random(); + Integer unequal = rand.nextInt(); + listAbsent1.add(absent1); + listAbsent2.add(absent2); + listDescent1.add(descent); + listUnequal.add(unequal); + } + ClassWithList de5 = new ClassWithList(); + ClassWithList de6 = new ClassWithList(); + de5.myIntegerList = listAbsent1; + de6.myIntegerList = listAbsent2; + Assertions.assertTrue(de5.deepEquals(de6)); + de5.myIntegerList = listDescent1; + de6.myIntegerList = listAbsent2; + Assertions.assertFalse(de5.deepEquals(de6)); + Assertions.assertFalse(de5.deepEquals(de6, true)); + Assertions.assertTrue(de5.deepEquals(de6, false)); + de5.myIntegerList = listAbsent1; + de6.myIntegerList = listUnequal; + Assertions.assertFalse(de5.deepEquals(de6)); + Assertions.assertFalse(de5.deepEquals(de6, true)); + Assertions.assertFalse(de5.deepEquals(de6, false)); + de5.myIntegerList = new ArrayList<>(); + de6.myIntegerList = new ArrayList<>(); + Assertions.assertTrue(de5.deepEquals(de6)); + Assertions.assertTrue(de5.deepEquals(de6, true)); + Assertions.assertTrue(de5.deepEquals(de6, false)); + //null check + de5.myIntegerList = null; + de6.myIntegerList = null; + Assertions.assertTrue(de5.deepEquals(de6)); + + //Test 2D list types + ClassWith2DimList de7 = new ClassWith2DimList(); + ClassWith2DimList de8 = new ClassWith2DimList(); + de7.my2dimList = new ArrayList<>(); + de8.my2dimList = new ArrayList<>(); + de7.my2dimList.add(listAbsent1); + de8.my2dimList.add(listAbsent2); + de7.my2dimList.add(new ArrayList<>()); + de8.my2dimList.add(new ArrayList<>()); + Assertions.assertTrue(de7.deepEquals(de8)); + Assertions.assertTrue(de7.deepEquals(de8, false)); + Assertions.assertTrue(de7.deepEquals(de8, true)); + List hSwap = de7.my2dimList.get(0); + de7.my2dimList.set(0, de7.my2dimList.get(1)); + de7.my2dimList.set(1, hSwap); + Assertions.assertFalse(de7.deepEquals(de8)); + Assertions.assertTrue(de7.deepEquals(de8, false)); + Assertions.assertFalse(de7.deepEquals(de8, true)); + de7.my2dimList.set(0, listDescent1); + Assertions.assertFalse(de7.deepEquals(de8)); + Assertions.assertTrue(de7.deepEquals(de8, false)); + Assertions.assertFalse(de7.deepEquals(de8, true)); + de7.my2dimList = new ArrayList<>(); + de7.my2dimList.add(listAbsent1); + de7.my2dimList.add(new ArrayList<>()); + de7.my2dimList.add(listAbsent2); + de8.my2dimList = new ArrayList<>(); + de8.my2dimList.add(listDescent1); + de8.my2dimList.add(listDescent1); + Assertions.assertFalse(de8.deepEquals(de7)); + Assertions.assertFalse(de8.deepEquals(de7, false)); + Assertions.assertFalse(de8.deepEquals(de7, true)); + Assertions.assertFalse(de7.deepEquals(de8)); + Assertions.assertFalse(de7.deepEquals(de8, false)); + Assertions.assertFalse(de7.deepEquals(de8, true)); + } + + @Test + public void testDeepEqualsSetTypes() { + Set set1 = new HashSet<>(); + Set set2 = new HashSet<>(); + Set setUnequal = new HashSet<>(); + for (int i = 0; i <= 10; i++) { + Integer absent1 = i; + Random rand = new Random(); + Integer unequal = rand.nextInt(); + set1.add(absent1); + set2.add(absent1); + setUnequal.add(unequal); + } + ClassWithSet de9 = new ClassWithSet(); + ClassWithSet de10 = new ClassWithSet(); + de9.mySet = set1; + de10.mySet = set2; + Assertions.assertTrue(de9.deepEquals(de10)); + Assertions.assertTrue(de9.deepEquals(de10, false)); + Assertions.assertTrue(de9.deepEquals(de10, true)); + de9.mySet = setUnequal; + de10.mySet = set2; + Assertions.assertFalse(de9.deepEquals(de10)); + Assertions.assertFalse(de9.deepEquals(de10, false)); + Assertions.assertFalse(de9.deepEquals(de10, true)); + de9.mySet = new HashSet<>(); + de10.mySet = new HashSet<>(); + Assertions.assertTrue(de9.deepEquals(de10)); + Assertions.assertTrue(de9.deepEquals(de10, false)); + Assertions.assertTrue(de9.deepEquals(de10, true)); + //null check + de9.mySet = null; + de10.mySet = null; + Assertions.assertTrue(de9.deepEquals(de10)); + + //Test 2D set types + ClassWith2DimSet de11 = new ClassWith2DimSet(); + ClassWith2DimSet de12 = new ClassWith2DimSet(); + de11.my2dimSet = new HashSet<>(); + de12.my2dimSet = new HashSet<>(); + de11.my2dimSet.add(set1); + de12.my2dimSet.add(set2); + de11.my2dimSet.add(new HashSet<>()); + de12.my2dimSet.add(new HashSet<>()); + Assertions.assertTrue(de11.deepEquals(de12)); + Assertions.assertTrue(de11.deepEquals(de12, false)); + Assertions.assertTrue(de11.deepEquals(de12, true)); + de12.my2dimSet = new HashSet<>(); + de12.my2dimSet.add(set2); + de12.my2dimSet.add(new HashSet<>()); + Assertions.assertTrue(de11.deepEquals(de12)); + Assertions.assertTrue(de11.deepEquals(de12, false)); + Assertions.assertTrue(de11.deepEquals(de12, true)); + } + + @Test + public void testDeepEqualsOptionalTypes() { + ClassWithOptional de13 = new ClassWithOptional(); + ClassWithOptional de14 = new ClassWithOptional(); + de13.myOptionalInteger = Optional.of(1); + de14.myOptionalInteger = Optional.of(1); + Assertions.assertTrue(de13.deepEquals(de14)); + Assertions.assertTrue(de13.deepEquals(de14, false)); + Assertions.assertTrue(de13.deepEquals(de14, true)); + de13.myOptionalInteger = Optional.of(2); + de14.myOptionalInteger = Optional.of(1); + Assertions.assertFalse(de13.deepEquals(de14)); + Assertions.assertFalse(de13.deepEquals(de14, false)); + Assertions.assertFalse(de13.deepEquals(de14, true)); + de13.myOptionalInteger = Optional.empty(); + de14.myOptionalInteger = Optional.empty(); + Assertions.assertTrue(de13.deepEquals(de14)); + de13.myOptionalInteger = Optional.of(1); + Assertions.assertFalse(de13.deepEquals(de14)); + Assertions.assertFalse(de13.deepEquals(de14, false)); + Assertions.assertFalse(de13.deepEquals(de14, true)); + //null check + de13.myOptionalInteger = null; + de14.myOptionalInteger = null; + Assertions.assertTrue(de13.deepEquals(de14)); + Assertions.assertTrue(de13.deepEquals(de14, false)); + Assertions.assertTrue(de13.deepEquals(de14, true)); + + //Test 2Dim Optional + ClassWith2DimOptional deO1 = new ClassWith2DimOptional(); + ClassWith2DimOptional deO2 = new ClassWith2DimOptional(); + deO1.my2DimOptional = Optional.of(Optional.of(new B())); + Assertions.assertFalse(deO1.deepEquals(deO2)); + deO2.my2DimOptional = Optional.empty(); + Assertions.assertFalse(deO1.deepEquals(deO2)); + deO2.my2DimOptional = Optional.of(Optional.empty()); + Assertions.assertFalse(deO1.deepEquals(deO2)); + deO2.my2DimOptional = Optional.of(Optional.of(new B())); + Assertions.assertTrue(deO1.deepEquals(deO2)); + //null check + deO1.my2DimOptional = null; + deO2.my2DimOptional = null; + Assertions.assertTrue(deO1.deepEquals(deO2)); + Assertions.assertFalse(deO1.deepEquals(null)); + } + + @Test + public void testDeepEqualsMapTypes() { + ClassWithMap deMap1 = new ClassWithMap(); + ClassWithMap deMap2 = new ClassWithMap(); + deMap1.myMap = null; + deMap2.myMap = null; + Assertions.assertTrue(deMap1.deepEquals(deMap2)); + Assertions.assertTrue(deMap1.deepEquals(deMap2, false)); + Assertions.assertTrue(deMap1.deepEquals(deMap2, true)); + deMap1.myMap = new HashMap<>(); + Assertions.assertFalse(deMap1.deepEquals(deMap2)); + Assertions.assertFalse(deMap1.deepEquals(deMap2, false)); + Assertions.assertFalse(deMap1.deepEquals(deMap2, true)); + deMap2.myMap = new HashMap<>(); + Assertions.assertTrue(deMap1.deepEquals(deMap2)); + Assertions.assertTrue(deMap1.deepEquals(deMap2, false)); + Assertions.assertTrue(deMap1.deepEquals(deMap2, true)); + deMap1.myMap.put("key", new B()); + deMap2.myMap.put("key", new B()); + Assertions.assertTrue(deMap1.deepEquals(deMap2)); + Assertions.assertTrue(deMap1.deepEquals(deMap2, false)); + Assertions.assertTrue(deMap1.deepEquals(deMap2, true)); + + //test 2D map types + ClassWith2DMap deMap3 = new ClassWith2DMap(); + ClassWith2DMap deMap4 = new ClassWith2DMap(); + deMap3.myMap = new HashMap<>(); + deMap4.myMap = new HashMap<>(); + deMap3.myMap.put("key", new HashMap<>()); + deMap4.myMap.put("key", new HashMap<>()); + Assertions.assertTrue(deMap3.deepEquals(deMap4)); + Assertions.assertTrue(deMap3.deepEquals(deMap4, false)); + Assertions.assertTrue(deMap3.deepEquals(deMap4, true)); + deMap3.myMap.get("key").put("key", new B()); + Assertions.assertFalse(deMap3.deepEquals(deMap4)); + Assertions.assertFalse(deMap3.deepEquals(deMap4, false)); + Assertions.assertFalse(deMap3.deepEquals(deMap4, true)); + deMap4.myMap.get("key").put("key", new B()); + Assertions.assertTrue(deMap3.deepEquals(deMap4)); + Assertions.assertTrue(deMap3.deepEquals(deMap4, false)); + Assertions.assertTrue(deMap3.deepEquals(deMap4, true)); + //null check + deMap3.myMap = null; + Assertions.assertFalse(deMap3.deepEquals(deMap4)); + Assertions.assertFalse(deMap3.deepEquals(deMap4, false)); + Assertions.assertFalse(deMap3.deepEquals(deMap4, true)); + deMap4.myMap = null; + Assertions.assertTrue(deMap3.deepEquals(deMap4)); + Assertions.assertTrue(deMap3.deepEquals(deMap4, false)); + Assertions.assertTrue(deMap3.deepEquals(deMap4, true)); + } + + @Test + public void testDeepEqualsAssociationTypes() { + // deepEquals association types + ClassWithAssociation de15 = new ClassWithAssociation(); + ClassWithAssociation de16 = new ClassWithAssociation(); + de15.owns = new HashSet<>(); + de16.owns = new HashSet<>(); + Assertions.assertTrue(de15.deepEquals(de16)); + Assertions.assertTrue(de15.deepEquals(de16, false)); + Assertions.assertTrue(de15.deepEquals(de16, true)); + B b1 = new B(); + de15.owns.add(b1); + Assertions.assertFalse(de15.deepEquals(de16)); + Assertions.assertFalse(de15.deepEquals(de16, false)); + Assertions.assertFalse(de15.deepEquals(de16, true)); + de16.owns.add(b1); + Assertions.assertTrue(de15.deepEquals(de16)); + Assertions.assertTrue(de15.deepEquals(de16, false)); + Assertions.assertTrue(de15.deepEquals(de16, true)); + //null check + de15.owns = null; + de16.owns = null; + Assertions.assertTrue(de15.deepEquals(de16)); + Assertions.assertTrue(de15.deepEquals(de16, false)); + Assertions.assertTrue(de15.deepEquals(de16, true)); + } + + @Test + public void testDeepEqualsCompositionTypes() { + // deepEquals composition types + ClassWithComposition de17 = new ClassWithComposition(); + ClassWithComposition de18 = new ClassWithComposition(); + de17.many = null; + de18.many = null; + de17.one = null; + de18.one = null; + de17.opt = null; + de18.opt = null; + Assertions.assertTrue(de17.deepEquals(de18)); + Assertions.assertTrue(de17.deepEquals(de18, false)); + Assertions.assertTrue(de17.deepEquals(de18, true)); + de17.many = new HashSet<>(); + Assertions.assertFalse(de17.deepEquals(de18, false)); + de17.many.add(new B()); + Assertions.assertFalse(de17.deepEquals(de18, false)); + de18.many = new HashSet<>(); + de18.many.add(new B()); + Assertions.assertTrue(de17.deepEquals(de18)); + Assertions.assertTrue(de17.deepEquals(de18, false)); + Assertions.assertTrue(de17.deepEquals(de18, true)); + de17.one = new B(); + Assertions.assertFalse(de17.deepEquals(de18)); + de18.one = new B(); + Assertions.assertTrue(de17.deepEquals(de18)); + Assertions.assertTrue(de17.deepEquals(de18, false)); + Assertions.assertTrue(de17.deepEquals(de18, true)); + de17.opt = Optional.empty(); + Assertions.assertFalse(de17.deepEquals(de18, false)); + de18.opt = Optional.of(new B()); + Assertions.assertFalse(de17.deepEquals(de18)); + Assertions.assertFalse(de17.deepEquals(de18, false)); + Assertions.assertFalse(de17.deepEquals(de18, true)); + de17.opt = Optional.of(new B()); + Assertions.assertTrue(de17.deepEquals(de18)); + Assertions.assertTrue(de17.deepEquals(de18, false)); + Assertions.assertTrue(de17.deepEquals(de18, true)); + } + + @Test + public void testDeepEqualsCircularRelations() { + //termination condition needs to be checked in circular references + ClassCircular1 de19 = new ClassCircular1(); + ClassCircular1 de20 = new ClassCircular1(); + ClassCircular2 c131 = new ClassCircular2(); + ClassCircular2 c141 = new ClassCircular2(); + de19.myClassCircular2 = c131; + de20.myClassCircular2 = c141; + c131.myClassCircular1 = de19; + c141.myClassCircular1 = de20; + Assertions.assertTrue(de19.deepEquals(de20)); + Assertions.assertTrue(de19.deepEquals(de20, false)); + Assertions.assertTrue(de19.deepEquals(de20, true)); + de19.myClassCircular2 = null; + Assertions.assertFalse(de19.deepEquals(de20)); + Assertions.assertFalse(de19.deepEquals(de20, false)); + Assertions.assertFalse(de19.deepEquals(de20, true)); + } + + @Test + public void testDeepEqualsUnequalCircularRelations() { + //check for deepEquals where the firstObject has another reference structure than the secondObject to compare to + //because the firstObject contains twice the same attributes, it has to be checked if the second objects attributes equal + // for the second option as well if it is not equal to the same object found before + ClassCircular1 deCircular11 = new ClassCircular1(); + ClassCircular1 deCircular12 = new ClassCircular1(); + ClassCircular1 deCircular1NotEqual = new ClassCircular1(); + ClassCircular2 deCircular21 = new ClassCircular2(); + ClassCircular2 deCircular22 = new ClassCircular2(); + //create a relation where we found our first element before, and it is in the map, but it does not match + // the second type on the second occasion. + + //create first circle + deCircular11.myClassCircular2 = deCircular21; + deCircular21.myClassCircular1 = deCircular11; + //create the second object which has no circle + deCircular12.myClassCircular2 = deCircular22; + deCircular22.myClassCircular1 = deCircular1NotEqual; + Assertions.assertFalse(deCircular11.deepEquals(deCircular12)); + Assertions.assertFalse(deCircular11.deepEquals(deCircular12, true)); + Assertions.assertFalse(deCircular11.deepEquals(deCircular12, false)); + + //create bigger circular relation + ClassCircular1 deCircular131 = new ClassCircular1(); + ClassCircular1 deCircular132 = new ClassCircular1(); + ClassCircular1 deCircular133 = new ClassCircular1(); + ClassCircular2 deCircular231 = new ClassCircular2(); + ClassCircular2 deCircular232 = new ClassCircular2(); + ClassCircular2 deCircular233 = new ClassCircular2(); + + deCircular131.myClassCircular2 = deCircular231; + deCircular231.myClassCircular1 = deCircular132; + deCircular132.myClassCircular2 = deCircular232; + deCircular232.myClassCircular1 = deCircular133; + deCircular133.myClassCircular2 = deCircular233; + deCircular233.myClassCircular1 = deCircular131; + + Assertions.assertTrue(deCircular131.deepEquals(deCircular11)); + Assertions.assertTrue(deCircular131.deepEquals(deCircular11, false)); + Assertions.assertTrue(deCircular131.deepEquals(deCircular11, true)); + } + + @Test + public void testDeepEqualsInterfaceTypes() { + ClassWithInterface deInterface1 = new ClassWithInterface(); + ClassWithInterface deInterface2 = new ClassWithInterface(); + Level2class level2class1 = new Level2class(); + Level2class level2class2 = new Level2class(); + deInterface1.myInterface = level2class1; + deInterface2.myInterface = level2class2; + Assertions.assertTrue(deInterface1.deepEquals(deInterface2)); + Assertions.assertTrue(deInterface1.deepEquals(deInterface2, true)); + Assertions.assertTrue(deInterface1.deepEquals(deInterface2, false)); + level2class1.myInt = 1; + level2class2.myInt = 1; + Level3class level3class1 = new Level3class(); + level3class1.myInt = 1; + Level3class level3class2 = new Level3class(); + level3class2.myInt = 1; + deInterface2.myInterface = level3class1; + Assertions.assertFalse(deInterface1.deepEquals(deInterface2)); + Assertions.assertFalse(deInterface1.deepEquals(deInterface2, true)); + Assertions.assertFalse(deInterface1.deepEquals(deInterface2, false)); + + //test sets + deInterface1.many = new HashSet(); + deInterface1.many.add(level2class1); + deInterface1.many.add(level3class1); + + deInterface2.many = new HashSet(); + deInterface2.many.add(level2class2); + deInterface2.many.add(level3class1); + + Assertions.assertFalse(deInterface1.deepEquals(deInterface2)); + Assertions.assertFalse(deInterface1.deepEquals(deInterface2, true)); + Assertions.assertFalse(deInterface1.deepEquals(deInterface2, false)); + + } + + private static Set getMany(ClassWithInterface deInterface2) { + return deInterface2.many; + } + + @Test + public void testDeepEqualsEnumTypes() { + ClassWithEnum deEnum1 = new ClassWithEnum(); + ClassWithEnum deEnum2 = new ClassWithEnum(); + deEnum1.myEnum = TestEnum.ERROR; + deEnum2.myEnum = TestEnum.ERROR; + Assertions.assertTrue(deEnum1.deepEquals(deEnum2)); + Assertions.assertTrue(deEnum1.deepEquals(deEnum2, false)); + Assertions.assertTrue(deEnum1.deepEquals(deEnum2, true)); + deEnum1.myEnum = TestEnum.ERROR; + deEnum2.myEnum = TestEnum.IDLE; + Assertions.assertFalse(deEnum1.deepEquals(deEnum2)); + Assertions.assertFalse(deEnum1.deepEquals(deEnum2, false)); + Assertions.assertFalse(deEnum1.deepEquals(deEnum2, true)); + } + + @Test + public void testDeepEqualsAllTogether() { + List listAbsent1 = new ArrayList<>(); + List listAbsent2 = new ArrayList<>(); + for (int i = 0; i <= 10; i++) { + listAbsent1.add(i); + listAbsent2.add(i); + } + AllTogether de21 = new AllTogether(); + AllTogether de22 = new AllTogether(); + de21.owns = new HashSet<>(); + de22.owns = new HashSet<>(); + de21.myBool = true; + de22.myBool = true; + de21.myInt = 1; + de22.myInt = 1; + de21.manyClassWith2DimList = new HashSet<>(); + de22.manyClassWith2DimList = new HashSet<>(); + ClassWith2DimList c112 = new ClassWith2DimList(); + ClassWith2DimList c122 = new ClassWith2DimList(); + c112.my2dimList = new ArrayList<>(); + c122.my2dimList = new ArrayList<>(); + c112.my2dimList.add(listAbsent1); + c122.my2dimList.add(listAbsent2); + c112.my2dimList.add(new ArrayList<>()); + c122.my2dimList.add(new ArrayList<>()); + de21.manyClassWith2DimList.add(c112); + de22.manyClassWith2DimList.add(c122); + de21.oneClassWith2DimList = c112; + de22.oneClassWith2DimList = c122; + de21.optClassWith2DimList = Optional.empty(); + de22.optClassWith2DimList = Optional.empty(); + Assertions.assertTrue(de21.deepEquals(de22)); + Assertions.assertTrue(de21.deepEquals(de22, false)); + Assertions.assertTrue(de21.deepEquals(de22, true)); + c112.my2dimList = new ArrayList<>(); + Assertions.assertFalse(de21.deepEquals(de22)); + Assertions.assertFalse(de21.deepEquals(de22, false)); + Assertions.assertFalse(de21.deepEquals(de22, true)); + c112.my2dimList = new ArrayList<>(); + c112.my2dimList.add(new ArrayList<>()); + c112.my2dimList.add(listAbsent1); + Assertions.assertFalse(de21.deepEquals(de22)); + Assertions.assertTrue(de21.deepEquals(de22, false)); + Assertions.assertFalse(de21.deepEquals(de22, true)); + } + + @Test + public void testDeepClonePrimitiveType() { + ClassWithPrimitiveType dc1 = new ClassWithPrimitiveType(); + dc1.myInt = 0; + ClassWithPrimitiveType dc2 = dc1.deepClone(); + Assertions.assertNotSame(dc1, dc2); + Assertions.assertTrue(dc1.deepEquals(dc2)); + dc1.myInt = 1; + Assertions.assertFalse(dc1.deepEquals(dc2)); + dc2 = dc1.deepClone(); + Assertions.assertNotSame(dc1, dc2); + Assertions.assertTrue(dc1.deepEquals(dc2)); + } + + @Test + public void testDeepCloneStringType() { + ClassWithString dcString1 = new ClassWithString(); + dcString1.myString = "test"; + ClassWithString dcString2 = dcString1.deepClone(); + Assertions.assertNotSame(dcString1, dcString2); + Assertions.assertTrue(dcString1.deepEquals(dcString2)); + dcString1.myString = "test1"; + Assertions.assertFalse(dcString1.deepEquals(dcString2)); + //null check + dcString1.myString = null; + dcString2 = dcString1.deepClone(); + Assertions.assertNotSame(dcString1, dcString2); + Assertions.assertTrue(dcString1.deepEquals(dcString2)); + Assertions.assertNull(dcString2.myString); + //test Map correctness + dcString1.myString = "test"; + dcString1.myString2 = dcString1.myString; + dcString2 = dcString1.deepClone(); + Assertions.assertNotSame(dcString1, dcString2); + Assertions.assertTrue(dcString1.deepEquals(dcString2)); + Assertions.assertSame(dcString2.myString, dcString2.myString2); + dcString1.myString2 = null; + dcString2 = dcString1.deepClone(); + Assertions.assertNotSame(dcString1, dcString2); + Assertions.assertTrue(dcString1.deepEquals(dcString2)); + Assertions.assertNotSame(dcString2.myString, dcString2.myString2); + } + + @Test + public void testDeepClonePojoType() { + ClassWithPrimitiveType dc1 = new ClassWithPrimitiveType(); + ClassWithPojoClassType dc3 = new ClassWithPojoClassType(); + dc3.pojoType = dc1; + dc1.myInt = 0; + ClassWithPojoClassType dc4 = dc3.deepClone(); + Assertions.assertNotSame(dc3, dc4); + Assertions.assertNotSame(dc3.pojoType, dc4.pojoType); + Assertions.assertTrue(dc3.deepEquals(dc4)); + dc3.pojoType.myInt = 1; + Assertions.assertFalse(dc3.deepEquals(dc4)); + dc4 = dc3.deepClone(); + Assertions.assertNotSame(dc3, dc4); + Assertions.assertNotSame(dc3.pojoType, dc4.pojoType); + Assertions.assertTrue(dc3.deepEquals(dc4)); + //null check + dc3.pojoType = null; + dc4 = dc3.deepClone(); + Assertions.assertNotSame(dc3, dc4); + Assertions.assertTrue(dc3.deepEquals(dc4)); + Assertions.assertNull(dc4.pojoType); + //test Map correctness + dc3.pojoType = dc1; + dc3.pojoType2 = dc3.pojoType; + dc4 = dc3.deepClone(); + Assertions.assertNotSame(dc3, dc4); + Assertions.assertNotSame(dc3.pojoType, dc4.pojoType); + Assertions.assertNotSame(dc3.pojoType2, dc4.pojoType2); + Assertions.assertTrue(dc3.deepEquals(dc4)); + Assertions.assertSame(dc4.pojoType, dc4.pojoType2); + } + + @Test + public void testDeepCloneArrayType() { + ClassWithArray dcArray1 = new ClassWithArray(); + ClassWithArray dcArray2; + dcArray1.arrayOfString = new ClassWithPrimitiveType[2]; + dcArray1.arrayOfString[0] = new ClassWithPrimitiveType(); + dcArray1.arrayOfString[1] = new ClassWithPrimitiveType(); + dcArray2 = dcArray1.deepClone(); + Assertions.assertNotSame(dcArray1, dcArray2); + Assertions.assertNotSame(dcArray1.arrayOfString, dcArray2.arrayOfString); + Assertions.assertNotSame(dcArray1.arrayOfString[0], dcArray2.arrayOfString[0]); + Assertions.assertNotSame(dcArray1.arrayOfString[1], dcArray2.arrayOfString[1]); + Assertions.assertTrue(dcArray1.deepEquals(dcArray2)); + dcArray1.arrayOfString[0].myInt = 1; + Assertions.assertFalse(dcArray1.deepEquals(dcArray2)); + dcArray2 = dcArray1.deepClone(); + Assertions.assertNotSame(dcArray1, dcArray2); + Assertions.assertNotSame(dcArray1.arrayOfString, dcArray2.arrayOfString); + Assertions.assertNotSame(dcArray1.arrayOfString[0], dcArray2.arrayOfString[0]); + Assertions.assertNotSame(dcArray1.arrayOfString[1], dcArray2.arrayOfString[1]); + Assertions.assertTrue(dcArray1.deepEquals(dcArray2)); + //null check + dcArray1.arrayOfString = null; + dcArray2 = dcArray1.deepClone(); + Assertions.assertNotSame(dcArray1, dcArray2); + Assertions.assertTrue(dcArray1.deepEquals(dcArray2)); + Assertions.assertNull(dcArray2.arrayOfString); + //test Map correctness + dcArray1.arrayOfString = new ClassWithPrimitiveType[2]; + dcArray1.arrayOfString[0] = new ClassWithPrimitiveType(); + dcArray1.arrayOfString[1] = dcArray1.arrayOfString[0]; + dcArray2 = dcArray1.deepClone(); + Assertions.assertNotSame(dcArray1, dcArray2); + Assertions.assertNotSame(dcArray1.arrayOfString, dcArray2.arrayOfString); + Assertions.assertNotSame(dcArray1.arrayOfString[0], dcArray2.arrayOfString[0]); + Assertions.assertNotSame(dcArray1.arrayOfString[1], dcArray2.arrayOfString[1]); + Assertions.assertTrue(dcArray1.deepEquals(dcArray2)); + Assertions.assertSame(dcArray2.arrayOfString[0], dcArray2.arrayOfString[1]); + + //test multidimensional arrays + ClassWith3DArray dcArray3 = new ClassWith3DArray(); + ClassWith3DArray dcArray4; + dcArray3.threeDimArrayOfString = new ClassWithPrimitiveType[2][2][2]; + dcArray3.threeDimArrayOfString[0][0][0] = new ClassWithPrimitiveType(); + dcArray3.threeDimArrayOfString[0][0][1] = new ClassWithPrimitiveType(); + dcArray3.threeDimArrayOfString[0][1][0] = new ClassWithPrimitiveType(); + dcArray3.threeDimArrayOfString[0][1][1] = new ClassWithPrimitiveType(); + dcArray3.threeDimArrayOfString[1][0][0] = new ClassWithPrimitiveType(); + dcArray3.threeDimArrayOfString[1][0][1] = new ClassWithPrimitiveType(); + dcArray3.threeDimArrayOfString[1][1][0] = new ClassWithPrimitiveType(); + dcArray3.threeDimArrayOfString[1][1][1] = new ClassWithPrimitiveType(); + dcArray4 = dcArray3.deepClone(); + Assertions.assertNotSame(dcArray3, dcArray4); + Assertions.assertNotSame(dcArray3.threeDimArrayOfString, dcArray4.threeDimArrayOfString); + Assertions.assertNotSame(dcArray3.threeDimArrayOfString[0][0][0], + dcArray4.threeDimArrayOfString[0][0][0]); + Assertions.assertNotSame(dcArray3.threeDimArrayOfString[0][0][1], + dcArray4.threeDimArrayOfString[0][0][1]); + Assertions.assertNotSame(dcArray3.threeDimArrayOfString[0][1][0], + dcArray4.threeDimArrayOfString[0][1][0]); + Assertions.assertNotSame(dcArray3.threeDimArrayOfString[0][1][1], + dcArray4.threeDimArrayOfString[0][1][1]); + Assertions.assertNotSame(dcArray3.threeDimArrayOfString[1][0][0], + dcArray4.threeDimArrayOfString[1][0][0]); + Assertions.assertNotSame(dcArray3.threeDimArrayOfString[1][0][1], + dcArray4.threeDimArrayOfString[1][0][1]); + Assertions.assertNotSame(dcArray3.threeDimArrayOfString[1][1][0], + dcArray4.threeDimArrayOfString[1][1][0]); + Assertions.assertNotSame(dcArray3.threeDimArrayOfString[1][1][1], + dcArray4.threeDimArrayOfString[1][1][1]); + Assertions.assertTrue(dcArray3.deepEquals(dcArray4)); + dcArray3.threeDimArrayOfString[0][0][0].myInt = 1; + Assertions.assertFalse(dcArray3.deepEquals(dcArray4)); + dcArray4 = dcArray3.deepClone(); + Assertions.assertNotSame(dcArray3, dcArray4); + Assertions.assertNotSame(dcArray3.threeDimArrayOfString, dcArray4.threeDimArrayOfString); + Assertions.assertNotSame(dcArray3.threeDimArrayOfString[0][0][0], + dcArray4.threeDimArrayOfString[0][0][0]); + Assertions.assertNotSame(dcArray3.threeDimArrayOfString[0][0][1], + dcArray4.threeDimArrayOfString[0][0][1]); + Assertions.assertNotSame(dcArray3.threeDimArrayOfString[0][1][0], + dcArray4.threeDimArrayOfString[0][1][0]); + Assertions.assertNotSame(dcArray3.threeDimArrayOfString[0][1][1], + dcArray4.threeDimArrayOfString[0][1][1]); + Assertions.assertNotSame(dcArray3.threeDimArrayOfString[1][0][0], + dcArray4.threeDimArrayOfString[1][0][0]); + Assertions.assertNotSame(dcArray3.threeDimArrayOfString[1][0][1], + dcArray4.threeDimArrayOfString[1][0][1]); + Assertions.assertNotSame(dcArray3.threeDimArrayOfString[1][1][0], + dcArray4.threeDimArrayOfString[1][1][0]); + Assertions.assertNotSame(dcArray3.threeDimArrayOfString[1][1][1], + dcArray4.threeDimArrayOfString[1][1][1]); + Assertions.assertTrue(dcArray3.deepEquals(dcArray4)); + //null check + dcArray3.threeDimArrayOfString = null; + dcArray4 = dcArray3.deepClone(); + Assertions.assertNotSame(dcArray3, dcArray4); + Assertions.assertTrue(dcArray3.deepEquals(dcArray4)); + Assertions.assertNull(dcArray4.threeDimArrayOfString); + //test Map correctness + dcArray3.threeDimArrayOfString = new ClassWithPrimitiveType[2][2][2]; + dcArray3.threeDimArrayOfString[0][0][0] = new ClassWithPrimitiveType(); + dcArray3.threeDimArrayOfString[0][0][1] = dcArray3.threeDimArrayOfString[0][0][0]; + dcArray3.threeDimArrayOfString[0][1][0] = dcArray3.threeDimArrayOfString[0][0][0]; + dcArray3.threeDimArrayOfString[0][1][1] = dcArray3.threeDimArrayOfString[0][0][0]; + dcArray3.threeDimArrayOfString[1][0][0] = dcArray3.threeDimArrayOfString[0][0][0]; + dcArray3.threeDimArrayOfString[1][0][1] = dcArray3.threeDimArrayOfString[0][0][0]; + dcArray3.threeDimArrayOfString[1][1][0] = dcArray3.threeDimArrayOfString[0][0][0]; + dcArray3.threeDimArrayOfString[1][1][1] = dcArray3.threeDimArrayOfString[0][0][0]; + dcArray4 = dcArray3.deepClone(); + Assertions.assertNotSame(dcArray3, dcArray4); + Assertions.assertNotSame(dcArray3.threeDimArrayOfString, dcArray4.threeDimArrayOfString); + Assertions.assertNotSame(dcArray3.threeDimArrayOfString[0][0][0], + dcArray4.threeDimArrayOfString[0][0][0]); + Assertions.assertNotSame(dcArray3.threeDimArrayOfString[0][0][1], + dcArray4.threeDimArrayOfString[0][0][1]); + Assertions.assertNotSame(dcArray3.threeDimArrayOfString[0][1][0], + dcArray4.threeDimArrayOfString[0][1][0]); + Assertions.assertNotSame(dcArray3.threeDimArrayOfString[0][1][1], + dcArray4.threeDimArrayOfString[0][1][1]); + Assertions.assertNotSame(dcArray3.threeDimArrayOfString[1][0][0], + dcArray4.threeDimArrayOfString[1][0][0]); + Assertions.assertNotSame(dcArray3.threeDimArrayOfString[1][0][1], + dcArray4.threeDimArrayOfString[1][0][1]); + Assertions.assertNotSame(dcArray3.threeDimArrayOfString[1][1][0], + dcArray4.threeDimArrayOfString[1][1][0]); + Assertions.assertNotSame(dcArray3.threeDimArrayOfString[1][1][1], + dcArray4.threeDimArrayOfString[1][1][1]); + Assertions.assertTrue(dcArray3.deepEquals(dcArray4)); + Assertions.assertSame(dcArray4.threeDimArrayOfString[0][0][0], + dcArray4.threeDimArrayOfString[0][0][1]); + Assertions.assertSame(dcArray4.threeDimArrayOfString[0][1][0], + dcArray4.threeDimArrayOfString[0][0][1]); + Assertions.assertSame(dcArray4.threeDimArrayOfString[0][1][1], + dcArray4.threeDimArrayOfString[0][0][1]); + Assertions.assertSame(dcArray4.threeDimArrayOfString[1][0][0], + dcArray4.threeDimArrayOfString[0][0][1]); + Assertions.assertSame(dcArray4.threeDimArrayOfString[1][0][1], + dcArray4.threeDimArrayOfString[0][0][1]); + Assertions.assertSame(dcArray4.threeDimArrayOfString[1][1][0], + dcArray4.threeDimArrayOfString[0][0][1]); + Assertions.assertSame(dcArray4.threeDimArrayOfString[1][1][1], + dcArray4.threeDimArrayOfString[0][0][1]); + //check for map correctness with array type + dcArray3.threeDimArrayOfString = new ClassWithPrimitiveType[2][2][2]; + dcArray3.threeDimArrayOfString[0][0][0] = new ClassWithPrimitiveType(); + dcArray3.threeDimArrayOfString[0][0][1] = dcArray3.threeDimArrayOfString[0][0][0]; + dcArray3.threeDimArrayOfString[0][1] = dcArray3.threeDimArrayOfString[0][0]; + dcArray3.threeDimArrayOfString[1][0] = dcArray3.threeDimArrayOfString[0][0]; + dcArray3.threeDimArrayOfString[1][1] = dcArray3.threeDimArrayOfString[0][0]; + dcArray4 = dcArray3.deepClone(); + Assertions.assertNotSame(dcArray3, dcArray4); + Assertions.assertNotSame(dcArray3.threeDimArrayOfString, dcArray4.threeDimArrayOfString); + Assertions.assertNotSame(dcArray3.threeDimArrayOfString[0][0], + dcArray4.threeDimArrayOfString[0][0]); + Assertions.assertNotSame(dcArray3.threeDimArrayOfString[0][1], + dcArray4.threeDimArrayOfString[0][1]); + Assertions.assertNotSame(dcArray3.threeDimArrayOfString[1][0], + dcArray4.threeDimArrayOfString[1][0]); + Assertions.assertNotSame(dcArray3.threeDimArrayOfString[1][1], + dcArray4.threeDimArrayOfString[1][1]); + Assertions.assertTrue(dcArray3.deepEquals(dcArray4)); + //check for deepClone with two equal references inside the first array + dcArray3.threeDimArrayOfString = new ClassWithPrimitiveType[2][2][2]; + dcArray3.threeDimArrayOfString[0][0] = new ClassWithPrimitiveType[2]; + dcArray3.threeDimArrayOfString[0][0][0] = new ClassWithPrimitiveType(); + dcArray3.threeDimArrayOfString[0][0][1] = new ClassWithPrimitiveType(); + dcArray3.threeDimArrayOfString[0][1] = dcArray3.threeDimArrayOfString[0][0]; + dcArray3.threeDimArrayOfString[1][0] = dcArray3.threeDimArrayOfString[0][0]; + dcArray3.threeDimArrayOfString[1][1] = dcArray3.threeDimArrayOfString[0][0]; + dcArray4 = dcArray3.deepClone(); + Assertions.assertNotSame(dcArray3, dcArray4); + Assertions.assertNotSame(dcArray3.threeDimArrayOfString, dcArray4.threeDimArrayOfString); + Assertions.assertNotSame(dcArray3.threeDimArrayOfString[0][0], + dcArray4.threeDimArrayOfString[0][0]); + Assertions.assertNotSame(dcArray3.threeDimArrayOfString[0][1], + dcArray4.threeDimArrayOfString[0][1]); + Assertions.assertNotSame(dcArray3.threeDimArrayOfString[1][0], + dcArray4.threeDimArrayOfString[1][0]); + Assertions.assertNotSame(dcArray3.threeDimArrayOfString[1][1], + dcArray4.threeDimArrayOfString[1][1]); + Assertions.assertTrue(dcArray3.deepEquals(dcArray4)); + } + + @Test + public void testDeepCloneListType() { + List listAbsent1 = new ArrayList<>(); + List listDescent1 = new ArrayList<>(); + for (int i = 0; i <= 10; i++) { + Integer absent1 = i; + Integer descent = 10 - i; + listAbsent1.add(absent1); + listDescent1.add(descent); + } + ClassWithList dc5 = new ClassWithList(); + dc5.myIntegerList = listAbsent1; + ClassWithList dc6 = dc5.deepClone(); + Assertions.assertNotSame(dc5, dc6); + Assertions.assertNotSame(dc5.myIntegerList, dc6.myIntegerList); + Assertions.assertTrue(dc5.deepEquals(dc6)); + dc5.myIntegerList = listDescent1; + dc6 = dc5.deepClone(); + Assertions.assertNotSame(dc5, dc6); + Assertions.assertNotSame(dc5.myIntegerList, dc6.myIntegerList); + Assertions.assertTrue(dc5.deepEquals(dc6)); + //null check + dc5.myIntegerList = null; + dc6 = dc5.deepClone(); + Assertions.assertTrue(dc5.deepEquals(dc6)); + Assertions.assertNull(dc6.myIntegerList); + //test Map correctness + dc5.myIntegerList = listAbsent1; + dc5.myIntegerList2 = dc5.myIntegerList; + dc6 = dc5.deepClone(); + Assertions.assertNotSame(dc5, dc6); + Assertions.assertNotSame(dc5.myIntegerList, dc6.myIntegerList); + Assertions.assertNotSame(dc5.myIntegerList2, dc6.myIntegerList2); + Assertions.assertTrue(dc5.deepEquals(dc6)); + Assertions.assertSame(dc6.myIntegerList, dc6.myIntegerList2); + + //Test 2D list types + ClassWith2DimList dc7 = new ClassWith2DimList(); + dc7.my2dimList = new ArrayList<>(); + dc7.my2dimList.add(listAbsent1); + dc7.my2dimList.add(listAbsent1); + dc7.my2dimList.add(new ArrayList<>()); + ClassWith2DimList dc8 = dc7.deepClone(); + Assertions.assertNotSame(dc7, dc8); + Assertions.assertNotSame(dc7.my2dimList, dc8.my2dimList); + Assertions.assertTrue(dc7.deepEquals(dc8)); + dc7.my2dimList = new ArrayList<>(); + Assertions.assertFalse(dc7.deepEquals(dc8)); + dc8 = dc7.deepClone(); + Assertions.assertNotSame(dc7, dc8); + Assertions.assertNotSame(dc7.my2dimList, dc8.my2dimList); + Assertions.assertTrue(dc7.deepEquals(dc8)); + //check for deepClone with zwo equal references inside the first list + dc7.my2dimList = new ArrayList<>(); + dc7.my2dimList.add(listAbsent1); + dc7.my2dimList.add(listAbsent1); + dc8 = dc7.deepClone(); + Assertions.assertSame(dc8.my2dimList.get(0), dc8.my2dimList.get(1)); + //null check + dc7.my2dimList = null; + dc8 = dc7.deepClone(); + Assertions.assertTrue(dc7.deepEquals(dc8)); + Assertions.assertNull(dc8.my2dimList); + //test map correctness + dc7.my2dimList = new ArrayList<>(); + dc7.my2dimList2 = dc7.my2dimList; + dc8 = dc7.deepClone(); + Assertions.assertNotSame(dc7, dc8); + Assertions.assertNotSame(dc7.my2dimList, dc8.my2dimList); + Assertions.assertNotSame(dc7.my2dimList2, dc8.my2dimList2); + Assertions.assertTrue(dc7.deepEquals(dc8)); + Assertions.assertSame(dc8.my2dimList, dc8.my2dimList2); + } + + @Test + public void testDeepCloneSetType() { + Set set1 = new HashSet<>(); + Set setUnequal = new HashSet<>(); + for (int i = 0; i <= 10; i++) { + Integer absent1 = i; + Random rand = new Random(); + Integer unequal = rand.nextInt(); + set1.add(absent1); + setUnequal.add(unequal); + } + ClassWithSet dc9 = new ClassWithSet(); + dc9.mySet = set1; + ClassWithSet dc10 = dc9.deepClone(); + Assertions.assertNotSame(dc9, dc10); + Assertions.assertNotSame(dc9.mySet, dc10.mySet); + Assertions.assertTrue(dc9.deepEquals(dc10)); + dc9.mySet = setUnequal; + Assertions.assertFalse(dc9.deepEquals(dc10)); + dc10 = dc9.deepClone(); + Assertions.assertNotSame(dc9, dc10); + Assertions.assertNotSame(dc9.mySet, dc10.mySet); + Assertions.assertTrue(dc9.deepEquals(dc10)); + //null check + dc9.mySet = null; + dc10 = dc9.deepClone(); + Assertions.assertTrue(dc9.deepEquals(dc10)); + Assertions.assertNull(dc10.mySet); + //test Map correctness + dc9.mySet = set1; + dc9.mySet2 = dc9.mySet; + dc10 = dc9.deepClone(); + Assertions.assertNotSame(dc9, dc10); + Assertions.assertNotSame(dc9.mySet, dc10.mySet); + Assertions.assertNotSame(dc9.mySet2, dc10.mySet2); + Assertions.assertTrue(dc9.deepEquals(dc10)); + Assertions.assertSame(dc10.mySet, dc10.mySet2); + + //Test 2D set types + ClassWith2DimSet dc11 = new ClassWith2DimSet(); + dc11.my2dimSet = new HashSet<>(); + dc11.my2dimSet.add(set1); + ClassWith2DimSet dc12 = dc11.deepClone(); + Assertions.assertNotSame(dc11, dc12); + Assertions.assertNotSame(dc11.my2dimSet, dc12.my2dimSet); + Assertions.assertTrue(dc11.deepEquals(dc12)); + dc11.my2dimSet = new HashSet<>(); + Assertions.assertFalse(dc11.deepEquals(dc12)); + dc12 = dc11.deepClone(); + Assertions.assertNotSame(dc11, dc12); + Assertions.assertNotSame(dc11.my2dimSet, dc12.my2dimSet); + Assertions.assertTrue(dc11.deepEquals(dc12)); + //null check + dc11.my2dimSet = null; + dc12 = dc11.deepClone(); + Assertions.assertTrue(dc11.deepEquals(dc12)); + Assertions.assertNull(dc12.my2dimSet); + //test map correctness + dc11.my2dimSet = new HashSet<>(); + dc11.my2dimSet2 = dc11.my2dimSet; + dc12 = dc11.deepClone(); + Assertions.assertNotSame(dc11, dc12); + Assertions.assertNotSame(dc11.my2dimSet, dc12.my2dimSet); + Assertions.assertNotSame(dc11.my2dimSet2, dc12.my2dimSet2); + Assertions.assertTrue(dc11.deepEquals(dc12)); + Assertions.assertSame(dc12.my2dimSet, dc12.my2dimSet2); + } + + @Test + public void testDeepCloneOptionalType() { + ClassWithOptional dc13 = new ClassWithOptional(); + dc13.myOptionalInteger = Optional.of(1); + ClassWithOptional dc14 = dc13.deepClone(); + Assertions.assertNotSame(dc13, dc14); + Assertions.assertNotSame(dc13.myOptionalInteger, dc14.myOptionalInteger); + Assertions.assertTrue(dc13.deepEquals(dc14)); + dc13.myOptionalInteger = Optional.of(2); + Assertions.assertFalse(dc13.deepEquals(dc14)); + dc14 = dc13.deepClone(); + Assertions.assertNotSame(dc13, dc14); + Assertions.assertNotSame(dc13.myOptionalInteger, dc14.myOptionalInteger); + Assertions.assertTrue(dc13.deepEquals(dc14)); + //null check + dc13.myOptionalInteger = null; + dc14 = dc13.deepClone(); + Assertions.assertTrue(dc13.deepEquals(dc14)); + Assertions.assertNull(dc14.myOptionalInteger); + //test Map correctness + Optional opt = Optional.of(1); + dc13.myOptionalInteger = opt; + dc13.myOptionalInteger2 = opt; + dc14 = dc13.deepClone(); + Assertions.assertNotSame(dc13, dc14); + //they are the same as Integer has no deepClone method, therefore we just copy the reference + //Assertions.assertNotSame(dc13.myOptionalInteger,dc14.myOptionalInteger); + //Assertions.assertNotSame(dc13.myOptionalInteger2,dc14.myOptionalInteger2); + Assertions.assertTrue(dc13.deepEquals(dc14)); + Assertions.assertSame(dc13.myOptionalInteger, dc13.myOptionalInteger2); + Assertions.assertSame(dc14.myOptionalInteger, dc14.myOptionalInteger2); + + //Test 2D Optional + ClassWith2DimOptional dcO1 = new ClassWith2DimOptional(); + ClassWith2DimOptional dcO2; + dcO1.my2DimOptional = Optional.of(Optional.of(new B())); + dcO2 = dcO1.deepClone(); + Assertions.assertNotSame(dcO1, dcO2); + Assertions.assertNotSame(dcO1.my2DimOptional, dcO2.my2DimOptional); + Assertions.assertTrue(dcO1.deepEquals(dcO2)); + dcO1.my2DimOptional = Optional.empty(); + Assertions.assertFalse(dcO1.deepEquals(dcO2)); + dcO2 = dcO1.deepClone(); + Assertions.assertNotSame(dcO1, dcO2); + //Because Optional.empty() == Optional.empty() is true + //Assertions.assertNotSame(dcO1.my2DimOptional,dcO3.my2DimOptional); + Assertions.assertTrue(dcO1.deepEquals(dcO2)); + //null check + dcO1.my2DimOptional = null; + dcO2 = dcO1.deepClone(); + Assertions.assertTrue(dcO1.deepEquals(dcO2)); + Assertions.assertNull(dcO2.my2DimOptional); + // further null checks are not possible because we can not set optional.of(null) + //test Map correctness + dcO1.my2DimOptional = Optional.of(Optional.of(new B())); + dcO1.my2DimOptional2 = dcO1.my2DimOptional; + dcO2 = dcO1.deepClone(); + Assertions.assertNotSame(dcO1, dcO2); + Assertions.assertNotSame(dcO1.my2DimOptional, dcO2.my2DimOptional); + Assertions.assertNotSame(dcO1.my2DimOptional2, dcO2.my2DimOptional2); + Assertions.assertTrue(dcO1.deepEquals(dcO2)); + Assertions.assertSame(dcO2.my2DimOptional, dcO2.my2DimOptional2); + } + + @Test + public void testDeepCloneMapType() { + ClassWithMap dcMap1 = new ClassWithMap(); + dcMap1.myMap = null; + ClassWithMap dcMap2 = dcMap1.deepClone(); + Assertions.assertNotSame(dcMap1, dcMap2); + Assertions.assertNull(dcMap2.myMap); + Assertions.assertTrue(dcMap1.deepEquals(dcMap2)); + dcMap1.myMap = new HashMap<>(); + Assertions.assertFalse(dcMap1.deepEquals(dcMap2)); + dcMap2 = dcMap1.deepClone(); + Assertions.assertNotSame(dcMap1, dcMap2); + Assertions.assertNotSame(dcMap1.myMap, dcMap2.myMap); + Assertions.assertTrue(dcMap1.deepEquals(dcMap2)); + dcMap1.myMap.put("key", new B()); + dcMap2 = dcMap1.deepClone(); + Assertions.assertNotSame(dcMap1, dcMap2); + Assertions.assertNotSame(dcMap1.myMap, dcMap2.myMap); + Assertions.assertTrue(dcMap1.deepEquals(dcMap2)); + //null check + dcMap1.myMap = null; + dcMap2 = dcMap1.deepClone(); + Assertions.assertNotSame(dcMap1, dcMap2); + Assertions.assertNull(dcMap2.myMap); + Assertions.assertTrue(dcMap1.deepEquals(dcMap2)); + //test Map correctness + dcMap1.myMap = new HashMap<>(); + dcMap1.myMap2 = dcMap1.myMap; + dcMap2 = dcMap1.deepClone(); + Assertions.assertNotSame(dcMap1, dcMap2); + Assertions.assertNotSame(dcMap1.myMap, dcMap2.myMap); + Assertions.assertNotSame(dcMap1.myMap2, dcMap2.myMap2); + Assertions.assertSame(dcMap2.myMap, dcMap2.myMap2); + Assertions.assertTrue(dcMap1.deepEquals(dcMap2)); + + //Test 2D map types + ClassWith2DMap dcMap3 = new ClassWith2DMap(); + ClassWith2DMap dcMap4; + dcMap3.myMap = new HashMap<>(); + dcMap3.myMap.put("key", new HashMap<>()); + dcMap3.myMap.put("key2", new HashMap<>()); + dcMap3.myMap.get("key").put("key", new B()); + dcMap3.myMap.get("key").put("key2", new B()); + dcMap3.myMap.get("key2").put("key", new B()); + dcMap3.myMap.get("key2").put("key2", new B()); + dcMap3.myMap.get("key").put("key3", new B()); + dcMap3.myMap.get("key2").put("key3", new B()); + dcMap4 = dcMap3.deepClone(); + Assertions.assertNotSame(dcMap3, dcMap4); + Assertions.assertNotSame(dcMap3.myMap, dcMap4.myMap); + Assertions.assertNotSame(dcMap3.myMap.get("key"), dcMap4.myMap.get("key")); + Assertions.assertNotSame(dcMap3.myMap.get("key").get("key"), dcMap4.myMap.get("key").get( + "key")); + Assertions.assertNotSame(dcMap3.myMap.get("key").get("key2"), dcMap4.myMap.get("key").get( + "key2")); + Assertions.assertNotSame(dcMap3.myMap.get("key2"), dcMap4.myMap.get("key2")); + Assertions.assertNotSame(dcMap3.myMap.get("key2").get("key"), dcMap4.myMap.get("key2").get( + "key")); + Assertions.assertNotSame(dcMap3.myMap.get("key2").get("key2"), dcMap4.myMap.get("key2").get( + "key2")); + Assertions.assertNotSame(dcMap3.myMap.get("key").get("key3"), dcMap4.myMap.get("key").get( + "key3")); + Assertions.assertNotSame(dcMap3.myMap.get("key2").get("key3"), dcMap4.myMap.get("key2").get( + "key3")); + Assertions.assertTrue(dcMap3.deepEquals(dcMap4)); + dcMap3.myMap.get("key").put("key4", new B()); + Assertions.assertFalse(dcMap3.deepEquals(dcMap4)); + dcMap4 = dcMap3.deepClone(); + Assertions.assertNotSame(dcMap3, dcMap4); + Assertions.assertTrue(dcMap3.deepEquals(dcMap4)); + //null check + dcMap3.myMap = null; + dcMap4 = dcMap3.deepClone(); + Assertions.assertNotSame(dcMap3, dcMap4); + Assertions.assertNull(dcMap4.myMap); + Assertions.assertTrue(dcMap3.deepEquals(dcMap4)); + //test Map correctness + dcMap3.myMap = new HashMap<>(); + dcMap3.myMap2 = dcMap3.myMap; + dcMap4 = dcMap3.deepClone(); + Assertions.assertNotSame(dcMap3, dcMap4); + Assertions.assertNotSame(dcMap3.myMap, dcMap4.myMap); + Assertions.assertNotSame(dcMap3.myMap2, dcMap4.myMap2); + } + + @Test + public void testDeepCloneAssociationType() { + ClassWithAssociation dc15 = new ClassWithAssociation(); + dc15.owns = new HashSet<>(); + ClassWithAssociation dc16 = dc15.deepClone(); + Assertions.assertNotSame(dc15, dc16); + Assertions.assertTrue(dc15.deepEquals(dc16)); + dc15.owns.add(new B()); + Assertions.assertFalse(dc15.deepEquals(dc16)); + dc16 = dc15.deepClone(); + Assertions.assertNotSame(dc15, dc16); + Assertions.assertNotSame(dc15.owns, dc16.owns); + Assertions.assertTrue(dc15.deepEquals(dc16)); + //null check + dc15.owns = null; + dc16 = dc15.deepClone(); + Assertions.assertTrue(dc15.deepEquals(dc16)); + Assertions.assertNull(dc16.owns); + //test Map correctness + dc15.owns = new HashSet<>(); + dc15.owns2 = dc15.owns; + dc16 = dc15.deepClone(); + Assertions.assertNotSame(dc15, dc16); + Assertions.assertNotSame(dc15.owns, dc16.owns); + Assertions.assertNotSame(dc15.owns2, dc16.owns2); + Assertions.assertTrue(dc15.deepEquals(dc16)); + Assertions.assertSame(dc16.owns, dc16.owns2); + } + + @Test + public void testDeepCloneCompositionType() { + ClassWithComposition dc17 = new ClassWithComposition(); + dc17.many = new HashSet<>(); + ClassWithComposition dc18 = dc17.deepClone(); + Assertions.assertNotSame(dc17, dc18); + Assertions.assertNotSame(dc17.many, dc18.many); + Assertions.assertTrue(dc17.deepEquals(dc18)); + dc17.many.add(new B()); + Assertions.assertFalse(dc17.deepEquals(dc18)); + dc18 = dc17.deepClone(); + Assertions.assertNotSame(dc17, dc18); + Assertions.assertNotSame(dc17.many, dc18.many); + Assertions.assertTrue(dc17.deepEquals(dc18)); + dc17.one = new B(); + Assertions.assertFalse(dc17.deepEquals(dc18)); + dc18 = dc17.deepClone(); + Assertions.assertNotSame(dc17, dc18); + Assertions.assertNotSame(dc17.one, dc18.one); + Assertions.assertTrue(dc17.deepEquals(dc18)); + dc17.opt = Optional.of(new B()); + Assertions.assertFalse(dc17.deepEquals(dc18)); + dc18 = dc17.deepClone(); + Assertions.assertNotSame(dc17, dc18); + Assertions.assertNotSame(dc17.opt, dc18.opt); + Assertions.assertTrue(dc17.deepEquals(dc18)); + dc17.opt = Optional.empty(); + Assertions.assertFalse(dc17.deepEquals(dc18)); + dc18 = dc17.deepClone(); + Assertions.assertNotSame(dc17, dc18); + //Assertions.assertNotSame(dc17.opt,dc18.opt); + // as Optional.empty() == Optional.empty() is true + Assertions.assertTrue(dc17.deepEquals(dc18)); + //null check + dc17.many = null; + dc18 = dc17.deepClone(); + Assertions.assertTrue(dc17.deepEquals(dc18)); + Assertions.assertNull(dc18.many); + dc17.one = null; + dc18 = dc17.deepClone(); + Assertions.assertTrue(dc17.deepEquals(dc18)); + Assertions.assertNull(dc18.one); + dc17.opt = null; + dc18 = dc17.deepClone(); + Assertions.assertTrue(dc17.deepEquals(dc18)); + Assertions.assertNull(dc18.opt); + //test Map correctness + dc17.many = new HashSet<>(); + dc17.many2 = dc17.many; + dc17.one = new B(); + dc17.one2 = dc17.one; + dc17.opt = Optional.of(new B()); + dc17.opt2 = dc17.opt; + dc18 = dc17.deepClone(); + Assertions.assertNotSame(dc17, dc18); + Assertions.assertNotSame(dc17.many, dc18.many); + Assertions.assertNotSame(dc17.many2, dc18.many2); + Assertions.assertNotSame(dc17.one, dc18.one); + Assertions.assertNotSame(dc17.one2, dc18.one2); + Assertions.assertNotSame(dc17.opt, dc18.opt); + Assertions.assertNotSame(dc17.opt2, dc18.opt2); + Assertions.assertTrue(dc17.deepEquals(dc18)); + Assertions.assertSame(dc18.many, dc18.many2); + Assertions.assertSame(dc18.one, dc18.one2); + Assertions.assertSame(dc18.opt, dc18.opt2); + } + + @Test + public void testDeepCloneCircularRelations() { + ClassCircular1 dc19 = new ClassCircular1(); + ClassCircular2 dc20 = new ClassCircular2(); + dc19.myClassCircular2 = dc20; + dc20.myClassCircular1 = dc19; + ClassCircular1 dc21 = dc19.deepClone(); + Assertions.assertSame(dc21, dc21.myClassCircular2.myClassCircular1); + Assertions.assertSame(dc21.myClassCircular2, + dc21.myClassCircular2.myClassCircular1.myClassCircular2); + Assertions.assertNotSame(dc19, dc21); + Assertions.assertNotSame(dc19.myClassCircular2, dc21.myClassCircular2); + + } + + @Test + public void testDeepCloneCheckSameCreation() { + List listAbsent1 = new ArrayList<>(); + for (int i = 0; i <= 10; i++) { + listAbsent1.add(i); + } + ClassWith2DimList dc22 = new ClassWith2DimList(); + dc22.my2dimList = new ArrayList<>(); + dc22.my2dimList.add(listAbsent1); + dc22.my2dimList.add(listAbsent1); + ClassWith2DimList dc23 = dc22.deepClone(); + Assertions.assertSame(dc23.my2dimList.get(0), dc23.my2dimList.get(1)); + Assertions.assertNotSame(dc22, dc23); + Assertions.assertNotSame(dc22.my2dimList, dc23.my2dimList); + Assertions.assertTrue(dc22.my2dimList.get(0) == dc22.my2dimList.get(1) && dc23.my2dimList.get(0) + == dc23.my2dimList.get(1)); + } + + @Test + public void testDeepCloneInterfaceTypes() { + Level2class deepCloneLevel2Class = new Level2class(); + deepCloneLevel2Class.myInt = 1; + ClassWithInterface dc27 = new ClassWithInterface(); + dc27.myInterface = deepCloneLevel2Class; + ClassWithInterface dc28 = dc27.deepClone(); + Assertions.assertNotSame(dc27, dc28); + Assertions.assertNotSame(dc27.myInterface, dc28.myInterface); + Assertions.assertTrue(dc27.deepEquals(dc28)); + deepCloneLevel2Class.myInt = 2; + Assertions.assertFalse(dc27.deepEquals(dc28)); + dc28 = dc27.deepClone(); + Assertions.assertNotSame(dc27, dc28); + Assertions.assertNotSame(dc27.myInterface, dc28.myInterface); + Assertions.assertTrue(dc27.deepEquals(dc28)); + Level3class deepCloneLevel3Class = new Level3class(); + deepCloneLevel3Class.myInt = 2; + Level3class dc29 = deepCloneLevel3Class.deepClone(); + Assertions.assertNotSame(deepCloneLevel3Class, dc29); + Assertions.assertTrue(deepCloneLevel3Class.deepEquals(dc29)); + + //test lists + Level2class level2class1 = new Level2class(); + level2class1.myInt = 1; + Level2class level2class2 = new Level2class(); + level2class2.myInt = 2; + Level3class level3class1 = new Level3class(); + level3class1.myInt = 3; + Level3class level3class2 = new Level3class(); + level3class2.myInt = 4; + dc27.many = new HashSet<>(); + dc27.many.add(level2class1); + dc27.many.add(level2class2); + dc27.many.add(level3class1); + dc27.many.add(level3class2); + + dc28 = dc27.deepClone(); + Assertions.assertNotSame(dc27, dc28); + Assertions.assertNotSame(dc27.myInterface, dc28.myInterface); + Level2class level2class1Cloned = (Level2class) dc28.many.stream().filter( + m -> m instanceof Level2class && ((Level2class) m).myInt == 1).findFirst().get(); + Level2class level2class2Cloned = (Level2class) dc28.many.stream().filter( + m -> m instanceof Level2class && ((Level2class) m).myInt == 2).findFirst().get(); + Level3class level3class1Cloned = (Level3class) dc28.many.stream().filter( + m -> m instanceof Level3class && ((Level3class) m).myInt == 3).findFirst().get(); + Level3class level3class2Cloned = (Level3class) dc28.many.stream().filter( + m -> m instanceof Level3class && ((Level3class) m).myInt == 4).findFirst().get(); + Assertions.assertNotSame(level2class1, level2class1Cloned); + Assertions.assertNotSame(level2class2, level2class2Cloned); + Assertions.assertNotSame(level3class1, level3class1Cloned); + Assertions.assertNotSame(level3class2, level3class2Cloned); + Assertions.assertTrue(level2class1.deepEquals(level2class1Cloned)); + Assertions.assertTrue(level2class2.deepEquals(level2class2Cloned)); + Assertions.assertTrue(level3class1.deepEquals(level3class1Cloned)); + Assertions.assertTrue(level3class2.deepEquals(level3class2Cloned)); + } + + @Test + public void testDeepCloneMultipleTypesAndDimensions() { + List listAbsent1 = new ArrayList<>(); + for (int i = 0; i <= 10; i++) { + listAbsent1.add(i); + } + AllTogether dc24 = new AllTogether(); + dc24.owns = new HashSet<>(); + dc24.manyClassWith2DimList = new HashSet<>(); + dc24.myBool = true; + dc24.myInt = -12121; + dc24.optClassWith2DimList = Optional.empty(); + dc24.oneClassWith2DimList = null; + AllTogether dc25 = dc24.deepClone(); + Assertions.assertNotSame(dc24, dc25); + Assertions.assertTrue(dc24.deepEquals(dc25)); + Assertions.assertNotSame(dc24.manyClassWith2DimList, dc25.manyClassWith2DimList); + Assertions.assertNotSame(dc24.owns, dc25.owns); + //are the same as they are null values + //Assertions.assertNotSame(dc24.oneClassWith2DimList,dc25.oneClassWith2DimList); + //Assertions.assertNotSame(dc24.optClassWith2DimList,dc25.optClassWith2DimList); + //Assertions.assertNotSame(dc24.manyClassWith2DimList,dc25.manyClassWith2DimList); + ClassWith2DimList dc26 = new ClassWith2DimList(); + + dc26.my2dimList = new ArrayList<>(); + dc26.my2dimList.add(listAbsent1); + dc24.manyClassWith2DimList.add(dc26); + dc24.optClassWith2DimList = Optional.of(dc26); + dc24.oneClassWith2DimList = dc26; + dc24.owns = null; + dc25 = dc24.deepClone(); + Assertions.assertNotSame(dc24.oneClassWith2DimList, dc25.oneClassWith2DimList); + Assertions.assertNotSame(dc24.optClassWith2DimList, dc25.optClassWith2DimList); + Assertions.assertNotSame(dc24.manyClassWith2DimList, dc25.manyClassWith2DimList); + Assertions.assertSame(dc25.manyClassWith2DimList.toArray()[0], dc25.oneClassWith2DimList); + Assertions.assertSame(dc25.manyClassWith2DimList.toArray()[0], dc25.optClassWith2DimList.get()); + Assertions.assertSame(dc25.optClassWith2DimList.get(), dc25.oneClassWith2DimList); + } + + @Test + public void testDeepCloneEnumTypes() { + ClassWithEnum dc29 = new ClassWithEnum(); + dc29.myEnum = TestEnum.ERROR; + ClassWithEnum dc30 = dc29.deepClone(); + Assertions.assertNotSame(dc29, dc30); + Assertions.assertTrue(dc29.deepEquals(dc30)); + dc29.myEnum = TestEnum.IDLE; + Assertions.assertFalse(dc29.deepEquals(dc30)); + dc30 = dc29.deepClone(); + Assertions.assertNotSame(dc29, dc30); + Assertions.assertTrue(dc29.deepEquals(dc30)); + } + + @Test + public void testDeepCloneWithBuilder() { + try { + File myObj = new File( + "target/cdGenOutTest/DeepCloneAndDeepEqualsDecoratorTest/TestDeepCloneAndDeepEquals/ClassWithBuilder.java"); + Scanner myReader = new Scanner(myObj); + StringBuilder stringBuilder = new StringBuilder(); + while (myReader.hasNextLine()) { + stringBuilder.append(myReader.nextLine()); + } + myReader.close(); + + //find deepClone1 method: + String regex = + "public\\s+TestDeepCloneAndDeepEquals\\.ClassWithBuilder\\s+deepClone\\s*\\(\\s*Map\\s+map\\s*\\)\\s*\\{[\\s\\S]*?new\\s+TestDeepCloneAndDeepEquals\\.ClassWithBuilderBuilder\\(\\)\\.unsafeBuild\\(\\);[\\s\\S]*?}"; + + Pattern pattern = Pattern.compile(regex); + Matcher matcher = pattern.matcher(stringBuilder.toString()); + + if (!matcher.find()) { + Assertions.fail(); + } + } + catch (FileNotFoundException e) { + Assertions.fail(); + } + + ClassWithBuilder classWithBuilder = new ClassWithBuilderBuilder().unsafeBuild(); + classWithBuilder.myInt = 1; + ClassWithBuilder classWithBuilderCloned = classWithBuilder.deepClone(); + Assertions.assertNotSame(classWithBuilder, classWithBuilderCloned); + Assertions.assertTrue(classWithBuilder.deepEquals(classWithBuilderCloned)); + } + +} diff --git a/cdlang/src/cdGenIntTest/java/observer/ObserverDecoratorResultTest.java b/cdlang/src/cdGenIntTest/java/observer/ObserverDecoratorResultTest.java new file mode 100644 index 000000000..89d971f35 --- /dev/null +++ b/cdlang/src/cdGenIntTest/java/observer/ObserverDecoratorResultTest.java @@ -0,0 +1,228 @@ +/* (c) https://github.com/MontiCore/monticore */ +package observer; + +import TestObserver.B; +import TestObserver.IOtherCObservable; +import TestObserver.IOtherCObserver; +import TestObserver.OtherC; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.*; + +/** + * Test the result of the Getter Decorator. + */ +public class ObserverDecoratorResultTest { + + @Test + public void test() throws Exception { + checkClassAndMethodExistence(); + + TestObserver.Observer observer = new TestObserver.Observer(); + TestObserver.Observer observer2 = new TestObserver.Observer(); + + OtherC pojo = new OtherC(); + pojo.addObserver(observer); + pojo.addObserver(observer2); + + B b = new B(); + Set set = new HashSet<>(Set.of(b)); + + //check if notify methods are implemented correctly + Assertions.assertEquals(0, observer.getCountUpdateObserver()); + Assertions.assertEquals(0, observer2.getCountUpdateObserver()); + pojo.notifyObserverMyBool(pojo, true); + Assertions.assertEquals(1, observer.getCountUpdateObserverMyBool()); + Assertions.assertEquals(1, observer2.getCountUpdateObserverMyBool()); + + Assertions.assertEquals(0, observer.getCountUpdateObserverMyInt()); + Assertions.assertEquals(0, observer2.getCountUpdateObserverMyInt()); + pojo.notifyObserverMyInt(pojo, 42); + Assertions.assertEquals(1, observer.getCountUpdateObserverMyInt()); + Assertions.assertEquals(1, observer2.getCountUpdateObserverMyInt()); + + Assertions.assertEquals(0, observer.getCountUpdateObserverManyB()); + Assertions.assertEquals(0, observer2.getCountUpdateObserverManyB()); + pojo.notifyObserverManyB(pojo, set); + Assertions.assertEquals(1, observer.getCountUpdateObserverManyB()); + Assertions.assertEquals(1, observer2.getCountUpdateObserverManyB()); + + Assertions.assertEquals(0, observer.getCountUpdateObserverOptB()); + Assertions.assertEquals(0, observer2.getCountUpdateObserverOptB()); + pojo.notifyObserverOptB(pojo, Optional.of(b)); + Assertions.assertEquals(1, observer.getCountUpdateObserverOptB()); + Assertions.assertEquals(1, observer2.getCountUpdateObserverOptB()); + + Assertions.assertEquals(0, observer.getCountUpdateObserverOneB()); + Assertions.assertEquals(0, observer2.getCountUpdateObserverOneB()); + pojo.notifyObserverOneB(pojo, b); + Assertions.assertEquals(1, observer.getCountUpdateObserverOneB()); + Assertions.assertEquals(1, observer2.getCountUpdateObserverOneB()); + + Assertions.assertEquals(0, observer.getCountUpdateObserver()); + Assertions.assertEquals(0, observer2.getCountUpdateObserver()); + pojo.notifyObservers(pojo); + Assertions.assertEquals(1, observer.getCountUpdateObserver()); + Assertions.assertEquals(1, observer2.getCountUpdateObserver()); + + //check if setters are implemented correctly + pojo.setMyInt(42); + Assertions.assertEquals(2, observer.getCountUpdateObserverMyInt()); + Assertions.assertEquals(2, observer2.getCountUpdateObserverMyInt()); + + pojo.setMyBool(true); + Assertions.assertEquals(2, observer.getCountUpdateObserverMyBool()); + Assertions.assertEquals(2, observer2.getCountUpdateObserverMyBool()); + + pojo.setManyB(new HashSet<>()); + Assertions.assertEquals(2, observer.getCountUpdateObserverManyB()); + Assertions.assertEquals(2, observer2.getCountUpdateObserverManyB()); + + pojo.setOptB(null); + Assertions.assertEquals(2, observer.getCountUpdateObserverOptB()); + Assertions.assertEquals(2, observer2.getCountUpdateObserverOptB()); + + pojo.setOneB(null); + Assertions.assertEquals(2, observer.getCountUpdateObserverOneB()); + Assertions.assertEquals(2, observer2.getCountUpdateObserverOneB()); + + //check if removeObserver works + pojo.removeObserver(observer); + + pojo.setMyBool(false); + Assertions.assertEquals(2, observer.getCountUpdateObserverMyBool()); + Assertions.assertEquals(3, observer2.getCountUpdateObserverMyBool()); + + pojo.removeObserver(observer2); + + pojo.setMyBool(false); + Assertions.assertEquals(2, observer.getCountUpdateObserverMyBool()); + Assertions.assertEquals(3, observer2.getCountUpdateObserverMyBool()); + } + + /** + * Check for the existence of the interfaces and the methods in the interfaces as well as in the + * pojo + * We check for the following: + * Class: TestObserver.IOtherCObservable with methods notify and notify${attributeName} and + * addObserver and removeObserver + * Class: TestObserver.OtherC with methods notify and notify${attributeName} and addObserver and + * removeObserver + * Class: TestObserver.IOtherCObserver with methods update and update${attributeName} + * Method: TestObserver.IOtherCObservable + * + * @throws Exception when the class or method does not exist + */ + @Test + public void checkClassAndMethodExistence() throws Exception { + //check for the existence of the interfaces + Class interfaceObservable = Class.forName("TestObserver.IOtherCObservable"); + Assertions.assertTrue(Modifier.isPublic(interfaceObservable.getModifiers())); + Assertions.assertTrue(interfaceObservable.isInterface()); + + Class interfaceObserver = Class.forName("TestObserver.IOtherCObserver"); + Assertions.assertTrue(Modifier.isPublic(interfaceObserver.getModifiers())); + Assertions.assertTrue(interfaceObserver.isInterface()); + + Class clazz = Class.forName("TestObserver.OtherC"); + Assertions.assertTrue(Modifier.isPublic(clazz.getModifiers())); + Assertions.assertFalse(clazz.isInterface()); + + //check for the methods in the interface Observe + Method[] methods = interfaceObservable.getDeclaredMethods(); + Assertions.assertEquals(9, methods.length); + + //check methods of the pojo + Method addObserver = IOtherCObservable.class.getDeclaredMethod("addObserver", + TestObserver.IOtherCObserver.class); + Assertions.assertTrue(Modifier.isPublic(addObserver.getModifiers())); + + Method removeObserver = IOtherCObservable.class.getDeclaredMethod("removeObserver", + TestObserver.IOtherCObserver.class); + Assertions.assertTrue(Modifier.isPublic(removeObserver.getModifiers())); + + Method notifyObservers = IOtherCObservable.class.getDeclaredMethod("notifyObservers", + OtherC.class); + Assertions.assertTrue(Modifier.isPublic(notifyObservers.getModifiers())); + + Method notifyObserverInt = IOtherCObservable.class.getDeclaredMethod("notifyObserverMyInt", + OtherC.class, int.class); + Assertions.assertTrue(Modifier.isPublic(notifyObserverInt.getModifiers())); + + Method notifyObserverBoolean = IOtherCObservable.class.getDeclaredMethod("notifyObserverMyBool", + OtherC.class, boolean.class); + Assertions.assertTrue(Modifier.isPublic(notifyObserverBoolean.getModifiers())); + + Method notifyObserverSet = IOtherCObservable.class.getDeclaredMethod("notifyObserverManyB", + OtherC.class, Set.class); + Assertions.assertTrue(Modifier.isPublic(notifyObserverSet.getModifiers())); + + Method notifyObserverOptional = IOtherCObservable.class.getDeclaredMethod("notifyObserverOptB", + OtherC.class, Optional.class); + Assertions.assertTrue(Modifier.isPublic(notifyObserverOptional.getModifiers())); + + Method notifyObserverB = IOtherCObservable.class.getDeclaredMethod("notifyObserverOneB", + OtherC.class, B.class); + Assertions.assertTrue(Modifier.isPublic(notifyObserverB.getModifiers())); + + //check for the methods in the interface Observe + Method notifyPojoAddObserver = OtherC.class.getDeclaredMethod("addObserver", + TestObserver.IOtherCObserver.class); + Assertions.assertTrue(Modifier.isPublic(notifyPojoAddObserver.getModifiers())); + + Method notifyPojoRemoveObserver = OtherC.class.getDeclaredMethod("removeObserver", + TestObserver.IOtherCObserver.class); + Assertions.assertTrue(Modifier.isPublic(notifyPojoRemoveObserver.getModifiers())); + + Method notifyPojoNotifyObservers = OtherC.class.getDeclaredMethod("notifyObservers", + OtherC.class); + Assertions.assertTrue(Modifier.isPublic(notifyPojoNotifyObservers.getModifiers())); + + Method notifyPojoNotifyObserverInt = OtherC.class.getDeclaredMethod("notifyObserverMyInt", + OtherC.class, int.class); + Assertions.assertTrue(Modifier.isPublic(notifyPojoNotifyObserverInt.getModifiers())); + + Method notifyPojoNotifyObserverBoolean = OtherC.class.getDeclaredMethod("notifyObserverMyBool", + OtherC.class, boolean.class); + Assertions.assertTrue(Modifier.isPublic(notifyPojoNotifyObserverBoolean.getModifiers())); + + Method notifyPojoNotifyObserverSet = OtherC.class.getDeclaredMethod("notifyObserverManyB", + OtherC.class, Set.class); + Assertions.assertTrue(Modifier.isPublic(notifyPojoNotifyObserverSet.getModifiers())); + + Method notifyPojoNotifyObserverOptional = OtherC.class.getDeclaredMethod("notifyObserverOptB", + OtherC.class, Optional.class); + Assertions.assertTrue(Modifier.isPublic(notifyPojoNotifyObserverOptional.getModifiers())); + + Method notifyPojoNotifyObserverB = OtherC.class.getDeclaredMethod("notifyObserverOneB", + OtherC.class, B.class); + Assertions.assertTrue(Modifier.isPublic(notifyPojoNotifyObserverB.getModifiers())); + + //check for the methods in the interface Observer + Method update = IOtherCObserver.class.getDeclaredMethod("update", OtherC.class); + Assertions.assertTrue(Modifier.isPublic(update.getModifiers())); + + Method updateObserverMyInt = IOtherCObserver.class.getDeclaredMethod("updateObserverMyInt", + OtherC.class, int.class); + Assertions.assertTrue(Modifier.isPublic(updateObserverMyInt.getModifiers())); + + Method updateObserverMyBool = IOtherCObserver.class.getDeclaredMethod("updateObserverMyBool", + OtherC.class, boolean.class); + Assertions.assertTrue(Modifier.isPublic(updateObserverMyBool.getModifiers())); + + Method updateObserverManyB = IOtherCObserver.class.getDeclaredMethod("updateObserverManyB", + OtherC.class, Set.class); + Assertions.assertTrue(Modifier.isPublic(updateObserverManyB.getModifiers())); + + Method updateObserverOptB = IOtherCObserver.class.getDeclaredMethod("updateObserverOptB", + OtherC.class, Optional.class); + Assertions.assertTrue(Modifier.isPublic(updateObserverOptB.getModifiers())); + + Method updateObserverOneB = IOtherCObserver.class.getDeclaredMethod("updateObserverOneB", + OtherC.class, B.class); + Assertions.assertTrue(Modifier.isPublic(updateObserverOneB.getModifiers())); + } + +} diff --git a/cdlang/src/cdGenIntTest/java/visitor/InheritanceVisitorDecoratorResultTest.java b/cdlang/src/cdGenIntTest/java/visitor/InheritanceVisitorDecoratorResultTest.java new file mode 100644 index 000000000..d7829b5b2 --- /dev/null +++ b/cdlang/src/cdGenIntTest/java/visitor/InheritanceVisitorDecoratorResultTest.java @@ -0,0 +1,667 @@ +/* (c) https://github.com/MontiCore/monticore */ +package visitor; + +import TestInheritanceVisitor.*; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.io.File; +import java.io.FileNotFoundException; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class InheritanceVisitorDecoratorResultTest { + + Visitor visitor; + + @Test + public void test() { + testPrimitiveTypes(); + testStringTypes(); + testArrayTypes(); + testPojoClassTypes(); + testListTypes(); + testSetTypes(); + testOptionalTypes(); + testMapTypes(); + testAssociationTypes(); + testCompositionTypes(); + testCircularRelations(); + testAllTogether(); + testClassToBeTopped(); + testInterfaceAndInherit(); + testCorrectVisitorCallOrder(); + } + + @Test + public void testPrimitiveTypes() { + ClassWithPrimitiveType classWithPrimitiveType = new ClassWithPrimitiveType(); + classWithPrimitiveType.myInt = 1; + visitor = new Visitor(); + classWithPrimitiveType.accept(visitor); + Assertions.assertSame(1, visitor.countVisitClassWithPrimitiveType); + Assertions.assertSame(1, visitor.countEndVisitClassWithPrimitiveType); + //check every stack is empty + Assertions.assertTrue(visitor.isAllEmpty()); + } + + @Test + public void testStringTypes() { + ClassWithString classWithString = new ClassWithString(); + classWithString.myString = "string"; + classWithString.myString2 = "string2"; + visitor = new Visitor(); + classWithString.accept(visitor); + Assertions.assertSame(1, visitor.countVisitClassWithString); + Assertions.assertSame(1, visitor.countEndVisitClassWithString); + //check every stack is empty + Assertions.assertTrue(visitor.isAllEmpty()); + } + + @Test + public void testArrayTypes() { + ClassWithArray classWithArray = new ClassWithArray(); + ClassWithPrimitiveType classWithPrimitiveType = new ClassWithPrimitiveType(); + classWithArray.arrayOfString = new ClassWithPrimitiveType[] { classWithPrimitiveType }; + visitor = new Visitor(); + classWithArray.accept(visitor); + Assertions.assertSame(1, visitor.countVisitClassWithArray); + Assertions.assertSame(1, visitor.countEndVisitClassWithArray); + Assertions.assertSame(1, visitor.countVisitClassWithPrimitiveType); + Assertions.assertSame(1, visitor.countEndVisitClassWithPrimitiveType); + classWithArray.arrayOfString2 = new ClassWithPrimitiveType[] { classWithPrimitiveType }; + visitor = new Visitor(); + classWithArray.accept(visitor); + Assertions.assertSame(1, visitor.countVisitClassWithArray); + Assertions.assertSame(1, visitor.countEndVisitClassWithArray); + Assertions.assertSame(2, visitor.countVisitClassWithPrimitiveType); + Assertions.assertSame(2, visitor.countEndVisitClassWithPrimitiveType); + + ClassWith3DimArray classWith3DimArray = new ClassWith3DimArray(); + classWith3DimArray.threeDimArrayOfString = new ClassWithPrimitiveType[][][] { { { + classWithPrimitiveType }, { classWithPrimitiveType } } }; + visitor = new Visitor(); + classWith3DimArray.accept(visitor); + Assertions.assertSame(1, visitor.countVisitClassWith3DimArray); + Assertions.assertSame(1, visitor.countEndVisitClassWith3DimArray); + Assertions.assertSame(2, visitor.countEndVisitClassWithPrimitiveType); + Assertions.assertSame(2, visitor.countVisitClassWithPrimitiveType); + classWith3DimArray.threeDimArrayOfString2 = new ClassWithPrimitiveType[][][] { { { + classWithPrimitiveType }, { classWithPrimitiveType } } }; + visitor = new Visitor(); + classWith3DimArray.accept(visitor); + Assertions.assertSame(1, visitor.countVisitClassWith3DimArray); + Assertions.assertSame(1, visitor.countEndVisitClassWith3DimArray); + Assertions.assertSame(4, visitor.countEndVisitClassWithPrimitiveType); + Assertions.assertSame(4, visitor.countVisitClassWithPrimitiveType); + //check every stack is empty + Assertions.assertTrue(visitor.isAllEmpty()); + } + + @Test + public void testPojoClassTypes() { + ClassWithPojoClassType classWithPojoClassType = new ClassWithPojoClassType(); + ClassWithPrimitiveType classWithPrimitiveType = new ClassWithPrimitiveType(); + classWithPojoClassType.pojoType = classWithPrimitiveType; + visitor = new Visitor(); + classWithPojoClassType.accept(visitor); + Assertions.assertSame(1, visitor.countVisitClassWithPojoClassType); + Assertions.assertSame(1, visitor.countEndVisitClassWithPojoClassType); + Assertions.assertSame(1, visitor.countVisitClassWithPrimitiveType); + Assertions.assertSame(1, visitor.countEndVisitClassWithPrimitiveType); + classWithPojoClassType.pojoType2 = classWithPrimitiveType; + visitor = new Visitor(); + classWithPojoClassType.accept(visitor); + Assertions.assertSame(1, visitor.countVisitClassWithPojoClassType); + Assertions.assertSame(1, visitor.countEndVisitClassWithPojoClassType); + Assertions.assertSame(2, visitor.countVisitClassWithPrimitiveType); + Assertions.assertSame(2, visitor.countEndVisitClassWithPrimitiveType); + //check every stack is empty + Assertions.assertTrue(visitor.isAllEmpty()); + } + + @Test + public void testListTypes() { + ArrayList oneDimArrayList = new ArrayList<>(); + ClassWithList classWithList = new ClassWithList(); + classWithList.myIntegerList = oneDimArrayList; + List> twoDimArrayList = new ArrayList<>(); + visitor = new Visitor(); + classWithList.accept(visitor); + Assertions.assertSame(1, visitor.countVisitClassWithList); + Assertions.assertSame(1, visitor.countEndVisitClassWithList); + + ClassWith2DimList classWith2DimList = new ClassWith2DimList(); + classWith2DimList.my2dimList = twoDimArrayList; + visitor = new Visitor(); + classWith2DimList.accept(visitor); + Assertions.assertSame(1, visitor.countVisitClassWith2DimList); + Assertions.assertSame(1, visitor.countEndVisitClassWith2DimList); + //check every stack is empty + Assertions.assertTrue(visitor.isAllEmpty()); + } + + @Test + public void testSetTypes() { + ClassWithSet classWithSet = new ClassWithSet(); + Set oneDimHashSet = new HashSet<>(); + Set> twoDimHashSet = new HashSet<>(); + twoDimHashSet.add(oneDimHashSet); + classWithSet.mySet = oneDimHashSet; + visitor = new Visitor(); + classWithSet.accept(visitor); + Assertions.assertSame(1, visitor.countVisitClassWithSet); + Assertions.assertSame(1, visitor.countEndVisitClassWithSet); + + ClassWith2DimSet classWith2DimSet = new ClassWith2DimSet(); + classWith2DimSet.my2dimSet = twoDimHashSet; + visitor = new Visitor(); + classWith2DimSet.accept(visitor); + Assertions.assertSame(1, visitor.countVisitClassWith2DimSet); + Assertions.assertSame(1, visitor.countEndVisitClassWith2DimSet); + //check every stack is empty + Assertions.assertTrue(visitor.isAllEmpty()); + } + + @Test + public void testOptionalTypes() { + ClassWithOptional classWithOptional = new ClassWithOptional(); + classWithOptional.myOptionalInteger = Optional.of(Integer.MAX_VALUE); + visitor = new Visitor(); + classWithOptional.accept(visitor); + Assertions.assertSame(1, visitor.countVisitClassWithOptional); + Assertions.assertSame(1, visitor.countEndVisitClassWithOptional); + + classWithOptional.myOptionalInteger2 = Optional.empty(); + visitor = new Visitor(); + classWithOptional.accept(visitor); + Assertions.assertSame(1, visitor.countVisitClassWithOptional); + Assertions.assertSame(1, visitor.countEndVisitClassWithOptional); + + ClassWith2DimOptional classWith2DimOptional = new ClassWith2DimOptional(); + classWith2DimOptional.my2DimOptional = Optional.empty(); + classWith2DimOptional.my2DimOptional2 = Optional.of(Optional.empty()); + visitor = new Visitor(); + classWith2DimOptional.accept(visitor); + Assertions.assertSame(1, visitor.countVisitClassWith2DimOptional); + Assertions.assertSame(1, visitor.countEndVisitClassWith2DimOptional); + + classWith2DimOptional.my2DimOptional = Optional.of(Optional.of(new B())); + visitor = new Visitor(); + classWith2DimOptional.accept(visitor); + Assertions.assertSame(1, visitor.countVisitClassWith2DimOptional); + Assertions.assertSame(1, visitor.countEndVisitClassWith2DimOptional); + Assertions.assertSame(1, visitor.countVisitB); + Assertions.assertSame(1, visitor.countEndVisitB); + + classWith2DimOptional.my2DimOptional2 = Optional.of(Optional.of(new B())); + visitor = new Visitor(); + classWith2DimOptional.accept(visitor); + Assertions.assertSame(1, visitor.countVisitClassWith2DimOptional); + Assertions.assertSame(1, visitor.countEndVisitClassWith2DimOptional); + Assertions.assertSame(2, visitor.countVisitB); + Assertions.assertSame(2, visitor.countEndVisitB); + //check every stack is empty + Assertions.assertTrue(visitor.isAllEmpty()); + } + + @Test + public void testMapTypes() { + ClassWithMap classWithMap = new ClassWithMap(); + HashMap oneDimHashMap = new HashMap<>(); + oneDimHashMap.put("first", new B()); + HashMap> twoDimHashMap = new HashMap<>(); + twoDimHashMap.put("String", oneDimHashMap); + classWithMap.myMap = oneDimHashMap; + visitor = new Visitor(); + classWithMap.accept(visitor); + Assertions.assertSame(1, visitor.countVisitClassWithMap); + Assertions.assertSame(1, visitor.countEndVisitClassWithMap); + Assertions.assertSame(1, visitor.countVisitB); + Assertions.assertSame(1, visitor.countEndVisitB); + + classWithMap.myMap2 = oneDimHashMap; + visitor = new Visitor(); + classWithMap.accept(visitor); + Assertions.assertSame(1, visitor.countVisitClassWithMap); + Assertions.assertSame(1, visitor.countEndVisitClassWithMap); + Assertions.assertSame(2, visitor.countVisitB); + Assertions.assertSame(2, visitor.countEndVisitB); + + ClassWith2DimMap classWith2DimMap = new ClassWith2DimMap(); + classWith2DimMap.myMap = twoDimHashMap; + visitor = new Visitor(); + classWith2DimMap.accept(visitor); + Assertions.assertSame(1, visitor.countVisitClassWith2DimMap); + Assertions.assertSame(1, visitor.countEndVisitClassWith2DimMap); + Assertions.assertSame(1, visitor.countVisitB); + Assertions.assertSame(1, visitor.countEndVisitB); + + classWith2DimMap.myMap2 = twoDimHashMap; + visitor = new Visitor(); + classWith2DimMap.accept(visitor); + Assertions.assertSame(1, visitor.countVisitClassWith2DimMap); + Assertions.assertSame(1, visitor.countEndVisitClassWith2DimMap); + Assertions.assertSame(2, visitor.countVisitB); + Assertions.assertSame(2, visitor.countEndVisitB); + //check every stack is empty + Assertions.assertTrue(visitor.isAllEmpty()); + } + + @Test + public void testAssociationTypes() { + ClassWithAssociation classWithAssociation = new ClassWithAssociation(); + Set setOfB = new HashSet<>(); + setOfB.add(new B()); + classWithAssociation.owns = setOfB; + visitor = new Visitor(); + classWithAssociation.accept(visitor); + Assertions.assertSame(1, visitor.countVisitClassWithAssociation); + Assertions.assertSame(1, visitor.countEndVisitClassWithAssociation); + Assertions.assertSame(1, visitor.countVisitB); + Assertions.assertSame(1, visitor.countEndVisitB); + + classWithAssociation.owns2 = setOfB; + visitor = new Visitor(); + classWithAssociation.accept(visitor); + Assertions.assertSame(1, visitor.countVisitClassWithAssociation); + Assertions.assertSame(1, visitor.countEndVisitClassWithAssociation); + Assertions.assertSame(2, visitor.countVisitB); + Assertions.assertSame(2, visitor.countEndVisitB); + //check every stack is empty + Assertions.assertTrue(visitor.isAllEmpty()); + } + + @Test + public void testCompositionTypes() { + ClassWithComposition classWithComposition = new ClassWithComposition(); + Set setOfB = new HashSet<>(); + setOfB.add(new B()); + classWithComposition.one = new B(); + visitor = new Visitor(); + classWithComposition.accept(visitor); + Assertions.assertSame(1, visitor.countVisitClassWithComposition); + Assertions.assertSame(1, visitor.countEndVisitClassWithComposition); + Assertions.assertSame(1, visitor.countVisitB); + Assertions.assertSame(1, visitor.countEndVisitB); + + classWithComposition.many = setOfB; + visitor = new Visitor(); + classWithComposition.accept(visitor); + Assertions.assertSame(1, visitor.countVisitClassWithComposition); + Assertions.assertSame(1, visitor.countEndVisitClassWithComposition); + Assertions.assertSame(2, visitor.countVisitB); + Assertions.assertSame(2, visitor.countEndVisitB); + + classWithComposition.opt = Optional.of(new B()); + visitor = new Visitor(); + classWithComposition.accept(visitor); + Assertions.assertSame(1, visitor.countVisitClassWithComposition); + Assertions.assertSame(1, visitor.countEndVisitClassWithComposition); + Assertions.assertSame(3, visitor.countVisitB); + Assertions.assertSame(3, visitor.countEndVisitB); + + classWithComposition.opt = Optional.empty(); + visitor = new Visitor(); + classWithComposition.accept(visitor); + Assertions.assertSame(1, visitor.countVisitClassWithComposition); + Assertions.assertSame(1, visitor.countEndVisitClassWithComposition); + Assertions.assertSame(2, visitor.countVisitB); + Assertions.assertSame(2, visitor.countEndVisitB); + + classWithComposition.opt = Optional.of(new B()); + classWithComposition.opt2 = Optional.of(new B()); + classWithComposition.many2 = setOfB; + classWithComposition.one2 = new B(); + visitor = new Visitor(); + classWithComposition.accept(visitor); + Assertions.assertSame(1, visitor.countVisitClassWithComposition); + Assertions.assertSame(1, visitor.countEndVisitClassWithComposition); + Assertions.assertSame(6, visitor.countVisitB); + Assertions.assertSame(6, visitor.countEndVisitB); + //check every stack is empty + Assertions.assertTrue(visitor.isAllEmpty()); + } + + @Test + public void testCircularRelations() { + ClassCircular1 classCircular1 = new ClassCircular1(); + ClassCircular2 classCircular2 = new ClassCircular2(); + classCircular1.myClassCircular2 = classCircular2; + classCircular2.myClassCircular1 = classCircular1; + visitor = new Visitor(); + classCircular1.accept(visitor); + Assertions.assertSame(1, visitor.countVisitClassCircular1); + Assertions.assertSame(1, visitor.countEndVisitClassCircular1); + Assertions.assertSame(1, visitor.countVisitClassCircular2); + Assertions.assertSame(1, visitor.countEndVisitClassCircular2); + visitor = new Visitor(); + classCircular2.accept(visitor); + Assertions.assertSame(1, visitor.countVisitClassCircular1); + Assertions.assertSame(1, visitor.countEndVisitClassCircular1); + Assertions.assertSame(1, visitor.countVisitClassCircular2); + Assertions.assertSame(1, visitor.countEndVisitClassCircular2); + + ClassCircular1 classCircular12 = new ClassCircular1(); + ClassCircular2 classCircular22 = new ClassCircular2(); + classCircular1.myClassCircular2 = classCircular2; + classCircular2.myClassCircular1 = classCircular12; + classCircular12.myClassCircular2 = classCircular22; + classCircular22.myClassCircular1 = classCircular1; + visitor = new Visitor(); + classCircular2.accept(visitor); + Assertions.assertSame(2, visitor.countVisitClassCircular1); + Assertions.assertSame(2, visitor.countEndVisitClassCircular1); + Assertions.assertSame(2, visitor.countVisitClassCircular2); + Assertions.assertSame(2, visitor.countEndVisitClassCircular2); + //check every stack is empty + Assertions.assertTrue(visitor.isAllEmpty()); + } + + @Test + public void testAllTogether() { + AllTogether allTogether = new AllTogether(); + ClassWith2DimList classWith2DimList = new ClassWith2DimList(); + Set setOfB = new HashSet<>(); + setOfB.add(new B()); + allTogether.myInt = 1; + allTogether.myBool = true; + allTogether.manyClassWith2DimList = new HashSet<>(List.of(classWith2DimList)); + visitor = new Visitor(); + allTogether.accept(visitor); + Assertions.assertSame(1, visitor.countVisitAllTogether); + Assertions.assertSame(1, visitor.countEndVisitAllTogether); + Assertions.assertSame(1, visitor.countVisitClassWith2DimList); + Assertions.assertSame(1, visitor.countEndVisitClassWith2DimList); + + allTogether.owns = setOfB; + visitor = new Visitor(); + allTogether.accept(visitor); + Assertions.assertSame(1, visitor.countVisitAllTogether); + Assertions.assertSame(1, visitor.countEndVisitAllTogether); + Assertions.assertSame(1, visitor.countVisitClassWith2DimList); + Assertions.assertSame(1, visitor.countEndVisitClassWith2DimList); + Assertions.assertSame(1, visitor.countVisitB); + Assertions.assertSame(1, visitor.countEndVisitB); + + allTogether.oneClassWith2DimList = classWith2DimList; + visitor = new Visitor(); + allTogether.accept(visitor); + Assertions.assertSame(1, visitor.countVisitAllTogether); + Assertions.assertSame(1, visitor.countEndVisitAllTogether); + Assertions.assertSame(2, visitor.countVisitClassWith2DimList); + Assertions.assertSame(2, visitor.countEndVisitClassWith2DimList); + Assertions.assertSame(1, visitor.countVisitB); + Assertions.assertSame(1, visitor.countEndVisitB); + + allTogether.optClassWith2DimList = Optional.empty(); + visitor = new Visitor(); + allTogether.accept(visitor); + Assertions.assertSame(1, visitor.countVisitAllTogether); + Assertions.assertSame(1, visitor.countEndVisitAllTogether); + Assertions.assertSame(2, visitor.countVisitClassWith2DimList); + Assertions.assertSame(2, visitor.countEndVisitClassWith2DimList); + Assertions.assertSame(1, visitor.countVisitB); + Assertions.assertSame(1, visitor.countEndVisitB); + + allTogether.optClassWith2DimList = Optional.of(classWith2DimList); + visitor = new Visitor(); + allTogether.accept(visitor); + Assertions.assertSame(1, visitor.countVisitAllTogether); + Assertions.assertSame(1, visitor.countEndVisitAllTogether); + Assertions.assertSame(3, visitor.countVisitClassWith2DimList); + Assertions.assertSame(3, visitor.countEndVisitClassWith2DimList); + Assertions.assertSame(1, visitor.countVisitB); + Assertions.assertSame(1, visitor.countEndVisitB); + //test all others are 0 + Assertions.assertSame(0, visitor.countVisitClassWithMap); + Assertions.assertSame(0, visitor.countEndVisitClassWithMap); + Assertions.assertSame(0, visitor.countVisitClassCircular1); + Assertions.assertSame(0, visitor.countEndVisitClassCircular1); + Assertions.assertSame(0, visitor.countVisitClassCircular2); + Assertions.assertSame(0, visitor.countEndVisitClassCircular2); + Assertions.assertSame(3, visitor.countVisitClassWith2DimList); + Assertions.assertSame(3, visitor.countEndVisitClassWith2DimList); + Assertions.assertSame(0, visitor.countVisitClassWith2DimOptional); + Assertions.assertSame(0, visitor.countEndVisitClassWith2DimOptional); + Assertions.assertSame(0, visitor.countVisitClassWith2DimSet); + Assertions.assertSame(0, visitor.countEndVisitClassWith2DimSet); + Assertions.assertSame(0, visitor.countVisitClassWith2DimMap); + Assertions.assertSame(0, visitor.countEndVisitClassWith2DimMap); + Assertions.assertSame(0, visitor.countVisitClassWith3DimArray); + Assertions.assertSame(0, visitor.countEndVisitClassWith3DimArray); + Assertions.assertSame(0, visitor.countVisitClassWithArray); + Assertions.assertSame(0, visitor.countEndVisitClassWithArray); + Assertions.assertSame(0, visitor.countVisitClassWithAssociation); + Assertions.assertSame(0, visitor.countEndVisitClassWithAssociation); + Assertions.assertSame(0, visitor.countVisitClassWithComposition); + Assertions.assertSame(0, visitor.countEndVisitClassWithComposition); + Assertions.assertSame(0, visitor.countVisitClassWithList); + Assertions.assertSame(0, visitor.countEndVisitClassWithList); + Assertions.assertSame(0, visitor.countEndVisitClassWithOptional); + Assertions.assertSame(0, visitor.countVisitClassWithOptional); + Assertions.assertSame(0, visitor.countEndVisitClassWithPojoClassType); + Assertions.assertSame(0, visitor.countVisitClassWithPojoClassType); + Assertions.assertSame(0, visitor.countEndVisitClassWithPrimitiveType); + Assertions.assertSame(0, visitor.countVisitClassWithPrimitiveType); + Assertions.assertSame(0, visitor.countEndVisitClassWithSet); + Assertions.assertSame(0, visitor.countVisitClassWithSet); + Assertions.assertSame(0, visitor.countEndVisitClassWithString); + Assertions.assertSame(0, visitor.countVisitClassWithString); + Assertions.assertSame(1, visitor.countVisitAllTogether); + Assertions.assertSame(1, visitor.countEndVisitAllTogether); + Assertions.assertSame(1, visitor.countVisitB); + Assertions.assertSame(1, visitor.countEndVisitB); + //check every stack is empty + Assertions.assertTrue(visitor.isAllEmpty()); + } + + @Test + public void testClassToBeTopped() { + ClassToBeTopped classToBeTopped = new ClassToBeTopped(); + classToBeTopped.pojoType = new ClassWithPrimitiveType(); + visitor = new Visitor(); + classToBeTopped.accept(visitor); + Assertions.assertSame(1, visitor.countVisitClassToBeTopped); + Assertions.assertSame(1, visitor.countEndVisitClassToBeTopped); + Assertions.assertSame(1, visitor.countVisitClassWithPrimitiveType); + Assertions.assertSame(1, visitor.countEndVisitClassWithPrimitiveType); + //check every stack is empty + Assertions.assertTrue(visitor.isAllEmpty()); + } + + /** + * Test for interface Lists and list with inheritance if the right visitor methods are called. + */ + @Test + public void testInterfaceAndInherit() { + Set level1InterfacesList = new HashSet<>(); + Level2class level2class1 = new Level2class(); + level2class1.myInt = 1; + Level2class level2class2 = new Level2class(); + level2class2.myInt = 2; + Level3class level3class1 = new Level3class(); + level3class1.myInt = 3; + Level3class level3class2 = new Level3class(); + level3class2.myInt = 4; + level1InterfacesList.add(level2class1); + level1InterfacesList.add(level2class2); + level1InterfacesList.add(level3class1); + level1InterfacesList.add(level3class2); + Level0class level0class = new Level0class(); + level0class.many = level1InterfacesList; + visitor = new Visitor(); + level0class.accept(visitor); + Assertions.assertSame(4, visitor.countVisitLevel1Interface); + Assertions.assertSame(4, visitor.countEndVisitLevel1Interface); + Assertions.assertSame(1, visitor.countVisitLevel0class); + Assertions.assertSame(1, visitor.countEndVisitLevel0class); + Assertions.assertSame(4, visitor.countVisitLevel2class); + Assertions.assertSame(4, visitor.countEndVisitLevel2class); + Assertions.assertSame(2, visitor.countVisitLevel3class); + Assertions.assertSame(2, visitor.countEndVisitLevel3class); + + Level4class level4class = new Level4class(); + level4class.myInt = 5; + Level5class level5class1 = new Level5class(); + level5class1.myInt = 6; + Level5class level5class2 = new Level5class(); + level5class2.myInt = 7; + level1InterfacesList.add(level4class); + level1InterfacesList.add(level5class1); + level1InterfacesList.add(level5class2); + level0class.many = level1InterfacesList; + visitor = new Visitor(); + Assertions.assertSame(0, visitor.countVisitLevel1Interface); + level0class.accept(visitor); + //1 Level0Class has a list of: 2 Level2Class, 2 Level3Class, 1 Level4Class, 2 Level5Class + Assertions.assertSame(1, visitor.countVisitLevel0class); + Assertions.assertSame(1, visitor.countEndVisitLevel0class); + + Assertions.assertSame(7, visitor.countVisitLevel1Interface); + Assertions.assertSame(7, visitor.countEndVisitLevel1Interface); + + Assertions.assertSame(7, visitor.countVisitLevel2class); + Assertions.assertSame(7, visitor.countEndVisitLevel2class); + Assertions.assertSame(5, visitor.countVisitLevel2Interface); + Assertions.assertSame(5, visitor.countEndVisitLevel2Interface); + Assertions.assertSame(2, visitor.countVisitLevel2Interface1); + Assertions.assertSame(2, visitor.countEndVisitLevel2Interface1); + Assertions.assertSame(2, visitor.countVisitLevel2Interface2); + Assertions.assertSame(2, visitor.countEndVisitLevel2Interface2); + + Assertions.assertSame(5, visitor.countVisitLevel3class); + Assertions.assertSame(5, visitor.countEndVisitLevel3class); + Assertions.assertSame(2, visitor.countVisitLevel3Interface1); + Assertions.assertSame(2, visitor.countEndVisitLevel3Interface1); + Assertions.assertSame(2, visitor.countVisitLevel3Interface2); + Assertions.assertSame(2, visitor.countEndVisitLevel3Interface2); + + Assertions.assertSame(3, visitor.countVisitLevel4class); + Assertions.assertSame(3, visitor.countEndVisitLevel4class); + Assertions.assertSame(2, visitor.countVisitLevel4Interface); + Assertions.assertSame(2, visitor.countEndVisitLevel4Interface); + + Assertions.assertSame(2, visitor.countVisitLevel5class); + Assertions.assertSame(2, visitor.countEndVisitLevel5class); + + Assertions.assertSame(0, visitor.countVisitClassWithMap); + Assertions.assertSame(0, visitor.countEndVisitClassWithMap); + Assertions.assertSame(0, visitor.countVisitClassCircular1); + Assertions.assertSame(0, visitor.countEndVisitClassCircular1); + Assertions.assertSame(0, visitor.countVisitClassCircular2); + Assertions.assertSame(0, visitor.countEndVisitClassCircular2); + Assertions.assertSame(0, visitor.countVisitClassWith2DimList); + Assertions.assertSame(0, visitor.countEndVisitClassWith2DimList); + Assertions.assertSame(0, visitor.countVisitClassWith2DimOptional); + Assertions.assertSame(0, visitor.countEndVisitClassWith2DimOptional); + Assertions.assertSame(0, visitor.countVisitClassWith2DimSet); + Assertions.assertSame(0, visitor.countEndVisitClassWith2DimSet); + Assertions.assertSame(0, visitor.countVisitClassWith2DimMap); + Assertions.assertSame(0, visitor.countEndVisitClassWith2DimMap); + Assertions.assertSame(0, visitor.countVisitClassWith3DimArray); + Assertions.assertSame(0, visitor.countEndVisitClassWith3DimArray); + Assertions.assertSame(0, visitor.countVisitClassWithArray); + Assertions.assertSame(0, visitor.countEndVisitClassWithArray); + Assertions.assertSame(0, visitor.countVisitClassWithAssociation); + Assertions.assertSame(0, visitor.countEndVisitClassWithAssociation); + Assertions.assertSame(0, visitor.countVisitClassWithComposition); + Assertions.assertSame(0, visitor.countEndVisitClassWithComposition); + Assertions.assertSame(0, visitor.countVisitClassWithList); + Assertions.assertSame(0, visitor.countEndVisitClassWithList); + Assertions.assertSame(0, visitor.countEndVisitClassWithOptional); + Assertions.assertSame(0, visitor.countVisitClassWithOptional); + Assertions.assertSame(0, visitor.countEndVisitClassWithPojoClassType); + Assertions.assertSame(0, visitor.countVisitClassWithPojoClassType); + Assertions.assertSame(0, visitor.countEndVisitClassWithPrimitiveType); + Assertions.assertSame(0, visitor.countVisitClassWithPrimitiveType); + Assertions.assertSame(0, visitor.countVisitClassWithSet); + Assertions.assertSame(0, visitor.countEndVisitClassWithString); + Assertions.assertSame(0, visitor.countVisitClassWithString); + Assertions.assertSame(0, visitor.countVisitAllTogether); + Assertions.assertSame(0, visitor.countEndVisitAllTogether); + Assertions.assertSame(0, visitor.countVisitB); + Assertions.assertSame(0, visitor.countEndVisitB); + Assertions.assertSame(0, visitor.countVisitClassToBeTopped); + Assertions.assertSame(0, visitor.countEndVisitClassToBeTopped); + //check every stack is empty + Assertions.assertTrue(visitor.isAllEmpty()); + } + + @Test + public void testCorrectVisitorCallOrder() { + try { + File myObj = new File( + "target/cdGenOutTest/InheritanceVisitorDecoratorTest/TestInheritanceVisitor/ITestInheritanceVisitorInheritanceVisitor.java"); + Scanner myReader = new Scanner(myObj); + StringBuilder stringBuilder = new StringBuilder(); + while (myReader.hasNextLine()) { + stringBuilder.append(myReader.nextLine()); + } + myReader.close(); + + String sourceCode = stringBuilder.toString(); + + Pattern pattern = Pattern.compile( + "public\\s+void\\s+handle\\s*\\(\\s*TestInheritanceVisitor\\.Level5class\\s+\\w+\\s*\\)\\s*\\{"); + Matcher matcher = pattern.matcher(sourceCode); + + if (!matcher.find()) { + throw new IllegalStateException("handle(...) method not found."); + } + + int startIndex = matcher.start(); + int braceCount = 0; + int endIndex = -1; + + for (int i = startIndex; i < sourceCode.length(); i++) { + char ch = sourceCode.charAt(i); + if (ch == '{') + braceCount++; + else if (ch == '}') + braceCount--; + + if (braceCount == 0) { + endIndex = i + 1; + break; + } + } + + if (endIndex == -1) { + throw new IllegalStateException("Method braces not balanced."); + } + + String handleMethod = sourceCode.substring(startIndex, endIndex); + + List visitLevels = extractLevelNumbers(handleMethod, + "visit\\(\\(TestInheritanceVisitor\\.Level(\\d+).*?\\)node\\);"); + List endVisitLevels = extractLevelNumbers(handleMethod, + "endVisit\\(\\(TestInheritanceVisitor\\.Level(\\d+).*?\\)node\\);"); + + // Check that visit levels are in ascending order + List sortedVisits = new ArrayList<>(visitLevels); + Collections.sort(sortedVisits); + Assertions.assertEquals(sortedVisits, visitLevels); + + // Check that endVisit levels are in descending order + List sortedEndVisits = new ArrayList<>(endVisitLevels); + sortedEndVisits.sort(Collections.reverseOrder()); + Assertions.assertEquals(sortedEndVisits, endVisitLevels); + } + catch (FileNotFoundException e) { + throw new RuntimeException(e); + } + } + + private List extractLevelNumbers(String methodBody, String regex) { + List levels = new ArrayList<>(); + Pattern pattern = Pattern.compile(regex); + Matcher matcher = pattern.matcher(methodBody); + while (matcher.find()) { + levels.add(Integer.parseInt(matcher.group(1))); + } + return levels; + } + +} diff --git a/cdlang/src/cdGenIntTest/java/visitor/VisitorDecoratorResultTest.java b/cdlang/src/cdGenIntTest/java/visitor/VisitorDecoratorResultTest.java new file mode 100644 index 000000000..ac8a14817 --- /dev/null +++ b/cdlang/src/cdGenIntTest/java/visitor/VisitorDecoratorResultTest.java @@ -0,0 +1,472 @@ +/* (c) https://github.com/MontiCore/monticore */ +package visitor; + +import TestVisitor.*; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import java.util.*; + +public class VisitorDecoratorResultTest { + + Visitor visitor; + + @Test + public void test() { + testPrimitiveTypes(); + testStringTypes(); + testArrayTypes(); + testPojoClassTypes(); + testListTypes(); + testSetTypes(); + testOptionalTypes(); + testMapTypes(); + testAssociationTypes(); + testCompositionTypes(); + testCircularRelations(); + testAllTogether(); + testClassToBeTopped(); + testInterfaceAndInherit(); + } + + @Test + public void testPrimitiveTypes() { + ClassWithPrimitiveType classWithPrimitiveType = new ClassWithPrimitiveType(); + classWithPrimitiveType.myInt = 1; + visitor = new Visitor(); + classWithPrimitiveType.accept(visitor); + Assertions.assertSame(1, visitor.countVisitClassWithPrimitiveType); + Assertions.assertSame(1, visitor.countEndVisitClassWithPrimitiveType); + } + + @Test + public void testStringTypes() { + ClassWithString classWithString = new ClassWithString(); + classWithString.myString = "string"; + classWithString.myString2 = "string2"; + visitor = new Visitor(); + classWithString.accept(visitor); + Assertions.assertSame(1, visitor.countVisitClassWithString); + Assertions.assertSame(1, visitor.countEndVisitClassWithString); + } + + @Test + public void testArrayTypes() { + ClassWithArray classWithArray = new ClassWithArray(); + ClassWithPrimitiveType classWithPrimitiveType = new ClassWithPrimitiveType(); + classWithArray.arrayOfString = new ClassWithPrimitiveType[] { classWithPrimitiveType }; + visitor = new Visitor(); + classWithArray.accept(visitor); + Assertions.assertSame(1, visitor.countVisitClassWithArray); + Assertions.assertSame(1, visitor.countEndVisitClassWithArray); + Assertions.assertSame(1, visitor.countVisitClassWithPrimitiveType); + Assertions.assertSame(1, visitor.countEndVisitClassWithPrimitiveType); + classWithArray.arrayOfString2 = new ClassWithPrimitiveType[] { classWithPrimitiveType }; + visitor = new Visitor(); + classWithArray.accept(visitor); + Assertions.assertSame(1, visitor.countVisitClassWithArray); + Assertions.assertSame(1, visitor.countEndVisitClassWithArray); + Assertions.assertSame(2, visitor.countVisitClassWithPrimitiveType); + Assertions.assertSame(2, visitor.countEndVisitClassWithPrimitiveType); + + ClassWith3DimArray classWith3DimArray = new ClassWith3DimArray(); + classWith3DimArray.threeDimArrayOfString = new ClassWithPrimitiveType[][][] { { { + classWithPrimitiveType }, { classWithPrimitiveType } } }; + visitor = new Visitor(); + classWith3DimArray.accept(visitor); + Assertions.assertSame(1, visitor.countVisitClassWith3DimArray); + Assertions.assertSame(1, visitor.countEndVisitClassWith3DimArray); + Assertions.assertSame(2, visitor.countEndVisitClassWithPrimitiveType); + Assertions.assertSame(2, visitor.countVisitClassWithPrimitiveType); + classWith3DimArray.threeDimArrayOfString2 = new ClassWithPrimitiveType[][][] { { { + classWithPrimitiveType }, { classWithPrimitiveType } } }; + visitor = new Visitor(); + classWith3DimArray.accept(visitor); + Assertions.assertSame(1, visitor.countVisitClassWith3DimArray); + Assertions.assertSame(1, visitor.countEndVisitClassWith3DimArray); + Assertions.assertSame(4, visitor.countEndVisitClassWithPrimitiveType); + Assertions.assertSame(4, visitor.countVisitClassWithPrimitiveType); + } + + @Test + public void testPojoClassTypes() { + ClassWithPojoClassType classWithPojoClassType = new ClassWithPojoClassType(); + ClassWithPrimitiveType classWithPrimitiveType = new ClassWithPrimitiveType(); + classWithPojoClassType.pojoType = classWithPrimitiveType; + visitor = new Visitor(); + classWithPojoClassType.accept(visitor); + Assertions.assertSame(1, visitor.countVisitClassWithPojoClassType); + Assertions.assertSame(1, visitor.countEndVisitClassWithPojoClassType); + Assertions.assertSame(1, visitor.countVisitClassWithPrimitiveType); + Assertions.assertSame(1, visitor.countEndVisitClassWithPrimitiveType); + classWithPojoClassType.pojoType2 = classWithPrimitiveType; + visitor = new Visitor(); + classWithPojoClassType.accept(visitor); + Assertions.assertSame(1, visitor.countVisitClassWithPojoClassType); + Assertions.assertSame(1, visitor.countEndVisitClassWithPojoClassType); + Assertions.assertSame(2, visitor.countVisitClassWithPrimitiveType); + Assertions.assertSame(2, visitor.countEndVisitClassWithPrimitiveType); + } + + @Test + public void testListTypes() { + ArrayList oneDimArrayList = new ArrayList<>(); + ClassWithList classWithList = new ClassWithList(); + classWithList.myIntegerList = oneDimArrayList; + List> twoDimArrayList = new ArrayList<>(); + visitor = new Visitor(); + classWithList.accept(visitor); + Assertions.assertSame(1, visitor.countVisitClassWithList); + Assertions.assertSame(1, visitor.countEndVisitClassWithList); + + ClassWith2DimList classWith2DimList = new ClassWith2DimList(); + classWith2DimList.my2dimList = twoDimArrayList; + visitor = new Visitor(); + classWith2DimList.accept(visitor); + Assertions.assertSame(1, visitor.countVisitClassWith2DimList); + Assertions.assertSame(1, visitor.countEndVisitClassWith2DimList); + } + + @Test + public void testSetTypes() { + ClassWithSet classWithSet = new ClassWithSet(); + Set oneDimHashSet = new HashSet<>(); + Set> twoDimHashSet = new HashSet<>(); + twoDimHashSet.add(oneDimHashSet); + classWithSet.mySet = oneDimHashSet; + visitor = new Visitor(); + classWithSet.accept(visitor); + Assertions.assertSame(1, visitor.countVisitClassWithSet); + Assertions.assertSame(1, visitor.countEndVisitClassWithSet); + + ClassWith2DimSet classWith2DimSet = new ClassWith2DimSet(); + classWith2DimSet.my2dimSet = twoDimHashSet; + visitor = new Visitor(); + classWith2DimSet.accept(visitor); + Assertions.assertSame(1, visitor.countVisitClassWith2DimSet); + Assertions.assertSame(1, visitor.countEndVisitClassWith2DimSet); + } + + @Test + public void testOptionalTypes() { + ClassWithOptional classWithOptional = new ClassWithOptional(); + classWithOptional.myOptionalInteger = Optional.of(Integer.MAX_VALUE); + visitor = new Visitor(); + classWithOptional.accept(visitor); + Assertions.assertSame(1, visitor.countVisitClassWithOptional); + Assertions.assertSame(1, visitor.countEndVisitClassWithOptional); + + classWithOptional.myOptionalInteger2 = Optional.empty(); + visitor = new Visitor(); + classWithOptional.accept(visitor); + Assertions.assertSame(1, visitor.countVisitClassWithOptional); + Assertions.assertSame(1, visitor.countEndVisitClassWithOptional); + + ClassWith2DimOptional classWith2DimOptional = new ClassWith2DimOptional(); + classWith2DimOptional.my2DimOptional = Optional.empty(); + classWith2DimOptional.my2DimOptional2 = Optional.of(Optional.empty()); + visitor = new Visitor(); + classWith2DimOptional.accept(visitor); + Assertions.assertSame(1, visitor.countVisitClassWith2DimOptional); + Assertions.assertSame(1, visitor.countEndVisitClassWith2DimOptional); + + classWith2DimOptional.my2DimOptional = Optional.of(Optional.of(new B())); + visitor = new Visitor(); + classWith2DimOptional.accept(visitor); + Assertions.assertSame(1, visitor.countVisitClassWith2DimOptional); + Assertions.assertSame(1, visitor.countEndVisitClassWith2DimOptional); + Assertions.assertSame(1, visitor.countVisitB); + Assertions.assertSame(1, visitor.countEndVisitB); + + classWith2DimOptional.my2DimOptional2 = Optional.of(Optional.of(new B())); + visitor = new Visitor(); + classWith2DimOptional.accept(visitor); + Assertions.assertSame(1, visitor.countVisitClassWith2DimOptional); + Assertions.assertSame(1, visitor.countEndVisitClassWith2DimOptional); + Assertions.assertSame(2, visitor.countVisitB); + Assertions.assertSame(2, visitor.countEndVisitB); + } + + @Test + public void testMapTypes() { + ClassWithMap classWithMap = new ClassWithMap(); + HashMap oneDimHashMap = new HashMap<>(); + oneDimHashMap.put("first", new B()); + HashMap> twoDimHashMap = new HashMap<>(); + twoDimHashMap.put("String", oneDimHashMap); + classWithMap.myMap = oneDimHashMap; + visitor = new Visitor(); + classWithMap.accept(visitor); + Assertions.assertSame(1, visitor.countVisitClassWithMap); + Assertions.assertSame(1, visitor.countEndVisitClassWithMap); + Assertions.assertSame(1, visitor.countVisitB); + Assertions.assertSame(1, visitor.countEndVisitB); + + classWithMap.myMap2 = oneDimHashMap; + visitor = new Visitor(); + classWithMap.accept(visitor); + Assertions.assertSame(1, visitor.countVisitClassWithMap); + Assertions.assertSame(1, visitor.countEndVisitClassWithMap); + Assertions.assertSame(2, visitor.countVisitB); + Assertions.assertSame(2, visitor.countEndVisitB); + + ClassWith2DimMap classWith2DimMap = new ClassWith2DimMap(); + classWith2DimMap.myMap = twoDimHashMap; + visitor = new Visitor(); + classWith2DimMap.accept(visitor); + Assertions.assertSame(1, visitor.countVisitClassWith2DimMap); + Assertions.assertSame(1, visitor.countEndVisitClassWith2DimMap); + Assertions.assertSame(1, visitor.countVisitB); + Assertions.assertSame(1, visitor.countEndVisitB); + + classWith2DimMap.myMap2 = twoDimHashMap; + visitor = new Visitor(); + classWith2DimMap.accept(visitor); + Assertions.assertSame(1, visitor.countVisitClassWith2DimMap); + Assertions.assertSame(1, visitor.countEndVisitClassWith2DimMap); + Assertions.assertSame(2, visitor.countVisitB); + Assertions.assertSame(2, visitor.countEndVisitB); + } + + @Test + public void testAssociationTypes() { + ClassWithAssociation classWithAssociation = new ClassWithAssociation(); + Set setOfB = new HashSet<>(); + setOfB.add(new B()); + classWithAssociation.owns = setOfB; + visitor = new Visitor(); + classWithAssociation.accept(visitor); + Assertions.assertSame(1, visitor.countVisitClassWithAssociation); + Assertions.assertSame(1, visitor.countEndVisitClassWithAssociation); + Assertions.assertSame(1, visitor.countVisitB); + Assertions.assertSame(1, visitor.countEndVisitB); + + classWithAssociation.owns2 = setOfB; + visitor = new Visitor(); + classWithAssociation.accept(visitor); + Assertions.assertSame(1, visitor.countVisitClassWithAssociation); + Assertions.assertSame(1, visitor.countEndVisitClassWithAssociation); + Assertions.assertSame(2, visitor.countVisitB); + Assertions.assertSame(2, visitor.countEndVisitB); + } + + @Test + public void testCompositionTypes() { + ClassWithComposition classWithComposition = new ClassWithComposition(); + Set setOfB = new HashSet<>(); + setOfB.add(new B()); + classWithComposition.one = new B(); + visitor = new Visitor(); + classWithComposition.accept(visitor); + Assertions.assertSame(1, visitor.countVisitClassWithComposition); + Assertions.assertSame(1, visitor.countEndVisitClassWithComposition); + Assertions.assertSame(1, visitor.countVisitB); + Assertions.assertSame(1, visitor.countEndVisitB); + + classWithComposition.many = setOfB; + visitor = new Visitor(); + classWithComposition.accept(visitor); + Assertions.assertSame(1, visitor.countVisitClassWithComposition); + Assertions.assertSame(1, visitor.countEndVisitClassWithComposition); + Assertions.assertSame(2, visitor.countVisitB); + Assertions.assertSame(2, visitor.countEndVisitB); + + classWithComposition.opt = Optional.of(new B()); + visitor = new Visitor(); + classWithComposition.accept(visitor); + Assertions.assertSame(1, visitor.countVisitClassWithComposition); + Assertions.assertSame(1, visitor.countEndVisitClassWithComposition); + Assertions.assertSame(3, visitor.countVisitB); + Assertions.assertSame(3, visitor.countEndVisitB); + + classWithComposition.opt = Optional.empty(); + visitor = new Visitor(); + classWithComposition.accept(visitor); + Assertions.assertSame(1, visitor.countVisitClassWithComposition); + Assertions.assertSame(1, visitor.countEndVisitClassWithComposition); + Assertions.assertSame(2, visitor.countVisitB); + Assertions.assertSame(2, visitor.countEndVisitB); + + classWithComposition.opt = Optional.of(new B()); + classWithComposition.opt2 = Optional.of(new B()); + classWithComposition.many2 = setOfB; + classWithComposition.one2 = new B(); + visitor = new Visitor(); + classWithComposition.accept(visitor); + Assertions.assertSame(1, visitor.countVisitClassWithComposition); + Assertions.assertSame(1, visitor.countEndVisitClassWithComposition); + Assertions.assertSame(6, visitor.countVisitB); + Assertions.assertSame(6, visitor.countEndVisitB); + } + + @Test + public void testCircularRelations() { + ClassCircular1 classCircular1 = new ClassCircular1(); + ClassCircular2 classCircular2 = new ClassCircular2(); + classCircular1.myClassCircular2 = classCircular2; + classCircular2.myClassCircular1 = classCircular1; + visitor = new Visitor(); + classCircular1.accept(visitor); + Assertions.assertSame(1, visitor.countVisitClassCircular1); + Assertions.assertSame(1, visitor.countEndVisitClassCircular1); + Assertions.assertSame(1, visitor.countVisitClassCircular2); + Assertions.assertSame(1, visitor.countEndVisitClassCircular2); + visitor = new Visitor(); + classCircular2.accept(visitor); + Assertions.assertSame(1, visitor.countVisitClassCircular1); + Assertions.assertSame(1, visitor.countEndVisitClassCircular1); + Assertions.assertSame(1, visitor.countVisitClassCircular2); + Assertions.assertSame(1, visitor.countEndVisitClassCircular2); + + ClassCircular1 classCircular12 = new ClassCircular1(); + ClassCircular2 classCircular22 = new ClassCircular2(); + classCircular1.myClassCircular2 = classCircular2; + classCircular2.myClassCircular1 = classCircular12; + classCircular12.myClassCircular2 = classCircular22; + classCircular22.myClassCircular1 = classCircular1; + visitor = new Visitor(); + classCircular2.accept(visitor); + Assertions.assertSame(2, visitor.countVisitClassCircular1); + Assertions.assertSame(2, visitor.countEndVisitClassCircular1); + Assertions.assertSame(2, visitor.countVisitClassCircular2); + Assertions.assertSame(2, visitor.countEndVisitClassCircular2); + } + + @Test + public void testAllTogether() { + AllTogether allTogether = new AllTogether(); + ClassWith2DimList classWith2DimList = new ClassWith2DimList(); + Set setOfB = new HashSet<>(); + setOfB.add(new B()); + allTogether.myInt = 1; + allTogether.myBool = true; + allTogether.manyClassWith2DimList = new HashSet<>(List.of(classWith2DimList)); + visitor = new Visitor(); + allTogether.accept(visitor); + Assertions.assertSame(1, visitor.countVisitAllTogether); + Assertions.assertSame(1, visitor.countEndVisitAllTogether); + Assertions.assertSame(1, visitor.countVisitClassWith2DimList); + Assertions.assertSame(1, visitor.countEndVisitClassWith2DimList); + + allTogether.owns = setOfB; + visitor = new Visitor(); + allTogether.accept(visitor); + Assertions.assertSame(1, visitor.countVisitAllTogether); + Assertions.assertSame(1, visitor.countEndVisitAllTogether); + Assertions.assertSame(1, visitor.countVisitClassWith2DimList); + Assertions.assertSame(1, visitor.countEndVisitClassWith2DimList); + Assertions.assertSame(1, visitor.countVisitB); + Assertions.assertSame(1, visitor.countEndVisitB); + + allTogether.oneClassWith2DimList = classWith2DimList; + visitor = new Visitor(); + allTogether.accept(visitor); + Assertions.assertSame(1, visitor.countVisitAllTogether); + Assertions.assertSame(1, visitor.countEndVisitAllTogether); + Assertions.assertSame(2, visitor.countVisitClassWith2DimList); + Assertions.assertSame(2, visitor.countEndVisitClassWith2DimList); + Assertions.assertSame(1, visitor.countVisitB); + Assertions.assertSame(1, visitor.countEndVisitB); + + allTogether.optClassWith2DimList = Optional.empty(); + visitor = new Visitor(); + allTogether.accept(visitor); + Assertions.assertSame(1, visitor.countVisitAllTogether); + Assertions.assertSame(1, visitor.countEndVisitAllTogether); + Assertions.assertSame(2, visitor.countVisitClassWith2DimList); + Assertions.assertSame(2, visitor.countEndVisitClassWith2DimList); + Assertions.assertSame(1, visitor.countVisitB); + Assertions.assertSame(1, visitor.countEndVisitB); + + allTogether.optClassWith2DimList = Optional.of(classWith2DimList); + visitor = new Visitor(); + allTogether.accept(visitor); + Assertions.assertSame(1, visitor.countVisitAllTogether); + Assertions.assertSame(1, visitor.countEndVisitAllTogether); + Assertions.assertSame(3, visitor.countVisitClassWith2DimList); + Assertions.assertSame(3, visitor.countEndVisitClassWith2DimList); + Assertions.assertSame(1, visitor.countVisitB); + Assertions.assertSame(1, visitor.countEndVisitB); + //test all others are 0 + Assertions.assertSame(0, visitor.countVisitClassWithMap); + Assertions.assertSame(0, visitor.countEndVisitClassWithMap); + Assertions.assertSame(0, visitor.countVisitClassCircular1); + Assertions.assertSame(0, visitor.countEndVisitClassCircular1); + Assertions.assertSame(0, visitor.countVisitClassCircular2); + Assertions.assertSame(0, visitor.countEndVisitClassCircular2); + Assertions.assertSame(3, visitor.countVisitClassWith2DimList); + Assertions.assertSame(3, visitor.countEndVisitClassWith2DimList); + Assertions.assertSame(0, visitor.countVisitClassWith2DimOptional); + Assertions.assertSame(0, visitor.countEndVisitClassWith2DimOptional); + Assertions.assertSame(0, visitor.countVisitClassWith2DimSet); + Assertions.assertSame(0, visitor.countEndVisitClassWith2DimSet); + Assertions.assertSame(0, visitor.countVisitClassWith2DimMap); + Assertions.assertSame(0, visitor.countEndVisitClassWith2DimMap); + Assertions.assertSame(0, visitor.countVisitClassWith3DimArray); + Assertions.assertSame(0, visitor.countEndVisitClassWith3DimArray); + Assertions.assertSame(0, visitor.countVisitClassWithArray); + Assertions.assertSame(0, visitor.countEndVisitClassWithArray); + Assertions.assertSame(0, visitor.countVisitClassWithAssociation); + Assertions.assertSame(0, visitor.countEndVisitClassWithAssociation); + Assertions.assertSame(0, visitor.countVisitClassWithComposition); + Assertions.assertSame(0, visitor.countEndVisitClassWithComposition); + Assertions.assertSame(0, visitor.countVisitClassWithList); + Assertions.assertSame(0, visitor.countEndVisitClassWithList); + Assertions.assertSame(0, visitor.countEndVisitClassWithOptional); + Assertions.assertSame(0, visitor.countVisitClassWithOptional); + Assertions.assertSame(0, visitor.countEndVisitClassWithPojoClassType); + Assertions.assertSame(0, visitor.countVisitClassWithPojoClassType); + Assertions.assertSame(0, visitor.countEndVisitClassWithPrimitiveType); + Assertions.assertSame(0, visitor.countVisitClassWithPrimitiveType); + Assertions.assertSame(0, visitor.countEndVisitClassWithSet); + Assertions.assertSame(0, visitor.countVisitClassWithSet); + Assertions.assertSame(0, visitor.countEndVisitClassWithString); + Assertions.assertSame(0, visitor.countVisitClassWithString); + Assertions.assertSame(1, visitor.countVisitAllTogether); + Assertions.assertSame(1, visitor.countEndVisitAllTogether); + Assertions.assertSame(1, visitor.countVisitB); + Assertions.assertSame(1, visitor.countEndVisitB); + } + + @Test + public void testClassToBeTopped() { + ClassToBeTopped classToBeTopped = new ClassToBeTopped(); + classToBeTopped.pojoType = new ClassWithPrimitiveType(); + visitor = new Visitor(); + classToBeTopped.accept(visitor); + Assertions.assertSame(1, visitor.countVisitClassToBeTopped); + Assertions.assertSame(1, visitor.countEndVisitClassToBeTopped); + Assertions.assertSame(1, visitor.countVisitClassWithPrimitiveType); + Assertions.assertSame(1, visitor.countEndVisitClassWithPrimitiveType); + } + + /** + * Test for interface Lists and list with inheritance if the right visitor methods are called. + */ + @Test + public void testInterfaceAndInherit() { + Set level1InterfacesList = new HashSet<>(); + Level2class level2class1 = new Level2class(); + level2class1.myInt = 1; + Level2class level2class2 = new Level2class(); + level2class2.myInt = 2; + Level3class level3class1 = new Level3class(); + level3class1.myInt = 3; + Level3class level3class2 = new Level3class(); + level3class2.myInt = 4; + level1InterfacesList.add(level2class1); + level1InterfacesList.add(level2class2); + level1InterfacesList.add(level3class1); + level1InterfacesList.add(level3class2); + Level0class level0class = new Level0class(); + level0class.many = level1InterfacesList; + visitor = new Visitor(); + level0class.accept(visitor); + Assertions.assertSame(1, visitor.countVisitLevel0class); + Assertions.assertSame(1, visitor.countEndVisitLevel0class); + Assertions.assertSame(2, visitor.countVisitLevel2class); + Assertions.assertSame(2, visitor.countEndVisitLevel2class); + Assertions.assertSame(2, visitor.countVisitLevel3class); + Assertions.assertSame(2, visitor.countEndVisitLevel3class); + } + +} diff --git a/cdlang/src/cdGenIntTestHwc/java/TestBuilder/TestBuilderWithSetterBuilder.java b/cdlang/src/cdGenIntTestHwc/java/TestBuilder/TestBuilderWithSetterBuilder.java new file mode 100644 index 000000000..29763614f --- /dev/null +++ b/cdlang/src/cdGenIntTestHwc/java/TestBuilder/TestBuilderWithSetterBuilder.java @@ -0,0 +1,6 @@ +/* (c) https://github.com/MontiCore/monticore */ +package TestBuilder; + +public class TestBuilderWithSetterBuilder extends TestBuilderWithSetterBuilderTOP { + +} diff --git a/cdlang/src/cdGenIntTestHwc/java/TestInheritanceVisitor/ClassToBeTopped.java b/cdlang/src/cdGenIntTestHwc/java/TestInheritanceVisitor/ClassToBeTopped.java new file mode 100644 index 000000000..cff980d61 --- /dev/null +++ b/cdlang/src/cdGenIntTestHwc/java/TestInheritanceVisitor/ClassToBeTopped.java @@ -0,0 +1,6 @@ +/* (c) https://github.com/MontiCore/monticore */ +package TestInheritanceVisitor; + +public class ClassToBeTopped extends ClassToBeToppedTOP { + +} diff --git a/cdlang/src/cdGenIntTestHwc/java/TestInheritanceVisitor/Visitor.java b/cdlang/src/cdGenIntTestHwc/java/TestInheritanceVisitor/Visitor.java new file mode 100644 index 000000000..33006616f --- /dev/null +++ b/cdlang/src/cdGenIntTestHwc/java/TestInheritanceVisitor/Visitor.java @@ -0,0 +1,688 @@ +/* (c) https://github.com/MontiCore/monticore */ +package TestInheritanceVisitor; + +import org.junit.jupiter.api.Assertions; + +import java.util.HashSet; +import java.util.Set; +import java.util.Stack; + +public class Visitor implements ITestInheritanceVisitorInheritanceVisitor { + + /** + * Because we deal with an interface, we cannot save the already traversed elements in it. + * Therefore, we need to overwrite the getTraversedElements method and handle the traversed + * elements on the Visitor side. + */ + Set traversedElements = new HashSet<>(); + + /** + * same as for traversedElements + */ + @Override + public Set getTraversedElements() { return traversedElements; } + + public int countVisitClassWithMap = 0; + public int countEndVisitClassWithMap = 0; + public int countVisitClassCircular1 = 0; + public int countEndVisitClassCircular1 = 0; + public int countVisitClassCircular2 = 0; + public int countEndVisitClassCircular2 = 0; + public int countVisitClassWith2DimList = 0; + public int countEndVisitClassWith2DimList = 0; + public int countVisitClassWith2DimOptional = 0; + public int countEndVisitClassWith2DimOptional = 0; + public int countVisitClassWith2DimSet = 0; + public int countEndVisitClassWith2DimSet = 0; + public int countVisitClassWith2DimMap = 0; + public int countEndVisitClassWith2DimMap = 0; + public int countVisitClassWith3DimArray = 0; + public int countEndVisitClassWith3DimArray = 0; + public int countVisitClassWithArray = 0; + public int countEndVisitClassWithArray = 0; + public int countVisitClassWithAssociation = 0; + public int countEndVisitClassWithAssociation = 0; + public int countVisitClassWithComposition = 0; + public int countEndVisitClassWithComposition = 0; + public int countVisitClassWithList = 0; + public int countEndVisitClassWithList = 0; + public int countEndVisitClassWithOptional = 0; + public int countVisitClassWithOptional = 0; + public int countEndVisitClassWithPojoClassType = 0; + public int countVisitClassWithPojoClassType = 0; + public int countEndVisitClassWithPrimitiveType = 0; + public int countVisitClassWithPrimitiveType = 0; + public int countEndVisitClassWithSet = 0; + public int countVisitClassWithSet = 0; + public int countEndVisitClassWithString = 0; + public int countVisitClassWithString = 0; + public int countVisitAllTogether = 0; + public int countEndVisitAllTogether = 0; + public int countVisitB = 0; + public int countEndVisitB = 0; + public int countVisitClassToBeTopped = 0; + public int countEndVisitClassToBeTopped = 0; + //hierarchy + public int countVisitLevel0class = 0; + public int countEndVisitLevel0class = 0; + + public int countVisitLevel1Interface = 0; + public int countEndVisitLevel1Interface = 0; + + public int countVisitLevel2class = 0; + public int countEndVisitLevel2class = 0; + public int countVisitLevel2Interface1 = 0; + public int countEndVisitLevel2Interface1 = 0; + public int countVisitLevel2Interface2 = 0; + public int countEndVisitLevel2Interface2 = 0; + public int countVisitLevel2Interface = 0; + public int countEndVisitLevel2Interface = 0; + + public int countVisitLevel3class = 0; + public int countEndVisitLevel3class = 0; + public int countVisitLevel3Interface1 = 0; + public int countEndVisitLevel3Interface1 = 0; + public int countVisitLevel3Interface2 = 0; + public int countEndVisitLevel3Interface2 = 0; + + public int countVisitLevel4class = 0; + public int countEndVisitLevel4class = 0; + public int countVisitLevel4Interface = 0; + public int countEndVisitLevel4Interface = 0; + + public int countVisitLevel5class = 0; + public int countEndVisitLevel5class = 0; + + Stack stackClassWithMap = new Stack<>(); + Stack stackClassCircular1 = new Stack<>(); + Stack stackClassCircular2 = new Stack<>(); + Stack stackClassWith2DimList = new Stack<>(); + Stack stackClassWith2DimOptional = new Stack<>(); + Stack stackClassWith2DimSet = new Stack<>(); + Stack stackClassWith2DimMap = new Stack<>(); + Stack stackClassWith3DimArray = new Stack<>(); + Stack stackClassWithArray = new Stack<>(); + Stack stackClassWithAssociation = new Stack<>(); + Stack stackClassWithComposition = new Stack<>(); + Stack stackClassWithList = new Stack<>(); + Stack stackClassWithOptional = new Stack<>(); + Stack stackClassWithPojoClassType = new Stack<>(); + Stack stackClassWithPrimitiveType = new Stack<>(); + Stack stackClassWithSet = new Stack<>(); + Stack stackClassWithString = new Stack<>(); + Stack stackAllTogether = new Stack<>(); + Stack stackB = new Stack<>(); + Stack stackClassToBeTopped = new Stack<>(); + Stack stackLevel0class = new Stack<>(); + Stack stackLevel1Interface = new Stack<>(); + Stack stackLevel2class = new Stack<>(); + Stack stackLevel2Interface1 = new Stack<>(); + Stack stackLevel2Interface2 = new Stack<>(); + Stack stackLevel2Interface = new Stack<>(); + Stack stackLevel3class = new Stack<>(); + Stack stackLevel3Interface1 = new Stack<>(); + Stack stackLevel3Interface2 = new Stack<>(); + Stack stackLevel4class = new Stack<>(); + Stack stackLevel4Interface = new Stack<>(); + Stack stackLevel5class = new Stack<>(); + + public boolean isAllEmpty() { + return stackClassWithMap.empty() && stackClassCircular1.empty() && stackClassCircular2.empty() + && stackClassWith2DimList.empty() && stackClassWith2DimOptional.empty() + && stackClassWith2DimSet.empty() && stackClassWith2DimMap.empty() && stackClassWith3DimArray + .empty() && stackClassWithArray.empty() && stackClassWithAssociation.empty() + && stackClassWithComposition.empty() && stackClassWithList.empty() && stackClassWithOptional + .empty() && stackClassWithPojoClassType.empty() && stackClassWithPrimitiveType.empty() + && stackClassWithSet.empty() && stackClassWithString.empty() && stackAllTogether.empty() + && stackB.empty() && stackClassToBeTopped.empty() && stackLevel0class.empty() + && stackLevel1Interface.empty() && stackLevel2class.empty() && stackLevel2Interface1.empty() + && stackLevel2Interface2.empty() && stackLevel2Interface.empty() && stackLevel3class.empty() + && stackLevel3Interface1.empty() && stackLevel3Interface2.empty() && stackLevel4class + .empty() && stackLevel4Interface.empty() && stackLevel5class.empty(); + } + + @Override + public void visit(ClassWith2DimMap node) { + countVisitClassWith2DimMap++; + stackClassWith2DimMap.push(node); + } + + @Override + public void endVisit(ClassWith2DimMap node) { + countEndVisitClassWith2DimMap++; + if (stackClassWith2DimMap.empty() || stackClassWith2DimMap.peek() != node) { + Assertions.fail(); + } + else { + stackClassWith2DimMap.pop(); + } + } + + @Override + public void visit(B node) { + countVisitB++; + stackB.push(node); + } + + @Override + public void endVisit(B node) { + countEndVisitB++; + if (stackB.empty() || stackB.peek() != node) { + Assertions.fail(); + } + else { + stackB.pop(); + } + } + + @Override + public void endVisit(ClassWithMap node) { + countEndVisitClassWithMap++; + if (stackClassWithMap.empty() || stackClassWithMap.peek() != node) { + Assertions.fail(); + } + else { + stackClassWithMap.pop(); + } + } + + @Override + public void visit(ClassWithMap node) { + countVisitClassWithMap++; + stackClassWithMap.push(node); + } + + @Override + public void endVisit(ClassWithString node) { + countEndVisitClassWithString++; + if (stackClassWithString.empty() || stackClassWithString.peek() != node) { + Assertions.fail(); + } + else { + stackClassWithString.pop(); + } + } + + @Override + public void visit(ClassWithString node) { + countVisitClassWithString++; + stackClassWithString.push(node); + } + + @Override + public void endVisit(ClassWith3DimArray node) { + countEndVisitClassWith3DimArray++; + if (stackClassWith3DimArray.empty() || stackClassWith3DimArray.peek() != node) { + Assertions.fail(); + } + else { + stackClassWith3DimArray.pop(); + } + } + + @Override + public void visit(ClassWith3DimArray node) { + countVisitClassWith3DimArray++; + stackClassWith3DimArray.push(node); + } + + @Override + public void endVisit(ClassWithArray node) { + countEndVisitClassWithArray++; + if (stackClassWithArray.empty() || stackClassWithArray.peek() != node) { + Assertions.fail(); + } + else { + stackClassWithArray.pop(); + } + } + + @Override + public void visit(ClassWithArray node) { + countVisitClassWithArray++; + stackClassWithArray.push(node); + } + + @Override + public void endVisit(ClassWithComposition node) { + countEndVisitClassWithComposition++; + if (stackClassWithComposition.empty() || stackClassWithComposition.peek() != node) { + Assertions.fail(); + } + else { + stackClassWithComposition.pop(); + } + } + + @Override + public void visit(ClassWithComposition node) { + countVisitClassWithComposition++; + stackClassWithComposition.push(node); + } + + @Override + public void endVisit(ClassWithAssociation node) { + countEndVisitClassWithAssociation++; + if (stackClassWithAssociation.empty() || stackClassWithAssociation.peek() != node) { + Assertions.fail(); + } + else { + stackClassWithAssociation.pop(); + } + } + + @Override + public void visit(ClassWithAssociation node) { + countVisitClassWithAssociation++; + stackClassWithAssociation.push(node); + } + + @Override + public void endVisit(ClassCircular2 node) { + countEndVisitClassCircular2++; + if (stackClassCircular2.empty() || stackClassCircular2.peek() != node) { + Assertions.fail(); + } + else { + stackClassCircular2.pop(); + } + } + + @Override + public void visit(ClassCircular2 node) { + countVisitClassCircular2++; + stackClassCircular2.push(node); + } + + @Override + public void endVisit(ClassCircular1 node) { + countEndVisitClassCircular1++; + if (stackClassCircular1.empty() || stackClassCircular1.peek() != node) { + Assertions.fail(); + } + else { + stackClassCircular1.pop(); + } + } + + @Override + public void visit(ClassCircular1 node) { + countVisitClassCircular1++; + stackClassCircular1.push(node); + } + + @Override + public void endVisit(ClassWithList node) { + countEndVisitClassWithList++; + if (stackClassWithList.empty() || stackClassWithList.peek() != node) { + Assertions.fail(); + } + else { + stackClassWithList.pop(); + } + } + + @Override + public void visit(ClassWithList node) { + countVisitClassWithList++; + stackClassWithList.push(node); + } + + @Override + public void endVisit(ClassWithSet node) { + countEndVisitClassWithSet++; + if (stackClassWithSet.empty() || stackClassWithSet.peek() != node) { + Assertions.fail(); + } + else { + stackClassWithSet.pop(); + } + } + + @Override + public void visit(ClassWithSet node) { + countVisitClassWithSet++; + stackClassWithSet.push(node); + } + + @Override + public void endVisit(ClassWithPrimitiveType node) { + countEndVisitClassWithPrimitiveType++; + if (stackClassWithPrimitiveType.empty() || stackClassWithPrimitiveType.peek() != node) { + Assertions.fail(); + } + else { + stackClassWithPrimitiveType.pop(); + } + } + + @Override + public void visit(ClassWithPrimitiveType node) { + countVisitClassWithPrimitiveType++; + stackClassWithPrimitiveType.push(node); + } + + @Override + public void endVisit(ClassWithPojoClassType node) { + countEndVisitClassWithPojoClassType++; + if (stackClassWithPojoClassType.empty() || stackClassWithPojoClassType.peek() != node) { + Assertions.fail(); + } + else { + stackClassWithPojoClassType.pop(); + } + } + + @Override + public void visit(ClassWithPojoClassType node) { + countVisitClassWithPojoClassType++; + stackClassWithPojoClassType.push(node); + } + + @Override + public void endVisit(ClassWith2DimOptional node) { + countEndVisitClassWith2DimOptional++; + if (stackClassWith2DimOptional.empty() || stackClassWith2DimOptional.peek() != node) { + Assertions.fail(); + } + else { + stackClassWith2DimOptional.pop(); + } + } + + @Override + public void visit(ClassWith2DimOptional node) { + countVisitClassWith2DimOptional++; + stackClassWith2DimOptional.push(node); + } + + @Override + public void endVisit(ClassWithOptional node) { + countEndVisitClassWithOptional++; + if (stackClassWithOptional.empty() || stackClassWithOptional.peek() != node) { + Assertions.fail(); + } + else { + stackClassWithOptional.pop(); + } + } + + @Override + public void visit(ClassWithOptional node) { + countVisitClassWithOptional++; + stackClassWithOptional.push(node); + } + + @Override + public void endVisit(ClassWith2DimSet node) { + countEndVisitClassWith2DimSet++; + if (stackClassWith2DimSet.empty() || stackClassWith2DimSet.peek() != node) { + Assertions.fail(); + } + else { + stackClassWith2DimSet.pop(); + } + } + + @Override + public void visit(ClassWith2DimSet node) { + countVisitClassWith2DimSet++; + stackClassWith2DimSet.push(node); + } + + @Override + public void endVisit(ClassWith2DimList node) { + countEndVisitClassWith2DimList++; + if (stackClassWith2DimList.empty() || stackClassWith2DimList.peek() != node) { + Assertions.fail(); + } + else { + stackClassWith2DimList.pop(); + } + } + + @Override + public void visit(ClassWith2DimList node) { + countVisitClassWith2DimList++; + stackClassWith2DimList.push(node); + } + + @Override + public void endVisit(AllTogether node) { + countEndVisitAllTogether++; + if (stackAllTogether.empty() || stackAllTogether.peek() != node) { + Assertions.fail(); + } + else { + stackAllTogether.pop(); + } + } + + @Override + public void visit(AllTogether node) { + countVisitAllTogether++; + stackAllTogether.push(node); + } + + @Override + public void endVisit(ClassToBeTopped node) { + countEndVisitClassToBeTopped++; + if (stackClassToBeTopped.empty() || stackClassToBeTopped.peek() != node) { + Assertions.fail(); + } + else { + stackClassToBeTopped.pop(); + } + } + + @Override + public void visit(ClassToBeTopped node) { + countVisitClassToBeTopped++; + stackClassToBeTopped.push(node); + } + + @Override + public void visit(Level0class node) { + countVisitLevel0class++; + stackLevel0class.push(node); + } + + @Override + public void endVisit(Level0class node) { + countEndVisitLevel0class++; + if (stackLevel0class.empty() || stackLevel0class.peek() != node) { + Assertions.fail(); + } + else { + stackLevel0class.pop(); + } + } + + @Override + public void visit(Level1Interface node) { + countVisitLevel1Interface++; + stackLevel1Interface.push(node); + } + + @Override + public void endVisit(Level1Interface node) { + countEndVisitLevel1Interface++; + if (stackLevel1Interface.empty() || stackLevel1Interface.peek() != node) { + Assertions.fail(); + } + else { + stackLevel1Interface.pop(); + } + } + + @Override + public void visit(Level2class node) { + countVisitLevel2class++; + stackLevel2class.push(node); + } + + @Override + public void endVisit(Level2class node) { + countEndVisitLevel2class++; + if (stackLevel2class.empty() || stackLevel2class.peek() != node) { + Assertions.fail(); + } + else { + stackLevel2class.pop(); + } + } + + @Override + public void visit(Level2Interface1 node) { + countVisitLevel2Interface1++; + stackLevel2Interface1.push(node); + } + + @Override + public void endVisit(Level2Interface1 node) { + countEndVisitLevel2Interface1++; + if (stackLevel2Interface1.empty() || stackLevel2Interface1.peek() != node) { + Assertions.fail(); + } + else { + stackLevel2Interface1.pop(); + } + } + + @Override + public void visit(Level2Interface2 node) { + countVisitLevel2Interface2++; + stackLevel2Interface2.push(node); + } + + @Override + public void endVisit(Level2Interface2 node) { + countEndVisitLevel2Interface2++; + if (stackLevel2Interface2.empty() || stackLevel2Interface2.peek() != node) { + Assertions.fail(); + } + else { + stackLevel2Interface2.pop(); + } + } + + @Override + public void visit(Level2Interface node) { + countVisitLevel2Interface++; + stackLevel2Interface.push(node); + } + + @Override + public void endVisit(Level2Interface node) { + countEndVisitLevel2Interface++; + if (stackLevel2Interface.empty() || stackLevel2Interface.peek() != node) { + Assertions.fail(); + } + else { + stackLevel2Interface.pop(); + } + } + + @Override + public void visit(Level3class node) { + countVisitLevel3class++; + stackLevel3class.push(node); + } + + @Override + public void endVisit(Level3class node) { + countEndVisitLevel3class++; + if (stackLevel3class.empty() || stackLevel3class.peek() != node) { + Assertions.fail(); + } + else { + stackLevel3class.pop(); + } + } + + @Override + public void visit(Level3Interface1 node) { + countVisitLevel3Interface1++; + stackLevel3Interface1.push(node); + } + + @Override + public void endVisit(Level3Interface1 node) { + countEndVisitLevel3Interface1++; + if (stackLevel3Interface1.empty() || stackLevel3Interface1.peek() != node) { + Assertions.fail(); + } + else { + stackLevel3Interface1.pop(); + } + } + + @Override + public void visit(Level3Interface2 node) { + countVisitLevel3Interface2++; + stackLevel3Interface2.push(node); + } + + @Override + public void endVisit(Level3Interface2 node) { + countEndVisitLevel3Interface2++; + if (stackLevel3Interface2.empty() || stackLevel3Interface2.peek() != node) { + Assertions.fail(); + } + else { + stackLevel3Interface2.pop(); + } + } + + @Override + public void visit(Level4class node) { + countVisitLevel4class++; + stackLevel4class.push(node); + } + + @Override + public void endVisit(Level4class node) { + countEndVisitLevel4class++; + if (stackLevel4class.empty() || stackLevel4class.peek() != node) { + Assertions.fail(); + } + else { + stackLevel4class.pop(); + } + } + + @Override + public void visit(Level4Interface node) { + countVisitLevel4Interface++; + stackLevel4Interface.push(node); + } + + @Override + public void endVisit(Level4Interface node) { + countEndVisitLevel4Interface++; + if (stackLevel4Interface.empty() || stackLevel4Interface.peek() != node) { + Assertions.fail(); + } + else { + stackLevel4Interface.pop(); + } + } + + @Override + public void visit(Level5class node) { + countVisitLevel5class++; + stackLevel5class.push(node); + } + + @Override + public void endVisit(Level5class node) { + countEndVisitLevel5class++; + if (stackLevel5class.empty() || stackLevel5class.peek() != node) { + Assertions.fail(); + } + else { + stackLevel5class.pop(); + } + } + +} diff --git a/cdlang/src/cdGenIntTestHwc/java/TestObserver/Observer.java b/cdlang/src/cdGenIntTestHwc/java/TestObserver/Observer.java new file mode 100644 index 000000000..688d0d492 --- /dev/null +++ b/cdlang/src/cdGenIntTestHwc/java/TestObserver/Observer.java @@ -0,0 +1,64 @@ +/* (c) https://github.com/MontiCore/monticore */ +package TestObserver; + +import java.util.Optional; +import java.util.Set; + +public class Observer implements TestObserver.IOtherCObserver { + + int countUpdateObserver = 0; + int countUpdateObserverMyInt = 0; + int countUpdateObserverMyBool = 0; + int countUpdateObserverManyB = 0; + int countUpdateObserverOptB = 0; + int countUpdateObserverOneB = 0; + int countUpdateObserverOv = 0; + + @Override + public void update(OtherC clazz) { + countUpdateObserver++; + } + + @Override + public void updateObserverMyInt(OtherC clazz, int ov) { + countUpdateObserverMyInt++; + } + + @Override + public void updateObserverMyBool(OtherC clazz, boolean ov) { + countUpdateObserverMyBool++; + } + + @Override + public void updateObserverManyB(OtherC clazz, Set ov) { + countUpdateObserverManyB++; + } + + @Override + public void updateObserverOptB(OtherC clazz, Optional ov) { + countUpdateObserverOptB++; + } + + @Override + public void updateObserverOneB(OtherC clazz, B ov) { + countUpdateObserverOneB++; + } + + @Override + public void updateObserverOv(OtherC clazz, int ov) { + countUpdateObserverOv++; + } + + public int getCountUpdateObserver() { return countUpdateObserver; } + + public int getCountUpdateObserverMyInt() { return countUpdateObserverMyInt; } + + public int getCountUpdateObserverMyBool() { return countUpdateObserverMyBool; } + + public int getCountUpdateObserverManyB() { return countUpdateObserverManyB; } + + public int getCountUpdateObserverOptB() { return countUpdateObserverOptB; } + + public int getCountUpdateObserverOneB() { return countUpdateObserverOneB; } + +} diff --git a/cdlang/src/cdGenIntTestHwc/java/TestVisitor/ClassToBeTopped.java b/cdlang/src/cdGenIntTestHwc/java/TestVisitor/ClassToBeTopped.java new file mode 100644 index 000000000..68634d101 --- /dev/null +++ b/cdlang/src/cdGenIntTestHwc/java/TestVisitor/ClassToBeTopped.java @@ -0,0 +1,6 @@ +/* (c) https://github.com/MontiCore/monticore */ +package TestVisitor; + +public class ClassToBeTopped extends ClassToBeToppedTOP { + +} diff --git a/cdlang/src/cdGenIntTestHwc/java/TestVisitor/Visitor.java b/cdlang/src/cdGenIntTestHwc/java/TestVisitor/Visitor.java new file mode 100644 index 000000000..3c09a0792 --- /dev/null +++ b/cdlang/src/cdGenIntTestHwc/java/TestVisitor/Visitor.java @@ -0,0 +1,487 @@ +/* (c) https://github.com/MontiCore/monticore */ +package TestVisitor; + +import org.junit.jupiter.api.Assertions; + +import java.util.HashSet; +import java.util.Set; +import java.util.Stack; + +public class Visitor implements ITestVisitorVisitor { + + /** + * Because we deal with an interface, we cannot save the already traversed elements in it. + * Therefore, we need to overwrite the getTraversedElements method and handle the traversed + * elements on the Visitor side. + */ + Set traversedElements = new HashSet<>(); + + /** + * same as for traversedElements + */ + @Override + public Set getTraversedElements() { return traversedElements; } + + public int countVisitClassWithMap = 0; + public int countEndVisitClassWithMap = 0; + public int countVisitClassCircular1 = 0; + public int countEndVisitClassCircular1 = 0; + public int countVisitClassCircular2 = 0; + public int countEndVisitClassCircular2 = 0; + public int countVisitClassWith2DimList = 0; + public int countEndVisitClassWith2DimList = 0; + public int countVisitClassWith2DimOptional = 0; + public int countEndVisitClassWith2DimOptional = 0; + public int countVisitClassWith2DimSet = 0; + public int countEndVisitClassWith2DimSet = 0; + public int countVisitClassWith2DimMap = 0; + public int countEndVisitClassWith2DimMap = 0; + public int countVisitClassWith3DimArray = 0; + public int countEndVisitClassWith3DimArray = 0; + public int countVisitClassWithArray = 0; + public int countEndVisitClassWithArray = 0; + public int countVisitClassWithAssociation = 0; + public int countEndVisitClassWithAssociation = 0; + public int countVisitClassWithComposition = 0; + public int countEndVisitClassWithComposition = 0; + public int countVisitClassWithList = 0; + public int countEndVisitClassWithList = 0; + public int countEndVisitClassWithOptional = 0; + public int countVisitClassWithOptional = 0; + public int countEndVisitClassWithPojoClassType = 0; + public int countVisitClassWithPojoClassType = 0; + public int countEndVisitClassWithPrimitiveType = 0; + public int countVisitClassWithPrimitiveType = 0; + public int countEndVisitClassWithSet = 0; + public int countVisitClassWithSet = 0; + public int countEndVisitClassWithString = 0; + public int countVisitClassWithString = 0; + public int countVisitAllTogether = 0; + public int countEndVisitAllTogether = 0; + public int countVisitB = 0; + public int countEndVisitB = 0; + public int countVisitClassToBeTopped = 0; + public int countEndVisitClassToBeTopped = 0; + public int countVisitLevel3class = 0; + public int countEndVisitLevel3class = 0; + public int countVisitLevel0class = 0; + public int countEndVisitLevel0class = 0; + public int countVisitLevel2class = 0; + public int countEndVisitLevel2class = 0; + + Stack stackClassWithMap = new Stack<>(); + Stack stackClassCircular1 = new Stack<>(); + Stack stackClassCircular2 = new Stack<>(); + Stack stackClassWith2DimList = new Stack<>(); + Stack stackClassWith2DimOptional = new Stack<>(); + Stack stackClassWith2DimSet = new Stack<>(); + Stack stackClassWith2DimMap = new Stack<>(); + Stack stackClassWith3DimArray = new Stack<>(); + Stack stackClassWithArray = new Stack<>(); + Stack stackClassWithAssociation = new Stack<>(); + Stack stackClassWithComposition = new Stack<>(); + Stack stackClassWithList = new Stack<>(); + Stack stackClassWithOptional = new Stack<>(); + Stack stackClassWithPojoClassType = new Stack<>(); + Stack stackClassWithPrimitiveType = new Stack<>(); + Stack stackClassWithSet = new Stack<>(); + Stack stackClassWithString = new Stack<>(); + Stack stackAllTogether = new Stack<>(); + Stack stackB = new Stack<>(); + Stack stackClassToBeTopped = new Stack<>(); + Stack stackLevel3class = new Stack<>(); + Stack stackLevel0class = new Stack<>(); + Stack stackLevel2class = new Stack<>(); + + @Override + public void visit(ClassWith2DimMap node) { + countVisitClassWith2DimMap++; + stackClassWith2DimMap.push(node); + } + + @Override + public void endVisit(ClassWith2DimMap node) { + countEndVisitClassWith2DimMap++; + if (stackClassWith2DimMap.empty() || stackClassWith2DimMap.peek() != node) { + Assertions.fail(); + } + else { + stackClassWith2DimMap.pop(); + } + } + + @Override + public void visit(B node) { + countVisitB++; + stackB.push(node); + } + + @Override + public void endVisit(B node) { + countEndVisitB++; + if (stackB.empty() || stackB.peek() != node) { + Assertions.fail(); + } + else { + stackB.pop(); + } + } + + @Override + public void endVisit(ClassWithMap node) { + countEndVisitClassWithMap++; + if (stackClassWithMap.empty() || stackClassWithMap.peek() != node) { + Assertions.fail(); + } + else { + stackClassWithMap.pop(); + } + } + + @Override + public void visit(ClassWithMap node) { + countVisitClassWithMap++; + stackClassWithMap.push(node); + } + + @Override + public void endVisit(ClassWithString node) { + countEndVisitClassWithString++; + if (stackClassWithString.empty() || stackClassWithString.peek() != node) { + Assertions.fail(); + } + else { + stackClassWithString.pop(); + } + } + + @Override + public void visit(ClassWithString node) { + countVisitClassWithString++; + stackClassWithString.push(node); + } + + @Override + public void endVisit(ClassWith3DimArray node) { + countEndVisitClassWith3DimArray++; + if (stackClassWith3DimArray.empty() || stackClassWith3DimArray.peek() != node) { + Assertions.fail(); + } + else { + stackClassWith3DimArray.pop(); + } + } + + @Override + public void visit(ClassWith3DimArray node) { + countVisitClassWith3DimArray++; + stackClassWith3DimArray.push(node); + } + + @Override + public void endVisit(ClassWithArray node) { + countEndVisitClassWithArray++; + if (stackClassWithArray.empty() || stackClassWithArray.peek() != node) { + Assertions.fail(); + } + else { + stackClassWithArray.pop(); + } + } + + @Override + public void visit(ClassWithArray node) { + countVisitClassWithArray++; + stackClassWithArray.push(node); + } + + @Override + public void endVisit(ClassWithComposition node) { + countEndVisitClassWithComposition++; + if (stackClassWithComposition.empty() || stackClassWithComposition.peek() != node) { + Assertions.fail(); + } + else { + stackClassWithComposition.pop(); + } + } + + @Override + public void visit(ClassWithComposition node) { + countVisitClassWithComposition++; + stackClassWithComposition.push(node); + } + + @Override + public void endVisit(ClassWithAssociation node) { + countEndVisitClassWithAssociation++; + if (stackClassWithAssociation.empty() || stackClassWithAssociation.peek() != node) { + Assertions.fail(); + } + else { + stackClassWithAssociation.pop(); + } + } + + @Override + public void visit(ClassWithAssociation node) { + countVisitClassWithAssociation++; + stackClassWithAssociation.push(node); + } + + @Override + public void endVisit(ClassCircular2 node) { + countEndVisitClassCircular2++; + if (stackClassCircular2.empty() || stackClassCircular2.peek() != node) { + Assertions.fail(); + } + else { + stackClassCircular2.pop(); + } + } + + @Override + public void visit(ClassCircular2 node) { + countVisitClassCircular2++; + stackClassCircular2.push(node); + } + + @Override + public void endVisit(ClassCircular1 node) { + countEndVisitClassCircular1++; + if (stackClassCircular1.empty() || stackClassCircular1.peek() != node) { + Assertions.fail(); + } + else { + stackClassCircular1.pop(); + } + } + + @Override + public void visit(ClassCircular1 node) { + countVisitClassCircular1++; + stackClassCircular1.push(node); + } + + @Override + public void endVisit(ClassWithList node) { + countEndVisitClassWithList++; + if (stackClassWithList.empty() || stackClassWithList.peek() != node) { + Assertions.fail(); + } + else { + stackClassWithList.pop(); + } + } + + @Override + public void visit(ClassWithList node) { + countVisitClassWithList++; + stackClassWithList.push(node); + } + + @Override + public void endVisit(ClassWithSet node) { + countEndVisitClassWithSet++; + if (stackClassWithSet.empty() || stackClassWithSet.peek() != node) { + Assertions.fail(); + } + else { + stackClassWithSet.pop(); + } + } + + @Override + public void visit(ClassWithSet node) { + countVisitClassWithSet++; + stackClassWithSet.push(node); + } + + @Override + public void endVisit(ClassWithPrimitiveType node) { + countEndVisitClassWithPrimitiveType++; + if (stackClassWithPrimitiveType.empty() || stackClassWithPrimitiveType.peek() != node) { + Assertions.fail(); + } + else { + stackClassWithPrimitiveType.pop(); + } + } + + @Override + public void visit(ClassWithPrimitiveType node) { + countVisitClassWithPrimitiveType++; + stackClassWithPrimitiveType.push(node); + } + + @Override + public void endVisit(ClassWithPojoClassType node) { + countEndVisitClassWithPojoClassType++; + if (stackClassWithPojoClassType.empty() || stackClassWithPojoClassType.peek() != node) { + Assertions.fail(); + } + else { + stackClassWithPojoClassType.pop(); + } + } + + @Override + public void visit(ClassWithPojoClassType node) { + countVisitClassWithPojoClassType++; + stackClassWithPojoClassType.push(node); + } + + @Override + public void endVisit(ClassWith2DimOptional node) { + countEndVisitClassWith2DimOptional++; + if (stackClassWith2DimOptional.empty() || stackClassWith2DimOptional.peek() != node) { + Assertions.fail(); + } + else { + stackClassWith2DimOptional.pop(); + } + } + + @Override + public void visit(ClassWith2DimOptional node) { + countVisitClassWith2DimOptional++; + stackClassWith2DimOptional.push(node); + } + + @Override + public void endVisit(ClassWithOptional node) { + countEndVisitClassWithOptional++; + if (stackClassWithOptional.empty() || stackClassWithOptional.peek() != node) { + Assertions.fail(); + } + else { + stackClassWithOptional.pop(); + } + } + + @Override + public void visit(ClassWithOptional node) { + countVisitClassWithOptional++; + stackClassWithOptional.push(node); + } + + @Override + public void endVisit(ClassWith2DimSet node) { + countEndVisitClassWith2DimSet++; + if (stackClassWith2DimSet.empty() || stackClassWith2DimSet.peek() != node) { + Assertions.fail(); + } + else { + stackClassWith2DimSet.pop(); + } + } + + @Override + public void visit(ClassWith2DimSet node) { + countVisitClassWith2DimSet++; + stackClassWith2DimSet.push(node); + } + + @Override + public void endVisit(ClassWith2DimList node) { + countEndVisitClassWith2DimList++; + if (stackClassWith2DimList.empty() || stackClassWith2DimList.peek() != node) { + Assertions.fail(); + } + else { + stackClassWith2DimList.pop(); + } + } + + @Override + public void visit(ClassWith2DimList node) { + countVisitClassWith2DimList++; + stackClassWith2DimList.push(node); + } + + @Override + public void endVisit(AllTogether node) { + countEndVisitAllTogether++; + if (stackAllTogether.empty() || stackAllTogether.peek() != node) { + Assertions.fail(); + } + else { + stackAllTogether.pop(); + } + } + + @Override + public void visit(AllTogether node) { + countVisitAllTogether++; + stackAllTogether.push(node); + } + + @Override + public void endVisit(ClassToBeTopped node) { + countEndVisitClassToBeTopped++; + if (stackClassToBeTopped.empty() || stackClassToBeTopped.peek() != node) { + Assertions.fail(); + } + else { + stackClassToBeTopped.pop(); + } + } + + @Override + public void visit(ClassToBeTopped node) { + countVisitClassToBeTopped++; + stackClassToBeTopped.push(node); + } + + @Override + public void visit(Level3class node) { + countVisitLevel3class++; + stackLevel3class.push(node); + } + + @Override + public void endVisit(Level3class node) { + countEndVisitLevel3class++; + if (stackLevel3class.empty() || stackLevel3class.peek() != node) { + Assertions.fail(); + } + else { + stackLevel3class.pop(); + } + } + + @Override + public void visit(Level0class node) { + countVisitLevel0class++; + stackLevel0class.push(node); + } + + @Override + public void endVisit(Level0class node) { + countEndVisitLevel0class++; + if (stackLevel0class.empty() || stackLevel0class.peek() != node) { + Assertions.fail(); + } + else { + stackLevel0class.pop(); + } + } + + @Override + public void visit(Level2class node) { + countVisitLevel2class++; + stackLevel2class.push(node); + } + + @Override + public void endVisit(Level2class node) { + countEndVisitLevel2class++; + if (stackLevel2class.empty() || stackLevel2class.peek() != node) { + Assertions.fail(); + } + else { + stackLevel2class.pop(); + } + } + +} diff --git a/cdlang/src/main/java/de/monticore/cd/codegen/DecoratorConfig.java b/cdlang/src/main/java/de/monticore/cd/codegen/DecoratorConfig.java index 45caa8f16..d4febf4f2 100644 --- a/cdlang/src/main/java/de/monticore/cd/codegen/DecoratorConfig.java +++ b/cdlang/src/main/java/de/monticore/cd/codegen/DecoratorConfig.java @@ -63,6 +63,18 @@ public ChainableGenSetup withBuilders() { return this.withDecorator(new BuilderDecorator()); } + public ChainableGenSetup withVisitors() { + return this.withDecorator(new VisitorDecorator()); + } + + public ChainableGenSetup withInheritanceVisitors() { + return this.withDecorator(new InheritanceVisitorDecorator()); + } + + public ChainableGenSetup withDeepCloneAndDeepEquals() { + return this.withDecorator(new DeepCloneAndDeepEqualsDecorator()); + } + public ChainableGenSetup withObservers() { return this.withDecorator(new ObserverDecorator()); } diff --git a/cdlang/src/main/java/de/monticore/cd/codegen/decorators/BuilderDecorator.java b/cdlang/src/main/java/de/monticore/cd/codegen/decorators/BuilderDecorator.java index bc8f7c405..3e5e33437 100644 --- a/cdlang/src/main/java/de/monticore/cd/codegen/decorators/BuilderDecorator.java +++ b/cdlang/src/main/java/de/monticore/cd/codegen/decorators/BuilderDecorator.java @@ -1,31 +1,41 @@ /* (c) https://github.com/MontiCore/monticore */ package de.monticore.cd.codegen.decorators; +import de.monticore.ast.ASTNode; import static de.monticore.cd.codegen.CD2JavaTemplates.EMPTY_BODY; - import com.google.common.collect.Iterables; +import de.monticore.cd._symboltable.CDSymbolTables; import de.monticore.cd.codegen.decorators.data.AbstractDecorator; import de.monticore.cd.facade.CDAttributeFacade; +import de.monticore.cd.facade.CDConstructorFacade; import de.monticore.cd.facade.CDMethodFacade; +import de.monticore.cd.methodtemplates.CD4C; +import de.monticore.cd4analysis._util.CD4AnalysisTypeDispatcher; import de.monticore.cd4code.CD4CodeMill; import de.monticore.cd4code._visitor.CD4CodeTraverser; +import de.monticore.cd4codebasis._ast.ASTCDConstructor; import de.monticore.cd4codebasis._ast.ASTCDMethod; +import de.monticore.cd4codebasis._ast.ASTCDParameter; import de.monticore.cdbasis._ast.ASTCDAttribute; import de.monticore.cdbasis._ast.ASTCDClass; +import de.monticore.cdbasis._ast.ASTCDClassBuilder; import de.monticore.cdbasis._visitor.CDBasisVisitor2; import de.monticore.generating.templateengine.StringHookPoint; import de.monticore.generating.templateengine.TemplateHookPoint; import de.monticore.types.MCTypeFacade; -import de.monticore.types.mccollectiontypes.types3.MCCollectionSymTypeRelations; +import de.monticore.types.mcbasictypes._ast.ASTMCType; import de.se_rwth.commons.StringTransformations; -import de.se_rwth.commons.logging.Log; import java.util.Collections; -import java.util.Stack; +import java.util.*; -/** Applies the Builder-Pattern to the CD */ +/** + * Applies the Builder-Pattern to the CD + */ public class BuilderDecorator extends AbstractDecorator implements CDBasisVisitor2 { + CD4AnalysisTypeDispatcher dispatcher = new CD4AnalysisTypeDispatcher(); + @Override @SuppressWarnings("rawtypes") public Iterable> getMustRunAfter() { @@ -35,27 +45,69 @@ public Iterable> getMustRunAfter() { SetterDecorator.class)); } - Stack decoratedBuilderClasses = new Stack<>(); - Stack decoratedBuildMethods = new Stack<>(); - Stack enabled = new Stack<>(); - + /** + * In this visitor we check if the class should be decorated, and if true, we create a Builder + * class + * Because classes can inherit other classes we resolve all need to resolve all ASTCDAttribute of + * the super classes + * and copy them into the builder class. + * For every ASTCDAttribute we generate setter and if needed isAbsent methods. + * Furthermore, we generate build, unsafeBuild, isValid, and constructor methods. + *

+ * Also, a default constructor is generated if the original class has no default constructor + * so the builder can initiate the class. + *

+ * We need to handle the ASTCDAttributes in the class as we do care about inheritance class + * attributes. + * Because they do not appear in the node.getCDAttributeList() we cannot resolve them only in the + * visitor + * of the class ASTCDAttributes + * + * @param node ASTCDClass the class + */ @Override public void visit(ASTCDClass node) { // Only act if we should decorate the class if (this.decoratorData.shouldDecorate(this.getClass(), node)) { // Get the parent (package or CDDef) - var origParent = this.decoratorData.getParent(node).get(); + ASTNode origParent = this.decoratorData.getParent(node).get(); // and the parent, but now the element of the target CD - var decParent = this.decoratorData.getAsDecorated(origParent); + ASTNode decParent = this.decoratorData.getAsDecorated(origParent); + // Get decorated pojo class + ASTCDClass decClazz = this.decoratorData.getAsDecorated(node); // Create a new class with the "Builder" suffix - var builderClassB = CD4CodeMill.cDClassBuilder(); + ASTCDClassBuilder builderClassB = CD4CodeMill.cDClassBuilder(); builderClassB.setName(node.getName() + "Builder"); builderClassB.setModifier(node.getModifier().deepClone()); - var builderClass = builderClassB.build(); + ASTCDClass builderClass = builderClassB.build(); // Add the builder class to the decorated CD addElementToParent(decParent, builderClass); + // Add Log import to the builder class + CD4C.getInstance().addImport(builderClass, "de.se_rwth.commons.logging.Log"); + + // Add builder attribute for TOP safety + builderClass.addCDMember(CDAttributeFacade.getInstance().createAttribute(CD4CodeMill + .modifierBuilder().PROTECTED().build(), builderClass.getName(), "realBuilder")); + + // Add a constructor to the builder class + ASTCDConstructor constructor = CDConstructorFacade.getInstance().createConstructor(CD4CodeMill + .modifierBuilder().PUBLIC().build(), builderClass.getName()); + glexOpt.ifPresent(glex -> glex.replaceTemplate(EMPTY_BODY, constructor, new StringHookPoint( + "this.realBuilder = (" + builderClass.getName() + ") this;"))); + addToClass(builderClass, constructor); + + // Add a isValid() method to the builder class + List allAttributeList = getAllCDAttributes(node); + String staticErrorCode = "0x16725"; + ASTCDMethod isValidMethod = CDMethodFacade.getInstance().createMethod(CD4CodeMill + .modifierBuilder().PRIVATE().build(), MCTypeFacade.getInstance().createBooleanType(), + "isValid"); + glexOpt.ifPresent(glex -> glex.replaceTemplate(EMPTY_BODY, isValidMethod, + new TemplateHookPoint("methods.builder.isValid", allAttributeList, staticErrorCode))); + addToClass(builderClass, isValidMethod); + // Add a build() method to the builder class ASTCDMethod buildMethod = CDMethodFacade.getInstance().createMethod(CD4CodeMill .modifierBuilder().PUBLIC().build(), node.getName(), "build"); @@ -63,74 +115,116 @@ public void visit(ASTCDClass node) { "methods.builder.build", node.getName()))); addToClass(builderClass, buildMethod); - // Add the builder class & build method to the stack - decoratedBuilderClasses.add(builderClass); - decoratedBuildMethods.add(buildMethod); - enabled.push(true); - } - else - enabled.push(false); - } - - @Override - public void endVisit(ASTCDClass node) { - if (this.decoratorData.shouldDecorate(this.getClass(), node)) { - decoratedBuilderClasses.pop(); - decoratedBuildMethods.pop(); + // Add the unsafeBuild() method to the builder class + ASTCDMethod unsafeBuildMethod = CDMethodFacade.getInstance().createMethod(CD4CodeMill + .modifierBuilder().PUBLIC().build(), node.getName(), "unsafeBuild"); + glexOpt.ifPresent(glex -> glex.replaceTemplate(EMPTY_BODY, unsafeBuildMethod, + new TemplateHookPoint("methods.builder.unsafeBuild", node.getName()))); + addToClass(builderClass, unsafeBuildMethod); + + // Add attributes to the builder class + for (ASTCDAttribute attribute : allAttributeList) { + builderClass.addCDMember(CDAttributeFacade.getInstance().createAttribute(CD4CodeMill + .modifierBuilder().PROTECTED().build(), attribute.getMCType(), attribute.getName())); + } + + // Add setter methods to the builder class + for (ASTCDAttribute attribute : allAttributeList) { + ASTCDParameter param = CD4CodeMill.cDParameterBuilder().setName(attribute.getName()) + .setMCType(attribute.getMCType()).build(); + if (dispatcher.isMCCollectionTypesASTMCOptionalType(attribute.getMCType())) { + //set of optional with type directly and not with optional + ASTMCType type = getCDGenService().getFirstTypeArgument(attribute.getMCType()) + .deepClone(); + param = CD4CodeMill.cDParameterBuilder().setName(attribute.getName()).setMCType(type) + .build(); + } + ASTCDMethod setMethod = CDMethodFacade.getInstance().createMethod(CD4CodeMill + .modifierBuilder().PUBLIC().build(), builderClass.getName(), "set" + + StringTransformations.capitalize(attribute.getName()), param); + glexOpt.ifPresent(glex -> glex.replaceTemplate(EMPTY_BODY, setMethod, new TemplateHookPoint( + "methods.builder.set", attribute))); + addToClass(builderClass, setMethod); + + // it is required to check if a setter method exists by checking the methods of the SetterDecorator for + // an exact match of "set" + attribute.getName() + // if this method does not exist, + // we need to reference the attribute directly in the build method + boolean hasSetterMethod; + List methods = decoratorData.getDecoratorData(SetterDecorator.class) != null + ? decoratorData.getDecoratorData(SetterDecorator.class).methods.get(attribute) : null; + if (methods == null || methods.isEmpty() || methods.stream().noneMatch(m -> m.getName() + .equals("set" + StringTransformations.capitalize(attribute.getName())))) { + hasSetterMethod = false; + } + else { + hasSetterMethod = true; + } + + // Add set attributes in the build method + glexOpt.ifPresent(glex -> glex.addAfterTemplate("methods.builder.build:Inner", buildMethod, + new TemplateHookPoint("methods.builder.setAttribute", attribute, hasSetterMethod))); + + // Add set attributes in the unsafeBuild method + glexOpt.ifPresent(glex -> glex.addAfterTemplate("methods.builder.unsafeBuild:Inner", + unsafeBuildMethod, new TemplateHookPoint("methods.builder.setAttribute", attribute, + hasSetterMethod))); + } + + // Add isAbsent methods for all attributes with cardinality != 1 + for (ASTCDAttribute attribute : allAttributeList) { + if (dispatcher.isMCCollectionTypesASTMCListType(attribute.getMCType()) || dispatcher + .isMCCollectionTypesASTMCOptionalType(attribute.getMCType()) || dispatcher + .isMCCollectionTypesASTMCSetType(attribute.getMCType())) { + ASTCDMethod setAbsentMethod = CDMethodFacade.getInstance().createMethod(CD4CodeMill + .modifierBuilder().PUBLIC().build(), builderClass.getName(), "set" + + StringTransformations.capitalize(attribute.getName()) + "Absent"); + glexOpt.ifPresent(glex -> glex.replaceTemplate(EMPTY_BODY, setAbsentMethod, + new TemplateHookPoint("methods.builder.setAbsent", attribute))); + addToClass(builderClass, setAbsentMethod); + } + } + + //add a default package private constructor to the pojo class when no one exists. Needed inside the Builder + if (!decClazz.getCDConstructorList().isEmpty()) { + boolean hasDefaultConstructor = false; + for (ASTCDConstructor constructorPojo : decClazz.getCDConstructorList()) { + if (constructorPojo.getCDParameterList().isEmpty()) { + if (constructorPojo.getModifier().isPrivate()) { + //if we have a default constructor which is private, e need to set it to protected at least + constructorPojo.setModifier(CD4CodeMill.modifierBuilder().PROTECTED().build()); + } + hasDefaultConstructor = true; + } + } + if (!hasDefaultConstructor) { + ASTCDConstructor constructor1 = CDConstructorFacade.getInstance() + .createDefaultConstructor(CD4CodeMill.modifierBuilder().PROTECTED().build(), node); + addToClass(decClazz, constructor1); + } + } } - enabled.pop(); } - @Override - public void visit(ASTCDAttribute attribute) { - // Only do work if we are in a builder-enabled class - if (!enabled.peek()) - return; - - // We expect that the SetterDecorator has added a Setter for this attribute to the pojo class - // TODO: In a perfect world, we would extract the name from the symbol or SetterDecorator data - var methods = decoratorData.getDecoratorData(SetterDecorator.class).methods.get(attribute); - if (methods == null || methods.isEmpty()) { - Log.warn("Skipping builder pattern of " + attribute.getName() - + " due to missing Setter methods", attribute.get_SourcePositionStart()); - return; - } - - var decClazz = this.decoratedBuilderClasses.peek(); - var decMethod = this.decoratedBuildMethods.peek(); - // Add an attribute to the builder class - decClazz.addCDMember(CDAttributeFacade.getInstance().createAttribute(CD4CodeMill - .modifierBuilder().PROTECTED().build(), attribute.getMCType(), attribute.getName())); + /** + * This method resolves the super classes and returns all their attributes in a list + *

+ * All interface attributes in java are automatically public static final + * Therefore, we do not need to check them when constructing a builder as they are always + * set and initialized + * + * @param node class that should be inspected for super classes + * @return a list of attributes from all classes inherited + */ + public List getAllCDAttributes(ASTCDClass node) { + List astcdAttributeList = new ArrayList<>(node.getCDAttributeList()); + List superClassesTransitive = CDSymbolTables.getTransitiveSuperClasses(node); - // Use the template hook-point to add a call to the setter to the build() method - if (MCTypeFacade.getInstance().isBooleanType(attribute.getMCType())) { - glexOpt.ifPresent(glex -> glex.addAfterTemplate("methods.builder.build:Inner", decMethod, - new StringHookPoint("v.set" + StringTransformations.capitalize(attribute.getName()) - + "(this." + attribute.getName() + ");\n"))); - } - else if (MCCollectionSymTypeRelations.isList(attribute.getSymbol().getType())) { - glexOpt.ifPresent(glex -> glex.addAfterTemplate("methods.builder.build:Inner", decMethod, - new StringHookPoint("v.add" + StringTransformations.capitalize(attribute.getName()) - + "(this." + attribute.getName() + ");\n"))); - } - else if (MCCollectionSymTypeRelations.isSet(attribute.getSymbol().getType())) { - glexOpt.ifPresent(glex -> glex.addAfterTemplate("methods.builder.build:Inner", decMethod, - new StringHookPoint("v.add" + StringTransformations.capitalize(attribute.getName()) - + "(this." + attribute.getName() + ");\n"))); - } - else if (MCCollectionSymTypeRelations.isOptional(attribute.getSymbol().getType())) { - glexOpt.ifPresent(glex -> glex.addAfterTemplate("methods.builder.build:Inner", decMethod, - new StringHookPoint("if(this." + attribute.getName() + ".isPresent())v.set" - + StringTransformations.capitalize(attribute.getName()) + "(this." + attribute - .getName() + ".get());\n"))); - } - else { - glexOpt.ifPresent(glex -> glex.addAfterTemplate("methods.builder.build:Inner", decMethod, - new StringHookPoint("v.set" + StringTransformations.capitalize(attribute.getName()) - + "(this." + attribute.getName() + ");\n"))); + for (ASTCDClass astcdClass : superClassesTransitive) { + astcdAttributeList.addAll(astcdClass.getCDAttributeList()); } - // TODO: Create chainable(?) methods + return astcdAttributeList; } @Override diff --git a/cdlang/src/main/java/de/monticore/cd/codegen/decorators/CardinalityDefaultDecorator.java b/cdlang/src/main/java/de/monticore/cd/codegen/decorators/CardinalityDefaultDecorator.java index 00c81108e..f9ecfe391 100644 --- a/cdlang/src/main/java/de/monticore/cd/codegen/decorators/CardinalityDefaultDecorator.java +++ b/cdlang/src/main/java/de/monticore/cd/codegen/decorators/CardinalityDefaultDecorator.java @@ -28,18 +28,20 @@ public void visit(ASTCDAttribute attribute) { if (decoratorData.shouldDecorate(this.getClass(), attribute)) { // Retrieve the parent of the attribute var originalClazz = decoratorData.getParent(attribute).get(); - // - var decClazz = (ASTCDClass) decoratorData.getAsDecorated(originalClazz); - if (MCTypeFacade.getInstance().isBooleanType(attribute.getMCType())) { - } - else if (MCCollectionSymTypeRelations.isList(attribute.getSymbol().getType())) { - decorateList(decClazz, attribute); - } - else if (MCCollectionSymTypeRelations.isSet(attribute.getSymbol().getType())) { - decorateSet(decClazz, attribute); - } - else if (MCCollectionSymTypeRelations.isOptional(attribute.getSymbol().getType())) { - decorateOptional(decClazz, attribute); + if (decoratorData.getAsDecorated( + originalClazz) instanceof de.monticore.cd4codebasis._ast.ASTCDClass) { + var decClazz = (ASTCDClass) decoratorData.getAsDecorated(originalClazz); + if (MCTypeFacade.getInstance().isBooleanType(attribute.getMCType())) { + } + else if (MCCollectionSymTypeRelations.isList(attribute.getSymbol().getType())) { + decorateList(decClazz, attribute); + } + else if (MCCollectionSymTypeRelations.isSet(attribute.getSymbol().getType())) { + decorateSet(decClazz, attribute); + } + else if (MCCollectionSymTypeRelations.isOptional(attribute.getSymbol().getType())) { + decorateOptional(decClazz, attribute); + } } } } diff --git a/cdlang/src/main/java/de/monticore/cd/codegen/decorators/DeepCloneAndDeepEqualsDecorator.java b/cdlang/src/main/java/de/monticore/cd/codegen/decorators/DeepCloneAndDeepEqualsDecorator.java new file mode 100644 index 000000000..966edfde7 --- /dev/null +++ b/cdlang/src/main/java/de/monticore/cd/codegen/decorators/DeepCloneAndDeepEqualsDecorator.java @@ -0,0 +1,615 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.cd.codegen.decorators; + +import com.google.common.collect.Iterables; +import de.monticore.cd._symboltable.CDSymbolTables; +import de.monticore.cd.codegen.decorators.data.AbstractDecorator; +import de.monticore.cd.codegen.decorators.data.CDTypeCollector; +import de.monticore.cd.facade.CDConstructorFacade; +import de.monticore.cd.facade.CDMethodFacade; +import de.monticore.cd4code.CD4CodeMill; +import de.monticore.cd4code._visitor.CD4CodeTraverser; +import de.monticore.cd4codebasis._ast.ASTCDConstructor; +import de.monticore.cd4codebasis._ast.ASTCDMethod; +import de.monticore.cd4codebasis._ast.ASTCDParameter; +import de.monticore.cdbasis._ast.*; +import de.monticore.cdbasis._visitor.CDBasisVisitor2; +import de.monticore.cdinterfaceandenum._ast.ASTCDInterface; +import de.monticore.cdinterfaceandenum._visitor.CDInterfaceAndEnumVisitor2; +import de.monticore.generating.templateengine.TemplateHookPoint; +import de.monticore.types.MCTypeFacade; +import de.monticore.types.mcbasictypes._ast.ASTMCQualifiedType; +import de.monticore.types.mcbasictypes._ast.ASTMCReturnType; +import de.monticore.types.mccollectiontypes._ast.ASTMCMapType; +import de.monticore.types.mccollectiontypes._ast.ASTMCSetType; +import java.util.*; +import java.util.stream.Collectors; +import static de.monticore.cd.codegen.CD2JavaTemplates.EMPTY_BODY; + +/** + * Decorator that adds deepClone and deepEquals methods to artifacts specified in the class + * diagram.
+ * The deepClone method is actually two methods:
+ * 1. deepClone():
+ * Creates a new instance of the class and calls the deepClone method with the new instance.
+ * returns a cloned instance by value.
+ *
+ * 2. deepClone(map: Map‹Object, Object›):
+ * This method is used to correctly clone with respect class diagrams which are cyclic or have + * data structures containing multiple references to the same object.
+ * To realize this, we need to pass a map of already visited objects to the deepClone method. + * When cloning an object, we first check if the object is already in the map.
+ * If we encounter an object we have not seen yet, we create a new one and add it to our map. + * This is crucial because if that object later contains a reference to itself (either directly or + * indirectly), + * we will recognize it from our map. + * This prevents us from getting stuck in an endless loop trying to create the same object over and + * over; + * instead, we just copy the existing reference found in the map
+ * The map is a Map‹Object, Object› where the key is the original object and the value is the copied + * object of the key. + * If we do not pass the map, we would end up with a stack overflow error when trying to clone + * cyclic references.
+ *
+ *
+ * The deepEquals method is also three methods:
+ * 1. deepEquals(o: Object):
+ * This method calls the deepEquals method with the signature deepEquals(o: Object, + * forceSameOrder: boolean).
+ *
+ * 2. deepEquals(o: Object, forceSameOrder: boolean):
+ * This method calls the deepEquals method with the signature deepEquals(o: Object, + * forceSameOrder: boolean, visitedObjects: Set). + * With a new Map‹Object, Set‹Object›› of visited objects to avoid cyclic references.
+ *
+ * 3. deepEquals(o: Object, forceSameOrder: boolean, visitedObjects: + * Set‹Object›):
+ * This method is the actual implementation of the deepEquals method.
+ * It compares the object with the current instance and checks if the attributes are equal.
+ * It begins by adding the currentObject to the map of visitedObjects.
+ * The map maps objects found in the first object onto objects found for that specific object in the + * second object.
+ * Then it resolves the currentObject.
+ * Because we added the currentObject to the set of visitedObjects of the + * specific first object, + * we can detect cyclic references and avoid them.
+ * Afterward, we remove the currentObject from the map of visitedObjects + * to allow for further comparisons.
+ * TODO currently the deepEquals method is not symmetric, meaning that if A.equals(B) is true, + * B.equals(A) is not necessarily true. + */ +public class DeepCloneAndDeepEqualsDecorator extends AbstractDecorator + implements CDBasisVisitor2, CDInterfaceAndEnumVisitor2 { + + /** + * a collection of all classes from the class diagram as strings + */ + List classesFromClassdiagramAsString = new ArrayList<>(); + boolean isInitialized = false; + + @Override + @SuppressWarnings("rawtypes") + public Iterable> getMustRunAfter() { + return Iterables.concat(super.getMustRunAfter(), Collections.singletonList( + AbstractMethodDecorator.class)); + } + + protected void initClassesFromClassDiagramAsString(ASTCDCompilationUnit compilationUnit) { + if (isInitialized) { + return; + } + //visitor to get all classes from the class diagram + CDTypeCollector cdTypeCollector = new CDTypeCollector(); + CD4CodeTraverser t2 = CD4CodeMill.inheritanceTraverser(); + t2.add4CDInterfaceAndEnum(cdTypeCollector); + t2.add4CDBasis(cdTypeCollector); + compilationUnit.accept(t2); + + classesFromClassdiagramAsString.addAll(cdTypeCollector.getClasses().stream().map(e -> e + .getSymbol().getFullName()).collect(Collectors.toList())); + classesFromClassdiagramAsString.addAll(cdTypeCollector.getInterfaces().stream().map(e -> e + .getSymbol().getFullName()).collect(Collectors.toList())); + // IMPORTANT: as enums do not have methods, we will ignore them and therefore the default case with .equals() will be used + //classesFromClassdiagramAsString.addAll(cdTypeCollector.getEnums().stream().map(e -> e + // .getSymbol().getFullName()).collect(Collectors.toList())); + isInitialized = true; + } + + /** + * Used to init the list of all artifacts defined in the cd + * + * @param compilationUnit the compilationUnit containing all artifacts + */ + public void visit(ASTCDCompilationUnit compilationUnit) { + initClassesFromClassDiagramAsString(compilationUnit); + } + + /** + * Only when visiting a class node, we add the real deepClone and deepEquals methods to the + * decorated + * class. + * + * @param node the ASTCDClass node + */ + @Override + public void visit(ASTCDClass node) { + if (this.decoratorData.shouldDecorate(this.getClass(), node)) { + ASTCDClass decClazz = decoratorData.getAsDecorated(node); + + //the numbers correspond to arguments of the deepClone and deepEquals methods + //if the AbstractMethodDecorator has already run and the class is abstract, + // we cannot call the deepClone methods + // as they contain a default constructor which is not allowed in abstract classes. + if (!decClazz.getModifier().isAbstract()) { + addDeepCloneMethod(node, decClazz); + addDeepCloneMethod1(node, decClazz); + addDeepCloneMethod2(node, decClazz); + } + else { + addDeepCloneAbstractMethod(node, decClazz); + } + addDeepEquals1Method(node, decClazz); + addDeepEquals2Method(node, decClazz); + addDeepEquals3Method(node, decClazz); + + //add a private constructor to the pojo class when no one exists. + // Needed for deepClone only if no Builder was generated + // (the BuilderDecorator itself also generates a default protected default Constructor) + if (!this.decoratorData.shouldDecorate(BuilderDecorator.class, node)) { + boolean hasDefaultConstructor = false; + if (!decClazz.getCDConstructorList().isEmpty()) { + hasDefaultConstructor = decClazz.getCDConstructorList().stream().anyMatch(c -> c + .getCDParameterList().isEmpty()); + } + if (!hasDefaultConstructor) { + ASTCDConstructor constructor1 = CDConstructorFacade.getInstance() + .createDefaultConstructor(CD4CodeMill.modifierBuilder().PUBLIC().build(), node); + addToClass(decClazz, constructor1); + } + } + } + } + + /** + * When visiting an interface node, we add the deepClone and deepEquals methods to the + * decorated class. + * + * @param node the ASTCDClass node + */ + @Override + public void visit(ASTCDInterface node) { + if (this.decoratorData.shouldDecorate(this.getClass(), node)) { + ASTCDInterface decInterface = decoratorData.getAsDecorated(node); + + //the numbers correspond to arguments of the deepClone and deepEquals methods + addDeepCloneMethod(node, decInterface); + addDeepCloneMethod1(node, decInterface); + addDeepCloneMethod2(node, decInterface); + addDeepEquals1Method(node, decInterface); + addDeepEquals2Method(node, decInterface); + addDeepEquals3Method(node, decInterface); + } + } + + /** + * Adds a deepClone method with the signature deepClone() + * + * @param originalClass the original class + * @param decoratedClass the decorated class where the method is added + */ + private void addDeepCloneMethod(ASTCDClass originalClass, ASTCDClass decoratedClass) { + String packageName = originalClass.getSymbol().getPackageName(); + String originalClassFullQualifiedName = packageName.isEmpty() ? originalClass.getName() + : packageName + "." + originalClass.getName(); + ASTMCQualifiedType originalClassQualifiedType = MCTypeFacade.getInstance().createQualifiedType( + originalClassFullQualifiedName); + + ASTMCReturnType originalClassReturnType = CD4CodeMill.mCReturnTypeBuilder().setMCType( + originalClassQualifiedType).build(); + ASTCDMethod deepCloneMethod = CDMethodFacade.getInstance().createMethod(CD4CodeMill + .modifierBuilder().PUBLIC().build(), originalClassReturnType, "deepClone", + new ArrayList<>()); + + decoratedClass.addCDMember(deepCloneMethod); + + glexOpt.ifPresent(glex -> glex.replaceTemplate(EMPTY_BODY, deepCloneMethod, + new TemplateHookPoint("methods.deepCloneAndDeepEquals.deepClone", originalClassQualifiedType + .printType()))); + } + + private void addDeepCloneMethod(ASTCDInterface originalInterface, + ASTCDInterface decoratedInterface) { + String packageName = originalInterface.getSymbol().getPackageName(); + String originalInterfaceFullQualifiedName = packageName.isEmpty() ? originalInterface.getName() + : packageName + "." + originalInterface.getName(); + ASTMCQualifiedType originalInterfaceQualifiedType = MCTypeFacade.getInstance() + .createQualifiedType(originalInterfaceFullQualifiedName); + + ASTMCReturnType originalInterfaceReturnType = CD4CodeMill.mCReturnTypeBuilder().setMCType( + originalInterfaceQualifiedType).build(); + ASTCDMethod deepCloneMethod = CDMethodFacade.getInstance().createMethod(CD4CodeMill + .modifierBuilder().PUBLIC().ABSTRACT().build(), originalInterfaceReturnType, "deepClone", + new ArrayList<>()); + + decoratedInterface.addCDMember(deepCloneMethod); + } + + /** + * Method needed to create the new Result Object, add it to the map and then runs the real + * DeepClone method + * Needed to avoid using a Builder while still avoiding public constructors + * + * @param originalClass the original class + * @param decoratedClass the decorated class where the method is added + */ + public void addDeepCloneMethod1(ASTCDClass originalClass, ASTCDClass decoratedClass) { + String packageName = originalClass.getSymbol().getPackageName(); + String originalClassFullQualifiedName = packageName.isEmpty() ? originalClass.getName() + : packageName + "." + originalClass.getName(); + ASTMCQualifiedType originalClassQualifiedType = MCTypeFacade.getInstance().createQualifiedType( + originalClassFullQualifiedName); + ASTMCQualifiedType objectType = MCTypeFacade.getInstance().createQualifiedType("Object"); + ASTMCMapType visitedObjectsType = MCTypeFacade.getInstance().createMapTypeOf(objectType, + objectType); + + ASTCDParameter parameter1 = CD4CodeMill.cDParameterBuilder().setMCType(visitedObjectsType) + .setName("map").build(); + ASTMCReturnType originalClassReturnType = CD4CodeMill.mCReturnTypeBuilder().setMCType( + originalClassQualifiedType).build(); + + ASTCDMethod deepClone1Method = CDMethodFacade.getInstance().createMethod(CD4CodeMill + .modifierBuilder().PUBLIC().build(), originalClassReturnType, "deepClone", List.of( + parameter1)); + + decoratedClass.addCDMember(deepClone1Method); + + //if the class has a builder, we construct the class using a builder. Else we use the default constructor generated. + if (this.decoratorData.shouldDecorate(BuilderDecorator.class, originalClass)) { + glexOpt.ifPresent(glex -> glex.replaceTemplate(EMPTY_BODY, deepClone1Method, + new TemplateHookPoint("methods.deepCloneAndDeepEquals.deepClone1", + originalClassQualifiedType, "new " + originalClassQualifiedType.printType() + + "Builder().unsafeBuild()"))); + } + else { + glexOpt.ifPresent(glex -> glex.replaceTemplate(EMPTY_BODY, deepClone1Method, + new TemplateHookPoint("methods.deepCloneAndDeepEquals.deepClone1", + originalClassQualifiedType, "new " + originalClassQualifiedType.printType() + "()"))); + } + } + + private void addDeepCloneMethod1(ASTCDInterface originalInterface, + ASTCDInterface decoratedInterface) { + String packageName = originalInterface.getSymbol().getPackageName(); + String originalInterfaceFullQualifiedName = packageName.isEmpty() ? originalInterface.getName() + : packageName + "." + originalInterface.getName(); + ASTMCQualifiedType originalInterfaceQualifiedType = MCTypeFacade.getInstance() + .createQualifiedType(originalInterfaceFullQualifiedName); + ASTMCQualifiedType objectType = MCTypeFacade.getInstance().createQualifiedType("Object"); + ASTMCMapType visitedObjectsType = MCTypeFacade.getInstance().createMapTypeOf(objectType, + objectType); + + ASTCDParameter parameter1 = CD4CodeMill.cDParameterBuilder().setMCType(visitedObjectsType) + .setName("map").build(); + ASTMCReturnType originalInterfaceReturnType = CD4CodeMill.mCReturnTypeBuilder().setMCType( + originalInterfaceQualifiedType).build(); + + ASTCDMethod deepClone2Method = CDMethodFacade.getInstance().createMethod(CD4CodeMill + .modifierBuilder().PUBLIC().ABSTRACT().build(), originalInterfaceReturnType, "deepClone", + List.of(parameter1)); + + decoratedInterface.addCDMember(deepClone2Method); + } + + /** + * Adds a deepClone method with the signature deepClone(result: ‹PojoClass›, map: Map‹PojoClass, + * PojoClass›) + * We need 2 parameters in the deepClone method to prevent cyclic references causing stack + * overflow errors and instead copy the cyclic references + * + * @param originalClass the original class + * @param decoratedClass the decorated class where the method is added + */ + private void addDeepCloneMethod2(ASTCDClass originalClass, ASTCDClass decoratedClass) { + String packageName = originalClass.getSymbol().getPackageName(); + String originalClassFullQualifiedName = packageName.isEmpty() ? originalClass.getName() + : packageName + "." + originalClass.getName(); + ASTMCQualifiedType originalClassQualifiedType = MCTypeFacade.getInstance().createQualifiedType( + originalClassFullQualifiedName); + ASTMCQualifiedType objectType = MCTypeFacade.getInstance().createQualifiedType("Object"); + ASTMCMapType visitedObjectsType = MCTypeFacade.getInstance().createMapTypeOf(objectType, + objectType); + + ASTCDParameter parameter1 = CD4CodeMill.cDParameterBuilder().setMCType( + originalClassQualifiedType).setName("result").build(); + ASTCDParameter parameter2 = CD4CodeMill.cDParameterBuilder().setMCType(visitedObjectsType) + .setName("map").build(); + ASTMCReturnType originalClassReturnType = CD4CodeMill.mCReturnTypeBuilder().setMCType( + originalClassQualifiedType).build(); + + ASTCDMethod deepClone2Method = CDMethodFacade.getInstance().createMethod(CD4CodeMill + .modifierBuilder().PUBLIC().build(), originalClassReturnType, "deepClone", List.of( + parameter1, parameter2)); + + decoratedClass.addCDMember(deepClone2Method); + + glexOpt.ifPresent(glex -> glex.replaceTemplate(EMPTY_BODY, deepClone2Method, + new TemplateHookPoint("methods.deepCloneAndDeepEquals.deepClone2", + originalClassQualifiedType, getAllCDAttributes(originalClass), + classesFromClassdiagramAsString))); + + //We need to add a deepEquals method fpr every implemented interface of the class. + // This method should just redirect to the "normal" deepClone method of the specific class + if (originalClass.isPresentCDInterfaceUsage()) { + List interfaces = originalClass.getInterfaceList().stream().map( + o -> ((ASTMCQualifiedType) o)).collect(Collectors.toList()); + for (ASTMCQualifiedType interfaceType : interfaces) { + if (classesFromClassdiagramAsString.contains(interfaceType.getDefiningSymbol().get() + .getFullName())) { + ASTMCMapType visitedObjectsInterfaceType = MCTypeFacade.getInstance().createMapTypeOf( + objectType, objectType); + ASTCDParameter parameter1Interface = CD4CodeMill.cDParameterBuilder().setMCType( + interfaceType).setName("result").build(); + ASTCDParameter parameter2Interface = CD4CodeMill.cDParameterBuilder().setMCType( + visitedObjectsInterfaceType).setName("map").build(); + ASTMCReturnType originalInterfaceReturnType = CD4CodeMill.mCReturnTypeBuilder().setMCType( + interfaceType).build(); + ASTCDMethod deepClone2MethodInterface = CDMethodFacade.getInstance().createMethod( + CD4CodeMill.modifierBuilder().PUBLIC().build(), originalInterfaceReturnType, + "deepClone", List.of(parameter1Interface, parameter2Interface)); + decoratedClass.addCDMember(deepClone2MethodInterface); + + glexOpt.ifPresent(glex -> glex.replaceTemplate(EMPTY_BODY, deepClone2MethodInterface, + new TemplateHookPoint("methods.deepCloneAndDeepEquals.deepClone2ForInterfaces", + originalClassFullQualifiedName))); + } + } + } + } + + private void addDeepCloneMethod2(ASTCDInterface originalInterface, + ASTCDInterface decoratedClass) { + String packageName = originalInterface.getSymbol().getPackageName(); + String originalInterfaceFullQualifiedName = packageName.isEmpty() ? originalInterface.getName() + : packageName + "." + originalInterface.getName(); + ASTMCQualifiedType originalInterfaceQualifiedType = MCTypeFacade.getInstance() + .createQualifiedType(originalInterfaceFullQualifiedName); + ASTMCQualifiedType objectType = MCTypeFacade.getInstance().createQualifiedType("Object"); + ASTMCMapType visitedObjectsType = MCTypeFacade.getInstance().createMapTypeOf(objectType, + objectType); + + ASTCDParameter parameter1 = CD4CodeMill.cDParameterBuilder().setMCType( + originalInterfaceQualifiedType).setName("result").build(); + ASTCDParameter parameter2 = CD4CodeMill.cDParameterBuilder().setMCType(visitedObjectsType) + .setName("map").build(); + ASTMCReturnType originalInterfaceReturnType = CD4CodeMill.mCReturnTypeBuilder().setMCType( + originalInterfaceQualifiedType).build(); + + ASTCDMethod deepClone2Method = CDMethodFacade.getInstance().createMethod(CD4CodeMill + .modifierBuilder().PUBLIC().ABSTRACT().build(), originalInterfaceReturnType, "deepClone", + List.of(parameter1, parameter2)); + + decoratedClass.addCDMember(deepClone2Method); + } + + private void addDeepCloneAbstractMethod(ASTCDClass originalClass, ASTCDClass decoratedClass) { + String packageName = originalClass.getSymbol().getPackageName(); + String originalClassFullQualifiedName = packageName.isEmpty() ? originalClass.getName() + : packageName + "." + originalClass.getName(); + ASTMCQualifiedType originalClassQualifiedType = MCTypeFacade.getInstance().createQualifiedType( + originalClassFullQualifiedName); + ASTMCQualifiedType objectType = MCTypeFacade.getInstance().createQualifiedType("Object"); + ASTMCMapType visitedObjectsType = MCTypeFacade.getInstance().createMapTypeOf(objectType, + objectType); + + ASTCDParameter parameter1 = CD4CodeMill.cDParameterBuilder().setMCType( + originalClassQualifiedType).setName("result").build(); + ASTCDParameter parameterMap = CD4CodeMill.cDParameterBuilder().setMCType(visitedObjectsType) + .setName("map").build(); + ASTMCReturnType originalClassReturnType = CD4CodeMill.mCReturnTypeBuilder().setMCType( + originalClassQualifiedType).build(); + + ASTCDMethod deepCloneMethod = CDMethodFacade.getInstance().createMethod(CD4CodeMill + .modifierBuilder().PUBLIC().ABSTRACT().build(), originalClassReturnType, "deepClone", + new ArrayList<>()); + + ASTCDMethod deepClone1Method = CDMethodFacade.getInstance().createMethod(CD4CodeMill + .modifierBuilder().PUBLIC().ABSTRACT().build(), originalClassReturnType, "deepClone", List + .of(parameterMap)); + + ASTCDMethod deepClone2Method = CDMethodFacade.getInstance().createMethod(CD4CodeMill + .modifierBuilder().PUBLIC().ABSTRACT().build(), originalClassReturnType, "deepClone", List + .of(parameter1, parameterMap)); + + decoratedClass.addCDMember(deepCloneMethod); + decoratedClass.addCDMember(deepClone1Method); + decoratedClass.addCDMember(deepClone2Method); + } + + /** + * Adds a deepEquals method with the signature deepEquals(o: ‹Object›) + * This method calls the deepEquals method with the signature deepEquals(o: ‹Object›, + * forceSameOrder: boolean) + * + * @param originalClass the original class + * @param decoratedClass the decorated class where the method is added + */ + private void addDeepEquals1Method(ASTCDClass originalClass, ASTCDClass decoratedClass) { + ASTMCQualifiedType originalClassQualifiedType = MCTypeFacade.getInstance().createQualifiedType( + "Object"); + ASTMCReturnType booleanReturnType = CD4CodeMill.mCReturnTypeBuilder().setMCType(CD4CodeMill + .mCPrimitiveTypeBuilder().setPrimitive(1).build()).build(); + ASTCDParameter parameter1 = CD4CodeMill.cDParameterBuilder().setMCType( + originalClassQualifiedType).setName("o").build(); + ASTCDMethod deepEquals1Method = CDMethodFacade.getInstance().createMethod(CD4CodeMill + .modifierBuilder().PUBLIC().build(), booleanReturnType, "deepEquals", List.of(parameter1)); + + decoratedClass.addCDMember(deepEquals1Method); + + glexOpt.ifPresent(glex -> glex.replaceTemplate(EMPTY_BODY, deepEquals1Method, + new TemplateHookPoint("methods.deepCloneAndDeepEquals.deepEquals1"))); + } + + private void addDeepEquals1Method(ASTCDInterface originalInterface, + ASTCDInterface decoratedInterface) { + ASTMCQualifiedType originalInterfaceQualifiedType = MCTypeFacade.getInstance() + .createQualifiedType("Object"); + ASTMCReturnType booleanReturnType = CD4CodeMill.mCReturnTypeBuilder().setMCType(CD4CodeMill + .mCPrimitiveTypeBuilder().setPrimitive(1).build()).build(); + ASTCDParameter parameter1 = CD4CodeMill.cDParameterBuilder().setMCType( + originalInterfaceQualifiedType).setName("o").build(); + ASTCDMethod deepEquals1Method = CDMethodFacade.getInstance().createMethod(CD4CodeMill + .modifierBuilder().PUBLIC().ABSTRACT().build(), booleanReturnType, "deepEquals", List.of( + parameter1)); + + decoratedInterface.addCDMember(deepEquals1Method); + } + + /** + * Adds a deepEquals method with the signature deepEquals(o: ‹Object›, forceSameOrder: boolean) + * to the decorated class. + * This class calls the deepEquals method with the signature deepEquals(o: ‹Object›, + * forceSameOrder: boolean, visitedObjects: Map‹Object›,Set‹Object››) + * + * @param originalClass the original class + * @param decoratedClass the decorated class where the method is added + */ + private void addDeepEquals2Method(ASTCDClass originalClass, ASTCDClass decoratedClass) { + ASTMCQualifiedType originalClassQualifiedType = MCTypeFacade.getInstance().createQualifiedType( + "Object"); + ASTMCReturnType booleanReturnType = CD4CodeMill.mCReturnTypeBuilder().setMCType(CD4CodeMill + .mCPrimitiveTypeBuilder().setPrimitive(1).build()).build(); + ASTCDParameter parameter1 = CD4CodeMill.cDParameterBuilder().setMCType( + originalClassQualifiedType).setName("o").build(); + ASTCDParameter parameter2 = CD4CodeMill.cDParameterBuilder().setMCType(CD4CodeMill + .mCPrimitiveTypeBuilder().setPrimitive(1).build()).setName("forceSameOrder").build(); + ASTCDMethod deepEquals2Method = CDMethodFacade.getInstance().createMethod(CD4CodeMill + .modifierBuilder().PUBLIC().build(), booleanReturnType, "deepEquals", List.of(parameter1, + parameter2)); + + decoratedClass.addCDMember(deepEquals2Method); + + glexOpt.ifPresent(glex -> glex.replaceTemplate(EMPTY_BODY, deepEquals2Method, + new TemplateHookPoint("methods.deepCloneAndDeepEquals.deepEquals2"))); + } + + private void addDeepEquals2Method(ASTCDInterface originalInterface, + ASTCDInterface decoratedInterface) { + ASTMCQualifiedType originalInterfaceQualifiedType = MCTypeFacade.getInstance() + .createQualifiedType("Object"); + ASTMCReturnType booleanReturnType = CD4CodeMill.mCReturnTypeBuilder().setMCType(CD4CodeMill + .mCPrimitiveTypeBuilder().setPrimitive(1).build()).build(); + ASTCDParameter parameter1 = CD4CodeMill.cDParameterBuilder().setMCType( + originalInterfaceQualifiedType).setName("o").build(); + ASTCDParameter parameter2 = CD4CodeMill.cDParameterBuilder().setMCType(CD4CodeMill + .mCPrimitiveTypeBuilder().setPrimitive(1).build()).setName("forceSameOrder").build(); + ASTCDMethod deepEquals2Method = CDMethodFacade.getInstance().createMethod(CD4CodeMill + .modifierBuilder().PUBLIC().ABSTRACT().build(), booleanReturnType, "deepEquals", List.of( + parameter1, parameter2)); + + decoratedInterface.addCDMember(deepEquals2Method); + } + + /** + * Adds a deepEquals method with the signature deepEquals(o: ‹Object›, forceSameOrder: boolean, + * visitedObjects: Map‹Object,Set‹Object››) + * We need 3 parameters in the deepEquals method: + * Because when iterating over lists and sets we need to declare a boolean for every type and + * check it afterward as return false would not work + * 1. the object to compare with + * 2. the forceSameOrder boolean + * 3. a map which maps objects found in the first object onto a set of objects found for that + * specific object in the second object + * + * @param originalClass the original class + * @param decoratedClass the decorated class where the method is added + */ + //TODO equals is not symmetric, meaning that if A.equals(B) is true, B.equals(A) is not necessarily true. + // this is only because of the parameter forceSameOrder, + // which when set to false results in not detecting differt objects in b + // example: a list with ob1, obj1, obj2 and b with obj1, obj2, ob3 will result in true when forceSameOrder is false + // as the second list contains all objects from the first list + // my solution: internally call a.deepEquals(b) and b.deepEquals(a) and return true if both are true + // remark: in MontiCore deepEquals is also not symmetric + private void addDeepEquals3Method(ASTCDClass originalClass, ASTCDClass decoratedClass) { + String packageName = originalClass.getSymbol().getPackageName(); + String originalClassFullQualifiedName = packageName.isEmpty() ? originalClass.getName() + : packageName + "." + originalClass.getName(); + ASTMCQualifiedType originalClassQualifiedType = MCTypeFacade.getInstance().createQualifiedType( + originalClassFullQualifiedName); + ASTMCQualifiedType objectType = MCTypeFacade.getInstance().createQualifiedType("Object"); + ASTMCReturnType booleanReturnType = CD4CodeMill.mCReturnTypeBuilder().setMCType(CD4CodeMill + .mCPrimitiveTypeBuilder().setPrimitive(1).build()).build(); + ASTMCSetType visitedObjectsSet = MCTypeFacade.getInstance().createSetTypeOf(objectType); + ASTMCMapType visitedObjectsMapOfSet = MCTypeFacade.getInstance().createMapTypeOf(objectType, + visitedObjectsSet); + + ASTCDParameter parameter1 = CD4CodeMill.cDParameterBuilder().setMCType(objectType).setName("o") + .build(); + ASTCDParameter parameter2 = CD4CodeMill.cDParameterBuilder().setMCType(CD4CodeMill + .mCPrimitiveTypeBuilder().setPrimitive(1).build()).setName("forceSameOrder").build(); + ASTCDParameter parameter3 = CD4CodeMill.cDParameterBuilder().setMCType(visitedObjectsMapOfSet) + .setName("visitedObjects").build(); + + ASTCDMethod deepEquals3Method = CDMethodFacade.getInstance().createMethod(CD4CodeMill + .modifierBuilder().PUBLIC().build(), booleanReturnType, "deepEquals", List.of(parameter1, + parameter2, parameter3)); + + decoratedClass.addCDMember(deepEquals3Method); + + glexOpt.ifPresent(glex -> glex.replaceTemplate(EMPTY_BODY, deepEquals3Method, + new TemplateHookPoint("methods.deepCloneAndDeepEquals.deepEquals3", + originalClassQualifiedType, getAllCDAttributes(originalClass), + classesFromClassdiagramAsString))); + } + + private void addDeepEquals3Method(ASTCDInterface originalInterface, + ASTCDInterface decoratedInterface) { + ASTMCQualifiedType objectType = MCTypeFacade.getInstance().createQualifiedType("Object"); + ASTMCReturnType booleanReturnType = CD4CodeMill.mCReturnTypeBuilder().setMCType(CD4CodeMill + .mCPrimitiveTypeBuilder().setPrimitive(1).build()).build(); + ASTMCSetType visitedObjectsSet = MCTypeFacade.getInstance().createSetTypeOf(objectType); + ASTMCMapType visitedObjectsMapOfSet = MCTypeFacade.getInstance().createMapTypeOf(objectType, + visitedObjectsSet); + + ASTCDParameter parameter1 = CD4CodeMill.cDParameterBuilder().setMCType(objectType).setName("o") + .build(); + ASTCDParameter parameter2 = CD4CodeMill.cDParameterBuilder().setMCType(CD4CodeMill + .mCPrimitiveTypeBuilder().setPrimitive(1).build()).setName("forceSameOrder").build(); + ASTCDParameter parameter3 = CD4CodeMill.cDParameterBuilder().setMCType(visitedObjectsMapOfSet) + .setName("visitedObjects").build(); + + ASTCDMethod deepEquals3Method = CDMethodFacade.getInstance().createMethod(CD4CodeMill + .modifierBuilder().ABSTRACT().PUBLIC().build(), booleanReturnType, "deepEquals", List.of( + parameter1, parameter2, parameter3)); + + decoratedInterface.addCDMember(deepEquals3Method); + } + + /** + * This method resolves the super classes and returns all their attributes in a list + *

+ * All interface attributes in java are automatically public static final + * Therefore, we do not need to check them when deepCloning or deepEqual as the result should + * always be true. + * + * @param node class that should be inspected for super classes + * @return a list of attributes from all classes inherited + */ + public List getAllCDAttributes(ASTCDClass node) { + List astcdAttributeList = new ArrayList<>(node.getCDAttributeList()); + List superClassesTransitive = CDSymbolTables.getTransitiveSuperClasses(node); + + for (ASTCDClass astcdClass : superClassesTransitive) { + astcdAttributeList.addAll(astcdClass.getCDAttributeList()); + } + + return astcdAttributeList; + } + + @Override + public void addToTraverser(CD4CodeTraverser traverser) { + traverser.add4CDBasis(this); + traverser.add4CDInterfaceAndEnum(this); + } + +} diff --git a/cdlang/src/main/java/de/monticore/cd/codegen/decorators/InheritanceVisitorDecorator.java b/cdlang/src/main/java/de/monticore/cd/codegen/decorators/InheritanceVisitorDecorator.java new file mode 100644 index 000000000..f19e03e3c --- /dev/null +++ b/cdlang/src/main/java/de/monticore/cd/codegen/decorators/InheritanceVisitorDecorator.java @@ -0,0 +1,386 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.cd.codegen.decorators; + +import com.google.common.collect.Iterables; +import de.monticore.ast.ASTCNode; +import de.monticore.cd._symboltable.CDSymbolTables; +import de.monticore.cd.codegen.decorators.data.AbstractDecorator; +import de.monticore.cd.codegen.decorators.data.CDTypeCollector; +import de.monticore.cd.facade.CDMethodFacade; +import de.monticore.cd4code.CD4CodeMill; +import de.monticore.cd4code._visitor.CD4CodeTraverser; +import de.monticore.cd4codebasis._ast.ASTCDInterface; +import de.monticore.cd4codebasis._ast.ASTCDMethod; +import de.monticore.cd4codebasis._ast.ASTCDParameter; +import de.monticore.cdbasis._ast.*; +import de.monticore.cdbasis._visitor.CDBasisVisitor2; +import de.monticore.cdinterfaceandenum._visitor.CDInterfaceAndEnumVisitor2; +import de.monticore.generating.templateengine.TemplateHookPoint; +import de.monticore.types.MCTypeFacade; +import de.monticore.types.mcbasictypes._ast.ASTMCQualifiedType; +import de.monticore.types.mcbasictypes._ast.ASTMCReturnType; +import de.monticore.types.mccollectiontypes._ast.ASTMCSetType; +import de.se_rwth.commons.StringTransformations; + +import java.util.*; +import java.util.stream.Collectors; +import static de.monticore.cd.codegen.CD2JavaTemplates.EMPTY_BODY; + +/** + * This decorator generates a visitor interface for each class and interface in the class diagram. + * The visitor interface contains methods to visit, endVisit, handle, traverse, + * All classes and interfaces contain an accept method that accepts the visitor interface as a + * parameter. + *

+ * The visitor interface is used to traverse over classes in the class diagram. + * When a class inherits from another class or implements an interface, these relations are visited + * before the + * class itself is visited. This is done in a deep-first manner. + * The endVisit method is called after all super classes and interfaces as well as the node have + * been visited and handled. + * The endVisit methods are then called in reverse order of the visit methods. + */ +public class InheritanceVisitorDecorator extends AbstractDecorator + implements CDBasisVisitor2, CDInterfaceAndEnumVisitor2 { + + Stack parameterOfPojo = new Stack<>(); + Stack currentDecoratedClass = new Stack<>(); + Stack currentDecoratedInterface = + new Stack<>(); + ASTCDInterface visitorInterface; + ASTCDParameter visitorInterfaceParameter; + Stack currentTraverseMethod = new Stack<>(); + /** + * a collection of all classes from the class diagram as strings + */ + List classesFromClassdiagramAsString = new ArrayList<>(); + boolean isInitialized = false; + + @Override + @SuppressWarnings("rawtypes") + public Iterable> getMustRunAfter() { + //We check that the SetterDecorator has added a Setter for an attribute, + // thus the Setter decorator has to run before. + return Iterables.concat(super.getMustRunAfter(), Collections.singletonList( + SetterDecorator.class)); + } + + @Override + public void visit(ASTCDCompilationUnit compilationUnit) { + init(compilationUnit, compilationUnit.getCDDefinition(), "I" + compilationUnit.getCDDefinition() + .getName() + "InheritanceVisitor"); + } + + public void init(ASTCDCompilationUnit compilationUnit, ASTCDDefinition definition, + String visitorInterfaceName) { + if (!isInitialized) { + isInitialized = true; + //create the visitor interface + visitorInterface = CD4CodeMill.cDInterfaceBuilder().setName(visitorInterfaceName).setModifier( + CD4CodeMill.modifierBuilder().PUBLIC().build()).build(); + + // add the visitor interface to the definition + ASTCDDefinition decoratedDefinition = this.decoratorData.getAsDecorated(definition); + decoratedDefinition.addCDElement(visitorInterface); + + // create the visitor interface parameter + String packageName = definition.getSymbol().getPackageName(); + String visitorInterfaceQualifiedName = packageName.isEmpty() ? visitorInterfaceName + : packageName + "." + visitorInterfaceName; + ASTMCQualifiedType visitorInterfaceQualifiedType = MCTypeFacade.getInstance() + .createQualifiedType(visitorInterfaceQualifiedName); + visitorInterfaceParameter = CD4CodeMill.cDParameterBuilder().setName("visitor").setMCType( + visitorInterfaceQualifiedType).build(); + + // add getTraversedElements Set method to the visitor interface + ASTMCSetType setType = MCTypeFacade.getInstance().createSetTypeOf("Object"); + ASTMCReturnType returnType = CD4CodeMill.mCReturnTypeBuilder().setMCType(setType).build(); + ASTCDMethod getTraversedElementsMethod = CDMethodFacade.getInstance().createMethod(CD4CodeMill + .modifierBuilder().setAbstract(true).build(), returnType, "getTraversedElements"); + visitorInterface.addCDMember(getTraversedElementsMethod); + + // add addTraversedElement method to the visitor interface + ASTMCReturnType returnTypeAddTraversedElement = CD4CodeMill.mCReturnTypeBuilder() + .setMCVoidType(CD4CodeMill.mCVoidTypeBuilder().build()).build(); + ASTCDParameter addTraversedElementParameter = CD4CodeMill.cDParameterBuilder().setName( + "element").setMCType(MCTypeFacade.getInstance().createQualifiedType("Object")).build(); + ASTCDMethod addTraversedElementMethod = CDMethodFacade.getInstance().createMethod(CD4CodeMill + .modifierBuilder().build(), returnTypeAddTraversedElement, "addTraversedElement", + addTraversedElementParameter); + visitorInterface.addCDMember(addTraversedElementMethod); + glexOpt.ifPresent(glex -> glex.replaceTemplate(EMPTY_BODY, addTraversedElementMethod, + new TemplateHookPoint("methods.visitor.addTraversedElement"))); + + // add removeTraversedElement method to the visitor interface + ASTMCReturnType returnTypeRemoveTraversedElement = CD4CodeMill.mCReturnTypeBuilder() + .setMCVoidType(CD4CodeMill.mCVoidTypeBuilder().build()).build(); + ASTCDParameter removeTraversedElementParameter = CD4CodeMill.cDParameterBuilder().setName( + "element").setMCType(MCTypeFacade.getInstance().createQualifiedType("Object")).build(); + ASTCDMethod removeTraversedElement = CDMethodFacade.getInstance().createMethod(CD4CodeMill + .modifierBuilder().build(), returnTypeAddTraversedElement, "removeTraversedElement", + addTraversedElementParameter); + visitorInterface.addCDMember(removeTraversedElement); + glexOpt.ifPresent(glex -> glex.replaceTemplate(EMPTY_BODY, removeTraversedElement, + new TemplateHookPoint("methods.visitor.removeTraversedElement"))); + + //visitor to get all classes from the original class diagram classes + CD4CodeTraverser t2 = CD4CodeMill.inheritanceTraverser(); + CDTypeCollector cdTypeCollector = new CDTypeCollector(); + t2.add4CDBasis(cdTypeCollector); + t2.add4CDInterfaceAndEnum(cdTypeCollector); + compilationUnit.accept(t2); + + classesFromClassdiagramAsString.addAll(cdTypeCollector.getClasses().stream().map(e -> e + .getSymbol().getFullName()).collect(Collectors.toList())); + classesFromClassdiagramAsString.addAll(cdTypeCollector.getInterfaces().stream().map(e -> e + .getSymbol().getFullName()).collect(Collectors.toList())); + classesFromClassdiagramAsString.addAll(cdTypeCollector.getEnums().stream().map(e -> e + .getSymbol().getFullName()).collect(Collectors.toList())); + } + } + + @Override + public void visit(ASTCDClass clazz) { + if (decoratorData.shouldDecorate(this.getClass(), clazz)) { + ASTCDClass decClazz = decoratorData.getAsDecorated(clazz); + currentDecoratedClass.add(decClazz); + + String packageName = clazz.getSymbol().getPackageName(); + + String visitorInterfaceName = packageName.isEmpty() ? "I" + clazz.getName() + "Visitor" + : packageName + ".I" + clazz.getName() + "Visitor"; + String pojoClassName = packageName.isEmpty() ? clazz.getName() : packageName + "." + clazz + .getName(); + ASTMCQualifiedType pojoClassQualifiedType = MCTypeFacade.getInstance().createQualifiedType( + pojoClassName); + ASTMCQualifiedType visitorInterfaceQualifiedType = MCTypeFacade.getInstance() + .createQualifiedType(visitorInterfaceName); + ASTCDParameter pojoClassParameter = CD4CodeMill.cDParameterBuilder().setName("node") + .setMCType(pojoClassQualifiedType).build(); + ASTCDParameter pojoInterfaceClassParameter = CD4CodeMill.cDParameterBuilder().setName("node") + .setMCType(visitorInterfaceQualifiedType).build(); + parameterOfPojo.add(pojoClassParameter); + + //create the methods for the visitor interface + //visit: + ASTCDMethod visitMethodHeader = CDMethodFacade.getInstance().createMethod(CD4CodeMill + .modifierBuilder().PUBLIC().build(), "visit", parameterOfPojo.peek()); + visitorInterface.addCDMember(visitMethodHeader); + // endVisit: + ASTCDMethod endVisitMethodHeader = CDMethodFacade.getInstance().createMethod(CD4CodeMill + .modifierBuilder().PUBLIC().build(), "endVisit", parameterOfPojo.peek()); + visitorInterface.addCDMember(endVisitMethodHeader); + // handle: + List upperInterfacesAndSuperClasses = getUpperInterfacesAndSuperClasses(clazz); + ASTCDMethod handleMethodHeader = CD4CodeMill.cDMethodBuilder().setModifier(CD4CodeMill + .modifierBuilder().PUBLIC().setAbstract(false).build()).setName("handle").setMCReturnType( + CD4CodeMill.mCReturnTypeBuilder().setMCVoidType(CD4CodeMill.mCVoidTypeBuilder() + .build()).build()).setCDParametersList(List.of(parameterOfPojo.peek())).build(); + glexOpt.ifPresent(glex -> glex.replaceTemplate(EMPTY_BODY, handleMethodHeader, + new TemplateHookPoint("methods.visitor.inheritanceHandle", + upperInterfacesAndSuperClasses))); + visitorInterface.addCDMember(handleMethodHeader); + + // traverse: + ASTCDMethod traverseMethodHeader = CDMethodFacade.getInstance().createMethod(CD4CodeMill + .modifierBuilder().PUBLIC().build(), "traverse", parameterOfPojo.peek()); + visitorInterface.addCDMember(traverseMethodHeader); + currentTraverseMethod.add(traverseMethodHeader); + glexOpt.ifPresent(glex -> glex.replaceTemplate(EMPTY_BODY, traverseMethodHeader, + new TemplateHookPoint("methods.visitor.traverse", classesFromClassdiagramAsString))); + + // add accept method to pojo class + ASTCDMethod acceptMethod = CDMethodFacade.getInstance().createDefaultMethod(CD4CodeMill + .modifierBuilder().PUBLIC().build(), "accept", visitorInterfaceParameter); + decClazz.addCDMember(acceptMethod); + + String errorCode = "0x01472"; + glexOpt.ifPresent(glex -> glex.replaceTemplate(EMPTY_BODY, acceptMethod, + new TemplateHookPoint("methods.visitor.accept", clazz, errorCode))); + } + } + + @Override + public void visit(de.monticore.cdinterfaceandenum._ast.ASTCDInterface node) { + if (decoratorData.shouldDecorate(this.getClass(), node)) { + String packageName = node.getSymbol().getPackageName(); + String visitorInterfaceName = packageName.isEmpty() ? "I" + node.getName() + "Visitor" + : packageName + ".I" + node.getName() + "Visitor"; + String pojoInterfaceName = packageName.isEmpty() ? node.getName() : packageName + "." + node + .getName(); + ASTMCQualifiedType visitorInterfaceQualifiedType = MCTypeFacade.getInstance() + .createQualifiedType(visitorInterfaceName); + ASTMCQualifiedType pojoInterfaceQualifiedType = MCTypeFacade.getInstance() + .createQualifiedType(pojoInterfaceName); + ASTCDParameter pojoInterfaceParameter = CD4CodeMill.cDParameterBuilder().setName("node") + .setMCType(pojoInterfaceQualifiedType).build(); + ASTCDParameter pojoInterfaceClassParameter = CD4CodeMill.cDParameterBuilder().setName("node") + .setMCType(visitorInterfaceQualifiedType).build(); + de.monticore.cdinterfaceandenum._ast.ASTCDInterface decInterface = decoratorData + .getAsDecorated(node); + currentDecoratedInterface.add(decInterface); + parameterOfPojo.add(pojoInterfaceParameter); + + //create the methods for the visitor interface + //visit: + ASTCDMethod visitMethodHeader = CDMethodFacade.getInstance().createMethod(CD4CodeMill + .modifierBuilder().PUBLIC().build(), "visit", parameterOfPojo.peek()); + visitorInterface.addCDMember(visitMethodHeader); + // endVisit: + ASTCDMethod endVisitMethodHeader = CDMethodFacade.getInstance().createMethod(CD4CodeMill + .modifierBuilder().PUBLIC().build(), "endVisit", parameterOfPojo.peek()); + visitorInterface.addCDMember(endVisitMethodHeader); + // handle: + List upperInterfacesAndSuperClasses = getUpperInterfacesAndSuperClasses(node); + ASTCDMethod handleMethodHeader = CD4CodeMill.cDMethodBuilder().setModifier(CD4CodeMill + .modifierBuilder().PUBLIC().setAbstract(false).build()).setName("handle").setMCReturnType( + CD4CodeMill.mCReturnTypeBuilder().setMCVoidType(CD4CodeMill.mCVoidTypeBuilder() + .build()).build()).setCDParametersList(List.of(parameterOfPojo.peek())).build(); + glexOpt.ifPresent(glex -> glex.replaceTemplate(EMPTY_BODY, handleMethodHeader, + new TemplateHookPoint("methods.visitor.inheritanceHandle", + upperInterfacesAndSuperClasses))); + visitorInterface.addCDMember(handleMethodHeader); + + // traverse: + ASTCDMethod traverseMethodHeader = CDMethodFacade.getInstance().createMethod(CD4CodeMill + .modifierBuilder().PUBLIC().build(), "traverse", parameterOfPojo.peek()); + visitorInterface.addCDMember(traverseMethodHeader); + currentTraverseMethod.add(traverseMethodHeader); + glexOpt.ifPresent(glex -> glex.replaceTemplate(EMPTY_BODY, traverseMethodHeader, + new TemplateHookPoint("methods.visitor.traverse", classesFromClassdiagramAsString))); + + // add accept method to pojo class + ASTCDMethod acceptMethod = CDMethodFacade.getInstance().createDefaultMethod(CD4CodeMill + .modifierBuilder().PUBLIC().build(), "accept", visitorInterfaceParameter); + decInterface.addCDMember(acceptMethod); + + String errorCode = "0x01472"; + glexOpt.ifPresent(glex -> glex.replaceTemplate(EMPTY_BODY, acceptMethod, + new TemplateHookPoint("methods.visitor.accept", node, errorCode))); + } + } + + @Override + public void endVisit(ASTCDClass clazz) { + if (decoratorData.shouldDecorate(this.getClass(), clazz)) { + parameterOfPojo.pop(); + currentDecoratedClass.pop(); + currentTraverseMethod.pop(); + } + } + + @Override + public void endVisit(de.monticore.cdinterfaceandenum._ast.ASTCDInterface node) { + if (decoratorData.shouldDecorate(this.getClass(), node)) { + parameterOfPojo.pop(); + currentDecoratedInterface.pop(); + currentTraverseMethod.pop(); + } + } + + @Override + public void visit(ASTCDAttribute attribute) { + if (!decoratorData.shouldDecorate(this.getClass(), attribute)) { + return; + } + // it is required to check if a setter method exists by checking the methods of the SetterDecorator for + // an exact match of "set" + attribute.getName() + // if this method does not exist, + // we need to reference the attribute directly in the build method + String attributeName; + List methods = decoratorData.getDecoratorData(SetterDecorator.class) != null + ? decoratorData.getDecoratorData(SetterDecorator.class).methods.get(attribute) : null; + if (methods == null || methods.isEmpty() || methods.stream().noneMatch(m -> m.getName().equals( + "set" + StringTransformations.capitalize(attribute.getName())))) { + attributeName = "node." + attribute.getName(); + } + else { + attributeName = "node.get" + attribute.getName().substring(0, 1).toUpperCase() + attribute + .getName().substring(1) + "()"; + } + + glexOpt.ifPresent(glex -> glex.addAfterTemplate("methods.visitor.traverse:Inner", + currentTraverseMethod.peek(), new TemplateHookPoint("methods.visitor.traverseInner", + classesFromClassdiagramAsString, attribute.getMCType(), attributeName))); + } + + /** + * This method resolves the super classes and interfaces of a class and returns all in a somewhat + * expected order + *

+ * The order is as follows: + * If the class has a single superclass or interface, the order is ascending from the + * superclass/interface to the class itself. + * If the class has multiple superclasses or interfaces, the order is unpredictable + * + * @param node class that should be inspected for super classes and interfaces + * @return an ordered list of super classes and interfaces + */ + private List getUpperInterfacesAndSuperClasses(ASTCNode node) { + List result = new ArrayList<>(); + List allVisited = new ArrayList<>(); + allVisited.add(node); + List lastRoundVisited = new ArrayList<>(); + lastRoundVisited.add(node); + List nextRoundVisited = new ArrayList<>(); + + while (!lastRoundVisited.isEmpty()) { + for (ASTCNode currentNode : lastRoundVisited) { + List resultOfTransitiveType = CDSymbolTables.getTransitiveSuperTypes( + (ASTCDType) currentNode); + //super class + Optional resultOfTransitiveClass = resultOfTransitiveType.stream().filter( + m -> m instanceof de.monticore.cd4codebasis._ast.ASTCDClass).findFirst(); + if (resultOfTransitiveClass.isPresent()) { + if (!allVisited.contains(resultOfTransitiveClass.get())) { + allVisited.add((ASTCNode) resultOfTransitiveClass.get()); + result.add(resultOfTransitiveClass.get().getSymbol().getFullName()); + nextRoundVisited.add((ASTCNode) resultOfTransitiveClass.get()); + } + } + + //interfaces + List resultOfTransitiveInterface = resultOfTransitiveType.stream().filter( + m -> m instanceof de.monticore.cdinterfaceandenum._ast.ASTCDInterface).collect( + Collectors.toList()); + //filter out all interfaces that are not implemented by the current class + resultOfTransitiveInterface = resultOfTransitiveInterface.stream().filter(i -> { + // The full name of the current interface we are checking. + String currentInterfaceName = ((de.monticore.cdinterfaceandenum._ast.ASTCDInterface) i) + .getSymbol().getFullName(); + + // Check if this name exists in the other list. + return ((ASTCDType) currentNode).getInterfaceList().stream().anyMatch(type -> { + if (type instanceof de.monticore.types.mcbasictypes._ast.ASTMCQualifiedType) { + return ((de.monticore.types.mcbasictypes._ast.ASTMCQualifiedType) type) + .getDefiningSymbol().map(symbol -> symbol.getFullName().equals( + currentInterfaceName)).orElse(false); + } + return false; + }); + }).collect(Collectors.toList()); + + //filter out all interfaces that have already been visited + resultOfTransitiveInterface = resultOfTransitiveInterface.stream().filter(m -> !allVisited + .contains(m)).collect(Collectors.toList()); + + for (ASTCDType resultOfTransitiveInterfaceElement : resultOfTransitiveInterface) { + allVisited.add((ASTCNode) resultOfTransitiveInterfaceElement); + result.add(resultOfTransitiveInterfaceElement.getSymbol().getFullName()); + nextRoundVisited.add((ASTCNode) resultOfTransitiveInterfaceElement); + } + } + lastRoundVisited.clear(); + lastRoundVisited.addAll(nextRoundVisited); + nextRoundVisited.clear(); + } + return result; + } + + @Override + public void addToTraverser(CD4CodeTraverser traverser) { + traverser.add4CDBasis(this); + traverser.add4CDInterfaceAndEnum(this); + } + +} diff --git a/cdlang/src/main/java/de/monticore/cd/codegen/decorators/ObserverDecorator.java b/cdlang/src/main/java/de/monticore/cd/codegen/decorators/ObserverDecorator.java index 148ac4a7a..4ac144fa6 100644 --- a/cdlang/src/main/java/de/monticore/cd/codegen/decorators/ObserverDecorator.java +++ b/cdlang/src/main/java/de/monticore/cd/codegen/decorators/ObserverDecorator.java @@ -1,33 +1,196 @@ /* (c) https://github.com/MontiCore/monticore */ package de.monticore.cd.codegen.decorators; +import com.google.common.collect.Iterables; +import de.monticore.ast.ASTNode; import de.monticore.cd.codegen.decorators.data.AbstractDecorator; -import de.monticore.cd.facade.CDAttributeFacade; +import de.monticore.cd.facade.CDMethodFacade; +import de.monticore.cd.methodtemplates.CD4C; import de.monticore.cd4code.CD4CodeMill; import de.monticore.cd4code._visitor.CD4CodeTraverser; -import de.monticore.cdbasis._ast.ASTCDClass; +import de.monticore.cd4codebasis._ast.ASTCDInterface; +import de.monticore.cd4codebasis._ast.ASTCDMethod; +import de.monticore.cd4codebasis._ast.ASTCDParameter; +import de.monticore.cdbasis._ast.*; import de.monticore.cdbasis._visitor.CDBasisVisitor2; +import de.monticore.generating.templateengine.StringHookPoint; +import de.monticore.generating.templateengine.TemplateHookPoint; import de.monticore.types.MCTypeFacade; +import de.monticore.types.mcbasictypes._ast.*; +import static de.monticore.cd.codegen.CD2JavaTemplates.EMPTY_BODY; +import static de.monticore.cd.codegen.CD2JavaTemplates.VALUE; +import de.se_rwth.commons.StringTransformations; +import de.se_rwth.commons.logging.Log; +import org.apache.commons.lang3.StringUtils; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; +/** + * Applies the Observer-Pattern to the CD + */ public class ObserverDecorator extends AbstractDecorator implements CDBasisVisitor2 { + @Override + @SuppressWarnings("rawtypes") + public Iterable> getMustRunAfter() { + //We check that the SetterDecorator has added a Setter for an attribute, + // thus the Setter decorator has to run before. + return Iterables.concat(super.getMustRunAfter(), Collections.singletonList( + SetterDecorator.class)); + } + @Override public void visit(ASTCDClass clazz) { if (decoratorData.shouldDecorate(this.getClass(), clazz)) { - var origParent = this.decoratorData.getParent(clazz).get(); - var decParent = this.decoratorData.getAsDecorated(origParent); + ASTCDClass decClazz = decoratorData.getAsDecorated(clazz); + // Get the parent (package or CDDef) + ASTNode origParent = this.decoratorData.getParent(clazz).get(); + ASTNode decParent = this.decoratorData.getAsDecorated(origParent); + + String packageName = clazz.getSymbol().getPackageName(); + + String observerInterfaceName = packageName.isEmpty() ? "I" + clazz.getName() + "Observer" + : packageName + ".I" + clazz.getName() + "Observer"; + String observableInterfaceName = packageName.isEmpty() ? "I" + clazz.getName() + "Observable" + : packageName + ".I" + clazz.getName() + "Observable"; + ASTMCQualifiedType observerInterfaceQualifiedType = MCTypeFacade.getInstance() + .createQualifiedType(observerInterfaceName); + ASTMCQualifiedType observableInterfaceQualifiedType = MCTypeFacade.getInstance() + .createQualifiedType(observableInterfaceName); + ASTCDParameter observerParameter = CD4CodeMill.cDParameterBuilder().setName("observer") + .setMCType(observerInterfaceQualifiedType).build(); + ASTCDParameter observeParameter = CD4CodeMill.cDParameterBuilder().setName("observable") + .setMCType(observableInterfaceQualifiedType).build(); + //create a type of the class + ASTMCType classType = MCTypeFacade.getInstance().createQualifiedType(clazz.getName()); + ASTCDParameter classParameter = CD4CodeMill.cDParameterBuilder().setName("clazz").setMCType( + classType).build(); + + //make sure an attribute of the type and the name of an observer is not already present + if (decClazz.getCDAttributeList().stream().anyMatch(attr -> attr.getMCType().printType() + .equals(observerInterfaceName) && attr.getName().equals("observerList"))) { + Log.error("0xA1234 The class " + decClazz.getName() + " already has an attribute of type " + + observerInterfaceName + " with the name observerList"); + } + else { + //create an attribute of the type and the name of an observer + ASTCDAttribute observerList = CD4CodeMill.cDAttributeBuilder().setName("observerList") + .setMCType(MCTypeFacade.getInstance().createListTypeOf(observerInterfaceQualifiedType)) + .setModifier(CD4CodeMill.modifierBuilder().PROTECTED().build()).build(); + decClazz.addCDMember(observerList); + + glexOpt.ifPresent(glex -> glex.replaceTemplate(VALUE, observerList, new StringHookPoint( + " = new ArrayList<>()"))); + } + + //create own interface Observable and Observer for every class + ASTCDInterface interfaceObservableArtifact = CD4CodeMill.cDInterfaceBuilder().setName("I" + + decClazz.getName() + "Observable").setModifier(CD4CodeMill.modifierBuilder().PUBLIC() + .build()).build(); + ASTCDInterface interfaceObserverArtifact = CD4CodeMill.cDInterfaceBuilder().setName("I" + + decClazz.getName() + "Observer").setModifier(CD4CodeMill.modifierBuilder().PUBLIC() + .build()).build(); - var observerInterface = CD4CodeMill.cDInterfaceBuilder().setName("I" + clazz.getName() - + "Observer").setModifier(CD4CodeMill.modifierBuilder().PUBLIC().build()).build(); + //add the interfaces to the package + addElementToParent(decParent, interfaceObservableArtifact); + addElementToParent(decParent, interfaceObserverArtifact); - addElementToParent(decParent, observerInterface); + //build the methods + ASTCDMethod addObserver = CDMethodFacade.getInstance().createMethod(CD4CodeMill + .modifierBuilder().PUBLIC().build(), "addObserver", observerParameter); + ASTCDMethod removeObserver = CDMethodFacade.getInstance().createMethod(CD4CodeMill + .modifierBuilder().PUBLIC().build(), "removeObserver", observerParameter); + ASTCDMethod notifyObservers = CDMethodFacade.getInstance().createMethod(CD4CodeMill + .modifierBuilder().PUBLIC().build(), "notifyObservers", classParameter); + ASTCDMethod update = CDMethodFacade.getInstance().createMethod(CD4CodeMill.modifierBuilder() + .PUBLIC().build(), "update", classParameter); + List attributeSpecificMethodStashes = new ArrayList<>(); + clazz.getCDAttributeList().forEach(attribute -> attributeSpecificMethodStashes.add( + new AttributeSpecificMethodStash(CDMethodFacade.getInstance().createMethod(CD4CodeMill + .modifierBuilder().PUBLIC().build(), "updateObserver" + StringUtils.capitalize( + attribute.getName()), List.of(classParameter, CD4CodeMill.cDParameterBuilder() + .setName("ov").setMCType(attribute.getMCType()).build())), CDMethodFacade + .getInstance().createMethod(CD4CodeMill.modifierBuilder().PUBLIC() + .build(), "notifyObserver" + StringUtils.capitalize(attribute + .getName()), List.of(classParameter, CD4CodeMill + .cDParameterBuilder().setName("ov").setMCType(attribute + .getMCType()).build())), attribute.getName()))); - var decClass = this.decoratorData.getAsDecorated(clazz); + //add the methods to the interface Observable + interfaceObservableArtifact.addCDMember(addObserver.deepClone()); + interfaceObservableArtifact.addCDMember(removeObserver.deepClone()); + interfaceObservableArtifact.addCDMember(notifyObservers.deepClone()); + attributeSpecificMethodStashes.forEach(stash -> interfaceObservableArtifact.addCDMember(stash + .getMethodObservable().deepClone())); - decClass.addCDMember(CDAttributeFacade.getInstance().createAttribute(CD4CodeMill - .modifierBuilder().PROTECTED().build(), MCTypeFacade.getInstance().createListTypeOf( - observerInterface.getName()), "_observers")); + // add the methods to the interface Observer + interfaceObserverArtifact.addCDMember(update.deepClone()); + attributeSpecificMethodStashes.forEach(stash -> interfaceObserverArtifact.addCDMember(stash + .getMethodObserver().deepClone())); + + //add the interface methods to the pojo class + addToClass(decClazz, addObserver); + glexOpt.ifPresent(glex -> glex.replaceTemplate(EMPTY_BODY, addObserver, new TemplateHookPoint( + "methods.observer.addObserver", "observerList", observerInterfaceQualifiedType + .getMCQualifiedName().getQName()))); + addToClass(decClazz, removeObserver); + glexOpt.ifPresent(glex -> glex.replaceTemplate(EMPTY_BODY, removeObserver, + new TemplateHookPoint("methods.observer.removeObserver.ftl", "observerList", + observerInterfaceQualifiedType.getMCQualifiedName().getQName()))); + addToClass(decClazz, notifyObservers); + glexOpt.ifPresent(glex -> glex.replaceTemplate(EMPTY_BODY, notifyObservers, + new TemplateHookPoint("methods.observer.notifyObserver", "observerList", observerParameter + .getMCType().printType()))); + attributeSpecificMethodStashes.forEach(stash -> { + addToClass(decClazz, stash.getMethodObservable()); + glexOpt.ifPresent(glex -> glex.replaceTemplate(EMPTY_BODY, stash.getMethodObservable(), + new TemplateHookPoint("methods.observer.notifyObserverAttributeSpecific", + "observerList", observerParameter.getMCType().printType(), stash + .getAttributeName()))); + }); + + //add an interface list if not present in the clazz + if (!decClazz.isPresentCDInterfaceUsage()) { + decClazz.setCDInterfaceUsage(CD4CodeMill.cDInterfaceUsageBuilder().build()); + } + //add the Observable interfaces to the class + decClazz.getCDInterfaceUsage().addInterface(observableInterfaceQualifiedType); + + // add an import statement for the Observer interface + CD4C.getInstance().addImport(decClazz, observableInterfaceName); + CD4C.getInstance().addImport(decClazz, observerInterfaceName); + + //To call a generated method whenever an attribute is changed in the pojo class, we need to transform the setters + // into additionally calling the attribute specific notifyObserver${attributeName} method + for (ASTCDAttribute attribute : clazz.getCDAttributeList()) { + //We expect that the SetterDecorator has added a Setter for this attribute to the pojo class + List methods = decoratorData.getDecoratorData(SetterDecorator.class) != null + ? decoratorData.getDecoratorData(SetterDecorator.class).methods.get(attribute) : null; + if (!(methods == null || methods.isEmpty())) { + List setMethods = methods.stream().filter(m -> m.getName().equals("set" + + StringTransformations.capitalize(attribute.getName()))).collect(Collectors + .toList()); + + for (ASTCDMethod setMethod : setMethods) { + //when we have an attribute with the same name as the helper attribute we need to create, + // we need to rename the new attribute to avoid conflicts + // (we only need to test it against the name of the parameter in the method signature which is the attribute.getName()) + String oldValueName; + if (attribute.getName().equals("ov")) { + oldValueName = "_ov"; + } + else { + oldValueName = "ov"; + } + glexOpt.ifPresent(glex -> glex.replaceTemplate(EMPTY_BODY, setMethod, + new TemplateHookPoint("methods.observer.setWithObservableMethodCall", attribute, + oldValueName))); + } + } + } } } @@ -36,4 +199,25 @@ public void addToTraverser(CD4CodeTraverser traverser) { traverser.add4CDBasis(this); } + static class AttributeSpecificMethodStash { + + private final ASTCDMethod methodObserver; + private final ASTCDMethod methodObservable; + private final String attributeName; + + public AttributeSpecificMethodStash(ASTCDMethod methodObserver, ASTCDMethod methodObservable, + String attributeName) { + this.methodObserver = methodObserver; + this.methodObservable = methodObservable; + this.attributeName = attributeName; + } + + public ASTCDMethod getMethodObserver() { return methodObserver; } + + public ASTCDMethod getMethodObservable() { return methodObservable; } + + public String getAttributeName() { return attributeName; } + + } + } diff --git a/cdlang/src/main/java/de/monticore/cd/codegen/decorators/VisitorDecorator.java b/cdlang/src/main/java/de/monticore/cd/codegen/decorators/VisitorDecorator.java new file mode 100644 index 000000000..96944a95b --- /dev/null +++ b/cdlang/src/main/java/de/monticore/cd/codegen/decorators/VisitorDecorator.java @@ -0,0 +1,256 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.cd.codegen.decorators; + +import com.google.common.collect.Iterables; +import de.monticore.cd.codegen.decorators.data.AbstractDecorator; +import de.monticore.cd.codegen.decorators.data.CDTypeCollector; +import de.monticore.cd.facade.CDMethodFacade; +import de.monticore.cd4code.CD4CodeMill; +import de.monticore.cd4code._visitor.CD4CodeTraverser; +import de.monticore.cd4codebasis._ast.ASTCDInterface; +import de.monticore.cd4codebasis._ast.ASTCDMethod; +import de.monticore.cd4codebasis._ast.ASTCDParameter; +import de.monticore.cdbasis._ast.*; +import de.monticore.cdbasis._visitor.CDBasisVisitor2; +import de.monticore.cdinterfaceandenum._visitor.CDInterfaceAndEnumVisitor2; +import de.monticore.generating.templateengine.TemplateHookPoint; +import de.monticore.types.MCTypeFacade; +import de.monticore.types.mcbasictypes._ast.ASTMCQualifiedType; +import de.monticore.types.mcbasictypes._ast.ASTMCReturnType; +import de.monticore.types.mccollectiontypes._ast.ASTMCSetType; +import de.se_rwth.commons.StringTransformations; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Stack; +import java.util.stream.Collectors; +import static de.monticore.cd.codegen.CD2JavaTemplates.EMPTY_BODY; + +/** + * This decorator generates a visitor interface for each class and interface in the class diagram. + * The visitor interface contains methods to visit, endVisit, handle, traverse, + * All classes and interfaces contain an accept method that accepts the visitor interface as a + * parameter. + *

+ * The visitor interface is used to traverse over classes in the class diagram. + *

+ * When visit(node) we add the visitedElements into a set and remove them after the endVisit again + * to account for circular relations which would otherwise not terminate. + */ +public class VisitorDecorator extends AbstractDecorator implements + CDBasisVisitor2, CDInterfaceAndEnumVisitor2 { + + Stack parameterOfPojo = new Stack<>(); + Stack currentDecoratedClass = new Stack<>(); + Stack currentDecoratedInterface = + new Stack<>(); + ASTCDInterface visitorInterface; + ASTCDParameter visitorInterfaceParameter; + Stack currentTraverseMethod = new Stack<>(); + /** + * a collection of all classes from the class diagram as strings + */ + List classesFromClassdiagramAsString = new ArrayList<>(); + boolean isInitialized = false; + + @Override + @SuppressWarnings("rawtypes") + public Iterable> getMustRunAfter() { + //We check that the SetterDecorator has added a Setter for an attribute, + // thus the Setter decorator has to run before. + return Iterables.concat(super.getMustRunAfter(), Collections.singletonList( + SetterDecorator.class)); + } + + @Override + public void visit(ASTCDCompilationUnit compilationUnit) { + init(compilationUnit, compilationUnit.getCDDefinition(), "I" + compilationUnit.getCDDefinition() + .getName() + "Visitor"); + } + + public void init(ASTCDCompilationUnit compilationUnit, ASTCDDefinition definition, + String visitorInterfaceName) { + if (isInitialized) { + return; + } + //create the visitor interface + visitorInterface = CD4CodeMill.cDInterfaceBuilder().setName(visitorInterfaceName).setModifier( + CD4CodeMill.modifierBuilder().PUBLIC().build()).build(); + + // add the visitor interface to the definition + ASTCDDefinition decoratedDefinition = this.decoratorData.getAsDecorated(definition); + decoratedDefinition.addCDElement(visitorInterface); + + // create the visitor interface parameter + String packageName = definition.getSymbol().getPackageName(); + String visitorInterfaceQualifiedName = packageName.isEmpty() ? visitorInterfaceName + : packageName + "." + visitorInterfaceName; + ASTMCQualifiedType visitorInterfaceQualifiedType = MCTypeFacade.getInstance() + .createQualifiedType(visitorInterfaceQualifiedName); + visitorInterfaceParameter = CD4CodeMill.cDParameterBuilder().setName("visitor").setMCType( + visitorInterfaceQualifiedType).build(); + + // add getTraversedElements Set method to the visitor interface + ASTMCSetType setType = MCTypeFacade.getInstance().createSetTypeOf("Object"); + ASTMCReturnType returnType = CD4CodeMill.mCReturnTypeBuilder().setMCType(setType).build(); + ASTCDMethod getTraversedElementsMethod = CDMethodFacade.getInstance().createMethod(CD4CodeMill + .modifierBuilder().setAbstract(true).build(), returnType, "getTraversedElements"); + visitorInterface.addCDMember(getTraversedElementsMethod); + + // add addTraversedElement method to the visitor interface + ASTMCReturnType returnTypeAddTraversedElement = CD4CodeMill.mCReturnTypeBuilder().setMCVoidType( + CD4CodeMill.mCVoidTypeBuilder().build()).build(); + ASTCDParameter addTraversedElementParameter = CD4CodeMill.cDParameterBuilder().setName( + "element").setMCType(MCTypeFacade.getInstance().createQualifiedType("Object")).build(); + ASTCDMethod addTraversedElementMethod = CDMethodFacade.getInstance().createMethod(CD4CodeMill + .modifierBuilder().build(), returnTypeAddTraversedElement, "addTraversedElement", + addTraversedElementParameter); + visitorInterface.addCDMember(addTraversedElementMethod); + glexOpt.ifPresent(glex -> glex.replaceTemplate(EMPTY_BODY, addTraversedElementMethod, + new TemplateHookPoint("methods.visitor.addTraversedElement"))); + + // add removeTraversedElement method to the visitor interface + ASTCDMethod removeTraversedElement = CDMethodFacade.getInstance().createMethod(CD4CodeMill + .modifierBuilder().build(), returnTypeAddTraversedElement, "removeTraversedElement", + addTraversedElementParameter); + visitorInterface.addCDMember(removeTraversedElement); + glexOpt.ifPresent(glex -> glex.replaceTemplate(EMPTY_BODY, removeTraversedElement, + new TemplateHookPoint("methods.visitor.removeTraversedElement"))); + + //visitor to get all classes from the original class diagram classes + CD4CodeTraverser t2 = CD4CodeMill.inheritanceTraverser(); + CDTypeCollector cdTypeCollector = new CDTypeCollector(); + t2.add4CDBasis(cdTypeCollector); + t2.add4CDInterfaceAndEnum(cdTypeCollector); + compilationUnit.accept(t2); + + classesFromClassdiagramAsString.addAll(cdTypeCollector.getClasses().stream().map(e -> e + .getSymbol().getFullName()).collect(Collectors.toList())); + classesFromClassdiagramAsString.addAll(cdTypeCollector.getInterfaces().stream().map(e -> e + .getSymbol().getFullName()).collect(Collectors.toList())); + classesFromClassdiagramAsString.addAll(cdTypeCollector.getEnums().stream().map(e -> e + .getSymbol().getFullName()).collect(Collectors.toList())); + } + + @Override + public void visit(ASTCDClass clazz) { + if (decoratorData.shouldDecorate(this.getClass(), clazz)) { + ASTCDClass decClazz = decoratorData.getAsDecorated(clazz); + currentDecoratedClass.add(decClazz); + + String packageName = clazz.getSymbol().getPackageName(); + + String visitorInterfaceName = packageName.isEmpty() ? "I" + clazz.getName() + "Visitor" + : packageName + ".I" + clazz.getName() + "Visitor"; + String pojoClassName = packageName.isEmpty() ? clazz.getName() : packageName + "." + clazz + .getName(); + ASTMCQualifiedType pojoClassQualifiedType = MCTypeFacade.getInstance().createQualifiedType( + pojoClassName); + ASTMCQualifiedType visitorInterfaceQualifiedType = MCTypeFacade.getInstance() + .createQualifiedType(visitorInterfaceName); + ASTCDParameter pojoClassParameter = CD4CodeMill.cDParameterBuilder().setName("node") + .setMCType(pojoClassQualifiedType).build(); + ASTCDParameter pojoInterfaceClassParameter = CD4CodeMill.cDParameterBuilder().setName("node") + .setMCType(visitorInterfaceQualifiedType).build(); + parameterOfPojo.add(pojoClassParameter); + + //create the methods for the visitor interface + //visit: + ASTCDMethod visitMethodHeader = CDMethodFacade.getInstance().createMethod(CD4CodeMill + .modifierBuilder().PUBLIC().build(), "visit", parameterOfPojo.peek()); + visitorInterface.addCDMember(visitMethodHeader); + // endVisit: + ASTCDMethod endVisitMethodHeader = CDMethodFacade.getInstance().createMethod(CD4CodeMill + .modifierBuilder().PUBLIC().build(), "endVisit", parameterOfPojo.peek()); + visitorInterface.addCDMember(endVisitMethodHeader); + // handle: + ASTCDMethod handleMethodHeader = CD4CodeMill.cDMethodBuilder().setModifier(CD4CodeMill + .modifierBuilder().PUBLIC().setAbstract(false).build()).setName("handle").setMCReturnType( + CD4CodeMill.mCReturnTypeBuilder().setMCVoidType(CD4CodeMill.mCVoidTypeBuilder() + .build()).build()).setCDParametersList(List.of(parameterOfPojo.peek())).build(); + visitorInterface.addCDMember(handleMethodHeader); + + glexOpt.ifPresent(glex -> glex.replaceTemplate(EMPTY_BODY, handleMethodHeader, + new TemplateHookPoint("methods.visitor.handle"))); + // traverse: + ASTCDMethod traverseMethodHeader = CDMethodFacade.getInstance().createMethod(CD4CodeMill + .modifierBuilder().PUBLIC().build(), "traverse", parameterOfPojo.peek()); + visitorInterface.addCDMember(traverseMethodHeader); + currentTraverseMethod.add(traverseMethodHeader); + glexOpt.ifPresent(glex -> glex.replaceTemplate(EMPTY_BODY, traverseMethodHeader, + new TemplateHookPoint("methods.visitor.traverse", classesFromClassdiagramAsString))); + + // add accept method to pojo class + ASTCDMethod acceptMethod = CDMethodFacade.getInstance().createDefaultMethod(CD4CodeMill + .modifierBuilder().PUBLIC().build(), "accept", visitorInterfaceParameter); + decClazz.addCDMember(acceptMethod); + + String errorCode = "0x01472"; + glexOpt.ifPresent(glex -> glex.replaceTemplate(EMPTY_BODY, acceptMethod, + new TemplateHookPoint("methods.visitor.accept", clazz, errorCode))); + } + } + + @Override + public void visit(de.monticore.cdinterfaceandenum._ast.ASTCDInterface node) { + if (decoratorData.shouldDecorate(this.getClass(), node)) { + de.monticore.cdinterfaceandenum._ast.ASTCDInterface decInterface = decoratorData + .getAsDecorated(node); + currentDecoratedInterface.add(decInterface); + + // add accept method to pojo class + ASTCDMethod acceptMethod = CDMethodFacade.getInstance().createMethod(CD4CodeMill + .modifierBuilder().PUBLIC().build(), "accept", visitorInterfaceParameter); + decInterface.addCDMember(acceptMethod); + } + } + + @Override + public void endVisit(ASTCDClass clazz) { + if (decoratorData.shouldDecorate(this.getClass(), clazz)) { + parameterOfPojo.pop(); + currentDecoratedClass.pop(); + currentTraverseMethod.pop(); + } + } + + @Override + public void endVisit(de.monticore.cdinterfaceandenum._ast.ASTCDInterface node) { + if (decoratorData.shouldDecorate(this.getClass(), node)) { + currentDecoratedInterface.pop(); + } + } + + @Override + public void visit(ASTCDAttribute attribute) { + if (!decoratorData.shouldDecorate(this.getClass(), attribute)) { + return; + } + // it is required to check if a setter method exists by checking the methods of the SetterDecorator for + // an exact match of "set" + attribute.getName() + // if this method does not exist, + // we need to reference the attribute directly in the build method + String attributeName; + List methods = decoratorData.getDecoratorData(SetterDecorator.class) != null + ? decoratorData.getDecoratorData(SetterDecorator.class).methods.get(attribute) : null; + if (methods == null || methods.isEmpty() || methods.stream().noneMatch(m -> m.getName().equals( + "set" + StringTransformations.capitalize(attribute.getName())))) { + attributeName = "node." + attribute.getName(); + } + else { + attributeName = "node.get" + attribute.getName().substring(0, 1).toUpperCase() + attribute + .getName().substring(1) + "()"; + } + + glexOpt.ifPresent(glex -> glex.addAfterTemplate("methods.visitor.traverse:Inner", + currentTraverseMethod.peek(), new TemplateHookPoint("methods.visitor.traverseInner", + classesFromClassdiagramAsString, attribute.getMCType(), attributeName))); + } + + @Override + public void addToTraverser(CD4CodeTraverser traverser) { + traverser.add4CDBasis(this); + traverser.add4CDInterfaceAndEnum(this); + } + +} diff --git a/cdlang/src/main/java/de/monticore/cd/codegen/decorators/data/CDTypeCollector.java b/cdlang/src/main/java/de/monticore/cd/codegen/decorators/data/CDTypeCollector.java new file mode 100644 index 000000000..49d3a10bb --- /dev/null +++ b/cdlang/src/main/java/de/monticore/cd/codegen/decorators/data/CDTypeCollector.java @@ -0,0 +1,58 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.cd.codegen.decorators.data; + +import de.monticore.cdbasis._ast.ASTCDClass; +import de.monticore.cdbasis._visitor.CDBasisVisitor2; +import de.monticore.cdinterfaceandenum._ast.ASTCDEnum; +import de.monticore.cdinterfaceandenum._ast.ASTCDInterface; +import de.monticore.cdinterfaceandenum._visitor.CDInterfaceAndEnumVisitor2; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Set; + +/** + * A visitor class that acts as a container to collect and hold data. + * It ensures that only one instance of this class exists globally. + */ +public class CDTypeCollector implements CDBasisVisitor2, CDInterfaceAndEnumVisitor2 { + + protected final Set classes = new HashSet<>(); + protected final Set interfaces = new HashSet<>(); + protected final Set enums = new HashSet<>(); + + protected ArrayList classList = new ArrayList<>(); + protected ArrayList interfaceList = new ArrayList<>(); + protected ArrayList enumList = new ArrayList<>(); + + @Override + public void visit(ASTCDClass node) { + classes.add(node); + classList.add(node); + } + + @Override + public void visit(ASTCDInterface node) { + interfaces.add(node); + interfaceList.add(node); + } + + @Override + public void visit(ASTCDEnum node) { + enums.add(node); + enumList.add(node); + } + + public Set getClasses() { return classes; } + + public Set getInterfaces() { return interfaces; } + + public Set getEnums() { return enums; } + + public ArrayList getClassList() { return classList; } + + public ArrayList getInterfaceList() { return interfaceList; } + + public ArrayList getEnumList() { return enumList; } + +} diff --git a/cdlang/src/main/java/de/monticore/cd/codegen/decorators/data/DecoratorData.java b/cdlang/src/main/java/de/monticore/cd/codegen/decorators/data/DecoratorData.java index 0a0605567..9efe4f4dd 100644 --- a/cdlang/src/main/java/de/monticore/cd/codegen/decorators/data/DecoratorData.java +++ b/cdlang/src/main/java/de/monticore/cd/codegen/decorators/data/DecoratorData.java @@ -15,6 +15,8 @@ import de.monticore.cdbasis._ast.ASTCDClass; import de.monticore.cdbasis._ast.ASTCDCompilationUnit; import de.monticore.cdbasis._ast.ASTCDDefinition; +import de.monticore.cdinterfaceandenum._ast.ASTCDEnum; +import de.monticore.cdinterfaceandenum._ast.ASTCDInterface; import de.monticore.symbols.oosymbols._symboltable.FieldSymbol; import de.monticore.symboltable.ISymbol; import de.monticore.tagging.SimpleSymbolTagger; @@ -144,6 +146,12 @@ else if (node instanceof ASTCDCompilationUnit) { else if (node instanceof ASTCDMethod) { result = matchCDMethod((ASTCDMethod) node, matcherData); } + else if (node instanceof ASTCDInterface) { + result = matchCDInterface(((ASTCDInterface) node), matcherData); + } + else if (node instanceof ASTCDEnum) { + result = matchCDEnum(((ASTCDEnum) node), matcherData); + } else { Log.error(INTERNAL_ERROR_CODE + ": Unable add to parent of unknown type " + node.getClass() .getName(), node.get_SourcePositionStart()); @@ -184,6 +192,48 @@ protected MatchResult matchClass(ASTCDClass node, MatcherData matcherData) { return MatchResult.DEFAULT; } + protected MatchResult matchCDInterface(ASTCDInterface node, MatcherData matcherData) { + if (node.getModifier().isPresentStereotype()) { + for (var s : node.getModifier().getStereotype().getValuesList()) { + var r = matchStereo(s, matcherData); + if (r != MatchResult.DEFAULT) + return r; + } + } + + if (node.isPresentSymbol()) { + var r = matchCLI(node.getSymbol(), matcherData); + if (r != MatchResult.DEFAULT) + return r; + r = matchTags(node.getSymbol(), matcherData); + if (r != MatchResult.DEFAULT) + return r; + } + + return MatchResult.DEFAULT; + } + + protected MatchResult matchCDEnum(ASTCDEnum node, MatcherData matcherData) { + if (node.getModifier().isPresentStereotype()) { + for (var s : node.getModifier().getStereotype().getValuesList()) { + var r = matchStereo(s, matcherData); + if (r != MatchResult.DEFAULT) + return r; + } + } + + if (node.isPresentSymbol()) { + var r = matchCLI(node.getSymbol(), matcherData); + if (r != MatchResult.DEFAULT) + return r; + r = matchTags(node.getSymbol(), matcherData); + if (r != MatchResult.DEFAULT) + return r; + } + + return MatchResult.DEFAULT; + } + protected MatchResult matchCDAttribute(ASTCDAttribute node, MatcherData matcherData) { if (node.getModifier().isPresentStereotype()) { for (var s : node.getModifier().getStereotype().getValuesList()) { diff --git a/cdlang/src/main/java/de/monticore/cdgen/CDGenTool.java b/cdlang/src/main/java/de/monticore/cdgen/CDGenTool.java index 2a037dc49..a895a2169 100644 --- a/cdlang/src/main/java/de/monticore/cdgen/CDGenTool.java +++ b/cdlang/src/main/java/de/monticore/cdgen/CDGenTool.java @@ -2,12 +2,14 @@ package de.monticore.cdgen; import de.monticore.CDGeneratorTool; +import de.monticore.cd.codegen.CDGenService; import de.monticore.cd.codegen.CDGenerator; import de.monticore.cd.codegen.CdUtilsPrinter; import de.monticore.cd.codegen.DecoratorConfig; import de.monticore.cd.codegen.trafo.DefaultVisibilityPublicTrafo; import de.monticore.cd.codegen.trafo.TOPTrafo; import de.monticore.cd.methodtemplates.CD4C; +import de.monticore.cd4analysis._util.CD4AnalysisTypeDispatcher; import de.monticore.cd4analysis.trafo.CDAssociationCreateFieldsFromAllRoles; import de.monticore.cd4analysis.trafo.CDAssociationCreateFieldsFromNavigableRoles; import de.monticore.cd4code.CD4CodeMill; @@ -25,6 +27,7 @@ import de.monticore.generating.templateengine.TemplateHookPoint; import de.monticore.io.paths.MCPath; import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.monticore.tagging.tags.TagsMill; import de.monticore.types.MCTypeFacade; import de.monticore.types.mcbasictypes._ast.ASTMCImportStatement; import de.monticore.types.mccollectiontypes.types3.MCCollectionSymTypeRelations; @@ -76,9 +79,12 @@ public static void main(String[] args) { * @param args array of the command line arguments */ public void run(String[] args) { + // We might be using tags + TagsMill.reset(); + TagsMill.init(); - de.monticore.cd4code.CD4CodeMill.reset(); - de.monticore.cd4code.CD4CodeMill.init(); + CD4CodeMill.reset(); + CD4CodeMill.init(); Options options = initOptions(); @@ -221,6 +227,9 @@ public void decorateAndGenerate(GlobalExtensionManagement glex, Runnable initDecoratedGlobalScope, Consumer postDecorate, Collection asts) { glex.setGlobalValue("cdPrinter", new CdUtilsPrinter()); + glex.setGlobalValue("mcTypeFacade", MCTypeFacade.getInstance()); + glex.setGlobalValue("cdGenService", new CDGenService()); + glex.setGlobalValue("cd4AnalysisTypeDispatcher", new CD4AnalysisTypeDispatcher()); CDGenerator generator = new CDGenerator(setup); DecoratorConfig decSetup = new DecoratorConfig(); @@ -246,7 +255,7 @@ public void decorateAndGenerate(GlobalExtensionManagement glex, t.add4CDBasis(new CDBasisDefaultPackageTrafo()); decorated.get().accept(t); // Post-Decorate: make methods in interfaces abstract - this.makeMethodsInInterfacesAbstract(decorated.get()); + //this.makeMethodsInInterfacesAbstract(decorated.get()); // Post-Decorate: map import statements to classes this.mapCD4CImports(decorated.get()); diff --git a/cdlang/src/main/resources/cd2java/init/CD2Pojo.ftl b/cdlang/src/main/resources/cd2java/init/CD2Pojo.ftl index 92390092c..c697b866e 100644 --- a/cdlang/src/main/resources/cd2java/init/CD2Pojo.ftl +++ b/cdlang/src/main/resources/cd2java/init/CD2Pojo.ftl @@ -29,6 +29,12 @@ ${decConfig.withSetters().ignoreOnName("noSetter").defaultApply()} <#-- And the NavigableSetters (for bidirectional assocs). --> <#-- The implementation of the NavigableSetters decorator requires that the Setter decorator has run before.--> ${decConfig.withNavigableSetters().ignoreOnName("noSetter").defaultApply()} +<#-- The DeepCloneAndDeepEqualsDecorator is applied by default--> +<#-- The implementation requires that no CD element excluded. Therefore we have no ignore Statement --> +${decConfig.withDeepCloneAndDeepEquals().defaultApply()} +<#-- The VisitorDecorator and the InheritanceVisitorDecorator are applied by default --> +${decConfig.withVisitors().defaultApply()} +${decConfig.withInheritanceVisitors().defaultApply()} <#--Method signatures will be turned into abstract methods--> ${decConfig.withAbstractMethodSignatures().ignoreOnName("nonAbstractMethod").defaultApply()} <#--The following decorators are not applied by default, instead they have to be explicitly configured using stereos/tags/etc--> diff --git a/cdlang/src/main/resources/methods/builder/build.ftl b/cdlang/src/main/resources/methods/builder/build.ftl index 7f71ee4a6..49dba5bd5 100644 --- a/cdlang/src/main/resources/methods/builder/build.ftl +++ b/cdlang/src/main/resources/methods/builder/build.ftl @@ -1,9 +1,8 @@ <#-- (c) https://github.com/MontiCore/monticore --> -${tc.signature("clazz")} - -var v = new ${clazz}(); - +${tc.signature("originalClazzName")} +if(!isValid()){ + throw new IllegalStateException("build called on an incomplete object of type ${originalClazzName}."); +} +var v = new ${originalClazzName}(); ${defineHookPoint("methods.builder.build:Inner")} - return v; - diff --git a/cdlang/src/main/resources/methods/builder/isValid.ftl b/cdlang/src/main/resources/methods/builder/isValid.ftl new file mode 100644 index 000000000..20c628be5 --- /dev/null +++ b/cdlang/src/main/resources/methods/builder/isValid.ftl @@ -0,0 +1,23 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +${tc.signature("attributes","staticErrorCode")} + +<#list attributes as attribute> +<#assign errorCode = staticErrorCode + cdGenService.getGeneratedErrorCode(attribute.getName()+attribute.getMCType().printType())> +<#assign CD4AnalysisTypeDispatcher = glex.getGlobalVar("cd4AnalysisTypeDispatcher")> + +<#-- Check if the attribute is not a list, set or optional as they have isAbsent methods--> +<#if (!(CD4AnalysisTypeDispatcher.isMCCollectionTypesASTMCListType(attribute.getMCType()) || + CD4AnalysisTypeDispatcher.isMCCollectionTypesASTMCSetType(attribute.getMCType()) || + CD4AnalysisTypeDispatcher.isMCCollectionTypesASTMCOptionalType(attribute.getMCType())))> + + <#-- as primitive types cannot be check for == null we need to ignore them --> + <#if (!(CD4AnalysisTypeDispatcher.isMCBasicTypesASTMCPrimitiveType(attribute.getMCType())))> +if (this.${attribute.getName()} == null) { + Log.error("${errorCode} ${attribute.getName()} of type ${attribute.printType()} must not be null"); + return false; +} + + + + +return true; diff --git a/cdlang/src/main/resources/methods/builder/set.ftl b/cdlang/src/main/resources/methods/builder/set.ftl new file mode 100644 index 000000000..6be3bbf25 --- /dev/null +++ b/cdlang/src/main/resources/methods/builder/set.ftl @@ -0,0 +1,9 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +${tc.signature("attribute")} +<#assign CD4AnalysisTypeDispatcher = glex.getGlobalVar("cd4AnalysisTypeDispatcher")> +<#if CD4AnalysisTypeDispatcher.isMCCollectionTypesASTMCOptionalType(attribute.getMCType())> +this.${attribute.getName()} = Optional.ofNullable(${attribute.getName()}); +<#else> +this.${attribute.getName()} = ${attribute.getName()}; + +return this.realBuilder; diff --git a/cdlang/src/main/resources/methods/builder/setAbsent.ftl b/cdlang/src/main/resources/methods/builder/setAbsent.ftl new file mode 100644 index 000000000..8976993d3 --- /dev/null +++ b/cdlang/src/main/resources/methods/builder/setAbsent.ftl @@ -0,0 +1,17 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +${tc.signature("attribute")} +<#assign CD4AnalysisTypeDispatcher = glex.getGlobalVar("cd4AnalysisTypeDispatcher")> +<#if CD4AnalysisTypeDispatcher.isMCCollectionTypesASTMCListType(attribute.getMCType())> +this.${attribute.name} = new ArrayList<>(); + <#else> + <#if CD4AnalysisTypeDispatcher.isMCCollectionTypesASTMCSetType(attribute)> +this.${attribute.name} = new HashSet<>(); + <#else> + <#if CD4AnalysisTypeDispatcher.isMCCollectionTypesASTMCOptionalType(attribute)> +this.${attribute.name} = Optional.empty(); + + + +return this.realBuilder; + + diff --git a/cdlang/src/main/resources/methods/builder/setAttribute.ftl b/cdlang/src/main/resources/methods/builder/setAttribute.ftl new file mode 100644 index 000000000..ac8586900 --- /dev/null +++ b/cdlang/src/main/resources/methods/builder/setAttribute.ftl @@ -0,0 +1,49 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +${tc.signature("attribute", "hasSetter")} +<#assign MCTypeFacade = glex.getGlobalVar("mcTypeFacade")> +<#assign CD4AnalysisTypeDispatcher = glex.getGlobalVar("cd4AnalysisTypeDispatcher")> +<#if MCTypeFacade.getInstance().isBooleanType(attribute.getMCType())> + <#if hasSetter> +v.set${attribute.getName()?cap_first}(this.${attribute.getName()}); + <#else> +v.${attribute.getName()} = this.${attribute.getName()}; + +<#------------------------------------> + <#else> + <#if (CD4AnalysisTypeDispatcher.isMCCollectionTypesASTMCListType(attribute.getMCType()) || CD4AnalysisTypeDispatcher.isMCCollectionTypesASTMCSetType(attribute.getMCType()))> + <#if hasSetter> +if(this.${attribute.getName()}!=null){ + v.set${attribute.getName()?cap_first}(this.${attribute.getName()}); +} + <#else> +if(this.${attribute.getName()}!=null){ + v.${attribute.getName()} = this.${attribute.getName()}; +} + +<#------------------------------------> + <#else> + <#if CD4AnalysisTypeDispatcher.isMCCollectionTypesASTMCOptionalType(attribute.getMCType())> + <#if hasSetter> +if(this.${attribute.getName()} != null && this.${attribute.getName()}.isPresent()){ + v.set${attribute.getName()?cap_first}(this.${attribute.getName()}.get()); +}else{ + v.set${attribute.getName()?cap_first}(null); +} + <#else> +if(this.${attribute.getName()} != null && this.${attribute.getName()}.isPresent()){ + v.${attribute.getName()} = this.${attribute.getName()}; +}else{ + v.${attribute.getName()} = Optional.empty(); +} + +<#------------------------------------> + <#else> + <#if hasSetter> +v.set${attribute.getName()?cap_first}(this.${attribute.getName()}); + <#else> +v.${attribute.getName()} = this.${attribute.getName()}; + +<#------------------------------------> + + + diff --git a/cdlang/src/main/resources/methods/builder/unsafeBuild.ftl b/cdlang/src/main/resources/methods/builder/unsafeBuild.ftl new file mode 100644 index 000000000..3e8147ea1 --- /dev/null +++ b/cdlang/src/main/resources/methods/builder/unsafeBuild.ftl @@ -0,0 +1,5 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +${tc.signature("originalClazzName")} +var v = new ${originalClazzName}(); +${defineHookPoint("methods.builder.unsafeBuild:Inner")} +return v; diff --git a/cdlang/src/main/resources/methods/deepCloneAndDeepEquals/deepClone.ftl b/cdlang/src/main/resources/methods/deepCloneAndDeepEquals/deepClone.ftl new file mode 100644 index 000000000..190022234 --- /dev/null +++ b/cdlang/src/main/resources/methods/deepCloneAndDeepEquals/deepClone.ftl @@ -0,0 +1,3 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +${tc.signature("originalClazzName")} + return this.deepClone(new ${originalClazzName}(), new HashMap<>()); diff --git a/cdlang/src/main/resources/methods/deepCloneAndDeepEquals/deepClone1.ftl b/cdlang/src/main/resources/methods/deepCloneAndDeepEquals/deepClone1.ftl new file mode 100644 index 000000000..83e3f5bc8 --- /dev/null +++ b/cdlang/src/main/resources/methods/deepCloneAndDeepEquals/deepClone1.ftl @@ -0,0 +1,4 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +${tc.signature("originalClazzType", "classCreationCall")} +${originalClazzType.printType()} result = ${classCreationCall}; +return this.deepClone(result, map); diff --git a/cdlang/src/main/resources/methods/deepCloneAndDeepEquals/deepClone2.ftl b/cdlang/src/main/resources/methods/deepCloneAndDeepEquals/deepClone2.ftl new file mode 100644 index 000000000..ea9b26741 --- /dev/null +++ b/cdlang/src/main/resources/methods/deepCloneAndDeepEquals/deepClone2.ftl @@ -0,0 +1,23 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +${tc.signature("originalClazzType","attributeList","PojoClazzesAsStringList")} +<#assign MCTypeFacade = glex.getGlobalVar("mcTypeFacade")> +<#assign CD4AnalysisTypeDispatcher = glex.getGlobalVar("cd4AnalysisTypeDispatcher")> +<#-- We need the map to check if the current object we want to copy was already copied to make sure that an object --> +<#-- with multiple references will be copied as such and not as multiple different objects --> +<#-- we create the Object of the pojo class and directly add it to the map before calling its deepClone method. --> +<#-- This is needed to prevent stack overflows when having circular relations --> +<#-- Because the deepClone method would not create a object if it is in the map we check if it is at first by looking in the map.--> +<#-- If the item is contained as a key we simple return the value of map.get(key). --> +if(map.containsKey(this)) { + return (${originalClazzType.printType()}) map.get(this); +}else{ +<#-- if the class is not in our map we have to compute the result --> + map.put(this, result); +} +<#list attributeList as attr> + <#assign thisObjectName = "this.${attr.getName()}"> + <#assign resultName = "result.${attr.getName()}"> + ${includeArgs("methods.deepCloneAndDeepEquals.deepClone2Inner", attr.getMCType(), PojoClazzesAsStringList, thisObjectName, resultName)} + + +return result; diff --git a/cdlang/src/main/resources/methods/deepCloneAndDeepEquals/deepClone2ForInterfaces.ftl b/cdlang/src/main/resources/methods/deepCloneAndDeepEquals/deepClone2ForInterfaces.ftl new file mode 100644 index 000000000..f105e7bc6 --- /dev/null +++ b/cdlang/src/main/resources/methods/deepCloneAndDeepEquals/deepClone2ForInterfaces.ftl @@ -0,0 +1,3 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +${tc.signature("originalClazzType")} +return deepClone((${originalClazzType}) result, map); diff --git a/cdlang/src/main/resources/methods/deepCloneAndDeepEquals/deepClone2Inner.ftl b/cdlang/src/main/resources/methods/deepCloneAndDeepEquals/deepClone2Inner.ftl new file mode 100644 index 000000000..e30d54ee1 --- /dev/null +++ b/cdlang/src/main/resources/methods/deepCloneAndDeepEquals/deepClone2Inner.ftl @@ -0,0 +1,202 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +<#-- inner method for deepClone --> +<#-- this method is used to clone different attributes of the pojo class --> +<#-- its primary purpose is to enable recursive which are need when resolving Lists, Sets and other data structures--> +<#-- To ensure termination we create the result object at the very start and fill thisObject and the resultObjects in the map --> +${tc.signature("mCType", "PojoClazzesAsStringList","thisObjectName", "resultObjectName")} +<#assign CD4AnalysisTypeDispatcher = glex.getGlobalVar("cd4AnalysisTypeDispatcher")> +<#assign newResultObjectName = "newResult" + mCType.hashCode()?replace(".","")?replace(",","")> +<#-- Array type --> +<#if (CD4AnalysisTypeDispatcher.isMCArrayTypesASTMCArrayType(mCType))> +if(${thisObjectName} == null) { + ${resultObjectName} = null; +} else { + if(map.get(${thisObjectName}) == null) { + <#assign arrayType = mCType.getMCType()> + <#assign arrayTypeName = arrayType.printType()> + <#assign depth = mCType.getDimensions()> + <#assign resultBracketsWithSize = ""> + <#assign resultBracketsInitialize = ""> + <#assign thisObjectArrayBracketsWith0index = ""> + <#assign resultObjectCurrentBrackets = ""> + <#list 0..depth-1 as i> + int arrayDim${i} = ${thisObjectName + thisObjectArrayBracketsWith0index}.length; + <#assign thisObjectArrayBracketsWith0index = thisObjectArrayBracketsWith0index + "[0]"> + <#assign resultBracketsWithSize = resultBracketsWithSize + "[arrayDim" + i + "]"> + <#assign resultBracketsInitialize = resultBracketsInitialize + "[]"> + <#assign resultObjectCurrentBrackets = resultObjectCurrentBrackets + "[i${i}]"> + + ${arrayTypeName}${resultBracketsInitialize} ${newResultObjectName} = new ${arrayTypeName}${resultBracketsWithSize}; + <#list 0..depth-1 as i> + for(int i${i} = 0; i${i} < arrayDim${i}; i${i}++) { + + <#assign innerTypeResultName = "innerType" + mCType.hashCode()?replace(".","")?replace(",","")> + ${arrayTypeName} ${innerTypeResultName}; + ${includeArgs("methods.deepCloneAndDeepEquals.deepClone2Inner", arrayType, PojoClazzesAsStringList, thisObjectName + resultObjectCurrentBrackets, innerTypeResultName)} + ${newResultObjectName + resultObjectCurrentBrackets} = ${innerTypeResultName}; + <#list 0..depth-1 as i> + } + <#assign mapAddArrayBrackets = ""> + <#list 0..(depth-(i+1)) as j> + <#if j == 0> + <#assign mapAddArrayBrackets = mapAddArrayBrackets > + <#else> + <#assign mapAddArrayBrackets = mapAddArrayBrackets + "[i" + (j-1) + "]"> + + + map.put(${thisObjectName} ${mapAddArrayBrackets},${newResultObjectName + mapAddArrayBrackets}); + + } + ${resultObjectName} = (${mCType.printType()}) map.get(${thisObjectName}); +} +<#-- Set types --> +<#elseif (CD4AnalysisTypeDispatcher.isMCCollectionTypesASTMCSetType(mCType))> +if(${thisObjectName} == null) { + ${resultObjectName} = null; +} else { + <#assign innerType = (mCType.getMCTypeArgument().getMCTypeOpt().get())> + <#assign iteratorName = "iterator"+mCType.hashCode()?replace(".","")?replace(",","")> + if(map.get(${thisObjectName}) == null) { + ${resultObjectName} = new HashSet<>(); + map.put(${thisObjectName}, ${resultObjectName}); + java.util.Iterator<${innerType.printType()}> ${iteratorName} = ${thisObjectName}.iterator(); + while(${iteratorName}.hasNext()) { + <#assign newInnerType = "newInnerType" + mCType.hashCode()?replace(".","")?replace(",","")> + ${innerType.printType()} ${newResultObjectName}; + ${innerType.printType()} ${newInnerType} = ${iteratorName}.next(); + ${includeArgs("methods.deepCloneAndDeepEquals.deepClone2Inner", innerType, PojoClazzesAsStringList, newInnerType, newResultObjectName)} + ${resultObjectName}.add(${newResultObjectName}); + } + }else{ + ${resultObjectName} = (${mCType.printType()}) map.get(${thisObjectName}); + } +} +<#-- List types --> +<#elseif (CD4AnalysisTypeDispatcher.isMCCollectionTypesASTMCListType(mCType))> +if(${thisObjectName} == null) { + ${resultObjectName} = null; +} else { + <#assign innerType = (mCType.getMCTypeArgument().getMCTypeOpt().get())> + <#assign iteratorName = "iterator"+mCType.hashCode()?replace(".","")?replace(",","")> + if(map.get(${thisObjectName}) == null) { + ${resultObjectName} = new ArrayList<>(); + map.put(${thisObjectName}, ${resultObjectName}); + java.util.Iterator<${innerType.printType()}> ${iteratorName} = ${thisObjectName}.iterator(); + while(${iteratorName}.hasNext()) { + <#assign newInnerType = "newInnerType" + mCType.hashCode()?replace(".","")?replace(",","")> + ${innerType.printType()} ${newResultObjectName}; + ${innerType.printType()} ${newInnerType} = ${iteratorName}.next(); + ${includeArgs("methods.deepCloneAndDeepEquals.deepClone2Inner", innerType, PojoClazzesAsStringList, newInnerType, newResultObjectName)} + ${resultObjectName}.add(${newResultObjectName}); + } + }else{ + ${resultObjectName} = (${mCType.printType()}) map.get(${thisObjectName}); + } +} +<#-- Map types --> +<#elseif (CD4AnalysisTypeDispatcher.isMCCollectionTypesASTMCMapType(mCType))> +if(${thisObjectName} == null) { + ${resultObjectName} = null; +} else { + <#assign keyType = mCType.getKey().getMCTypeOpt().get()> + <#assign valueType = mCType.getValue().getMCTypeOpt().get()> + <#assign iteratorName = "iterator"+mCType.hashCode()?replace(".","")?replace(",","")> + if(map.get(${thisObjectName}) == null) { + ${resultObjectName} = new HashMap<>(); + map.put(${thisObjectName}, ${resultObjectName}); + java.util.Iterator<${keyType.printType()}> ${iteratorName} = ${thisObjectName}.keySet().iterator(); + while(${iteratorName}.hasNext()) { + <#assign thisKeyName = "thisKey" + mCType.hashCode()?replace(".","")?replace(",","")> + <#assign thisValueName = "thisValue" + mCType.hashCode()?replace(".","")?replace(",","")> + <#assign clonedKeyName = "clonedKey" + mCType.hashCode()?replace(".","")?replace(",","")> + <#assign clonedValueName = "clonedValue" + mCType.hashCode()?replace(".","")?replace(",","")> + ${keyType.printType()} ${thisKeyName} = ${iteratorName}.next(); + ${valueType.printType()} ${thisValueName} = ${thisObjectName}.get(${thisKeyName}); + ${keyType.printType()} ${clonedKeyName}; + ${valueType.printType()} ${clonedValueName}; + ${includeArgs("methods.deepCloneAndDeepEquals.deepClone2Inner", keyType, PojoClazzesAsStringList, thisKeyName, clonedKeyName)} + ${includeArgs("methods.deepCloneAndDeepEquals.deepClone2Inner", valueType, PojoClazzesAsStringList, thisValueName, clonedValueName)} + ${resultObjectName}.put(${clonedKeyName},${clonedValueName}); + } + }else{ + ${resultObjectName} = (${mCType.printType()}) map.get(${thisObjectName}); + } +} +<#-- Optional types --> +<#elseif (CD4AnalysisTypeDispatcher.isMCCollectionTypesASTMCOptionalType(mCType))> +if(${thisObjectName} == null) { + ${resultObjectName} = null; +} else { + ${resultObjectName} = Optional.empty(); + if(map.get(${thisObjectName}) == null) { + if(${thisObjectName}.isPresent()) { + <#assign innerType = (mCType.getMCTypeArgument().getMCTypeOpt().get())> + <#assign newInnerType = "newInnerType" + innerType.hashCode()?replace(".","")?replace(",","")> + ${innerType.printType()} ${newInnerType} = ${thisObjectName}.get(); + ${innerType.printType()} ${newResultObjectName}; + if(map.get(${newInnerType}) == null) { + map.put(${thisObjectName}, ${resultObjectName}); + ${includeArgs("methods.deepCloneAndDeepEquals.deepClone2Inner", innerType, PojoClazzesAsStringList, newInnerType, newResultObjectName)} + <#-- this is needed because the optional.empty() reference is changed when filling the optional with a value->> + <#-- Because we can not have circular references in Optionals it is ok in this case to add the optional to the list after it has been resolved --> + ${resultObjectName} = Optional.of(${newResultObjectName}); + map.put(${thisObjectName}, ${resultObjectName}); + }else{ + ${newResultObjectName} = (${innerType.printType()}) map.get(${newInnerType}); + ${resultObjectName} = Optional.of(${newResultObjectName}); + map.put(${thisObjectName}, ${resultObjectName}); + } + } else { + ${resultObjectName} = Optional.empty(); + map.put(${thisObjectName}, ${resultObjectName}); + } + }else{ + ${resultObjectName} = (${mCType.printType()}) map.get(${thisObjectName}); + } +} +<#-- Primitive types --> +<#-- Primitive types can not be null --> +<#elseif (CD4AnalysisTypeDispatcher.isMCBasicTypesASTMCPrimitiveType(mCType))> +${resultObjectName} = ${thisObjectName}; +<#-- Pojo class types --> +<#else> +<#-- only when the type is present in the class diagram the getDefiningSymbol is present --> + <#if mCType.getDefiningSymbol().isPresent()> + <#assign resolvedClassName = mCType.getDefiningSymbol().get().getFullName()> + <#else> + <#assign resolvedClassName = mCType.getMCQualifiedName().getQName()> + + <#if (PojoClazzesAsStringList?seq_contains(resolvedClassName))> +if(${thisObjectName} == null) { + ${resultObjectName} = null; +} else { + if(map.get(${thisObjectName}) == null) { + ${resultObjectName} = ${thisObjectName}.deepClone(map); + }else{ + ${resultObjectName} = (${mCType.printType()}) map.get(${thisObjectName}); + } +} +<#-- All other types --> + <#else> +if(${thisObjectName} == null) { + ${resultObjectName} = null; +} else { +<#if mCType?has_content && mCType.printType()?has_content && (mCType.printType() == "java.lang.String" || mCType.printType() == "String")> +if(${thisObjectName} == null) { + ${resultObjectName} = null; +} else { + if(map.get(${thisObjectName}) == null) { + ${resultObjectName} = new String(${thisObjectName}); + map.put(${thisObjectName}, ${resultObjectName}); + }else{ + ${resultObjectName} = (${mCType.printType()}) map.get(${thisObjectName}); + } +} +<#else> +<#-- we cannot do this correctly if we land here the user has to implement the deepClone method via the TOP-Mechanism --> +<#-- adding to the map would not contribute, as we will copy the object anyway and multiple references will still be multiple references afterwards --> + ${resultObjectName} = ${thisObjectName}; + +} + + diff --git a/cdlang/src/main/resources/methods/deepCloneAndDeepEquals/deepEquals1.ftl b/cdlang/src/main/resources/methods/deepCloneAndDeepEquals/deepEquals1.ftl new file mode 100644 index 000000000..18b368adf --- /dev/null +++ b/cdlang/src/main/resources/methods/deepCloneAndDeepEquals/deepEquals1.ftl @@ -0,0 +1,5 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +<#-- 1 stands for 1 argument which is the object to compare with --> +<#-- this method just calls the deepEquals method with the second argument being set to true --> +<#-- therefore enforcing the right order of elements in set and lists --> +return deepEquals(o, true); diff --git a/cdlang/src/main/resources/methods/deepCloneAndDeepEquals/deepEquals2.ftl b/cdlang/src/main/resources/methods/deepCloneAndDeepEquals/deepEquals2.ftl new file mode 100644 index 000000000..f8bcdb188 --- /dev/null +++ b/cdlang/src/main/resources/methods/deepCloneAndDeepEquals/deepEquals2.ftl @@ -0,0 +1,6 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +<#-- 2 stands for 2 argument which is the object to compare with --> +<#-- the first argument is the object to compare with --> +<#-- the second argument is a boolean which will decide if the right order of elements in set and lists is enforced --> +return deepEquals(o, forceSameOrder, new HashMap>()); + diff --git a/cdlang/src/main/resources/methods/deepCloneAndDeepEquals/deepEquals3.ftl b/cdlang/src/main/resources/methods/deepCloneAndDeepEquals/deepEquals3.ftl new file mode 100644 index 000000000..6a404ca3b --- /dev/null +++ b/cdlang/src/main/resources/methods/deepCloneAndDeepEquals/deepEquals3.ftl @@ -0,0 +1,40 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +<#-- 3 stands for 3 argument which is the object to compare with --> +<#-- the first argument is the object to compare with --> +<#-- the second argument is a boolean which will decide if the right order of elements in set and lists is enforced --> +<#-- the third argument is a map which will be used to store the already visited objects as the language can have circular structure --> +<#-- to remember the visited objects we therefore need to save them --> +${tc.signature("originalClazzType", "attributeList", "PojoClazzesAsStringList")} +<#-- as we want terminate on cyclic relations we need to add the object before we compare it sto our visited objects --> +<#-- we will later delete it after comparing, so that if a object exists multiple times in a non cyclic way, it is checked anyways--> +if(o == null){ + return false; +} +if(!(o.getClass() == ${originalClazzType.printType()}.class)){ + return false; +} +${originalClazzType.printType()} castO = (${originalClazzType.printType()}) o; +if(visitedObjects.get(this) != null){ + if(visitedObjects.get(this).contains(castO)){ + return true; + } + visitedObjects.get(this).add(castO); +}else{ + visitedObjects.put(this,new HashSet(Collections.singletonList(castO))); +} +<#if attributeList??> +<#list attributeList as attr> +<#-- we need to declare a boolean result, as in recursive list checks we cannot return false when we check while having the flag forceSameOrder set to false --> +<#assign resultBooleanName = "result" + attr.getName()?cap_first + attr.getMCType().printType()?cap_first?replace(".","")?replace("<","")?replace(">","")?replace("[","")?replace("]","")?replace(",","")> +boolean ${resultBooleanName} = true; +<#assign firstObjectName = "this." + attr.getName()> +<#assign secondObjectName = "castO." + attr.getName()> + <#-- we call the deepEquals3Inner template here which can be called repulsively when the type is a List or a Set --> + ${includeArgs("methods.deepCloneAndDeepEquals.deepEquals3Inner", attr.getMCType(), PojoClazzesAsStringList, firstObjectName, secondObjectName, resultBooleanName)} +visitedObjects.get(this).remove(castO); +if(! ${resultBooleanName}){ + return false; +} + + +return true; diff --git a/cdlang/src/main/resources/methods/deepCloneAndDeepEquals/deepEquals3Inner.ftl b/cdlang/src/main/resources/methods/deepCloneAndDeepEquals/deepEquals3Inner.ftl new file mode 100644 index 000000000..a23dad961 --- /dev/null +++ b/cdlang/src/main/resources/methods/deepCloneAndDeepEquals/deepEquals3Inner.ftl @@ -0,0 +1,264 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +<#-- inner method for deepEquals --> +<#-- this method is used to compare the types of the current object with the types of the given object --> +<#-- its primary purpose is to enable recursive which are need when resolving Lists and Sets --> +${tc.signature("mCType", "PojoClazzesAsStringList","firstObjectName", "secondObjectName","resultBooleanName")} +<#assign CD4AnalysisTypeDispatcher = glex.getGlobalVar("cd4AnalysisTypeDispatcher")> +<#-- Define a macro to repeat a string n times --> +<#-- Array types --> +<#if (CD4AnalysisTypeDispatcher.isMCArrayTypesASTMCArrayType(mCType))> +<#assign arrayType = mCType.getMCType()> +<#assign arrayTypeName = arrayType.printType()> +<#assign depth = mCType.getDimensions()> +if(${firstObjectName} == null && ${secondObjectName} == null){ + ${resultBooleanName} = true; +}else{ + if((${firstObjectName} == null || ${secondObjectName} == null)||(${firstObjectName}.length != ${secondObjectName}.length)){ + ${resultBooleanName} = false; + }else{ + <#assign thisObjectArrayBracketsWith0index = ""> + <#list 0..depth-1 as i> + int firstArrayDim${i} = ${firstObjectName + thisObjectArrayBracketsWith0index}.length; + <#assign thisObjectArrayBracketsWith0index = thisObjectArrayBracketsWith0index + "[0]"> + + <#list 0..depth-1 as i> + <#assign mapAddArrayBrackets = ""> + <#list 0..i as j> + <#if j == 0> + <#assign mapAddArrayBrackets = mapAddArrayBrackets > + <#else> + <#assign mapAddArrayBrackets = mapAddArrayBrackets + "[i${j-1}]" > + + + if(${firstObjectName}${mapAddArrayBrackets}.length != ${secondObjectName}${mapAddArrayBrackets}.length){ + ${resultBooleanName} = false; + }else{ + for(int i${i} = 0; i${i} < firstArrayDim${i}; i${i}++) { + + <#assign isEqual = "isEqual"> + boolean isEqual = true; + if(forceSameOrder){ + <#assign currentObjectArrayBrackets = ""> + <#list 0..depth-1 as j> + <#assign currentObjectArrayBrackets = currentObjectArrayBrackets + "[i${j}]"> + + ${includeArgs("methods.deepCloneAndDeepEquals.deepEquals3Inner", arrayType, PojoClazzesAsStringList, firstObjectName + currentObjectArrayBrackets, secondObjectName + currentObjectArrayBrackets, isEqual)} + }else{ + <#assign matchFound = "matchFound"> + boolean matchFound = false; + <#assign resultObjectCurrentBrackets = ""> + <#list 0..depth-1 as i> + <#assign resultObjectCurrentBrackets = resultObjectCurrentBrackets + "[i${i}]"> + + <#assign firstObject = "firstObject"> + ${arrayType.printType()} ${firstObject} = ${firstObjectName}${resultObjectCurrentBrackets}; + <#list 0..depth-1 as i> + for(int innerI${i} = 0; innerI${i} < firstArrayDim${i}; innerI${i}++) { + + <#assign secondObject = "secondObject"> + <#assign resultObjectCurrentBrackets = ""> + <#list 0..depth-1 as i> + <#assign resultObjectCurrentBrackets = resultObjectCurrentBrackets + "[innerI${i}]"> + + ${arrayType.printType()} ${secondObject} = ${secondObjectName}${resultObjectCurrentBrackets}; + ${includeArgs("methods.deepCloneAndDeepEquals.deepEquals3Inner", arrayType, PojoClazzesAsStringList, firstObject, secondObject, matchFound)} + + <#list 0..depth-1 as i> + if(${matchFound}){ + break; + } + } + + if(!${matchFound}){ + isEqual = false; + } + } + if(!isEqual){ + ${resultBooleanName} = false; + } + <#list 0..depth-1 as i> + } + } + + } +} +<#-- Set types --> +<#elseif (CD4AnalysisTypeDispatcher.isMCCollectionTypesASTMCSetType(mCType))> +<#assign innerType = (mCType.getMCTypeArgument().getMCTypeOpt().get())> +if(${firstObjectName} == null && ${secondObjectName} == null){ + ${resultBooleanName} = true; +}else{ + if((${firstObjectName} == null || ${secondObjectName} == null)||(${firstObjectName}.size() != ${secondObjectName}.size())){ + ${resultBooleanName} = false; + } else { + <#assign firstIteratorName = "it1" + mCType.hashCode()?replace(".","")?replace(",","")> + <#assign secondIteratorName = "it2" + mCType.hashCode()?replace(".","")?replace(",","")> + <#assign it1NextName = "it1Next" + mCType.hashCode()?replace(".","")?replace(",","")> + <#assign it2NextName = "it2Next" + mCType.hashCode()?replace(".","")?replace(",","")> + <#assign matchFoundName = "matchFound" + mCType.hashCode()?replace(".","")?replace(",","")> + java.util.Iterator<${innerType.printType()}> ${firstIteratorName} = ${firstObjectName}.iterator(); + while(${firstIteratorName}.hasNext()){ + ${innerType.printType()} ${it1NextName} = ${firstIteratorName}.next(); + boolean ${matchFoundName} = true; + java.util.Iterator<${innerType.printType()}> ${secondIteratorName} = ${secondObjectName}.iterator(); + while(${secondIteratorName}.hasNext()){ + ${matchFoundName} = true; + ${innerType.printType()} ${it2NextName} = ${secondIteratorName}.next(); + ${includeArgs("methods.deepCloneAndDeepEquals.deepEquals3Inner", innerType, PojoClazzesAsStringList, it1NextName, it2NextName, matchFoundName)}; + if(${matchFoundName}){ + break; + } + } + if(!${matchFoundName}){ + ${resultBooleanName} = false; + } + } + } +} +<#-- List types --> +<#elseif (CD4AnalysisTypeDispatcher.isMCCollectionTypesASTMCListType(mCType))> +<#assign innerType = (mCType.getMCTypeArgument().getMCTypeOpt().get())> +if(${firstObjectName} == null && ${secondObjectName} == null){ + ${resultBooleanName} = true; +}else{ + if((${firstObjectName} == null || ${secondObjectName} == null)||(${firstObjectName}.size() != ${secondObjectName}.size())){ + ${resultBooleanName} = false; + } + if(forceSameOrder){ + <#assign firstIteratorName = "it1" + mCType.hashCode()?replace(".","")?replace(",","")> + <#assign secondIteratorName = "it2" + mCType.hashCode()?replace(".","")?replace(",","")> + <#assign it1NextName = "it1Next" + mCType.hashCode()?replace(".","")?replace(",","")> + <#assign it2NextName = "it2Next" + mCType.hashCode()?replace(".","")?replace(",","")> + <#assign isEqual = "isEqual" + mCType.hashCode()?replace(".","")?replace(",","")> + java.util.Iterator<${innerType.printType()}> ${firstIteratorName} = ${firstObjectName}.iterator(); + java.util.Iterator<${innerType.printType()}> ${secondIteratorName} = ${secondObjectName}.iterator(); + while(${firstIteratorName}.hasNext()){ + ${innerType.printType()} ${it1NextName} = ${firstIteratorName}.next(); + ${innerType.printType()} ${it2NextName} = ${secondIteratorName}.next(); + boolean ${isEqual} = true; + ${includeArgs("methods.deepCloneAndDeepEquals.deepEquals3Inner", innerType,PojoClazzesAsStringList, it1NextName, it2NextName, isEqual)}; + if(!${isEqual}){ + return false; + } + } + } else { + <#assign firstIteratorName = "it1" + mCType.hashCode()?replace(".","")?replace(",","")> + <#assign secondIteratorName = "it2" + mCType.hashCode()?replace(".","")?replace(",","")> + <#assign it1NextName = "it1Next" + mCType.hashCode()?replace(".","")?replace(",","")> + <#assign it2NextName = "it2Next" + mCType.hashCode()?replace(".","")?replace(",","")> + <#assign matchFoundName = "matchFound" + mCType.hashCode()?replace(".","")?replace(",","")> + java.util.Iterator<${innerType.printType()}> ${firstIteratorName} = ${firstObjectName}.iterator(); + while(${firstIteratorName}.hasNext()){ + ${innerType.printType()} ${it1NextName} = ${firstIteratorName}.next(); + boolean ${matchFoundName} = true; + java.util.Iterator<${innerType.printType()}> ${secondIteratorName} = ${secondObjectName}.iterator(); + while(${secondIteratorName}.hasNext()){ + ${matchFoundName} = true; + ${innerType.printType()} ${it2NextName} = ${secondIteratorName}.next(); + ${includeArgs("methods.deepCloneAndDeepEquals.deepEquals3Inner", innerType, PojoClazzesAsStringList, it1NextName, it2NextName, matchFoundName)}; + if(${matchFoundName}){ + break; + } + } + if(!${matchFoundName}){ + ${resultBooleanName} = false; + } + } + } +} +<#-- Map types --> +<#elseif (CD4AnalysisTypeDispatcher.isMCCollectionTypesASTMCMapType(mCType))> +if(${firstObjectName} == null && ${secondObjectName} == null){ + ${resultBooleanName} = true; +}else{ + if((${firstObjectName} == null || ${secondObjectName} == null)||(${firstObjectName}.size() != ${secondObjectName}.size())){ + ${resultBooleanName} = false; + }else{ + <#assign keyType = mCType.getKey().getMCTypeOpt().get()> + <#assign valueType = mCType.getValue().getMCTypeOpt().get()> + <#assign firstKeySetName = "firstKeySet" + mCType.hashCode()?replace(".","")?replace(",","")> + <#assign secondKeySetName = "secondKeySet" + mCType.hashCode()?replace(".","")?replace(",","")> + <#assign firstKeySetIteratorName = "firstKeySetIterator" + mCType.hashCode()?replace(".","")?replace(",","")> + Set<${keyType.printType()}> ${firstKeySetName} = ${firstObjectName}.keySet(); + Set<${keyType.printType()}> ${secondKeySetName} = ${secondObjectName}.keySet(); + Iterator<${keyType.printType()}> ${firstKeySetIteratorName} = ${firstKeySetName}.iterator(); + while(${firstKeySetIteratorName}.hasNext()){ + <#assign firstKeyObjectName = "firstKeyObjectName" + mCType.hashCode()?replace(".","")?replace(",","")> + <#assign firstValueObjectName = "firstValueObjectName" + mCType.hashCode()?replace(".","")?replace(",","")> + ${keyType.printType()} ${firstKeyObjectName} = ${firstKeySetIteratorName}.next(); + ${valueType.printType()} ${firstValueObjectName} = ${firstObjectName}.get(${firstKeyObjectName}); + <#assign secondKeySetIteratorName = "secondKeySetIteratorName" + mCType.hashCode()?replace(".","")?replace(",","")> + <#assign secondKeySetName = "secondKeySet" + mCType.hashCode()?replace(".","")?replace(",","")> + Iterator<${keyType.printType()}> ${secondKeySetIteratorName} = ${secondKeySetName}.iterator(); + <#assign matchFoundName = "matchFound" + mCType.hashCode()?replace(".","")?replace(",","")> + boolean ${matchFoundName} = false; + while(${secondKeySetIteratorName}.hasNext()){ + <#assign secondKeyObjectName = "secondKeyObject" + mCType.hashCode()?replace(".","")?replace(",","")> + <#assign secondValueObjectName = "secondValueObjectName" + mCType.hashCode()?replace(".","")?replace(",","")> + ${keyType.printType()} ${secondKeyObjectName} = ${secondKeySetIteratorName}.next(); + ${valueType.printType()} ${secondValueObjectName} = ${secondObjectName}.get(${secondKeyObjectName}); + <#assign keyIsEqual = "keyIsEqual" + mCType.hashCode()?replace(".","")?replace(",","")> + <#assign valueIsEqual = "valueIsEqual" + mCType.hashCode()?replace(".","")?replace(",","")> + boolean ${keyIsEqual} = true; + boolean ${valueIsEqual} = true; + ${includeArgs("methods.deepCloneAndDeepEquals.deepEquals3Inner", keyType, PojoClazzesAsStringList, firstKeyObjectName, secondKeyObjectName, keyIsEqual)}; + ${includeArgs("methods.deepCloneAndDeepEquals.deepEquals3Inner", valueType, PojoClazzesAsStringList, firstValueObjectName, secondValueObjectName, valueIsEqual)}; + if(${keyIsEqual} && ${valueIsEqual}){ + ${matchFoundName} = true; + break; + } + } + if(!${matchFoundName}){ + return false; + } + } + } +} +<#-- Optional types --> +<#elseif (CD4AnalysisTypeDispatcher.isMCCollectionTypesASTMCOptionalType(mCType))> +<#assign innerType = (mCType.getMCTypeArgument().getMCTypeOpt().get())> +<#-- if the first object is not present and the second object is present, return false --> +if(${firstObjectName} == null && ${secondObjectName} == null){ + ${resultBooleanName} = true; +} else if(${firstObjectName} == null || ${secondObjectName} == null){ + ${resultBooleanName} = false; +}else{ + if(${firstObjectName}.isPresent() && ${secondObjectName}.isEmpty() || + ${firstObjectName}.isEmpty() && ${secondObjectName}.isPresent()){ + ${resultBooleanName} = false; + } else if(${firstObjectName}.isPresent() && ${secondObjectName}.isPresent()){ + ${includeArgs("methods.deepCloneAndDeepEquals.deepEquals3Inner", innerType, PojoClazzesAsStringList, firstObjectName + ".get()", secondObjectName + ".get()", resultBooleanName)}; + } +} +<#-- Primitive types --> +<#-- Primitive types can not be null --> +<#elseif (CD4AnalysisTypeDispatcher.isMCBasicTypesASTMCPrimitiveType(mCType))> +${resultBooleanName} = ${firstObjectName} == ${secondObjectName}; +<#-- pojo class types --> +<#else> +<#-- only when the type is present in the class diagram the getDefiningSymbol is present --> + <#if mCType.getDefiningSymbol().isPresent()> + <#assign resolvedClassName = mCType.getDefiningSymbol().get().getFullName()> + <#else> + <#assign resolvedClassName = mCType.getMCQualifiedName().getQName()> + + <#if (PojoClazzesAsStringList?seq_contains(resolvedClassName))> +if(${firstObjectName} == null && ${secondObjectName} == null){ + ${resultBooleanName} = true; +}else if(${firstObjectName} == null || ${secondObjectName} == null){ + ${resultBooleanName} = false; +}else{ + ${resultBooleanName} = ${firstObjectName}.deepEquals(${secondObjectName}, forceSameOrder, visitedObjects); +} +<#-- all other types --> + <#else> + if(${firstObjectName} == null && ${secondObjectName} == null){ + ${resultBooleanName} = true; + } else if(${firstObjectName} == null || ${secondObjectName} == null){ + ${resultBooleanName} = false; + } else { + ${resultBooleanName} = ${secondObjectName}.equals(${firstObjectName}); + } + + + diff --git a/cdlang/src/main/resources/methods/observer/addObserver.ftl b/cdlang/src/main/resources/methods/observer/addObserver.ftl new file mode 100644 index 000000000..b174d3067 --- /dev/null +++ b/cdlang/src/main/resources/methods/observer/addObserver.ftl @@ -0,0 +1,3 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +${tc.signature("ObserverList","ObserverType")} +this.${ObserverList}.add(observer); diff --git a/cdlang/src/main/resources/methods/observer/notifyObserver.ftl b/cdlang/src/main/resources/methods/observer/notifyObserver.ftl new file mode 100644 index 000000000..c72a3eca0 --- /dev/null +++ b/cdlang/src/main/resources/methods/observer/notifyObserver.ftl @@ -0,0 +1,5 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +${tc.signature("ObserverList","ObserverType")} +for(${ObserverType} observer : this.${ObserverList}) { + observer.update(this); +} diff --git a/cdlang/src/main/resources/methods/observer/notifyObserverAttributeSpecific.ftl b/cdlang/src/main/resources/methods/observer/notifyObserverAttributeSpecific.ftl new file mode 100644 index 000000000..a527afe44 --- /dev/null +++ b/cdlang/src/main/resources/methods/observer/notifyObserverAttributeSpecific.ftl @@ -0,0 +1,5 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +${tc.signature("ObserverList","ObserverType","AttributeName")} +for(${ObserverType} observer : this.${ObserverList}) { + observer.updateObserver${AttributeName?cap_first}(this, ov); +} diff --git a/cdlang/src/main/resources/methods/observer/removeObserver.ftl b/cdlang/src/main/resources/methods/observer/removeObserver.ftl new file mode 100644 index 000000000..7b5aa2664 --- /dev/null +++ b/cdlang/src/main/resources/methods/observer/removeObserver.ftl @@ -0,0 +1,3 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +${tc.signature("ObserverList","ObserverType")} +this.${ObserverList}.remove(observer); diff --git a/cdlang/src/main/resources/methods/observer/setWithObservableMethodCall.ftl b/cdlang/src/main/resources/methods/observer/setWithObservableMethodCall.ftl new file mode 100644 index 000000000..74f629a51 --- /dev/null +++ b/cdlang/src/main/resources/methods/observer/setWithObservableMethodCall.ftl @@ -0,0 +1,10 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +${tc.signature("attribute","oldValueName")} +<#assign CD4AnalysisTypeDispatcher = glex.getGlobalVar("cd4AnalysisTypeDispatcher")> +${attribute.getMCType().printType()} ${oldValueName} = this.${attribute.getName()}; +<#if CD4AnalysisTypeDispatcher.isMCCollectionTypesASTMCOptionalType(attribute.getMCType())> +this.${attribute.getName()} = Optional.ofNullable(${attribute.getName()}); +<#else> +this.${attribute.getName()} = ${attribute.getName()}; + +notifyObserver${attribute.getName()?cap_first}(this,${oldValueName}); diff --git a/cdlang/src/main/resources/methods/visitor/accept.ftl b/cdlang/src/main/resources/methods/visitor/accept.ftl new file mode 100644 index 000000000..61a7254a3 --- /dev/null +++ b/cdlang/src/main/resources/methods/visitor/accept.ftl @@ -0,0 +1,5 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +${tc.signature("astcdClass","staticErrorCode")} +<#assign plainName = astcdClass.getName()?remove_ending("TOP")> +<#assign errorCode = staticErrorCode + cdGenService.getGeneratedErrorCode(astcdClass.getName())> + visitor.handle((${plainName}) this); diff --git a/cdlang/src/main/resources/methods/visitor/addTraversedElement.ftl b/cdlang/src/main/resources/methods/visitor/addTraversedElement.ftl new file mode 100644 index 000000000..430a3cd00 --- /dev/null +++ b/cdlang/src/main/resources/methods/visitor/addTraversedElement.ftl @@ -0,0 +1,2 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +getTraversedElements().add(element); diff --git a/cdlang/src/main/resources/methods/visitor/handle.ftl b/cdlang/src/main/resources/methods/visitor/handle.ftl new file mode 100644 index 000000000..9d86423f4 --- /dev/null +++ b/cdlang/src/main/resources/methods/visitor/handle.ftl @@ -0,0 +1,8 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +if (!getTraversedElements().contains(node)) { + addTraversedElement(node); + visit(node); + traverse(node); + endVisit(node); + removeTraversedElement(node); +} diff --git a/cdlang/src/main/resources/methods/visitor/inheritanceHandle.ftl b/cdlang/src/main/resources/methods/visitor/inheritanceHandle.ftl new file mode 100644 index 000000000..57471bfd6 --- /dev/null +++ b/cdlang/src/main/resources/methods/visitor/inheritanceHandle.ftl @@ -0,0 +1,15 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +${tc.signature("clazzStrings")} +if (!getTraversedElements().contains(node)) { + addTraversedElement(node); + <#list clazzStrings?reverse as clazzName> + visit((${clazzName})node); + + visit(node); + traverse(node); + endVisit(node); + <#list clazzStrings as clazzName> + endVisit((${clazzName})node); + + removeTraversedElement(node); +} diff --git a/cdlang/src/main/resources/methods/visitor/removeTraversedElement.ftl b/cdlang/src/main/resources/methods/visitor/removeTraversedElement.ftl new file mode 100644 index 000000000..f0cf0b4b9 --- /dev/null +++ b/cdlang/src/main/resources/methods/visitor/removeTraversedElement.ftl @@ -0,0 +1,2 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +getTraversedElements().remove(element); diff --git a/cdlang/src/main/resources/methods/visitor/traverse.ftl b/cdlang/src/main/resources/methods/visitor/traverse.ftl new file mode 100644 index 000000000..10cbd03f7 --- /dev/null +++ b/cdlang/src/main/resources/methods/visitor/traverse.ftl @@ -0,0 +1,2 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +${defineHookPoint("methods.visitor.traverse:Inner")} diff --git a/cdlang/src/main/resources/methods/visitor/traverseInner.ftl b/cdlang/src/main/resources/methods/visitor/traverseInner.ftl new file mode 100644 index 000000000..dcd98f3ea --- /dev/null +++ b/cdlang/src/main/resources/methods/visitor/traverseInner.ftl @@ -0,0 +1,83 @@ +<#-- (c) https://github.com/MontiCore/monticore --> +${tc.signature("classesFromClassdiagramAsString","mCType","objectName")} +<#assign CD4AnalysisTypeDispatcher = glex.getGlobalVar("cd4AnalysisTypeDispatcher")> +<#-- Define a macro to repeat a string n times --> +<#-- Array types --> +<#if (CD4AnalysisTypeDispatcher.isMCArrayTypesASTMCArrayType(mCType))> +<#assign arrayType = mCType.getMCType()> +<#assign arrayTypeName = arrayType.printType()> +<#assign depth = mCType.getDimensions()> +<#assign thisObjectArrayBracketsWith0index = ""> +if(${objectName}!=null){ + <#list 0..depth-1 as i> + + <#list 0..depth-1 as i> + for(int i${i} = 0; i${i} < ${objectName + thisObjectArrayBracketsWith0index}.length; i${i}++) { + <#assign thisObjectArrayBracketsWith0index = thisObjectArrayBracketsWith0index + "[0]"> + + <#assign currentObjectArrayBrackets = ""> + <#list 0..depth-1 as j> + <#assign currentObjectArrayBrackets = currentObjectArrayBrackets + "[i${j}]"> + + ${includeArgs("methods.visitor.traverseInner", classesFromClassdiagramAsString, arrayType, objectName + currentObjectArrayBrackets)} + <#list 0..depth-1 as i> + } + +} +<#-- Set/List types --> +<#elseif (CD4AnalysisTypeDispatcher.isMCCollectionTypesASTMCSetType(mCType)) || (CD4AnalysisTypeDispatcher.isMCCollectionTypesASTMCListType(mCType))> +<#assign innerType = (mCType.getMCTypeArgument().getMCTypeOpt().get())> +<#assign iteratorName = "it" + mCType.hashCode()?replace(".","")?replace(",","")> +<#assign itNextName = "itNext" + mCType.hashCode()?replace(".","")?replace(",","")> +if(${objectName}!=null){ + java.util.Iterator<${innerType.printType()}> ${iteratorName} = ${objectName}.iterator(); + while(${iteratorName}.hasNext()){ + ${innerType.printType()} ${itNextName} = ${iteratorName}.next(); + ${includeArgs("methods.visitor.traverseInner", classesFromClassdiagramAsString, innerType, itNextName)}; + } +} +<#-- Map types --> +<#elseif (CD4AnalysisTypeDispatcher.isMCCollectionTypesASTMCMapType(mCType))> +<#assign keyType = mCType.getKey().getMCTypeOpt().get()> +<#assign valueType = mCType.getValue().getMCTypeOpt().get()> +<#assign keySetName = "KeySet" + mCType.hashCode()?replace(".","")?replace(",","")> +<#assign keySetIteratorName = "keySetIterator" + mCType.hashCode()?replace(".","")?replace(",","")> +if(${objectName}!=null){ + Set<${keyType.printType()}> ${keySetName} = ${objectName}.keySet(); + Iterator<${keyType.printType()}> ${keySetIteratorName} = ${keySetName}.iterator(); + while(${keySetIteratorName}.hasNext()){ + <#assign keyObjectName = "keyObjectName" + mCType.hashCode()?replace(".","")?replace(",","")> + <#assign valueObjectName = "valueObjectName" + mCType.hashCode()?replace(".","")?replace(",","")> + ${keyType.printType()} ${keyObjectName} = ${keySetIteratorName}.next(); + ${valueType.printType()} ${valueObjectName} = ${objectName}.get(${keyObjectName}); + ${includeArgs("methods.visitor.traverseInner", classesFromClassdiagramAsString, keyType, keyObjectName)}; + ${includeArgs("methods.visitor.traverseInner", classesFromClassdiagramAsString, valueType, valueObjectName)}; + } +} +<#-- Optional types --> +<#elseif (CD4AnalysisTypeDispatcher.isMCCollectionTypesASTMCOptionalType(mCType))> +<#assign innerType = (mCType.getMCTypeArgument().getMCTypeOpt().get())> +<#-- if the first object is not present and the second object is present, return false --> +if(${objectName}!=null && ${objectName}.isPresent()){ + ${includeArgs("methods.visitor.traverseInner", classesFromClassdiagramAsString, innerType, objectName + ".get()")}; +} +<#-- Primitive types --> +<#-- Primitive types can not be null --> +// primitive types are no pojo types +<#-- pojo class types --> +<#else> + <#-- only when the type is present in the class diagram the getDefiningSymbol is present --> + <#if mCType.getDefiningSymbol().isPresent()> + <#assign resolvedClassName = mCType.getDefiningSymbol().get().getFullName()> + <#else> + <#assign resolvedClassName = mCType.getMCQualifiedName().getQName()> + + <#if (classesFromClassdiagramAsString?seq_contains(resolvedClassName))> +if(${objectName}!=null){ + ${objectName}.accept(this); +} + <#else> + <#-- all other types --> + //not a pojo type + + diff --git a/cdlang/src/test/java/de/monticore/cd/cdgen/BuilderDecoratorTest.java b/cdlang/src/test/java/de/monticore/cd/cdgen/BuilderDecoratorTest.java new file mode 100644 index 000000000..180302141 --- /dev/null +++ b/cdlang/src/test/java/de/monticore/cd/cdgen/BuilderDecoratorTest.java @@ -0,0 +1,111 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.cd.cdgen; + +import de.monticore.cd.codegen.DecoratorConfig; +import de.monticore.cd.codegen.decorators.*; +import de.monticore.cd.codegen.decorators.matcher.MatchResult; +import de.monticore.cd4code.CD4CodeMill; +import de.monticore.generating.GeneratorSetup; +import de.monticore.generating.templateengine.GlobalExtensionManagement; +import de.monticore.io.paths.MCPath; +import de.monticore.tagging.tags.TagsMill; +import de.se_rwth.commons.logging.Log; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Assertions; +import java.nio.file.Paths; +import java.nio.file.Path; +import java.util.ArrayList; +import java.nio.file.Files; +import java.util.List; +import java.util.Optional; + +class BuilderDecoratorTest extends AbstractDecoratorTest { + + @Test + public void testBuilder() throws Exception { + TagsMill.init(); + var opt = CD4CodeMill.parser() + .parse_String( // @formatter:off + "classdiagram TestBuilder {\n" + + " <> public class TestBuilderWithSetter { \n" + + " public int myInt;\n" + + " public boolean myBool;\n" + + " -> (manyB) B [*] public;\n" + + " -> (optB) B [0..1] public;\n" + + " -> (oneB) B [1] public;\n" + + " public TestEnum myTestEnum;\n" + + " public Level1Interface myLevel1;\n" + + " }\n" + + " <> public class TestBuilderWithSuperClass extends TestBuilderWithSetter;" + + " <> public class TestBuilderWithoutSetter { \n" + + " public int myInt;\n" + + " public boolean myBool;\n" + + " -> (manyB) B [*] public;\n" + + " -> (optB) B [0..1] public;\n" + + " -> (oneB) B [1] public;\n" + + " public TestEnum myTestEnum;\n" + + " public Level1Interface myLevel1;\n" + + " }\n" + + " <> public class B { \n" + + " }\n" + + " <> public class NoDefaultConstructor { \n " + + " public NoDefaultConstructor(int i);\n" + + " int i; \n" + + " } \n" + + " <> public class PrivateDefaultConstructor { \n " + + " private PrivateDefaultConstructor();\n" + + " int i; \n" + + " } \n" + + " enum TestEnum { RUNNING, IDLE, ERROR; }\n" + + " interface Level1Interface;\n" + + " class Level2class implements Level1Interface{\n" + + " int myInt;\n" + + " }\n" + + "}"); + // @formatter:on + + Assertions.assertTrue(opt.isPresent()); + + super.doTest(opt.get()); + + // TODO: Remove once WIP Set Setter is implemented + Log.getFindings().clear(); + } + + @Test + public void testTemplateExistence() { + //test existence of the templates + List templatePaths = new ArrayList<>(); + templatePaths.add(Paths.get("src/main/resources/methods/builder/unsafeBuild.ftl")); + templatePaths.add(Paths.get("src/main/resources/methods/builder/build.ftl")); + templatePaths.add(Paths.get("src/main/resources/methods/builder/isValid.ftl")); + templatePaths.add(Paths.get("src/main/resources/methods/builder/set.ftl")); + templatePaths.add(Paths.get("src/main/resources/methods/builder/setAbsent.ftl")); + for (Path temPath : templatePaths) { + Assertions.assertTrue(Files.exists(temPath)); + } + } + + @Override + protected Optional getHandWrittenPath() { + return Optional.of(new MCPath("src/cdGenIntTestHwc/java")); + } + + @Override + public void initializeDecConf(GlobalExtensionManagement glex, DecoratorConfig config, + GeneratorSetup setup) { + config.withCopyCreator().defaultApply(); + config.withDecorator(new SetterDecorator()); + config.configApplyMatchName(SetterDecorator.class, ("setter")); + config.configIgnoreMatchName(SetterDecorator.class, ("noSetter")); + config.withDecorator(new GetterDecorator()); + config.configApplyMatchName(GetterDecorator.class, "getter"); + config.configIgnoreMatchName(GetterDecorator.class, "noGetter"); + config.withDecorator(new BuilderDecorator()); + config.configApplyMatchName(BuilderDecorator.class, "builder"); + config.configIgnoreMatchName(BuilderDecorator.class, "noBuilder"); + config.withDecorator(new CardinalityDefaultDecorator()); + config.configDefault(CardinalityDefaultDecorator.class, MatchResult.APPLY); + } + +} diff --git a/cdlang/src/test/java/de/monticore/cd/cdgen/CDGenTest.java b/cdlang/src/test/java/de/monticore/cd/cdgen/CDGenTest.java index 69aa49fae..410102362 100644 --- a/cdlang/src/test/java/de/monticore/cd/cdgen/CDGenTest.java +++ b/cdlang/src/test/java/de/monticore/cd/cdgen/CDGenTest.java @@ -1,10 +1,12 @@ /* (c) https://github.com/MontiCore/monticore */ package de.monticore.cd.cdgen; +import de.monticore.cd.codegen.CDGenService; import de.monticore.cd.codegen.CDGenerator; import de.monticore.cd.codegen.CdUtilsPrinter; import de.monticore.cd.codegen.DecoratorConfig; import de.monticore.cd.codegen.decorators.*; +import de.monticore.cd4analysis._util.CD4AnalysisTypeDispatcher; import de.monticore.cd4analysis.trafo.CD4AnalysisAfterParseTrafo; import de.monticore.cd4analysis.trafo.CDAssociationCreateFieldsFromAllRoles; import de.monticore.cd4analysis.trafo.CDAssociationCreateFieldsFromNavigableRoles; @@ -15,6 +17,7 @@ import de.monticore.generating.GeneratorSetup; import de.monticore.generating.templateengine.GlobalExtensionManagement; import de.monticore.symbols.basicsymbols.BasicSymbolsMill; +import de.monticore.types.MCTypeFacade; import de.monticore.types.mccollectiontypes.types3.MCCollectionSymTypeRelations; import de.se_rwth.commons.logging.LogStub; import java.io.File; @@ -91,6 +94,9 @@ public void doTest() throws Exception { // Prepare GlobalExtensionManagement glex = new GlobalExtensionManagement(); glex.setGlobalValue("cdPrinter", new CdUtilsPrinter()); + glex.setGlobalValue("mcTypeFacade", MCTypeFacade.getInstance()); + glex.setGlobalValue("cdGenService", new CDGenService()); + glex.setGlobalValue("cd4AnalysisTypeDispatcher", new CD4AnalysisTypeDispatcher()); GeneratorSetup generatorSetup = new GeneratorSetup(); generatorSetup.setGlex(glex); generatorSetup.setOutputDirectory(new File("target/outtest")); diff --git a/cdlang/src/test/java/de/monticore/cd/cdgen/DeepCloneAndDeepEqualsDecoratorTest.java b/cdlang/src/test/java/de/monticore/cd/cdgen/DeepCloneAndDeepEqualsDecoratorTest.java new file mode 100644 index 000000000..ab657af16 --- /dev/null +++ b/cdlang/src/test/java/de/monticore/cd/cdgen/DeepCloneAndDeepEqualsDecoratorTest.java @@ -0,0 +1,163 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.cd.cdgen; + +import de.monticore.cd.codegen.DecoratorConfig; +import de.monticore.cd.codegen.decorators.BuilderDecorator; +import de.monticore.cd.codegen.decorators.CardinalityDefaultDecorator; +import de.monticore.cd.codegen.decorators.DeepCloneAndDeepEqualsDecorator; +import de.monticore.cd.codegen.decorators.matcher.MatchResult; +import de.monticore.cd4code.CD4CodeMill; +import de.monticore.cd4codebasis._ast.ASTCDClass; +import de.monticore.cdbasis._ast.ASTCDAttribute; +import de.monticore.cdbasis._ast.ASTCDCompilationUnit; +import de.monticore.cdgen.CDGenTool; +import de.monticore.generating.GeneratorSetup; +import de.monticore.generating.templateengine.GlobalExtensionManagement; +import de.se_rwth.commons.logging.Log; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class DeepCloneAndDeepEqualsDecoratorTest extends AbstractDecoratorTest { + + @Test + public void testDeepCopyAndDeepEquals() throws Exception { + var opt = CD4CodeMill.parser().parse_String("classdiagram TestDeepCloneAndDeepEquals {\n" + + " public class AllTogether { \n" + " public int myInt;\n" + " public boolean myBool;\n" + + " -> (manyClassWith2DimList)ClassWith2DimList [*] public;\n" + + " -> (optClassWith2DimList)ClassWith2DimList [0..1] public;\n" + + " -> (oneClassWith2DimList)ClassWith2DimList [1] public;\n" + " }\n" + + " public class ClassWith2DimList { \n" + " public List> my2dimList;\n" + + " public List> my2dimList2;\n" + " }\n" + + " public class ClassWith2DimSet { \n" + " public Set> my2dimSet;\n" + + " public Set> my2dimSet2;\n" + " }\n" + "public class ClassWithOptional { " + + " public Optional myOptionalInteger;\n" + + " public Optional myOptionalInteger2;\n" + "}\n " + + "public class ClassWith2DimOptional { " + + " public Optional> my2DimOptional;\n" + + " public Optional> my2DimOptional2;\n" + "}\n " + + "public class ClassWithPojoClassType { " + " public ClassWithPrimitiveType pojoType;\n" + + " public ClassWithPrimitiveType pojoType2;\n" + "}\n " + + "public class ClassWithPrimitiveType { " + " public int myInt;\n" + "}\n " + + "public class ClassWithSet { " + " public Set mySet;\n" + + " public Set mySet2;\n" + "}\n " + "public class ClassWithList { \n" + + " public List myIntegerList;\n" + " public List myIntegerList2;\n" + + "} \n" + "public class ClassCircular1 { \n" + "public ClassCircular2 myClassCircular2;\n" + + "}\n" + "public class ClassCircular2 { \n" + "public ClassCircular1 myClassCircular1;\n" + + "}\n" + "public class ClassWithAssociation { \n" + "}\n" + + "public class ClassWithNoDefaultConstructor {\n" + + " public ClassWithNoDefaultConstructor(int i);\n" + " int i; \n" + "}\n" + + "public class ClassWithComposition { \n" + "-> (opt)B [0..1] public;\n" + + "-> (many)B [*] public;\n" + "-> (one)B [1] public;\n" + "-> (opt2)B [0..1] public;\n" + + "-> (many2)B [*] public;\n" + "-> (one2)B [1] public;\n" + "}\n" + + "public class ClassWithArray { \n" + " public ClassWithPrimitiveType[] arrayOfString; \n" + + " public ClassWithPrimitiveType[] arrayOfString2; \n" + "}\n" + + "public class ClassWith3DArray { \n" + + " public ClassWithPrimitiveType[][][] threeDimArrayOfString; \n" + + " public ClassWithPrimitiveType[][][] threeDimArrayOfString2; \n" + "}\n" + + "public class ClassWithString { \n" + " public String myString;\n" + + " public String myString2;\n" + "}\n" + "public class ClassWithEnum { \n" + + " public TestEnum myEnum;\n" + " public TestEnum myEnum2;\n" + "}\n" + + "public class ClassWithInterface { \n" + " public Level1Interface myInterface;\n" + + " public Level1Interface myInterface2 ;\n" + " -> (many)Level1Interface [*] public;\n" + + " -> (many2)Level1Interface [*] public;\n" + "}\n" + "public class ClassWithMap { \n" + + " public Map myMap;\n" + " public Map myMap2;\n" + "}\n" + + "public class ClassWith2DMap { \n" + " public Map> myMap;\n" + + " public Map> myMap2;\n" + "}\n" + "public class B { \n" + "}\n" + + " enum TestEnum { RUNNING, IDLE, ERROR; }\n" + + " interface Level1Interface { public boolean myBool = false;} \n" + + " class Level2class implements Level1Interface{\n" + " int myInt;\n" + " }\n" + + "class Level3class extends Level2class implements Level1Interface;" + + " <> public class ClassWithBuilder { \n" + " public ClassWithBuilder(int i);\n" + + " public int myInt;\n" + " }\n" + + "association [1] AllTogether (owner) -> (owns) B [*]public; " + + "association [1] ClassWithAssociation (owner) -> (owns) B [*]public; " + + "association [1] ClassWithAssociation (owner2) -> (owns2) B [*]public; " + "}"); + + Assertions.assertTrue(opt.isPresent()); + + super.doTest(opt.get()); + + // TODO: Remove once non primitive types in CD files are supported and Set and List Setters are implemented + Log.getFindings().clear(); + } + + @Test + public void testTemplateExistence() { + //test existence of the templates + List templatePaths = new ArrayList<>(); + templatePaths.add(Paths.get( + "src/main/resources/methods/deepCloneAndDeepEquals/deepClone1.ftl")); + templatePaths.add(Paths.get( + "src/main/resources/methods/deepCloneAndDeepEquals/deepClone2.ftl")); + templatePaths.add(Paths.get( + "src/main/resources/methods/deepCloneAndDeepEquals/deepClone2Inner.ftl")); + templatePaths.add(Paths.get( + "src/main/resources/methods/deepCloneAndDeepEquals/deepEquals1.ftl")); + templatePaths.add(Paths.get( + "src/main/resources/methods/deepCloneAndDeepEquals/deepEquals2.ftl")); + templatePaths.add(Paths.get( + "src/main/resources/methods/deepCloneAndDeepEquals/deepEquals3.ftl")); + templatePaths.add(Paths.get( + "src/main/resources/methods/deepCloneAndDeepEquals/deepEquals3Inner.ftl")); + for (Path temPath : templatePaths) { + Assertions.assertTrue(Files.exists(temPath)); + } + } + + @Test + public void testGetAllCDAttributes() throws IOException { + var opt = CD4CodeMill.parser().parse_String("classdiagram TestDeepCloneAndDeepEquals {\n" + + "public class B { \n" + "}\n" + " interface Level1Interface { public boolean myBool;} \n" + + " class Level2class implements Level1Interface{\n" + " int myInt;\n" + " }\n" + + " class Level3class extends Level2class implements Level1Interface{\n" + + " boolean myBool;\n" + " }\n" + + " class Level4class extends Level3class implements Level1Interface;" + "}"); + + Assertions.assertTrue(opt.isPresent()); + ASTCDCompilationUnit cd = opt.get(); + CDGenTool tool = new CDGenTool(); + tool.trafoBeforeSymtab(Collections.singletonList(cd)); + + final boolean class2mc = this.withClass2MC(); + tool.initializeSymbolTable(class2mc); + + // Create ST + tool.createSymbolTable(cd); + + // Complete ST + tool.completeSymbolTable(cd); + + ASTCDClass astcdClass = (ASTCDClass) cd.getCDDefinition().getCDElement(4); + DeepCloneAndDeepEqualsDecorator deepCloneAndDeepEqualsDecorator = + new DeepCloneAndDeepEqualsDecorator(); + List attributes = deepCloneAndDeepEqualsDecorator.getAllCDAttributes( + astcdClass); + + //as we do not care about interface attributes, they should be ignored. + //the class has 2 super class with 1 attribute each. + //Therefore, the resulting list should be of size 2 + Assertions.assertSame(2, attributes.size()); + } + + @Override + public void initializeDecConf(GlobalExtensionManagement glex, DecoratorConfig config, + GeneratorSetup setup) { + config.withCopyCreator().defaultApply(); + config.withDecorator(new CardinalityDefaultDecorator()); + config.configDefault(CardinalityDefaultDecorator.class, MatchResult.APPLY); + config.withDecorator(new DeepCloneAndDeepEqualsDecorator()); + config.configDefault(DeepCloneAndDeepEqualsDecorator.class, MatchResult.APPLY); + config.withDecorator(new BuilderDecorator()); + config.configApplyMatchName(BuilderDecorator.class, "builder"); + config.configIgnoreMatchName(BuilderDecorator.class, "noBuilder"); + } + +} diff --git a/cdlang/src/test/java/de/monticore/cd/cdgen/DefaultCD2PojoDecoratorTest.java b/cdlang/src/test/java/de/monticore/cd/cdgen/DefaultCD2PojoDecoratorTest.java index 5e12fa9bc..ab8e98859 100644 --- a/cdlang/src/test/java/de/monticore/cd/cdgen/DefaultCD2PojoDecoratorTest.java +++ b/cdlang/src/test/java/de/monticore/cd/cdgen/DefaultCD2PojoDecoratorTest.java @@ -18,7 +18,7 @@ * cdlang/src/cdGenIntTest/java/getter/GetterDecoratorResultTest then tests the generated result */ public class DefaultCD2PojoDecoratorTest extends AbstractDecoratorTest { - + @Test public void testAll() throws Exception { var opt = CD4CodeMill.parser().parse_String("classdiagram TestDefaultCD2Pojo {\n" @@ -28,18 +28,25 @@ public void testAll() throws Exception { + " public association TestGetterC -> (roleB) Other [*];\n" + " public association TestGetterC -> (orderedRole) Other [*] {ordered};\n" + " <> public class Other { \n" + "}\n" + "}"); - + Assertions.assertTrue(opt.isPresent()); - + super.doTest(opt.get()); - + // TODO: Remove once WIP Set Setter is implemented Log.getFindings().remove(0); Log.getFindings().remove(0); - + // TODO: Remove once Map and Set are accepted + Log.getFindings().remove(0); + Log.getFindings().remove(0); + Log.getFindings().remove(0); + Log.getFindings().remove(0); + Log.getFindings().remove(0); + Log.getFindings().remove(0); + Assertions.assertTrue(Log.getFindings().isEmpty()); } - + @Override public void initializeDecConf(GlobalExtensionManagement glex, DecoratorConfig config, GeneratorSetup setup) { @@ -52,5 +59,5 @@ public void initializeDecConf(GlobalExtensionManagement glex, DecoratorConfig co glex.setGlobalValue("genSetup", setup); hpp.processValue(tc, new ArrayList<>()); } - + } diff --git a/cdlang/src/test/java/de/monticore/cd/cdgen/InheritanceVisitorDecoratorTest.java b/cdlang/src/test/java/de/monticore/cd/cdgen/InheritanceVisitorDecoratorTest.java new file mode 100644 index 000000000..711994cbb --- /dev/null +++ b/cdlang/src/test/java/de/monticore/cd/cdgen/InheritanceVisitorDecoratorTest.java @@ -0,0 +1,108 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.cd.cdgen; + +import de.monticore.cd.codegen.DecoratorConfig; +import de.monticore.cd.codegen.decorators.InheritanceVisitorDecorator; +import de.monticore.cd.codegen.decorators.matcher.MatchResult; +import de.monticore.cd4code.CD4CodeMill; +import de.monticore.generating.GeneratorSetup; +import de.monticore.generating.templateengine.GlobalExtensionManagement; +import de.monticore.io.paths.MCPath; +import de.se_rwth.commons.logging.Log; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import java.util.Optional; + +public class InheritanceVisitorDecoratorTest extends AbstractDecoratorTest { + + /** + * Test the {@link InheritanceVisitorDecorator} by applying it to a CD. The + * cdlang/src/cdGenIntTest/java/visitor/InheritanceVisitorDecoratorTest then tests the generated + * result + */ + @Test + public void testInheritanceVisitor() throws Exception { + var opt = CD4CodeMill.parser() + .parse_String( // @formatter:off + "classdiagram TestInheritanceVisitor {\n" + + " public class AllTogether { \n" + " public int myInt;\n" + " public boolean myBool;\n" + + " -> (manyClassWith2DimList)ClassWith2DimList [*] public;\n" + + " -> (optClassWith2DimList)ClassWith2DimList [0..1] public;\n" + + " -> (oneClassWith2DimList)ClassWith2DimList [1] public;\n" + " }\n" + + " public class ClassWith2DimList { \n" + " public List> my2dimList;\n" + + " public List> my2dimList2;\n" + " }\n" + + " public class ClassWith2DimSet { \n" + " public Set> my2dimSet;\n" + + " public Set> my2dimSet2;\n" + " }\n" + "public class ClassWithOptional { " + + " public Optional myOptionalInteger;\n" + + " public Optional myOptionalInteger2;\n" + "}\n " + + "public class ClassWith2DimOptional { " + + " public Optional> my2DimOptional;\n" + + " public Optional> my2DimOptional2;\n" + "}\n " + + "public class ClassWithPojoClassType { " + " public ClassWithPrimitiveType pojoType;\n" + + " public ClassWithPrimitiveType pojoType2;\n" + "}\n " + + "public class ClassToBeTopped { " + " public ClassWithPrimitiveType pojoType;\n" + + " public ClassWithPrimitiveType pojoType2;\n" + "}\n " + + "public class ClassWithPrimitiveType { " + " public int myInt;\n" + "}\n " + + "public class ClassWithSet { " + " public Set mySet;\n" + + " public Set mySet2;\n" + "}\n " + "public class ClassWithList { \n" + + " public List myIntegerList;\n" + " public List myIntegerList2;\n" + + "} \n" + "public class ClassCircular1 { \n" + "public ClassCircular2 myClassCircular2;\n" + + "}\n" + "public class ClassCircular2 { \n" + "public ClassCircular1 myClassCircular1;\n" + + "}\n" + "public class ClassWithAssociation { \n" + "}\n" + + "public class ClassWithComposition { \n" + "-> (opt)B [0..1] public;\n" + + "-> (many)B [*] public;\n" + "-> (one)B [1] public;\n" + "-> (opt2)B [0..1] public;\n" + + "-> (many2)B [*] public;\n" + "-> (one2)B [1] public;\n" + "}\n" + + "public class ClassWithArray { \n" + " public ClassWithPrimitiveType[] arrayOfString; \n" + + " public ClassWithPrimitiveType[] arrayOfString2; \n" + "}\n" + + "public class ClassWith3DimArray { \n" + + " public ClassWithPrimitiveType[][][] threeDimArrayOfString; \n" + + " public ClassWithPrimitiveType[][][] threeDimArrayOfString2; \n" + "}\n" + + "public class ClassWithString { \n" + " public String myString;\n" + + " public String myString2;\n" + "}\n" + "public class ClassWithMap { \n" + + " public Map myMap;\n" + " public Map myMap2;\n" + "}\n" + + "public class ClassWith2DimMap { \n" + " public Map> myMap;\n" + + " public Map> myMap2;\n" + "}\n" + "public class B { \n" + "}\n" + + "association [1] AllTogether (owner) -> (owns) B [*]public; " + + "association [1] ClassWithAssociation (owner) -> (owns) B [*]public; " + + "association [1] ClassWithAssociation (owner2) -> (owns2) B [*]public; " + + "interface Level1Interface;" + + "class Level2class implements Level1Interface{" + + " int myInt;" + + "}" + + "interface Level2Interface;" + + "class Level3class extends Level2class implements Level2Interface;" + + "class Level0class {" + + "-> (many)Level1Interface [*];" + + "}" + + "class Level4class extends Level3class;" + + "class Level5class extends Level4class implements Level4Interface;" + + "interface Level4Interface extends Level3Interface1, Level3Interface2;" + + "interface Level3Interface1 extends Level2Interface1;" + + "interface Level3Interface2 extends Level2Interface2;" + + "interface Level2Interface1;" + + "interface Level2Interface2;" + + "}"); + // @formatter:on + + Assertions.assertTrue(opt.isPresent()); + + super.doTest(opt.get()); + + // TODO: Remove once non primitive types in CD files are supported and Set and List Setters are implemented + Log.clearFindings(); + } + + @Override + protected Optional getHandWrittenPath() { + return Optional.of(new MCPath("src/cdGenIntTestHwc/java")); + } + + @Override + public void initializeDecConf(GlobalExtensionManagement glex, DecoratorConfig config, + GeneratorSetup setup) { + config.withCopyCreator().defaultApply(); + config.withDecorator(new InheritanceVisitorDecorator()); + config.configDefault(InheritanceVisitorDecorator.class, MatchResult.APPLY); + } + +} diff --git a/cdlang/src/test/java/de/monticore/cd/cdgen/ObserverDecoratorTest.java b/cdlang/src/test/java/de/monticore/cd/cdgen/ObserverDecoratorTest.java new file mode 100644 index 000000000..cf86accb8 --- /dev/null +++ b/cdlang/src/test/java/de/monticore/cd/cdgen/ObserverDecoratorTest.java @@ -0,0 +1,83 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.cd.cdgen; + +import de.monticore.cd.codegen.DecoratorConfig; +import de.monticore.cd.codegen.decorators.*; +import de.monticore.cd.codegen.decorators.matcher.MatchResult; +import de.monticore.cd4code.CD4CodeMill; +import de.monticore.generating.GeneratorSetup; +import de.monticore.generating.templateengine.GlobalExtensionManagement; +import de.se_rwth.commons.logging.Log; +import org.junit.Assert; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Assertions; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; + +class ObserverDecoratorTest extends AbstractDecoratorTest { + + /** + * Test the {@link ObserverDecorator} by applying it to a CD. The + * cdlang/src/cdGenIntTest/java/observer/ObserverDecoratorTest then tests the generated result + */ + @Test + void testObserver() throws Exception { + var opt = // @formatter:off + CD4CodeMill.parser() + .parse_String("classdiagram TestObserver {\n" + + " <> public class OtherC { \n" + + " public int myInt;\n" + + " public boolean myBool;\n" + + " -> (manyB) B [*] public;\n" + + " -> (optB) B [0..1] public;\n" + + " -> (oneB) B [1] public;\n" + + " public int ov;\n" + + " }\n" + + "<>public class B { " + + "}\n " + + "}"); + // @formatter:on + + Assertions.assertTrue(opt.isPresent()); + + super.doTest(opt.get()); + + // TODO: Remove once WIP Set Setter is implemented + Log.getFindings().remove(0); + } + + @Test + public void testTemplateExistence() { + //test existence of the templates + List templatePaths = new ArrayList<>(); + templatePaths.add(Paths.get("src/main/resources/methods/observer/addObserver.ftl")); + templatePaths.add(Paths.get("src/main/resources/methods/observer/removeObserver.ftl")); + templatePaths.add(Paths.get("src/main/resources/methods/observer/notifyObserver.ftl")); + templatePaths.add(Paths.get( + "src/main/resources/methods/observer/notifyObserverAttributeSpecific.ftl")); + for (Path temPath : templatePaths) { + Assert.assertTrue(Files.exists(temPath)); + } + } + + @Override + public void initializeDecConf(GlobalExtensionManagement glex, DecoratorConfig config, + GeneratorSetup setup) { + config.withCopyCreator().defaultApply(); + config.withDecorator(new GetterDecorator()); + config.configApplyMatchName(GetterDecorator.class, "getter"); + config.configIgnoreMatchName(GetterDecorator.class, "noGetter"); + config.withDecorator(new SetterDecorator()); + config.configApplyMatchName(SetterDecorator.class, "setter"); + config.configIgnoreMatchName(SetterDecorator.class, "noSetter"); + config.withDecorator(new ObserverDecorator()); + config.configApplyMatchName(ObserverDecorator.class, "observer"); + config.configIgnoreMatchName(ObserverDecorator.class, "noObserver"); + config.withDecorator(new CardinalityDefaultDecorator()); + config.configDefault(CardinalityDefaultDecorator.class, MatchResult.APPLY); + } + +} diff --git a/cdlang/src/test/java/de/monticore/cd/cdgen/VisitorDecoratorTest.java b/cdlang/src/test/java/de/monticore/cd/cdgen/VisitorDecoratorTest.java new file mode 100644 index 000000000..6d949b975 --- /dev/null +++ b/cdlang/src/test/java/de/monticore/cd/cdgen/VisitorDecoratorTest.java @@ -0,0 +1,99 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.cd.cdgen; + +import de.monticore.cd.codegen.DecoratorConfig; +import de.monticore.cd.codegen.decorators.VisitorDecorator; +import de.monticore.cd.codegen.decorators.matcher.MatchResult; +import de.monticore.cd4code.CD4CodeMill; +import de.monticore.generating.GeneratorSetup; +import de.monticore.generating.templateengine.GlobalExtensionManagement; +import de.monticore.io.paths.MCPath; +import de.se_rwth.commons.logging.Log; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import java.util.Optional; + +public class VisitorDecoratorTest extends AbstractDecoratorTest { + + /** + * Test the {@link VisitorDecorator} by applying it to a CD. The + * cdlang/src/cdGenIntTest/java/visitor/VisitorDecoratorTest then tests the generated result + */ + @Test + public void testVisitor() throws Exception { + var opt = CD4CodeMill.parser() + .parse_String( // @formatter:off + "classdiagram TestVisitor {\n" + + " public class AllTogether { \n" + " public int myInt;\n" + " public boolean myBool;\n" + + " -> (manyClassWith2DimList)ClassWith2DimList [*] public;\n" + + " -> (optClassWith2DimList)ClassWith2DimList [0..1] public;\n" + + " -> (oneClassWith2DimList)ClassWith2DimList [1] public;\n" + " }\n" + + " public class ClassWith2DimList { \n" + " public List> my2dimList;\n" + + " public List> my2dimList2;\n" + " }\n" + + " public class ClassWith2DimSet { \n" + " public Set> my2dimSet;\n" + + " public Set> my2dimSet2;\n" + " }\n" + "public class ClassWithOptional { " + + " public Optional myOptionalInteger;\n" + + " public Optional myOptionalInteger2;\n" + "}\n " + + "public class ClassWith2DimOptional { " + + " public Optional> my2DimOptional;\n" + + " public Optional> my2DimOptional2;\n" + "}\n " + + "public class ClassWithPojoClassType { " + " public ClassWithPrimitiveType pojoType;\n" + + " public ClassWithPrimitiveType pojoType2;\n" + "}\n " + + "public class ClassToBeTopped { " + " public ClassWithPrimitiveType pojoType;\n" + + " public ClassWithPrimitiveType pojoType2;\n" + "}\n " + + "public class ClassWithPrimitiveType { " + " public int myInt;\n" + "}\n " + + "public class ClassWithSet { " + " public Set mySet;\n" + + " public Set mySet2;\n" + "}\n " + "public class ClassWithList { \n" + + " public List myIntegerList;\n" + " public List myIntegerList2;\n" + + "} \n" + "public class ClassCircular1 { \n" + "public ClassCircular2 myClassCircular2;\n" + + "}\n" + "public class ClassCircular2 { \n" + "public ClassCircular1 myClassCircular1;\n" + + "}\n" + "public class ClassWithAssociation { \n" + "}\n" + + "public class ClassWithComposition { \n" + "-> (opt)B [0..1] public;\n" + + "-> (many)B [*] public;\n" + "-> (one)B [1] public;\n" + "-> (opt2)B [0..1] public;\n" + + "-> (many2)B [*] public;\n" + "-> (one2)B [1] public;\n" + "}\n" + + "public class ClassWithArray { \n" + " public ClassWithPrimitiveType[] arrayOfString; \n" + + " public ClassWithPrimitiveType[] arrayOfString2; \n" + "}\n" + + "public class ClassWith3DimArray { \n" + + " public ClassWithPrimitiveType[][][] threeDimArrayOfString; \n" + + " public ClassWithPrimitiveType[][][] threeDimArrayOfString2; \n" + "}\n" + + "public class ClassWithString { \n" + " public String myString;\n" + + " public String myString2;\n" + "}\n" + "public class ClassWithMap { \n" + + " public Map myMap;\n" + " public Map myMap2;\n" + "}\n" + + "public class ClassWith2DimMap { \n" + " public Map> myMap;\n" + + " public Map> myMap2;\n" + "}\n" + "public class B { \n" + "}\n" + + "association [1] AllTogether (owner) -> (owns) B [*]public; " + + "association [1] ClassWithAssociation (owner) -> (owns) B [*]public; " + + "association [1] ClassWithAssociation (owner2) -> (owns2) B [*]public; " + + "interface Level1Interface;" + + "class Level2class implements Level1Interface{" + + " int myInt;" + + "}" + + "class Level3class extends Level2class implements Level1Interface;" + + " class Level0class {" + + "-> (many)Level1Interface [*];" + + "}" + + "}"); + // @formatter:on + + Assertions.assertTrue(opt.isPresent()); + + super.doTest(opt.get()); + + // TODO: Remove once non primitive types in CD files are supported and Set and List Setters are implemented + Log.clearFindings(); + } + + @Override + protected Optional getHandWrittenPath() { + return Optional.of(new MCPath("src/cdGenIntTestHwc/java")); + } + + @Override + public void initializeDecConf(GlobalExtensionManagement glex, DecoratorConfig config, + GeneratorSetup setup) { + config.withCopyCreator().defaultApply(); + config.withDecorator(new VisitorDecorator()); + config.configDefault(VisitorDecorator.class, MatchResult.APPLY); + } + +} diff --git a/cdlang/src/test/resources/de/monticore/cd/codegen/TOPMechanismTest.cd b/cdlang/src/test/resources/de/monticore/cd/codegen/TOPMechanismTest.cd new file mode 100644 index 000000000..ee5b76f2e --- /dev/null +++ b/cdlang/src/test/resources/de/monticore/cd/codegen/TOPMechanismTest.cd @@ -0,0 +1,12 @@ +/* (c) https://github.com/MontiCore/monticore */ + +classdiagram TOPMechanismTest { + + <> public class TOPMechanismTest { + public int myInt; + public boolean myBool; + -> (manyB) Integer [*]; + -> (optB) Integer [0..1] ; + -> (oneB) Integer [1]; + } +} diff --git a/cdlang/src/test/resources/de/monticore/cd/codegen/hwc/TestBuilder/TestBuilderWithSetterBuilder.java b/cdlang/src/test/resources/de/monticore/cd/codegen/hwc/TestBuilder/TestBuilderWithSetterBuilder.java new file mode 100644 index 000000000..63d2590de --- /dev/null +++ b/cdlang/src/test/resources/de/monticore/cd/codegen/hwc/TestBuilder/TestBuilderWithSetterBuilder.java @@ -0,0 +1,5 @@ +package TestBuilder; + +public class TestBuilderWithSetterBuilder extends TestBuilderWithSetterBuilderTOP { + +} diff --git a/cdlang/src/test/resources/de/monticore/cd/codegen/hwc/TestObserver/Observer.java b/cdlang/src/test/resources/de/monticore/cd/codegen/hwc/TestObserver/Observer.java new file mode 100644 index 000000000..4f06a0856 --- /dev/null +++ b/cdlang/src/test/resources/de/monticore/cd/codegen/hwc/TestObserver/Observer.java @@ -0,0 +1,68 @@ +package TestObserver; + +import java.util.Optional; +import java.util.Set; + +public class Observer implements TestObserver.IOtherCObserver { + + int countUpdate = 0; + int countUpdateObserverMyInt = 0; + int countUpdateObserverMyBool = 0; + int countUpdateObserverManyB = 0; + int countUpdateObserverOptB = 0; + int countUpdateObserverOneB = 0; + + @Override + public void update(OtherC clazz) { + countUpdate++; + } + + @Override + public void updateObserverMyInt(OtherC clazz, int ov) { + countUpdateObserverMyInt++; + } + + @Override + public void updateObserverMyBool(OtherC clazz, boolean ov) { + countUpdateObserverMyBool++; + } + + @Override + public void updateObserverManyB(OtherC clazz, Set ov) { + countUpdateObserverManyB++; + } + + @Override + public void updateObserverOptB(OtherC clazz, Optional ov) { + countUpdateObserverOptB++; + } + + @Override + public void updateObserverOneB(OtherC clazz, B ov) { + countUpdateObserverOneB++; + } + + public int getCountUpdate() { + return countUpdate; + } + + public int getCountUpdateObserverMyInt() { + return countUpdateObserverMyInt; + } + + public int getCountUpdateObserverMyBool() { + return countUpdateObserverMyBool; + } + + public int getCountUpdateObserverManyB() { + return countUpdateObserverManyB; + } + + public int getCountUpdateObserverOptB() { + return countUpdateObserverOptB; + } + + public int getCountUpdateObserverOneB() { + return countUpdateObserverOneB; + } +} diff --git a/cdtool/cdgradle/src/test/java/de/monticore/cdgen/CDGenGradlePluginTest.java b/cdtool/cdgradle/src/test/java/de/monticore/cdgen/CDGenGradlePluginTest.java index 8a72a5373..8b7a4da15 100644 --- a/cdtool/cdgradle/src/test/java/de/monticore/cdgen/CDGenGradlePluginTest.java +++ b/cdtool/cdgradle/src/test/java/de/monticore/cdgen/CDGenGradlePluginTest.java @@ -71,16 +71,35 @@ void testCDGen(String version) throws IOException { File cd4aJarFile = new File(libs, "cd4analysis-" + projVersion + ".jar"); assertTrue(libs.exists()); - String buildFileContent = "plugins {" + " id 'de.rwth.se.cdgen' " + "}\n " - + "repositories {\n" + " if ((\"true\").equals(getProperty('useLocalRepo'))) {\n " - + " mavenLocal()\n" + " }\n" - + " maven{ url 'https://nexus.se.rwth-aachen.de/content/groups/public' }\n" - + " mavenCentral()\n" + "}\n" + - // We have to inject the cdlang jar for this project (as it is not yet published) - "dependencies {\n" + " cdTool files('" + cd4aJarFile.getAbsolutePath().replace("\\", "\\\\") - + "')\n" + - // Along with the transitive dependencies - " cdTool \"de.monticore:monticore-grammar:" + projVersion + "\" \n " + "}"; + // @formatter:off + String buildFileContent = + "plugins {" + + " id 'de.rwth.se.cdgen' " + + "}\n " + + "repositories {\n" + + " if ((\"true\").equals(getProperty('useLocalRepo'))) {\n " + + " mavenLocal()\n" + + " }\n" + + " maven{ url 'https://nexus.se.rwth-aachen.de/content/groups/public' }\n" + + " mavenCentral()\n" + + "}\n" + + + // We have to inject the cdlang jar for this project (as it is not yet published) + "dependencies {\n" + + " cdTool files('" + + cd4aJarFile.getAbsolutePath().replace("\\", "\\\\") + + "')\n" + // add the custom Log dependency + + "implementation \"de.monticore:monticore-runtime:" + + projVersion + + "\" \n" + + + // Along with the transitive dependencies + " cdTool \"de.monticore:monticore-grammar:" + + projVersion + + "\" \n " + + "}"; + // @formatter:on writeFile(buildFile, buildFileContent); Files.copy(new File("src/test/resources/MyCD.cd").toPath(), new File(cdsDir, "MyCD.cd") .toPath()); @@ -153,23 +172,36 @@ void testCDGenOwnDecorator(String version) throws IOException { File cd4aJarFile = new File(libs, "cd4analysis-" + projVersion + ".jar"); assertTrue(libs.exists()); - String buildFileContent = "plugins {" + " id 'de.rwth.se.cdgen' " + "}\n " - + "repositories {\n" + " if ((\"true\").equals(getProperty('useLocalRepo'))) {\n " - + " mavenLocal()\n" + " }\n" + // @formatter:off + String buildFileContent = "plugins {" + + " id 'de.rwth.se.cdgen' " + + "}\n " + + "repositories {\n" + + " if ((\"true\").equals(getProperty('useLocalRepo'))) {\n " + + " mavenLocal()\n" + + " }\n" + " maven{ url 'https://nexus.se.rwth-aachen.de/content/groups/public' }\n" - + " mavenCentral()\n" + "}\n" + + + " mavenCentral()\n" + + "}\n" + // Define a sourceset in which we write our own decorator - "sourceSets{\n" + " decorators {\n" + " java.srcDir('src/dec/java') \n" + " }" + "}\n" + + "sourceSets{\n" + + " decorators {\n" + + " java.srcDir('src/dec/java') \n" + + " }" + "}\n" + // We have to inject the cdlang jar for this project (as it is not yet published) - "dependencies {\n" + " cdTool files('" + cd4aJarFile.getAbsolutePath().replace("\\", "\\\\") - + "')\n" + + "dependencies {\n" + + " cdTool files('" + cd4aJarFile.getAbsolutePath().replace("\\", "\\\\") + "')\n" + // Along with the transitive dependencies - " cdTool \"de.monticore:monticore-grammar:" + projVersion + "\" \n " + "}\n" + + " cdTool \"de.monticore:monticore-grammar:" + projVersion + "\" \n " + + "}\n" + // the decorator sourceset requires the same dependencies as cdTool "configurations.decoratorsImplementation.extendsFrom(configurations.cdTool)\n" + "generateClassDiagrams {\n" + " configTemplate='CD2OwnDecorator' \n " + " tmplDir=file('src/main/resources') \n " - + " getExtraClasspathElements().from(sourceSets.decorators.output) \n " + "}\n" + "\n"; + + " getExtraClasspathElements().from(sourceSets.decorators.output) \n " + + "}\n" + + "\n"; + // @formatter:on writeFile(buildFile, buildFileContent); Files.copy(new File("src/test/resources/MyCD.cd").toPath(), new File(cdsDir, "MyCD.cd") .toPath()); diff --git a/cdtool/cdgradle/src/test/resources/MyCD.cd b/cdtool/cdgradle/src/test/resources/MyCD.cd index b84d43405..9766c3040 100644 --- a/cdtool/cdgradle/src/test/resources/MyCD.cd +++ b/cdtool/cdgradle/src/test/resources/MyCD.cd @@ -32,8 +32,8 @@ classdiagram MyCD { interface MyEmptyInterface { } interface MyInterface { - boolean doStuff(); - public boolean doStuffInPublic(); + abstract boolean doStuff(); + public abstract boolean doStuffInPublic(); } <>