diff --git a/build.gradle b/build.gradle
index 149926618f6de1bf2d5774cf5ea3b874ddc3726f..b87e92bca0da5825980e9885e4e0ae8d1a63d0d0 100644
--- a/build.gradle
+++ b/build.gradle
@@ -115,7 +115,7 @@ task preprocessRelationTest(type: JavaExec, group: 'verification') {
     classpath = sourceSets.main.runtimeClasspath
     main = 'org.jastadd.relast.compiler.Compiler'
     //noinspection GroovyAssignabilityCheck
-    args 'src/test/jastadd/relations/Relations.relast', '--file', '--grammarName=src/test/jastadd/relations/Relations'
+    args 'src/test/jastadd/relations/Relations.relast', '--useJastAddNames', '--file', '--grammarName=src/test/jastadd/relations/Relations'
 }
 
 task doublePreprocessRelationTest(type: JavaExec, group: 'verification') {
@@ -127,7 +127,7 @@ task doublePreprocessRelationTest(type: JavaExec, group: 'verification') {
     classpath = sourceSets.main.runtimeClasspath
     main = 'org.jastadd.relast.compiler.Compiler'
     //noinspection GroovyAssignabilityCheck
-    args 'src/test/jastadd/relations/Relations.ast', '--file', '--grammarName=src/test/jastadd/relations/Relations2'
+    args 'src/test/jastadd/relations/Relations.ast', '--useJastAddNames', '--file', '--grammarName=src/test/jastadd/relations/Relations2'
 }
 
 task compileRelationTest(type: JavaExec, group: 'verification') {
@@ -145,6 +145,59 @@ task compileRelationTest(type: JavaExec, group: 'verification') {
             'src/test/jastadd/Utils.jadd'
 }
 
+task preprocessDefaultNamesTest(type: JavaExec, group: 'verification') {
+
+    doFirst {
+        delete 'src/test/jastadd/relations/Relations.ast', 'src/test/jastadd/relations/Relations.jadd'
+    }
+
+    classpath = sourceSets.main.runtimeClasspath
+    main = 'org.jastadd.relast.compiler.Compiler'
+    args 'src/test/jastadd/relations/Relations.relast', '--file', '--grammarName=src/test/jastadd/relations/Relations'
+}
+
+task compileDefaultNamesTest(type: JavaExec, group: 'verification') {
+
+    doFirst {
+        delete 'src/test/java-gen/relations'
+    }
+
+    classpath = sourceSets.main.runtimeClasspath
+    main = 'org.jastadd.JastAdd'
+    args '--o=src/test/java-gen/', '--package=defaultnames.ast', 'src/test/jastadd/relations/Relations.ast', 'src/test/jastadd/relations/Relations.jadd', 'src/test/jastadd/Utils.jadd'
+}
+
+task preprocessDefaultNamesResolverTest(type: JavaExec, group: 'verification') {
+
+    doFirst {
+        delete 'src/test/jastadd/resolver/Resolver.ast', 'src/test/jastadd/resolver/Resolver.jadd', 'src/test/jastadd/resolver/ResolverRefResolver.jadd'
+    }
+
+    classpath = sourceSets.main.runtimeClasspath
+    main = 'org.jastadd.relast.compiler.Compiler'
+    args 'src/test/jastadd/resolver/Resolver.relast', '--file', '--grammarName=src/test/jastadd/resolver/Resolver', '--resolverHelper'
+}
+
+task compileDefaultNamesResolverTest(type: JavaExec, group: 'verification') {
+
+    doFirst {
+        delete 'src/test/java-gen/resolver'
+    }
+
+    classpath = sourceSets.main.runtimeClasspath
+    main = 'org.jastadd.JastAdd'
+    //noinspection GroovyAssignabilityCheck
+    args '--o=src/test/java-gen/',
+            '--package=defaultnames.resolver.ast',
+            'src/test/jastadd/resolver/Resolver.ast',
+            'src/test/jastadd/resolver/Resolver.jadd',
+            'src/test/jastadd/resolver/ResolverUtils.jadd',
+            'src/test/jastadd/resolver/ResolverResolverStubs.jrag',
+            'src/test/jastadd/resolver/ResolverRefResolver.jadd',
+            'src/test/jastadd/resolver/MyRefResolver.jadd',
+            'src/test/jastadd/Utils.jadd'
+}
+
 task preprocessLowerBoundsTest(type: JavaExec, group: 'verification') {
 
     doFirst {
@@ -154,7 +207,7 @@ task preprocessLowerBoundsTest(type: JavaExec, group: 'verification') {
     classpath = sourceSets.main.runtimeClasspath
     main = 'org.jastadd.relast.compiler.Compiler'
     //noinspection GroovyAssignabilityCheck
-    args 'src/test/jastadd/lowerbounds/LowerBounds.relast', '--file', '--grammarName=src/test/jastadd/lowerbounds/LowerBounds'
+    args 'src/test/jastadd/lowerbounds/LowerBounds.relast', '--useJastAddNames', '--file', '--grammarName=src/test/jastadd/lowerbounds/LowerBounds'
 }
 
 task compileLowerBoundsTest(type: JavaExec, group: 'verification') {
@@ -181,7 +234,7 @@ task preprocessMultipleTest(type: JavaExec, group: 'verification') {
     classpath = sourceSets.main.runtimeClasspath
     main = 'org.jastadd.relast.compiler.Compiler'
     //noinspection GroovyAssignabilityCheck
-    args 'src/test/jastadd/multiple/Part1.relast', 'src/test/jastadd/multiple/Part2.relast', 'src/test/jastadd/multiple/Part3.relast', '--file', '--grammarName=src/test/jastadd/multiple/Multiple'
+    args 'src/test/jastadd/multiple/Part1.relast', '--useJastAddNames', 'src/test/jastadd/multiple/Part2.relast', 'src/test/jastadd/multiple/Part3.relast', '--file', '--grammarName=src/test/jastadd/multiple/Multiple'
 }
 
 task compileMultipleTest(type: JavaExec, group: 'verification') {
@@ -208,7 +261,7 @@ task preprocessResolverTest(type: JavaExec, group: 'verification') {
     classpath = sourceSets.main.runtimeClasspath
     main = 'org.jastadd.relast.compiler.Compiler'
     //noinspection GroovyAssignabilityCheck
-    args 'src/test/jastadd/resolver/Resolver.relast', '--file', '--grammarName=src/test/jastadd/resolver/Resolver', '--resolverHelper'
+    args 'src/test/jastadd/resolver/Resolver.relast', '--useJastAddNames', '--file', '--grammarName=src/test/jastadd/resolver/Resolver', '--resolverHelper'
 }
 
 task compileResolverTest(type: JavaExec, group: 'verification') {
@@ -240,7 +293,7 @@ task preprocessResolver2Test(type: JavaExec, group: 'verification') {
     classpath = sourceSets.main.runtimeClasspath
     main = 'org.jastadd.relast.compiler.Compiler'
     //noinspection GroovyAssignabilityCheck
-    args 'src/test/jastadd/resolver2/Resolver.relast', '--file', '--grammarName=src/test/jastadd/resolver2/Resolver', '--resolverHelper'
+    args 'src/test/jastadd/resolver2/Resolver.relast', '--useJastAddNames', '--file', '--grammarName=src/test/jastadd/resolver2/Resolver', '--resolverHelper'
 }
 
 task compileResolver2Test(type: JavaExec, group: 'verification') {
@@ -271,7 +324,7 @@ task preprocessListNamesTest(type: JavaExec, group: 'verification') {
     classpath = sourceSets.main.runtimeClasspath
     main = 'org.jastadd.relast.compiler.Compiler'
     //noinspection GroovyAssignabilityCheck
-    args 'src/test/jastadd/listnames/ListNames.relast', '--file', '--grammarName=src/test/jastadd/listnames/ListNames', '--jastAddList=ListyMcListface'
+    args 'src/test/jastadd/listnames/ListNames.relast', '--useJastAddNames', '--file', '--grammarName=src/test/jastadd/listnames/ListNames', '--jastAddList=ListyMcListface'
 }
 
 task compileListNamesTest(type: JavaExec, group: 'verification') {
@@ -303,7 +356,7 @@ task preprocessSerializerTest(type: JavaExec, group: 'verification') {
     classpath = sourceSets.main.runtimeClasspath
     main = 'org.jastadd.relast.compiler.Compiler'
     //noinspection GroovyAssignabilityCheck
-    args 'src/test/jastadd/serializer/Serializer.relast', '--file', '--grammarName=src/test/jastadd/serializer/Serializer', '--serializer=jackson'
+    args 'src/test/jastadd/serializer/Serializer.relast', '--useJastAddNames', '--file', '--grammarName=src/test/jastadd/serializer/Serializer', '--serializer=jackson'
 }
 
 task compileSerializerTest(type: JavaExec, group: 'verification') {
@@ -334,6 +387,11 @@ test.dependsOn compileRelationTest
 compileRelationTest.dependsOn doublePreprocessRelationTest
 doublePreprocessRelationTest.dependsOn preprocessRelationTest
 
+test.dependsOn compileDefaultNamesTest
+compileDefaultNamesTest.dependsOn preprocessDefaultNamesTest
+test.dependsOn compileDefaultNamesResolverTest
+compileDefaultNamesResolverTest.dependsOn preprocessDefaultNamesResolverTest
+
 test.dependsOn compileLowerBoundsTest
 compileLowerBoundsTest.dependsOn preprocessLowerBoundsTest
 
@@ -348,6 +406,5 @@ compileResolver2Test.dependsOn preprocessResolver2Test
 
 test.dependsOn compileListNamesTest
 compileListNamesTest.dependsOn preprocessListNamesTest
-
 test.dependsOn compileSerializerTest
-compileSerializerTest.dependsOn preprocessSerializerTest
\ No newline at end of file
+compileSerializerTest.dependsOn preprocessSerializerTest
diff --git a/src/main/jastadd/Backend.jadd b/src/main/jastadd/Backend.jadd
index f4f43224957206f7e4949fb5d01850a11ff50c8b..3e294aaf9f80c0b4a61478ef01901083f2835592 100644
--- a/src/main/jastadd/Backend.jadd
+++ b/src/main/jastadd/Backend.jadd
@@ -5,6 +5,7 @@ aspect BackendAbstractGrammar {
 
   public static boolean ASTNode.resolverHelper = false;
   public static boolean ASTNode.serializer = false;
+  public static boolean ASTNode.useJastAddNames = false;
 
   public String Program.generateAbstractGrammar() {
     StringBuilder sb = new StringBuilder();
@@ -191,15 +192,19 @@ aspect BackendDirectedAPI {
 
   public void ManyRelationComponent.generateDirectedAPI(StringBuilder sb) {
     // Get
-    // getXs
-    sb.append(ind(1) + "public java.util.List<" + ofTypeDecl() + "> " + toTypeDecl());
-    sb.append(".get" + nameCapitalized() + "s() {\n");
-    sb.append(ind(2) + "return get" + nameCapitalized() + "List();\n");
-    sb.append(ind(1) + "}\n");
+    sb.append(ind(1) + "public java.util.List<" + ofTypeDecl() + "> " + toTypeDecl() + ".");
+    if (useJastAddNames) {
+      // getXs
+      sb.append("get" + nameCapitalized() + "s() {\n");
+      sb.append(ind(2) + "return get" + nameCapitalized() + "List();\n");
+      sb.append(ind(1) + "}\n");
 
-    // getXList
-    sb.append(ind(1) + "public java.util.List<" + ofTypeDecl() + "> " + toTypeDecl());
-    sb.append(".get" + nameCapitalized() + "List() {\n");
+      // getXList
+      sb.append(ind(1) + "public java.util.List<" + ofTypeDecl() + "> " + toTypeDecl());
+      sb.append(".get" + nameCapitalized() + "List() {\n");
+    } else {
+      sb.append(name() + "() {\n");
+    }
     sb.append(ind(2) + ASTNode.listClass + "<" + ofTypeDecl() + "> l = get" + getImplAttributeName() + "();\n");
     // resolve the entire list
     if (resolverHelper | serializer) {
@@ -223,6 +228,9 @@ aspect BackendDirectedAPI {
 
     // Add
     sb.append(ind(1) + "public void " + toTypeDecl() + ".add");
+    if (!useJastAddNames) {
+      sb.append("To");
+    }
     sb.append(nameCapitalized() + "(" + ofTypeDecl() + " o) {\n");
     sb.append(ind(2) + "assertNotNull(o);\n");
     sb.append(ind(2) + ASTNode.listClass + "<" + ofTypeDecl() + "> list = get" + getImplAttributeName() + "();\n");
@@ -234,7 +242,11 @@ aspect BackendDirectedAPI {
     sb.append(ind(1) + "}\n");
 
     // Insert / add at specific position
-    sb.append(ind(1) + "public void " + toTypeDecl() + ".add" + nameCapitalized() + "(int index, " + ofTypeDecl() + " o) {\n");
+    sb.append(ind(1) + "public void " + toTypeDecl() + ".add");
+    if (!useJastAddNames) {
+      sb.append("To");
+    }
+    sb.append(nameCapitalized() + "(int index, " + ofTypeDecl() + " o) {\n");
     sb.append(ind(2) + "assertNotNull(o);\n");
     sb.append(ind(2) + ASTNode.listClass + "<" + ofTypeDecl() + "> list = get" + getImplAttributeName() + "();\n");
     sb.append(ind(2) + "if (list == null) {\n");
@@ -246,6 +258,9 @@ aspect BackendDirectedAPI {
 
     // Remove
     sb.append(ind(1) + "public void " + toTypeDecl() + ".remove");
+    if (!useJastAddNames) {
+      sb.append("From");
+    }
     sb.append(nameCapitalized() + "(" + ofTypeDecl() + " o) {\n");
     sb.append(ind(2) + "assertNotNull(o);\n");
     sb.append(ind(2) + ASTNode.listClass + "<" + ofTypeDecl() + "> list = get" + getImplAttributeName() + "();\n");
@@ -256,8 +271,13 @@ aspect BackendDirectedAPI {
   }
 
   public void RelationComponent.generateGetOne(StringBuilder sb) {
-    sb.append(ind(1) + "public " + ofTypeDecl() + " " + toTypeDecl());
-    sb.append(".get" + nameCapitalized() + "() {\n");
+    sb.append(ind(1) + "public " + ofTypeDecl() + " " + toTypeDecl() + ".");
+    if (useJastAddNames) {
+      sb.append("get" + nameCapitalized());
+    } else {
+      sb.append(name());
+    }
+    sb.append("() {\n");
     if (resolverHelper | serializer) {
       sb.append(ind(2) + "if (get" + getImplAttributeName() + "() != null && get" + getImplAttributeName() + "().unresolved()) {\n");
         sb.append(ind(3) + "if (get" + getImplAttributeName() + "().asUnresolved().get__resolve_opposite()) {\n");
@@ -275,7 +295,13 @@ aspect BackendDirectedAPI {
     // has
     sb.append(ind(1) + "public boolean " + toTypeDecl());
     sb.append(".has" + nameCapitalized() + "() {\n");
-    sb.append(ind(2) + "return get" + nameCapitalized() + "() != null;\n");
+    sb.append(ind(2) + "return ");
+    if (useJastAddNames) {
+      sb.append("get" + nameCapitalized());
+    } else {
+      sb.append(name());
+    }
+    sb.append("() != null;\n");
     sb.append(ind(1) + "}\n");
 
     // clear
@@ -379,15 +405,19 @@ aspect BackendBidirectionalAPI {
 
   public void RelationComponent.generateBiManyMany(StringBuilder sb, RelationComponent opposite) {
     // Get
-    // getXs
-    sb.append(ind(1) + "public java.util.List<" + ofTypeDecl() + "> " + toTypeDecl());
-    sb.append(".get" + nameCapitalized() + "s() {\n");
-    sb.append(ind(2) + "return get" + nameCapitalized() + "List();\n");
-    sb.append(ind(1) + "}\n");
+    sb.append(ind(1) + "public java.util.List<" + ofTypeDecl() + "> " + toTypeDecl() + ".");
+    if (useJastAddNames) {
+      // getXs
+      sb.append("get" + nameCapitalized() + "s() {\n");
+      sb.append(ind(2) + "return get" + nameCapitalized() + "List();\n");
+      sb.append(ind(1) + "}\n");
 
-    // getXList
-    sb.append(ind(1) + "public java.util.List<" + ofTypeDecl() + "> " + toTypeDecl());
-    sb.append(".get" + nameCapitalized() + "List() {\n");
+      // getXList
+      sb.append(ind(1) + "public java.util.List<" + ofTypeDecl() + "> " + toTypeDecl());
+      sb.append(".get" + nameCapitalized() + "List() {\n");
+    } else {
+      sb.append(name() + "() {\n");
+    }
     sb.append(ind(2) + ASTNode.listClass + "<" + ofTypeDecl() + "> l = get" + getImplAttributeName() + "();\n");
     // resolve the entire list
     if (resolverHelper | serializer) {
@@ -419,6 +449,9 @@ aspect BackendBidirectionalAPI {
 
     // Add
     sb.append(ind(1) + "public void " + toTypeDecl() + ".add");
+    if (!useJastAddNames) {
+      sb.append("To");
+    }
     sb.append(nameCapitalized() + "(" + ofTypeDecl() + " o) {\n");
     sb.append(ind(2) + "assertNotNull(o);\n");
     sb.append(ind(2) + ASTNode.listClass + "<" + ofTypeDecl() + "> list = get" + getImplAttributeName() + "();\n");
@@ -437,7 +470,11 @@ aspect BackendBidirectionalAPI {
     sb.append(ind(1) + "}\n");
 
     // Insert / add at specific position
-    sb.append(ind(1) + "public void " + toTypeDecl() + ".add" + nameCapitalized() + "(int index, " + ofTypeDecl() + " o) {\n");
+    sb.append(ind(1) + "public void " + toTypeDecl() + ".add");
+    if (!useJastAddNames) {
+      sb.append("To");
+    }
+    sb.append(nameCapitalized() + "(int index, " + ofTypeDecl() + " o) {\n");
     sb.append(ind(2) + "assertNotNull(o);\n");
     sb.append(ind(2) + ASTNode.listClass + "<" + ofTypeDecl() + "> list = get" + getImplAttributeName() + "();\n");
     sb.append(ind(2) + "if (list == null) {\n");
@@ -456,6 +493,9 @@ aspect BackendBidirectionalAPI {
 
     // Remove
     sb.append(ind(1) + "public void " + toTypeDecl() + ".remove");
+    if (!useJastAddNames) {
+      sb.append("From");
+    }
     sb.append(nameCapitalized() + "(" + ofTypeDecl() + " o) {\n");
     sb.append(ind(2) + "assertNotNull(o);\n");
     sb.append(ind(2) + ASTNode.listClass + "<" + ofTypeDecl() + "> list = get" + getImplAttributeName() + "();\n");
@@ -472,15 +512,19 @@ aspect BackendBidirectionalAPI {
 
   public void RelationComponent.generateBiManyOne(StringBuilder sb, RelationComponent opposite) {
     // Get
-    // getXs
-    sb.append(ind(1) + "public java.util.List<" + ofTypeDecl() + "> " + toTypeDecl());
-    sb.append(".get" + nameCapitalized() + "s() {\n");
-    sb.append(ind(2) + "return get" + nameCapitalized() + "List();\n");
-    sb.append(ind(1) + "}\n");
+    sb.append(ind(1) + "public java.util.List<" + ofTypeDecl() + "> " + toTypeDecl() + ".");
+    if (useJastAddNames) {
+      // getXs
+      sb.append("get" + nameCapitalized() + "s() {\n");
+      sb.append(ind(2) + "return get" + nameCapitalized() + "List();\n");
+      sb.append(ind(1) + "}\n");
 
-    // getXList
-    sb.append(ind(1) + "public java.util.List<" + ofTypeDecl() + "> " + toTypeDecl());
-    sb.append(".get" + nameCapitalized() + "List() {\n");
+      // getXList
+      sb.append(ind(1) + "public java.util.List<" + ofTypeDecl() + "> " + toTypeDecl());
+      sb.append(".get" + nameCapitalized() + "List() {\n");
+    } else {
+      sb.append(name() + "() {\n");
+    }
     sb.append(ind(2) + ASTNode.listClass + "<" + ofTypeDecl() + "> l = get" + getImplAttributeName() + "();\n");
     // resolve the entire list
     if (resolverHelper | serializer) {
@@ -518,6 +562,9 @@ aspect BackendBidirectionalAPI {
 
     // Add
     sb.append(ind(1) + "public void " + toTypeDecl() + ".add");
+    if (!useJastAddNames) {
+      sb.append("To");
+    }
     sb.append(nameCapitalized() + "(" + ofTypeDecl() + " o) {\n");
     sb.append(ind(2) + "assertNotNull(o);\n");
     sb.append(ind(2) + "if (o != null && o.get" + otherSide().getImplAttributeName() + "() != null) {\n");
@@ -537,7 +584,11 @@ aspect BackendBidirectionalAPI {
     sb.append(ind(1) + "}\n");
 
     // Insert / add at specific position
-    sb.append(ind(1) + "public void " + toTypeDecl() + ".add" + nameCapitalized() + "(int index, " + ofTypeDecl() + " o) {\n");
+    sb.append(ind(1) + "public void " + toTypeDecl() + ".add");
+    if (!useJastAddNames) {
+      sb.append("To");
+    }
+    sb.append(nameCapitalized() + "(int index, " + ofTypeDecl() + " o) {\n");
       sb.append(ind(2) + "assertNotNull(o);\n");
       sb.append(ind(2) + "if (o != null && o.get" + otherSide().getImplAttributeName() + "() != null) {\n");
         sb.append(ind(3) + ASTNode.listClass + "<" + ofTypeDecl() + "> list2 = o.get" + otherSide().getImplAttributeName() + "().get" + getImplAttributeName() + "();\n");
@@ -555,6 +606,9 @@ aspect BackendBidirectionalAPI {
 
     // Remove
     sb.append(ind(1) + "public void " + toTypeDecl() + ".remove");
+    if (!useJastAddNames) {
+      sb.append("From");
+    }
     sb.append(nameCapitalized() + "(" + ofTypeDecl() + " o) {\n");
     sb.append(ind(2) + "assertNotNull(o);\n");
     sb.append(ind(2) + ASTNode.listClass + "<" + ofTypeDecl() + "> list = get" + getImplAttributeName() + "();\n");
@@ -649,7 +703,13 @@ aspect LowerBoundCheck {
   }
 
   public void OneRelationComponent.generateLowerBoundCheck(StringBuilder sb) {
-    sb.append(ind(2) + "if (get" + nameCapitalized() + "() == null) {\n");
+    sb.append(ind(2) + "if (");
+    if (useJastAddNames) {
+      sb.append("get" + nameCapitalized());
+    } else {
+      sb.append(name());
+    }
+    sb.append("() == null) {\n");
     sb.append(ind(3) + "list.add(new Pair<>(this, \"" + name() + "\"));\n");
     sb.append(ind(2) + "}\n");
   }
@@ -880,7 +940,13 @@ aspect NameResolutionHelper {
     sb.append(ind(1) + "// enforce resolving of all non-containment relations of the current non-terminal\n");
     sb.append(ind(1) + "public void " + getID() + ".resolveAll() {\n");
     for (RelationComponent relationComponent : relationComponents()) {
-      sb.append(ind(2) + "get" + relationComponent.nameCapitalized() + (relationComponent.isMany()?"List":"") + "();\n");
+      sb.append(ind(2));
+      if (useJastAddNames) {
+        sb.append("get" + relationComponent.nameCapitalized());
+      } else {
+        sb.append(relationComponent.name());
+      }
+      sb.append(relationComponent.isMany() && useJastAddNames ? "List" : "").append("();\n");
     }
       sb.append(ind(2) + "super.resolveAll();\n");
     sb.append(ind(1) + "}\n");
diff --git a/src/main/java/org/jastadd/relast/compiler/Compiler.java b/src/main/java/org/jastadd/relast/compiler/Compiler.java
index 533b30d02bcffc1949207d4e24fc19a1ac925189..93afb42b34eba9a1718af25658ecd2a414916d00 100644
--- a/src/main/java/org/jastadd/relast/compiler/Compiler.java
+++ b/src/main/java/org/jastadd/relast/compiler/Compiler.java
@@ -21,6 +21,7 @@ public class Compiler {
   private StringOption optionGrammarName;
   private EnumOption optionSerializer;
   private FlagOption optionResolverHelper;
+  private FlagOption optionUseJastaddNames;
   private CommandLine commandLine;
 
   public Compiler(String[] args) throws CommandLineException {
@@ -80,6 +81,11 @@ public class Compiler {
         ASTNode.listClass = optionListClass.getValue();
       }
 
+      if (optionUseJastaddNames.isSet()) {
+        System.out.println("Using JastAdd names");
+        ASTNode.useJastAddNames = true;
+      }
+
       String grammarName = "Grammar";
       if (optionGrammarName.isSet()) {
         grammarName = optionGrammarName.getValue();
@@ -143,6 +149,7 @@ public class Compiler {
     optionGrammarName = addOption(new StringOption("grammarName", "name of the generated grammar and aspect (without file extension)"));
     optionResolverHelper = addOption(new FlagOption("resolverHelper", "create a subtype for each type containing a string that can be used to resolve the type later"));
     optionJastAddList = addOption(new StringOption("jastAddList", "set the name of the List type in JastAdd (has to match the option '--List' or its default List)"));
+    optionUseJastaddNames = addOption(new FlagOption("useJastAddNames", "generate names in the form of addX, removeX and setX. If omitted, the default, original naming scheme resulting in addToX, removeFromX and setX will be used."));
     optionSerializer = addOption(new EnumOption("serializer", "generate a (de-)serializer", Arrays.asList("jackson"), "jackson"));
 
   }
diff --git a/src/test/java/org/jastadd/relast/tests/DefaultNames.java b/src/test/java/org/jastadd/relast/tests/DefaultNames.java
new file mode 100644
index 0000000000000000000000000000000000000000..1335e1ef1ba7b7fabf340b716b0d7e9a87a64a3f
--- /dev/null
+++ b/src/test/java/org/jastadd/relast/tests/DefaultNames.java
@@ -0,0 +1,561 @@
+package org.jastadd.relast.tests;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import defaultnames.ast.A;
+import defaultnames.ast.B;
+import defaultnames.ast.Root;
+
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.util.Arrays;
+
+import static org.jastadd.relast.tests.TestHelpers.readFile;
+import static org.junit.jupiter.api.Assertions.*;
+
+
+@SuppressWarnings({"ArraysAsListWithZeroOrOneArgument", "Duplicates"})
+class DefaultNames {
+  private Root r;
+  private A a1;
+  private A a2;
+  private A a3;
+  private B b1;
+  private B b2;
+  private B b3;
+
+  @Test
+  void doubleRelastRun() throws IOException {
+
+    String firstRun = "./src/test/jastadd/relations/Relations.ast";
+    String secondRun = "./src/test/jastadd/relations/Relations2.ast";
+
+
+    String first = readFile(firstRun, Charset.defaultCharset());
+    String second = readFile(secondRun, Charset.defaultCharset());
+
+    Assertions.assertEquals(first, second);
+  }
+
+  /**
+   * rel A.Di1 -> B;
+   */
+  @Test
+  void testDi1() {
+    setup();
+    a1.setDi1(b2);
+    a2.setDi1(b1);
+
+    assertSame(a1.Di1(), b2);
+    assertSame(a2.Di1(), b1);
+
+    a2.setDi1(b2);
+
+    assertSame(a1.Di1(), b2);
+    assertSame(a2.Di1(), b2);
+
+    try {
+      a3.setDi1(null);
+      fail("should throw an exception");
+    } catch (Exception e) {
+      // OK
+    }
+  }
+
+
+  /**
+   * rel A.Di2? -> B;
+   */
+  @Test
+  void testDi2() {
+    setup();
+    a1.setDi2(b2);
+    a2.setDi2(b1);
+
+    assertSame(a1.Di2(), b2);
+    assertSame(a2.Di2(), b1);
+
+    a2.setDi2(b2);
+
+    assertSame(a1.Di2(), b2);
+    assertSame(a2.Di2(), b2);
+
+    a2.clearDi2();
+
+    assertSame(a1.Di2(), b2);
+    assertNull(a2.Di2());
+
+    assertTrue(a1.hasDi2());
+    assertFalse(a2.hasDi2());
+    assertFalse(a3.hasDi2());
+  }
+
+
+  /**
+   * rel A.Di3* -> B;
+   */
+  @Test
+  void testDi3() {
+    setup();
+    a1.addToDi3(b1);
+    a1.addToDi3(b2);
+    a1.addToDi3(b3);
+    a2.addToDi3(b2);
+
+    assertEquals(a1.Di3(), Arrays.asList(b1, b2, b3));
+    assertEquals(a2.Di3(), Arrays.asList(b2));
+    assertEquals(a3.Di3(), Arrays.asList());
+
+    a1.addToDi3(b1);
+    a2.addToDi3(b1);
+    a2.addToDi3(b2);
+
+    assertEquals(a1.Di3(), Arrays.asList(b1, b2, b3, b1));
+    assertEquals(a2.Di3(), Arrays.asList(b2, b1, b2));
+    assertEquals(a3.Di3(), Arrays.asList());
+
+    a1.removeFromDi3(b1);
+    a2.removeFromDi3(b2);
+
+    assertEquals(a1.Di3(), Arrays.asList(b2, b3, b1));
+    assertEquals(a2.Di3(), Arrays.asList(b1, b2));
+    assertEquals(a3.Di3(), Arrays.asList());
+  }
+
+
+  /**
+   * rel A.Bi1 <-> B.Bi1;
+   */
+
+
+  @Test
+  void testBi11() {
+    // Init
+    setup();
+    a1.setBi1(b1);
+    a2.setBi1(b2);
+
+    // Change
+    a2.setBi1(b1);
+
+    assertNull(a1.Bi1());
+    assertSame(a2.Bi1(), b1);
+    assertSame(b1.Bi1(), a2);
+    assertNull(b2.Bi1());
+  }
+
+  @Test
+  void testBi12() {
+    // Init
+    setup();
+    a1.setBi1(b2);
+
+    // Change
+    a2.setBi1(b2);
+
+    assertNull(a1.Bi1());
+    assertSame(a2.Bi1(), b2);
+    assertNull(b1.Bi1());
+    assertSame(b2.Bi1(), a2);
+  }
+
+
+  /**
+   * rel A.Bi2 <-> B.Bi2?;
+   */
+
+  @Test
+  void testBi21() {
+    // Init
+    setup();
+    a1.setBi2(b1);
+    a2.setBi2(b2);
+
+    // Change
+    a2.setBi2(b1);
+
+    assertNull(a1.Bi2());
+    assertSame(a2.Bi2(), b1);
+    assertSame(b1.Bi2(), a2);
+    assertNull(b2.Bi2());
+  }
+
+  @Test
+  void testBi22() {
+    // Init
+    setup();
+    a1.setBi2(b2);
+
+    // Change
+    a2.setBi2(b2);
+
+    assertNull(a1.Bi2());
+    assertSame(a2.Bi2(), b2);
+    assertNull(b1.Bi2());
+    assertSame(b2.Bi2(), a2);
+  }
+
+
+  /**
+   * rel A.Bi3 <-> B.Bi3*;
+   */
+  @Test
+  void testBi3() {
+    setup();
+    a2.setBi3(b2);
+
+    assertNull(a1.Bi3());
+    assertSame(a2.Bi3(), b2);
+    assertEquals(b1.Bi3(), Arrays.asList());
+    assertEquals(b2.Bi3(), Arrays.asList(a2));
+    assertEquals(b3.Bi3(), Arrays.asList());
+
+    a2.setBi3(b3);
+
+    assertNull(a1.Bi3());
+    assertSame(a2.Bi3(), b3);
+    assertEquals(b1.Bi3(), Arrays.asList());
+    assertEquals(b2.Bi3(), Arrays.asList());
+    assertEquals(b3.Bi3(), Arrays.asList(a2));
+
+    a1.setBi3(b3);
+    a3.setBi3(b3);
+
+    assertSame(a1.Bi3(), b3);
+    assertSame(a2.Bi3(), b3);
+    assertSame(a3.Bi3(), b3);
+    assertEquals(b1.Bi3(), Arrays.asList());
+    assertEquals(b2.Bi3(), Arrays.asList());
+    assertEquals(b3.Bi3(), Arrays.asList(a2, a1, a3));
+
+    a2.setBi3(b1);
+
+    assertSame(a1.Bi3(), b3);
+    assertSame(a2.Bi3(), b1);
+    assertSame(a3.Bi3(), b3);
+    assertEquals(b1.Bi3(), Arrays.asList(a2));
+    assertEquals(b2.Bi3(), Arrays.asList());
+    assertEquals(b3.Bi3(), Arrays.asList(a1, a3));
+
+    try {
+      a2.setBi3(null);
+      fail("should throw an exception");
+    } catch (Exception e) {
+      // OK
+    }
+  }
+
+
+  /**
+   * rel A.Bi4? <-> B.Bi4;
+   */
+  @Test
+  void testBi41() {
+    // Init
+    setup();
+    a1.setBi4(b1);
+    a2.setBi4(b2);
+
+    // Change
+    a2.setBi4(b1);
+
+    assertNull(a1.Bi4());
+    assertSame(a2.Bi4(), b1);
+    assertSame(b1.Bi4(), a2);
+    assertNull(b2.Bi4());
+  }
+
+  @Test
+  void testBi42() {
+    // Init
+    setup();
+    a1.setBi4(b2);
+
+    // Change
+    a2.setBi4(b2);
+
+    assertNull(a1.Bi4());
+    assertSame(a2.Bi4(), b2);
+    assertNull(b1.Bi4());
+    assertSame(b2.Bi4(), a2);
+  }
+
+
+  /**
+   * rel A.Bi5? <-> B.Bi5?;
+   */
+  @Test
+  void testBi51() {
+    // Init
+    setup();
+    a1.setBi5(b1);
+    a2.setBi5(b2);
+
+    // Change
+    a2.setBi5(b1);
+
+    assertNull(a1.Bi5());
+    assertSame(a2.Bi5(), b1);
+    assertSame(b1.Bi5(), a2);
+    assertNull(b2.Bi5());
+  }
+
+  @Test
+  void testBi52() {
+    // Init
+    setup();
+    a1.setBi5(b2);
+
+    // Change
+    a2.setBi5(b2);
+
+    assertNull(a1.Bi5());
+    assertSame(a2.Bi5(), b2);
+    assertNull(b1.Bi5());
+    assertSame(b2.Bi5(), a2);
+  }
+
+
+  /**
+   * rel A.Bi6? <-> B.Bi6*;
+   */
+  @Test
+  void testBi6() {
+    setup();
+    a2.setBi6(b2);
+
+    assertNull(a1.Bi6());
+    assertSame(a2.Bi6(), b2);
+    assertEquals(b1.Bi6(), Arrays.asList());
+    assertEquals(b2.Bi6(), Arrays.asList(a2));
+    assertEquals(b3.Bi6(), Arrays.asList());
+
+    a2.setBi6(b3);
+
+    assertNull(a1.Bi6());
+    assertSame(a2.Bi6(), b3);
+    assertEquals(b1.Bi6(), Arrays.asList());
+    assertEquals(b2.Bi6(), Arrays.asList());
+    assertEquals(b3.Bi6(), Arrays.asList(a2));
+
+    a1.setBi6(b3);
+    a3.setBi6(b3);
+
+    assertSame(a1.Bi6(), b3);
+    assertSame(a2.Bi6(), b3);
+    assertSame(a3.Bi6(), b3);
+    assertEquals(b1.Bi6(), Arrays.asList());
+    assertEquals(b2.Bi6(), Arrays.asList());
+    assertEquals(b3.Bi6(), Arrays.asList(a2, a1, a3));
+
+    a2.setBi6(b1);
+
+    assertSame(a1.Bi6(), b3);
+    assertSame(a2.Bi6(), b1);
+    assertSame(a3.Bi6(), b3);
+    assertEquals(b1.Bi6(), Arrays.asList(a2));
+    assertEquals(b2.Bi6(), Arrays.asList());
+    assertEquals(b3.Bi6(), Arrays.asList(a1, a3));
+
+    a2.clearBi6();
+
+    assertSame(a1.Bi6(), b3);
+    assertNull(a2.Bi6());
+    assertSame(a3.Bi6(), b3);
+    assertEquals(b1.Bi6(), Arrays.asList());
+    assertEquals(b2.Bi6(), Arrays.asList());
+    assertEquals(b3.Bi6(), Arrays.asList(a1, a3));
+
+    assertTrue(a1.hasBi6());
+    assertFalse(a2.hasBi6());
+    assertTrue(a3.hasBi6());
+  }
+
+
+  /**
+   * rel A.Bi7* <-> B.Bi7;
+   */
+  @Test
+  void testBi7() {
+    setup();
+    a2.addToBi7(b2);
+
+    assertEquals(a1.Bi7(), Arrays.asList());
+    assertEquals(a2.Bi7(), Arrays.asList(b2));
+    assertNull(b1.Bi7());
+    assertSame(b2.Bi7(), a2);
+    assertNull(b3.Bi7());
+
+    a2.addToBi7(b3);
+    a1.addToBi7(b2);
+
+    assertEquals(a1.Bi7(), Arrays.asList(b2));
+    assertEquals(a2.Bi7(), Arrays.asList(b3));
+    assertNull(b1.Bi7());
+    assertSame(b2.Bi7(), a1);
+    assertSame(b3.Bi7(), a2);
+
+    a1.addToBi7(b1);
+
+    assertEquals(a1.Bi7(), Arrays.asList(b2, b1));
+    assertEquals(a2.Bi7(), Arrays.asList(b3));
+    assertSame(b1.Bi7(), a1);
+    assertSame(b2.Bi7(), a1);
+    assertSame(b3.Bi7(), a2);
+
+    a1.addToBi7(b1);
+
+    assertEquals(a1.Bi7(), Arrays.asList(b2, b1));
+    assertEquals(a2.Bi7(), Arrays.asList(b3));
+    assertSame(b1.Bi7(), a1);
+    assertSame(b2.Bi7(), a1);
+    assertSame(b3.Bi7(), a2);
+
+    a1.removeFromBi7(b1);
+
+    assertEquals(a1.Bi7(), Arrays.asList(b2));
+    assertEquals(a2.Bi7(), Arrays.asList(b3));
+    assertNull(b1.Bi7());
+    assertSame(b2.Bi7(), a1);
+    assertSame(b3.Bi7(), a2);
+  }
+
+
+  /**
+   * rel A.Bi8* <-> B.Bi8?;
+   */
+  @Test
+  void testBi8() {
+    setup();
+    a2.addToBi8(b2);
+
+    assertEquals(a1.Bi8(), Arrays.asList());
+    assertEquals(a2.Bi8(), Arrays.asList(b2));
+    assertNull(b1.Bi8());
+    assertSame(b2.Bi8(), a2);
+    assertNull(b3.Bi8());
+
+    a2.addToBi8(b3);
+    a1.addToBi8(b2);
+
+    assertEquals(a1.Bi8(), Arrays.asList(b2));
+    assertEquals(a2.Bi8(), Arrays.asList(b3));
+    assertNull(b1.Bi8());
+    assertSame(b2.Bi8(), a1);
+    assertSame(b3.Bi8(), a2);
+
+    a1.addToBi8(b1);
+
+    assertEquals(a1.Bi8(), Arrays.asList(b2, b1));
+    assertEquals(a2.Bi8(), Arrays.asList(b3));
+    assertSame(b1.Bi8(), a1);
+    assertSame(b2.Bi8(), a1);
+    assertSame(b3.Bi8(), a2);
+
+    a1.addToBi8(b1);
+
+    assertEquals(a1.Bi8(), Arrays.asList(b2, b1));
+    assertEquals(a2.Bi8(), Arrays.asList(b3));
+    assertSame(b1.Bi8(), a1);
+    assertSame(b2.Bi8(), a1);
+    assertSame(b3.Bi8(), a2);
+
+    a1.removeFromBi8(b1);
+
+    assertEquals(a1.Bi8(), Arrays.asList(b2));
+    assertEquals(a2.Bi8(), Arrays.asList(b3));
+    assertNull(b1.Bi8());
+    assertSame(b2.Bi8(), a1);
+    assertSame(b3.Bi8(), a2);
+  }
+
+
+  /**
+   * rel A.Bi9* <-> B.Bi9*;
+   */
+  @Test
+  void testBi9() {
+    setup();
+    a1.addToBi9(b1);
+    a1.addToBi9(b2);
+
+    assertEquals(a1.Bi9(), Arrays.asList(b1, b2));
+    assertEquals(a2.Bi9(), Arrays.asList());
+    assertEquals(a3.Bi9(), Arrays.asList());
+    assertEquals(b1.Bi9(), Arrays.asList(a1));
+    assertEquals(b2.Bi9(), Arrays.asList(a1));
+    assertEquals(b3.Bi9(), Arrays.asList());
+
+    b3.addToBi9(a1);
+    b3.addToBi9(a3);
+    b3.addToBi9(a1);
+
+    assertEquals(a1.Bi9(), Arrays.asList(b1, b2, b3, b3));
+    assertEquals(a2.Bi9(), Arrays.asList());
+    assertEquals(a3.Bi9(), Arrays.asList(b3));
+    assertEquals(b1.Bi9(), Arrays.asList(a1));
+    assertEquals(b2.Bi9(), Arrays.asList(a1));
+    assertEquals(b3.Bi9(), Arrays.asList(a1, a3, a1));
+
+    b3.removeFromBi9(a1);
+
+    assertEquals(a1.Bi9(), Arrays.asList(b1, b2, b3));
+    assertEquals(a2.Bi9(), Arrays.asList());
+    assertEquals(a3.Bi9(), Arrays.asList(b3));
+    assertEquals(b1.Bi9(), Arrays.asList(a1));
+    assertEquals(b2.Bi9(), Arrays.asList(a1));
+    assertEquals(b3.Bi9(), Arrays.asList(a3, a1));
+  }
+
+
+  @Test
+  void testImmutableList() {
+    setup();
+
+    a1.addToDi3(b1);
+    a1.addToDi3(b2);
+    try {
+      a1.Di3().add(b3);
+      fail("should throw an exception");
+    } catch (Exception e) {
+      // OK
+    }
+
+    a1.addToBi7(b1);
+    a1.addToBi7(b2);
+    try {
+      a1.Bi7().add(b3);
+      fail("should throw an exception");
+    } catch (Exception e) {
+      // OK
+    }
+
+    a1.addToBi9(b1);
+    a1.addToBi9(b2);
+    try {
+      a1.Bi9().add(b3);
+      fail("should throw an exception");
+    } catch (Exception e) {
+      // OK
+    }
+  }
+
+  @BeforeEach
+  void setup() {
+    r = new Root();
+    a1 = new A("a1");
+    a2 = new A("a2");
+    a3 = new A("a3");
+    b1 = new B("b1");
+    b2 = new B("b2");
+    b3 = new B("b3");
+
+    r.addA(a1);
+    r.addA(a2);
+    r.addA(a3);
+    r.addB(b1);
+    r.addB(b2);
+    r.addB(b3);
+  }
+}
diff --git a/src/test/java/org/jastadd/relast/tests/DefaultNamesResolverHelper.java b/src/test/java/org/jastadd/relast/tests/DefaultNamesResolverHelper.java
new file mode 100644
index 0000000000000000000000000000000000000000..defcbfa826be16abe6533e61b115944c707516fc
--- /dev/null
+++ b/src/test/java/org/jastadd/relast/tests/DefaultNamesResolverHelper.java
@@ -0,0 +1,694 @@
+package org.jastadd.relast.tests;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import defaultnames.resolver.ast.A;
+import defaultnames.resolver.ast.B;
+import defaultnames.resolver.ast.NamedElement;
+import defaultnames.resolver.ast.Root;
+
+import java.util.Arrays;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+@SuppressWarnings("ArraysAsListWithZeroOrOneArgument")
+class DefaultNamesResolverHelper {
+  private Root r;
+  private A a1;
+  private A a2;
+  private A a3;
+  private B b1;
+  private B b2;
+  private B b3;
+
+
+  /**
+   * rel A.Di1 -> B;
+   */
+  @Test
+  void testNameRes1() {
+    setup();
+    a1.setRel1(NamedElement.createRef("b2"));
+    System.out.println("Rel 1 of a1 has type " + a1.Rel1().getClass().getSimpleName());
+    assertSame(a1.Rel1(), b2);
+  }
+
+  /**
+   * rel A.Di1 -> B;
+   */
+  @Test
+  void testDi1() {
+    setup();
+    a1.setDi1(B.createRef("b2"));
+    a2.setDi1(B.createRef("b1"));
+
+    assertSame(a1.Di1(), b2);
+    assertSame(a2.Di1(), b1);
+
+    a2.setDi1(b2);
+
+    assertSame(a1.Di1(), b2);
+    assertSame(a2.Di1(), b2);
+
+    try {
+      a3.setDi1(null);
+      fail("should throw an exception");
+    } catch (Exception e) {
+      // OK
+    }
+  }
+
+
+  /**
+   * rel A.Di2? -> B;
+   */
+  @Test
+  void testDi2() {
+    setup();
+    a1.setDi2(B.createRef("b2"));
+    a2.setDi2(B.createRef("b1"));
+
+    assertSame(a1.Di2(), b2);
+    assertSame(a2.Di2(), b1);
+
+    a2.setDi2(b2);
+
+    assertSame(a1.Di2(), b2);
+    assertSame(a2.Di2(), b2);
+
+    a2.clearDi2();
+
+    assertSame(a1.Di2(), b2);
+    assertNull(a2.Di2());
+
+    assertTrue(a1.hasDi2());
+    assertFalse(a2.hasDi2());
+    assertFalse(a3.hasDi2());
+  }
+
+
+  /**
+   * rel A.Di3* -> B;
+   */
+  @Test
+  void testDi3() {
+    setup();
+    a1.addToDi3(B.createRef("b1"));
+    a1.addToDi3(B.createRef("b2"));
+    a1.addToDi3(B.createRef("b3"));
+    a2.addToDi3(B.createRef("b2"));
+
+    assertEquals(a1.Di3(), Arrays.asList(b1, b2, b3));
+    assertEquals(a2.Di3(), Arrays.asList(b2));
+    assertEquals(a3.Di3(), Arrays.asList());
+
+    a1.addToDi3(B.createRef("b1"));
+    a2.addToDi3(B.createRef("b1"));
+    a2.addToDi3(B.createRef("b2"));
+
+    assertEquals(a1.Di3(), Arrays.asList(b1, b2, b3, b1));
+    assertEquals(a2.Di3(), Arrays.asList(b2, b1, b2));
+    assertEquals(a3.Di3(), Arrays.asList());
+
+    a1.removeFromDi3(b1);
+    a2.removeFromDi3(b2);
+
+    assertEquals(a1.Di3(), Arrays.asList(b2, b3, b1));
+    assertEquals(a2.Di3(), Arrays.asList(b1, b2));
+    assertEquals(a3.Di3(), Arrays.asList());
+  }
+
+
+  /**
+   * rel A.Bi1 <-> B.Bi1;
+   */
+
+  @Test
+  void testBi11() {
+    // Init
+    setup();
+    a1.setBi1(B.createRef("b1"));
+
+    // before a1.Bi1() is resolved, the opposite direction is null
+    assertNull(b1.Bi1());
+
+    // now, the relation is resolved, and thus the opposite direction is also set
+    assertSame(b1, a1.Bi1());
+    assertSame(a1, b1.Bi1());
+
+    // create another, unrelated relation, perform the same tests
+    a2.setBi1(B.createRef("b2"));
+    assertNull(b2.Bi1());
+    assertSame(b2, a2.Bi1());
+    assertSame(a2, b2.Bi1());
+
+    // change the relation of a2 to overwrite the existing one in a1<->b1
+    a2.setBi1(B.createRef("b1"));
+
+    // as long as no resolution took place, the old reference still exists
+    assertSame(b1, a1.Bi1());
+    assertSame(a1, b1.Bi1());
+
+    // now, we resolve a2->b1
+    assertSame(b1, a2.Bi1());
+
+    // now the final situation should be a2<->b1, a1->null, b2->null
+    assertSame(a2.Bi1(), b1);
+    assertSame(b1.Bi1(), a2);
+    assertNull(a1.Bi1());
+    assertNull(b2.Bi1());
+  }
+
+  @Test
+  void testBi12() {
+    // Init
+    setup();
+    a1.setBi1(B.createRef("b2"));
+
+    // Change
+    a2.setBi1(B.createRef("b2"));
+
+    // unresolved
+    assertNull(b2.Bi1());
+
+    assertSame(b2, a1.Bi1());
+    // a1 resolved
+    assertSame(a1, b2.Bi1());
+
+    // now resolve the change:
+    assertSame(b2, a2.Bi1());
+
+    // now finally, a1->null
+    assertNull(a1.Bi1());
+    assertSame(a2.Bi1(), b2);
+    assertNull(b1.Bi1());
+    assertSame(b2.Bi1(), a2);
+  }
+
+
+  /**
+   * rel A.Bi2 <-> B.Bi2?;
+   */
+
+  @Test
+  void testBi21() {
+    // Init
+    setup();
+    a1.setBi2(B.createRef("b1"));
+
+    // before a1.Bi2() is resolved, the opposite direction is null
+    assertNull(b1.Bi2());
+
+    // now, the relation is resolved, and thus the opposite direction is also set
+    assertSame(b1, a1.Bi2());
+    assertSame(a1, b1.Bi2());
+
+    // create another, unrelated relation, perform the same tests
+    a2.setBi2(B.createRef("b2"));
+    assertNull(b2.Bi2());
+    assertSame(b2, a2.Bi2());
+    assertSame(a2, b2.Bi2());
+
+    // change the relation of a2 to overwrite the existing one in a1<->b1
+    a2.setBi2(B.createRef("b1"));
+
+    // as long as no resolution took place, the old reference still exists
+    assertSame(b1, a1.Bi2());
+    assertSame(a1, b1.Bi2());
+
+    // now, we resolve a2->b1
+    assertSame(b1, a2.Bi2());
+
+    // now the final situation should be a2<->b1, a1->null, b2->null
+    assertSame(a2.Bi2(), b1);
+    assertSame(b1.Bi2(), a2);
+    assertNull(a1.Bi2());
+    assertNull(b2.Bi2());
+  }
+
+  @Test
+  void testBi22() {
+    // Init
+    setup();
+    a1.setBi2(B.createRef("b2"));
+
+    // Change
+    a2.setBi2(B.createRef("b2"));
+
+    // unresolved
+    assertNull(b2.Bi2());
+
+    assertSame(b2, a1.Bi2());
+    // a1 resolved
+    assertSame(a1, b2.Bi2());
+
+    // now resolve the change:
+    assertSame(b2, a2.Bi2());
+
+    // now finally, a1->null
+    assertNull(a1.Bi2());
+    assertSame(a2.Bi2(), b2);
+    assertNull(b1.Bi2());
+    assertSame(b2.Bi2(), a2);
+  }
+
+
+
+  /**
+   * rel A.Bi3 <-> B.Bi3*;
+   */
+  @Test
+  void testBi3() {
+    setup();
+    a2.setBi3(B.createRef("b2"));
+
+    assertNull(a1.Bi3());
+    assertSame(a2.Bi3(), b2);
+    assertEquals(b1.Bi3(), Arrays.asList());
+    assertEquals(b2.Bi3(), Arrays.asList(a2));
+    assertEquals(b3.Bi3(), Arrays.asList());
+
+    a2.setBi3(B.createRef("b3"));
+
+    assertNull(a1.Bi3());
+    assertSame(a2.Bi3(), b3);
+    assertEquals(b1.Bi3(), Arrays.asList());
+    assertEquals(b2.Bi3(), Arrays.asList());
+    assertEquals(b3.Bi3(), Arrays.asList(a2));
+
+    a1.setBi3(B.createRef("b3"));
+    a3.setBi3(B.createRef("b3"));
+
+    assertSame(a1.Bi3(), b3);
+    assertSame(a2.Bi3(), b3);
+    assertSame(a3.Bi3(), b3);
+    assertEquals(b1.Bi3(), Arrays.asList());
+    assertEquals(b2.Bi3(), Arrays.asList());
+    assertEquals(b3.Bi3(), Arrays.asList(a2, a1, a3));
+
+    a2.setBi3(B.createRef("b1"));
+
+    assertSame(a1.Bi3(), b3);
+    assertSame(a2.Bi3(), b1);
+    assertSame(a3.Bi3(), b3);
+    assertEquals(b1.Bi3(), Arrays.asList(a2));
+    assertEquals(b2.Bi3(), Arrays.asList());
+    assertEquals(b3.Bi3(), Arrays.asList(a1, a3));
+
+    try {
+      a2.setBi3(null);
+      fail("should throw an exception");
+    } catch (Exception e) {
+      // OK
+    }
+  }
+
+
+  /**
+   * rel A.Bi4? <-> B.Bi4;
+   */
+  @Test
+  void testBi41() {
+    // Init
+    setup();
+    a1.setBi4(B.createRef("b1"));
+
+    // before a1.Bi4() is resolved, the opposite direction is null
+    assertNull(b1.Bi4());
+
+    // now, the relation is resolved, and thus the opposite direction is also set
+    assertSame(b1, a1.Bi4());
+    assertSame(a1, b1.Bi4());
+
+    // create another, unrelated relation, perform the same tests
+    a2.setBi4(B.createRef("b2"));
+    assertNull(b2.Bi4());
+    assertSame(b2, a2.Bi4());
+    assertSame(a2, b2.Bi4());
+
+    // change the relation of a2 to overwrite the existing one in a1<->b1
+    a2.setBi4(B.createRef("b1"));
+
+    // as long as no resolution took place, the old reference still exists
+    assertSame(b1, a1.Bi4());
+    assertSame(a1, b1.Bi4());
+
+    // now, we resolve a2->b1
+    assertSame(b1, a2.Bi4());
+
+    // now the final situation should be a2<->b1, a1->null, b2->null
+    assertSame(a2.Bi4(), b1);
+    assertSame(b1.Bi4(), a2);
+    assertNull(a1.Bi4());
+    assertNull(b2.Bi4());
+  }
+
+  @Test
+  void testBi42() {
+    // Init
+    setup();
+    a1.setBi4(B.createRef("b2"));
+
+    // Change
+    a2.setBi4(B.createRef("b2"));
+
+    // unresolved
+    assertNull(b2.Bi4());
+
+    assertSame(b2, a1.Bi4());
+    // a1 resolved
+    assertSame(a1, b2.Bi4());
+
+    // now resolve the change:
+    assertSame(b2, a2.Bi4());
+
+    // now finally, a1->null
+    assertNull(a1.Bi4());
+    assertSame(a2.Bi4(), b2);
+    assertNull(b1.Bi4());
+    assertSame(b2.Bi4(), a2);
+  }
+
+
+  /**
+   * rel A.Bi5? <-> B.Bi5?;
+   */
+  @Test
+  void testBi51() {
+    // Init
+    setup();
+    a1.setBi5(B.createRef("b1"));
+
+    // before a1.Bi5() is resolved, the opposite direction is null
+    assertNull(b1.Bi5());
+
+    // now, the relation is resolved, and thus the opposite direction is also set
+    assertSame(b1, a1.Bi5());
+    assertSame(a1, b1.Bi5());
+
+    // create another, unrelated relation, perform the same tests
+    a2.setBi5(B.createRef("b2"));
+    assertNull(b2.Bi5());
+    assertSame(b2, a2.Bi5());
+    assertSame(a2, b2.Bi5());
+
+    // change the relation of a2 to overwrite the existing one in a1<->b1
+    a2.setBi5(B.createRef("b1"));
+
+    // as long as no resolution took place, the old reference still exists
+    assertSame(b1, a1.Bi5());
+    assertSame(a1, b1.Bi5());
+
+    // now, we resolve a2->b1
+    assertSame(b1, a2.Bi5());
+
+    // now the final situation should be a2<->b1, a1->null, b2->null
+    assertSame(a2.Bi5(), b1);
+    assertSame(b1.Bi5(), a2);
+    assertNull(a1.Bi5());
+    assertNull(b2.Bi5());
+  }
+
+  @Test
+  void testBi52() {
+    // Init
+    setup();
+    a1.setBi5(B.createRef("b2"));
+
+    // Change
+    a2.setBi5(B.createRef("b2"));
+
+    // unresolved
+    assertNull(b2.Bi5());
+
+    assertSame(b2, a1.Bi5());
+    // a1 resolved
+    assertSame(a1, b2.Bi5());
+
+    // now resolve the change:
+    assertSame(b2, a2.Bi5());
+
+    // now finally, a1->null
+    assertNull(a1.Bi5());
+    assertSame(a2.Bi5(), b2);
+    assertNull(b1.Bi5());
+    assertSame(b2.Bi5(), a2);
+  }
+
+
+  /**
+   * rel A.Bi6? <-> B.Bi6*;
+   */
+  @Test
+  void testBi6() {
+    setup();
+    a2.setBi6(B.createRef("b2"));
+
+    assertNull(a1.Bi6());
+    assertSame(a2.Bi6(), b2);
+    assertEquals(b1.Bi6(), Arrays.asList());
+    assertEquals(b2.Bi6(), Arrays.asList(a2));
+    assertEquals(b3.Bi6(), Arrays.asList());
+
+    a2.setBi6(B.createRef("b3"));
+
+    assertNull(a1.Bi6());
+    assertSame(a2.Bi6(), b3);
+    assertEquals(b1.Bi6(), Arrays.asList());
+    assertEquals(b2.Bi6(), Arrays.asList());
+    assertEquals(b3.Bi6(), Arrays.asList(a2));
+
+    a1.setBi6(B.createRef("b3"));
+    a3.setBi6(B.createRef("b3"));
+
+    assertSame(a1.Bi6(), b3);
+    assertSame(a2.Bi6(), b3);
+    assertSame(a3.Bi6(), b3);
+    assertEquals(b1.Bi6(), Arrays.asList());
+    assertEquals(b2.Bi6(), Arrays.asList());
+    assertEquals(b3.Bi6(), Arrays.asList(a2, a1, a3));
+
+    a2.setBi6(B.createRef("b1"));
+
+    assertSame(a1.Bi6(), b3);
+    assertSame(a2.Bi6(), b1);
+    assertSame(a3.Bi6(), b3);
+    assertEquals(b1.Bi6(), Arrays.asList(a2));
+    assertEquals(b2.Bi6(), Arrays.asList());
+    assertEquals(b3.Bi6(), Arrays.asList(a1, a3));
+
+    a2.clearBi6();
+
+    assertSame(a1.Bi6(), b3);
+    assertNull(a2.Bi6());
+    assertSame(a3.Bi6(), b3);
+    assertEquals(b1.Bi6(), Arrays.asList());
+    assertEquals(b2.Bi6(), Arrays.asList());
+    assertEquals(b3.Bi6(), Arrays.asList(a1, a3));
+
+    assertTrue(a1.hasBi6());
+    assertFalse(a2.hasBi6());
+    assertTrue(a3.hasBi6());
+  }
+
+
+  /**
+   * rel A.Bi7* <-> B.Bi7;
+   */
+  @Test
+  void testBi7() {
+    setup();
+    a2.addToBi7(B.createRef("b2"));
+
+    // a1 list is empty
+    assertEquals(a1.Bi7(), Arrays.asList());
+
+    // a2 list contains b2 (because resolution took place)
+    assertEquals(a2.Bi7(), Arrays.asList(b2));
+
+    // of all the bs, only b2 contains a back ref to a2
+    assertNull(b1.Bi7());
+    assertSame(b2.Bi7(), a2);
+    assertNull(b3.Bi7());
+
+    a2.addToBi7(B.createRef("b3"));
+    a1.addToBi7(B.createRef("b2"));
+
+    assertEquals(a1.Bi7(), Arrays.asList(b2));
+    assertEquals(a2.Bi7(), Arrays.asList(b3));
+    assertNull(b1.Bi7());
+    assertSame(b2.Bi7(), a1);
+    assertSame(b3.Bi7(), a2);
+
+    a1.addToBi7(B.createRef("b1"));
+
+    assertEquals(a1.Bi7(), Arrays.asList(b2, b1));
+    assertEquals(a2.Bi7(), Arrays.asList(b3));
+    assertSame(b1.Bi7(), a1);
+    assertSame(b2.Bi7(), a1);
+    assertSame(b3.Bi7(), a2);
+
+    a1.addToBi7(B.createRef("b1"));
+
+    assertEquals(a1.Bi7(), Arrays.asList(b2, b1));
+    assertEquals(a2.Bi7(), Arrays.asList(b3));
+    assertSame(b1.Bi7(), a1);
+    assertSame(b2.Bi7(), a1);
+    assertSame(b3.Bi7(), a2);
+
+    a1.removeFromBi7(b1);
+
+    assertEquals(a1.Bi7(), Arrays.asList(b2));
+    assertEquals(a2.Bi7(), Arrays.asList(b3));
+    assertNull(b1.Bi7());
+    assertSame(b2.Bi7(), a1);
+    assertSame(b3.Bi7(), a2);
+  }
+
+
+  /**
+   * rel A.Bi8* <-> B.Bi8?;
+   */
+  @Test
+  void testBi8() {
+    setup();
+    a2.addToBi8(B.createRef("b2"));
+
+    assertEquals(a1.Bi8(), Arrays.asList());
+    assertEquals(a2.Bi8(), Arrays.asList(b2));
+    assertNull(b1.Bi8());
+    assertSame(b2.Bi8(), a2);
+    assertNull(b3.Bi8());
+
+    a2.addToBi8(B.createRef("b3"));
+    a1.addToBi8(B.createRef("b2"));
+
+    assertEquals(a1.Bi8(), Arrays.asList(b2));
+    assertEquals(a2.Bi8(), Arrays.asList(b3));
+    assertNull(b1.Bi8());
+    assertSame(b2.Bi8(), a1);
+    assertSame(b3.Bi8(), a2);
+
+    a1.addToBi8(B.createRef("b1"));
+
+    assertEquals(a1.Bi8(), Arrays.asList(b2, b1));
+    assertEquals(a2.Bi8(), Arrays.asList(b3));
+    assertSame(b1.Bi8(), a1);
+    assertSame(b2.Bi8(), a1);
+    assertSame(b3.Bi8(), a2);
+
+    a1.addToBi8(B.createRef("b1"));
+
+    assertEquals(a1.Bi8(), Arrays.asList(b2, b1));
+    assertEquals(a2.Bi8(), Arrays.asList(b3));
+    assertSame(b1.Bi8(), a1);
+    assertSame(b2.Bi8(), a1);
+    assertSame(b3.Bi8(), a2);
+
+    a1.removeFromBi8(b1);
+
+    assertEquals(a1.Bi8(), Arrays.asList(b2));
+    assertEquals(a2.Bi8(), Arrays.asList(b3));
+    assertNull(b1.Bi8());
+    assertSame(b2.Bi8(), a1);
+    assertSame(b3.Bi8(), a2);
+  }
+
+
+  /**
+   * rel A.Bi9* <-> B.Bi9*;
+   */
+  @Test
+  void testBi9() {
+    setup();
+    a1.addToBi9(B.createRef("b1"));
+    a1.addToBi9(B.createRef("b2"));
+
+    assertEquals(a1.Bi9(), Arrays.asList(b1, b2));
+    assertEquals(a2.Bi9(), Arrays.asList());
+    assertEquals(a3.Bi9(), Arrays.asList());
+    assertEquals(b1.Bi9(), Arrays.asList(a1));
+    assertEquals(b2.Bi9(), Arrays.asList(a1));
+    assertEquals(b3.Bi9(), Arrays.asList());
+
+    b3.addToBi9(A.createRef("a1"));
+    b3.addToBi9(A.createRef("a3"));
+    b3.addToBi9(A.createRef("a1"));
+
+    // b3 is not resolved yet, so the as look like before
+    assertEquals(a1.Bi9(), Arrays.asList(b1, b2));
+    assertEquals(a2.Bi9(), Arrays.asList());
+    assertEquals(a3.Bi9(), Arrays.asList());
+
+    // let's resolve b3
+    assertEquals(b1.Bi9(), Arrays.asList(a1));
+    assertEquals(b2.Bi9(), Arrays.asList(a1));
+    assertEquals(b3.Bi9(), Arrays.asList(a1, a3, a1));
+
+    // now the as are updated
+    assertEquals(a1.Bi9(), Arrays.asList(b1, b2, b3, b3));
+    assertEquals(a2.Bi9(), Arrays.asList());
+    assertEquals(a3.Bi9(), Arrays.asList(b3));
+
+    b3.removeFromBi9(a1);
+
+    assertEquals(a1.Bi9(), Arrays.asList(b1, b2, b3));
+    assertEquals(a2.Bi9(), Arrays.asList());
+    assertEquals(a3.Bi9(), Arrays.asList(b3));
+    assertEquals(b1.Bi9(), Arrays.asList(a1));
+    assertEquals(b2.Bi9(), Arrays.asList(a1));
+    assertEquals(b3.Bi9(), Arrays.asList(a3, a1));
+  }
+
+
+  @Test
+  void testImmutableList() {
+    setup();
+
+    a1.addToDi3(B.createRef("b1"));
+    a1.addToDi3(B.createRef("b2"));
+    try {
+      a1.Di3().add(b3);
+      fail("should throw an exception");
+    } catch (Exception e) {
+      // OK
+    }
+
+    a1.addToBi7(B.createRef("b1"));
+    a1.addToBi7(B.createRef("b2"));
+    try {
+      a1.Bi7().add(B.createRef("b3"));
+      fail("should throw an exception");
+    } catch (Exception e) {
+      // OK
+    }
+
+    a1.addToBi9(B.createRef("b1"));
+    a1.addToBi9(B.createRef("b2"));
+    try {
+      a1.Bi9().add(B.createRef("b3"));
+      fail("should throw an exception");
+    } catch (Exception e) {
+      // OK
+    }
+  }
+
+  @BeforeEach
+  void setup() {
+    r = new Root();
+    a1 = new A("a1");
+    a2 = new A("a2");
+    a3 = new A("a3");
+    b1 = new B("b1");
+    b2 = new B("b2");
+    b3 = new B("b3");
+
+    r.addA(a1);
+    r.addA(a2);
+    r.addA(a3);
+    r.addB(b1);
+    r.addB(b2);
+    r.addB(b3);
+  }
+
+}