From 8e910ed0ae90598db35bee2e09c9cd67a3c2a84f Mon Sep 17 00:00:00 2001
From: rschoene <rene.schoene@tu-dresden.de>
Date: Fri, 25 Feb 2022 19:33:17 +0100
Subject: [PATCH] Null nodes have separate ids, and working ordered output.

- grammar order was not taken into account so far, but is now, see #7
- whenever a DumpNode is created, a counter is increased, so all null nodes get different ids, see #10
- some relations seem to point to null after this change, maybe resulting in new bugs, let's see
---
 dumpAst/src/main/jastadd/DumpAst.relast       |   2 +-
 .../src/main/jastadd/GeneratedNavigation.jrag | 224 ++++++++++++++++
 .../src/main/jastadd/GenerationBackend.jadd   | 250 ++++++++++++------
 dumpAst/src/main/jastadd/Navigation.jrag      |  25 --
 dumpAst/src/main/jastadd/Printing.jrag        |   9 +-
 .../st/jastadd/testDumper/TestDumperMain.java |  25 +-
 .../inf/st/jastadd/testDumper/TestSimple.java |  33 ++-
 .../inf/st/jastadd/testDumper/TestUtils.java  |   4 +-
 8 files changed, 448 insertions(+), 124 deletions(-)
 create mode 100644 dumpAst/src/main/jastadd/GeneratedNavigation.jrag

diff --git a/dumpAst/src/main/jastadd/DumpAst.relast b/dumpAst/src/main/jastadd/DumpAst.relast
index 2fad59a..3701605 100644
--- a/dumpAst/src/main/jastadd/DumpAst.relast
+++ b/dumpAst/src/main/jastadd/DumpAst.relast
@@ -35,7 +35,7 @@ DumpListRelation : DumpRelation ::= InnerDumpNode* ;
 // type of NTA
 InvisiblePath ::= InnerDumpNode* ;
 
-ClassAnalysisResult ::= AnalysedMethod* ;
+ClassAnalysisResult ::= ContainmentMethod:AnalysedMethod* OtherMethod:AnalysedMethod* ;
 abstract AnalysedMethod ::= <Method:java.lang.reflect.Method> <Name> ;
 
 abstract SingleChildMethod : AnalysedMethod ;
diff --git a/dumpAst/src/main/jastadd/GeneratedNavigation.jrag b/dumpAst/src/main/jastadd/GeneratedNavigation.jrag
new file mode 100644
index 0000000..be7166a
--- /dev/null
+++ b/dumpAst/src/main/jastadd/GeneratedNavigation.jrag
@@ -0,0 +1,224 @@
+aspect Navigation {
+
+  /** Tests if TokenMethod is a IntrinsicTokenMethod.
+  *  @return 'true' if this is a IntrinsicTokenMethod, otherwise 'false'
+  */
+  syn boolean TokenMethod.isIntrinsicTokenMethod() = false;
+  eq IntrinsicTokenMethod.isIntrinsicTokenMethod() = true;
+
+  /** Tests if TokenMethod is a AttributeMethod.
+  *  @return 'true' if this is a AttributeMethod, otherwise 'false'
+  */
+  syn boolean TokenMethod.isAttributeMethod() = false;
+  eq AttributeMethod.isAttributeMethod() = true;
+
+  /** Tests if AnalysedMethod is a SingleChildMethod.
+  *  @return 'true' if this is a SingleChildMethod, otherwise 'false'
+  */
+  syn boolean AnalysedMethod.isSingleChildMethod() = false;
+  eq SingleChildMethod.isSingleChildMethod() = true;
+
+  /** Tests if AnalysedMethod is a ListChildMethod.
+  *  @return 'true' if this is a ListChildMethod, otherwise 'false'
+  */
+  syn boolean AnalysedMethod.isListChildMethod() = false;
+  eq ListChildMethod.isListChildMethod() = true;
+
+  /** Tests if AnalysedMethod is a SingleRelationMethod.
+  *  @return 'true' if this is a SingleRelationMethod, otherwise 'false'
+  */
+  syn boolean AnalysedMethod.isSingleRelationMethod() = false;
+  eq SingleRelationMethod.isSingleRelationMethod() = true;
+
+  /** Tests if AnalysedMethod is a ListRelationMethod.
+  *  @return 'true' if this is a ListRelationMethod, otherwise 'false'
+  */
+  syn boolean AnalysedMethod.isListRelationMethod() = false;
+  eq ListRelationMethod.isListRelationMethod() = true;
+
+  /** Tests if AnalysedMethod is a TokenMethod.
+  *  @return 'true' if this is a TokenMethod, otherwise 'false'
+  */
+  syn boolean AnalysedMethod.isTokenMethod() = false;
+  eq TokenMethod.isTokenMethod() = true;
+
+  /** Tests if DumpRelation is a DumpNormalRelation.
+  *  @return 'true' if this is a DumpNormalRelation, otherwise 'false'
+  */
+  syn boolean DumpRelation.isDumpNormalRelation() = false;
+  eq DumpNormalRelation.isDumpNormalRelation() = true;
+
+  /** Tests if DumpRelation is a DumpListRelation.
+  *  @return 'true' if this is a DumpListRelation, otherwise 'false'
+  */
+  syn boolean DumpRelation.isDumpListRelation() = false;
+  eq DumpListRelation.isDumpListRelation() = true;
+
+  /** Tests if DumpChildNode is a DumpNormalChildNode.
+  *  @return 'true' if this is a DumpNormalChildNode, otherwise 'false'
+  */
+  syn boolean DumpChildNode.isDumpNormalChildNode() = false;
+  eq DumpNormalChildNode.isDumpNormalChildNode() = true;
+
+  /** Tests if DumpChildNode is a DumpListChildNode.
+  *  @return 'true' if this is a DumpListChildNode, otherwise 'false'
+  */
+  syn boolean DumpChildNode.isDumpListChildNode() = false;
+  eq DumpListChildNode.isDumpListChildNode() = true;
+
+  /** Tests if ListChildMethod is a NormalListChildMethod.
+  *  @return 'true' if this is a NormalListChildMethod, otherwise 'false'
+  */
+  syn boolean ListChildMethod.isNormalListChildMethod() = false;
+  eq NormalListChildMethod.isNormalListChildMethod() = true;
+
+  /** Tests if ListChildMethod is a NTAListChildMethod.
+  *  @return 'true' if this is a NTAListChildMethod, otherwise 'false'
+  */
+  syn boolean ListChildMethod.isNTAListChildMethod() = false;
+  eq NTAListChildMethod.isNTAListChildMethod() = true;
+
+  /** Tests if SingleChildMethod is a NormalSingleChildMethod.
+  *  @return 'true' if this is a NormalSingleChildMethod, otherwise 'false'
+  */
+  syn boolean SingleChildMethod.isNormalSingleChildMethod() = false;
+  eq NormalSingleChildMethod.isNormalSingleChildMethod() = true;
+
+  /** Tests if SingleChildMethod is a NTASingleChildMethod.
+  *  @return 'true' if this is a NTASingleChildMethod, otherwise 'false'
+  */
+  syn boolean SingleChildMethod.isNTASingleChildMethod() = false;
+  eq NTASingleChildMethod.isNTASingleChildMethod() = true;
+
+  /** Tests if DumpToken is a DumpReferenceToken.
+  *  @return 'true' if this is a DumpReferenceToken, otherwise 'false'
+  */
+  syn boolean DumpToken.isDumpReferenceToken() = false;
+  eq DumpReferenceToken.isDumpReferenceToken() = true;
+
+  /** Tests if DumpToken is a DumpValueToken.
+  *  @return 'true' if this is a DumpValueToken, otherwise 'false'
+  */
+  syn boolean DumpToken.isDumpValueToken() = false;
+  eq DumpValueToken.isDumpValueToken() = true;
+
+  /** casts a TokenMethod into a IntrinsicTokenMethod if possible.
+   *  @return 'this' cast to a IntrinsicTokenMethod or 'null'
+   */
+  syn IntrinsicTokenMethod TokenMethod.asIntrinsicTokenMethod();
+  eq TokenMethod.asIntrinsicTokenMethod() = null;
+  eq IntrinsicTokenMethod.asIntrinsicTokenMethod() = this;
+
+  /** casts a TokenMethod into a AttributeMethod if possible.
+   *  @return 'this' cast to a AttributeMethod or 'null'
+   */
+  syn AttributeMethod TokenMethod.asAttributeMethod();
+  eq TokenMethod.asAttributeMethod() = null;
+  eq AttributeMethod.asAttributeMethod() = this;
+
+  /** casts a AnalysedMethod into a SingleChildMethod if possible.
+   *  @return 'this' cast to a SingleChildMethod or 'null'
+   */
+  syn SingleChildMethod AnalysedMethod.asSingleChildMethod();
+  eq AnalysedMethod.asSingleChildMethod() = null;
+  eq SingleChildMethod.asSingleChildMethod() = this;
+
+  /** casts a AnalysedMethod into a ListChildMethod if possible.
+   *  @return 'this' cast to a ListChildMethod or 'null'
+   */
+  syn ListChildMethod AnalysedMethod.asListChildMethod();
+  eq AnalysedMethod.asListChildMethod() = null;
+  eq ListChildMethod.asListChildMethod() = this;
+
+  /** casts a AnalysedMethod into a SingleRelationMethod if possible.
+   *  @return 'this' cast to a SingleRelationMethod or 'null'
+   */
+  syn SingleRelationMethod AnalysedMethod.asSingleRelationMethod();
+  eq AnalysedMethod.asSingleRelationMethod() = null;
+  eq SingleRelationMethod.asSingleRelationMethod() = this;
+
+  /** casts a AnalysedMethod into a ListRelationMethod if possible.
+   *  @return 'this' cast to a ListRelationMethod or 'null'
+   */
+  syn ListRelationMethod AnalysedMethod.asListRelationMethod();
+  eq AnalysedMethod.asListRelationMethod() = null;
+  eq ListRelationMethod.asListRelationMethod() = this;
+
+  /** casts a AnalysedMethod into a TokenMethod if possible.
+   *  @return 'this' cast to a TokenMethod or 'null'
+   */
+  syn TokenMethod AnalysedMethod.asTokenMethod();
+  eq AnalysedMethod.asTokenMethod() = null;
+  eq TokenMethod.asTokenMethod() = this;
+
+  /** casts a DumpRelation into a DumpNormalRelation if possible.
+   *  @return 'this' cast to a DumpNormalRelation or 'null'
+   */
+  syn DumpNormalRelation DumpRelation.asDumpNormalRelation();
+  eq DumpRelation.asDumpNormalRelation() = null;
+  eq DumpNormalRelation.asDumpNormalRelation() = this;
+
+  /** casts a DumpRelation into a DumpListRelation if possible.
+   *  @return 'this' cast to a DumpListRelation or 'null'
+   */
+  syn DumpListRelation DumpRelation.asDumpListRelation();
+  eq DumpRelation.asDumpListRelation() = null;
+  eq DumpListRelation.asDumpListRelation() = this;
+
+  /** casts a DumpChildNode into a DumpNormalChildNode if possible.
+   *  @return 'this' cast to a DumpNormalChildNode or 'null'
+   */
+  syn DumpNormalChildNode DumpChildNode.asDumpNormalChildNode();
+  eq DumpChildNode.asDumpNormalChildNode() = null;
+  eq DumpNormalChildNode.asDumpNormalChildNode() = this;
+
+  /** casts a DumpChildNode into a DumpListChildNode if possible.
+   *  @return 'this' cast to a DumpListChildNode or 'null'
+   */
+  syn DumpListChildNode DumpChildNode.asDumpListChildNode();
+  eq DumpChildNode.asDumpListChildNode() = null;
+  eq DumpListChildNode.asDumpListChildNode() = this;
+
+  /** casts a ListChildMethod into a NormalListChildMethod if possible.
+   *  @return 'this' cast to a NormalListChildMethod or 'null'
+   */
+  syn NormalListChildMethod ListChildMethod.asNormalListChildMethod();
+  eq ListChildMethod.asNormalListChildMethod() = null;
+  eq NormalListChildMethod.asNormalListChildMethod() = this;
+
+  /** casts a ListChildMethod into a NTAListChildMethod if possible.
+   *  @return 'this' cast to a NTAListChildMethod or 'null'
+   */
+  syn NTAListChildMethod ListChildMethod.asNTAListChildMethod();
+  eq ListChildMethod.asNTAListChildMethod() = null;
+  eq NTAListChildMethod.asNTAListChildMethod() = this;
+
+  /** casts a SingleChildMethod into a NormalSingleChildMethod if possible.
+   *  @return 'this' cast to a NormalSingleChildMethod or 'null'
+   */
+  syn NormalSingleChildMethod SingleChildMethod.asNormalSingleChildMethod();
+  eq SingleChildMethod.asNormalSingleChildMethod() = null;
+  eq NormalSingleChildMethod.asNormalSingleChildMethod() = this;
+
+  /** casts a SingleChildMethod into a NTASingleChildMethod if possible.
+   *  @return 'this' cast to a NTASingleChildMethod or 'null'
+   */
+  syn NTASingleChildMethod SingleChildMethod.asNTASingleChildMethod();
+  eq SingleChildMethod.asNTASingleChildMethod() = null;
+  eq NTASingleChildMethod.asNTASingleChildMethod() = this;
+
+  /** casts a DumpToken into a DumpReferenceToken if possible.
+   *  @return 'this' cast to a DumpReferenceToken or 'null'
+   */
+  syn DumpReferenceToken DumpToken.asDumpReferenceToken();
+  eq DumpToken.asDumpReferenceToken() = null;
+  eq DumpReferenceToken.asDumpReferenceToken() = this;
+
+  /** casts a DumpToken into a DumpValueToken if possible.
+   *  @return 'this' cast to a DumpValueToken or 'null'
+   */
+  syn DumpValueToken DumpToken.asDumpValueToken();
+  eq DumpToken.asDumpValueToken() = null;
+  eq DumpValueToken.asDumpValueToken() = this;
+
+}
diff --git a/dumpAst/src/main/jastadd/GenerationBackend.jadd b/dumpAst/src/main/jastadd/GenerationBackend.jadd
index beeb183..44f6283 100644
--- a/dumpAst/src/main/jastadd/GenerationBackend.jadd
+++ b/dumpAst/src/main/jastadd/GenerationBackend.jadd
@@ -52,7 +52,7 @@ aspect GenerationBackend {
     } else {
       node = new DumpNode();
       node.setObject(obj);
-      node.setName("node" + tti.transformed.size());
+      node.setName("node" + (tti.nodeCounter++));
       tti.transformed.put(obj, node);
       this.addDumpNode(node);
     }
@@ -73,85 +73,119 @@ aspect GenerationBackend {
       return node;
     }
     final ClassAnalysisResult car = analyzeClass(obj.getClass());
-    // -- singleChild --
-    for (SingleChildMethod singleChildMethod : car.singleChildMethods()) {
-      Object target = singleChildMethod.getMethod().invoke(obj);
-      String childName = singleChildMethod.getName();
-      DumpNode targetNode = transform(tti, target, nextSource(source, !isChildEnabled(objClassName, childName)), !getBuildConfig().getExcludeNullNodes());
-      if (targetNode != null) {
-        DumpNormalChildNode normalChild = new DumpNormalChildNode();
-        normalChild.setName(childName);
-        normalChild.setDumpNode(targetNode);
-        normalChild.setComputed(singleChildMethod.isNTASingleChildMethod());
-        node.addDumpChildNode(normalChild);
-      }
-    }
-    // -- listChild --
-    for (ListChildMethod listChildMethod : car.listChildMethods()) {
-      Iterable<?> targetList = (Iterable<?>) listChildMethod.getMethod().invoke(obj);
-      DumpListChildNode listChild = new DumpListChildNode();
-      listChild.setComputed(listChildMethod.isNTAListChildMethod());
-      String childName = listChildMethod.getName();
-      boolean shouldBeInvisisble = !isChildEnabled(objClassName, childName);
-      listChild.setName(childName);
-      for (Object target : targetList) {
-        DumpNode targetNode = transform(tti, target, nextSource(source, shouldBeInvisisble));
-        if (target != null && targetNode != null) {
-          listChild.addInnerDumpNode(new InnerDumpNode(targetNode));
+    for (AnalysedMethod containmentMethod : car.getContainmentMethodList()) {
+      if (containmentMethod.isSingleChildMethod()) {
+        // -- singleChild --
+        Object target = containmentMethod.getMethod().invoke(obj);
+        String childName = containmentMethod.getName();
+        DumpNode targetNode = transform(tti, target, nextSource(source, !isChildEnabled(objClassName, childName)), !getBuildConfig().getExcludeNullNodes());
+        if (targetNode != null) {
+          DumpNormalChildNode normalChild = new DumpNormalChildNode();
+          normalChild.setName(childName);
+          normalChild.setDumpNode(targetNode);
+          normalChild.setComputed(false);
+          node.addDumpChildNode(normalChild);
         }
-      }
-      if (listChild.getNumInnerDumpNode() > 0) {
-        node.addDumpChildNode(listChild);
-      }
-    }
-    // -- singleRelation --
-    for (SingleRelationMethod singleRelationMethod : car.singleRelationMethods()) {
-      Object target = singleRelationMethod.getMethod().invoke(obj);
-      DumpNode targetNode = transform(tti, target, Source.RELATION);
-      if (target != null && targetNode != null) {
-        DumpNormalRelation normalRelation = new DumpNormalRelation();
-        normalRelation.setName(singleRelationMethod.getName());
-        normalRelation.setDumpNode(targetNode);
-        node.addDumpRelation(normalRelation);
+      } else if (containmentMethod.isListChildMethod()) {
+        // -- listChild --
+        Iterable<?> targetList = (Iterable<?>) containmentMethod.getMethod().invoke(obj);
+        DumpListChildNode listChild = new DumpListChildNode();
+        listChild.setComputed(false);
+        String childName = containmentMethod.getName();
+        boolean shouldBeInvisisble = !isChildEnabled(objClassName, childName);
+        listChild.setName(childName);
+        for (Object target : targetList) {
+          DumpNode targetNode = transform(tti, target, nextSource(source, shouldBeInvisisble));
+          if (target != null && targetNode != null) {
+            listChild.addInnerDumpNode(new InnerDumpNode().setDumpNode(targetNode));
+          }
+        }
+        if (listChild.getNumInnerDumpNode() > 0) {
+          node.addDumpChildNode(listChild);
+        }
+      } else {
+        throw new RuntimeException("Unknown containment method type " + containmentMethod);
       }
     }
-    // -- listRelation --
-    for (ListRelationMethod listRelationMethod : car.listRelationMethods()) {
-      Iterable<?> targetList = (Iterable<?>) listRelationMethod.getMethod().invoke(obj);
-      DumpListRelation listRelation = new DumpListRelation();
-      listRelation.setName(listRelationMethod.getName());
-      for (Object target : targetList) {
+    for (AnalysedMethod otherMethod : car.getOtherMethodList()) {
+      if (otherMethod.isSingleChildMethod()) {
+        // -- singleChild --
+        Object target = otherMethod.getMethod().invoke(obj);
+        String childName = otherMethod.getName();
+        DumpNode targetNode = transform(tti, target, nextSource(source, !isChildEnabled(objClassName, childName)), !getBuildConfig().getExcludeNullNodes());
+        if (targetNode != null) {
+          DumpNormalChildNode normalChild = new DumpNormalChildNode();
+          normalChild.setName(childName);
+          normalChild.setDumpNode(targetNode);
+          normalChild.setComputed(otherMethod.asSingleChildMethod().isNTASingleChildMethod());
+          node.addDumpChildNode(normalChild);
+        }
+      } else if (otherMethod.isListChildMethod()) {
+        // -- listChild --
+        Iterable<?> targetList = (Iterable<?>) otherMethod.getMethod().invoke(obj);
+        DumpListChildNode listChild = new DumpListChildNode();
+        listChild.setComputed(otherMethod.asListChildMethod().isNTAListChildMethod());
+        String childName = otherMethod.getName();
+        boolean shouldBeInvisisble = !isChildEnabled(objClassName, childName);
+        listChild.setName(childName);
+        for (Object target : targetList) {
+          DumpNode targetNode = transform(tti, target, nextSource(source, shouldBeInvisisble));
+          if (target != null && targetNode != null) {
+            listChild.addInnerDumpNode(new InnerDumpNode().setDumpNode(targetNode));
+          }
+        }
+        if (listChild.getNumInnerDumpNode() > 0) {
+          node.addDumpChildNode(listChild);
+        }
+      } else if (otherMethod.isSingleRelationMethod()) {
+        // -- singleRelation --
+        Object target = otherMethod.getMethod().invoke(obj);
         DumpNode targetNode = transform(tti, target, Source.RELATION);
         if (target != null && targetNode != null) {
-          listRelation.addInnerDumpNode(new InnerDumpNode(targetNode));
+          DumpNormalRelation normalRelation = new DumpNormalRelation();
+          normalRelation.setName(otherMethod.getName());
+          normalRelation.setDumpNode(targetNode);
+          node.addDumpRelation(normalRelation);
         }
-      }
-      if (listRelation.getNumInnerDumpNode() > 0) {
-        node.addDumpRelation(listRelation);
-      }
-    }
-    // -- token --
-    for (TokenMethod tokenMethod : car.tokenMethods()) {
-      Object target = tokenMethod.getMethod().invoke(obj);
-      if (target != null) {
-        DumpNode targetNode = transform(tti, target, Source.RELATION);
-        DumpToken token = null;
-        // TODO check, if Iterable.isAssignableFrom(target.getClass()).
-        //      if so, check isAstNode for first non-null to add DumpReferenceToken's, otherwise DumpValueToken's
-        if (targetNode != null && targetNode.isAstNode()) {
-          token = new DumpReferenceToken().setValue(targetNode);
-        } else {
-          if (target != null && (getBuildConfig().getIncludeEmptyString() || !target.toString().isEmpty())) {
-            DumpValueToken valueToken = new DumpValueToken();
-            valueToken.setValue(target);
-            token = valueToken;
+      } else if (otherMethod.isListRelationMethod()) {
+        // -- listRelation --
+        Iterable<?> targetList = (Iterable<?>) otherMethod.getMethod().invoke(obj);
+        DumpListRelation listRelation = new DumpListRelation();
+        listRelation.setName(otherMethod.getName());
+        for (Object target : targetList) {
+          DumpNode targetNode = transform(tti, target, Source.RELATION);
+          if (target != null && targetNode != null) {
+            listRelation.addInnerDumpNode(new InnerDumpNode(targetNode));
           }
         }
-        if (token != null) {
-          token.setName(tokenMethod.getName());
-          token.setComputed(tokenMethod.isAttributeMethod());
-          node.addDumpToken(token);
+        if (listRelation.getNumInnerDumpNode() > 0) {
+          node.addDumpRelation(listRelation);
         }
+      } else if (otherMethod.isTokenMethod()) {
+        // -- token --
+        Object target = otherMethod.getMethod().invoke(obj);
+        if (target != null) {
+          DumpNode targetNode = transform(tti, target, Source.RELATION);
+          DumpToken token = null;
+          // TODO check, if Iterable.isAssignableFrom(target.getClass()).
+          //      if so, check isAstNode for first non-null to add DumpReferenceToken's, otherwise DumpValueToken's
+          if (targetNode != null && targetNode.isAstNode()) {
+            token = new DumpReferenceToken().setValue(targetNode);
+          } else {
+            if (target != null && (getBuildConfig().getIncludeEmptyString() || !target.toString().isEmpty())) {
+              DumpValueToken valueToken = new DumpValueToken();
+              valueToken.setValue(target);
+              token = valueToken;
+            }
+          }
+          if (token != null) {
+            token.setName(otherMethod.getName());
+            token.setComputed(otherMethod.asTokenMethod().isAttributeMethod());
+            node.addDumpToken(token);
+          }
+        }
+      } else {
+        throw new RuntimeException("Unknown other method type " + otherMethod);
       }
     }
     return node;
@@ -172,39 +206,43 @@ aspect GenerationBackend {
   syn nta ClassAnalysisResult DumpAst.analyzeClass(java.lang.Class<?> clazz) {
     ClassAnalysisResult result = new ClassAnalysisResult();
     String clazzName = clazz.getSimpleName();
+    java.util.List<String> targetOrder = targetOrder(clazz);
     for (java.lang.reflect.Method method : clazz.getMethods()) {
       for (java.lang.annotation.Annotation annotation : method.getAnnotations()) {
         String canonicalName = annotation.annotationType().getCanonicalName();
         if (canonicalName.startsWith(astNodeAnnotationPrefix())) {
           String simpleName = annotation.annotationType().getSimpleName();
+          String contextNameToAdd = null;
+          AnalysedMethod containmentMethodToAdd = null;
           switch (simpleName) {
             case "Child":
-              String singleChildName = invokeName(annotation);
+              contextNameToAdd = invokeName(annotation);
               NormalSingleChildMethod singleChildMethod = new NormalSingleChildMethod();
               singleChildMethod.setMethod(method);
-              singleChildMethod.setName(singleChildName);
-              result.addAnalysedMethod(singleChildMethod);
+              singleChildMethod.setName(contextNameToAdd);
+              containmentMethodToAdd = singleChildMethod;
               break;
             case "OptChild":
-              String optChildName = invokeName(annotation);
+              contextNameToAdd = invokeName(annotation);
               try {
                 // the annotated method is "get???Opt", but we want "get???"
-                java.lang.reflect.Method realGetter = clazz.getMethod("get" + optChildName);
+                java.lang.reflect.Method realGetter = clazz.getMethod("get" + contextNameToAdd);
                 NormalSingleChildMethod normalSingleChildMethod = new NormalSingleChildMethod();
                 normalSingleChildMethod.setMethod(realGetter);
-                normalSingleChildMethod.setName(optChildName);
-                result.addAnalysedMethod(normalSingleChildMethod);
+                normalSingleChildMethod.setName(contextNameToAdd);
+                containmentMethodToAdd = normalSingleChildMethod;
               } catch (NoSuchMethodException e) {
-                System.err.println("Could not find getter for Opt-child " + optChildName + " in " + clazzName);
+                System.err.println("Could not find getter for Opt-child " + contextNameToAdd + " in " + clazzName);
                 throw new RuntimeException(e);
               }
               break;
             case "ListChild":
               String listChildName = invokeName(annotation);
+              contextNameToAdd = listChildName;
               NormalListChildMethod normalListChildMethod = new NormalListChildMethod();
               normalListChildMethod.setMethod(method);
               normalListChildMethod.setName(listChildName);
-              result.addAnalysedMethod(normalListChildMethod);
+              containmentMethodToAdd = normalListChildMethod;
               break;
             case "Token":
               // heuristic for relations
@@ -218,7 +256,7 @@ aspect GenerationBackend {
                     SingleRelationMethod singleRelationMethod = new SingleRelationMethod();
                     singleRelationMethod.setMethod(relationMethod);
                     singleRelationMethod.setName(relationName);
-                    result.addAnalysedMethod(singleRelationMethod);
+                    result.addOtherMethod(singleRelationMethod);
                   }
                   continue;
                 } catch (NoSuchMethodException e) {
@@ -232,7 +270,7 @@ aspect GenerationBackend {
                     ListRelationMethod listRelationMethod = new ListRelationMethod();
                     listRelationMethod.setMethod(relationMethod);
                     listRelationMethod.setName(relationName);
-                    result.addAnalysedMethod(listRelationMethod);
+                    result.addOtherMethod(listRelationMethod);
                   }
                   continue;
                 } catch (NoSuchMethodException e) {
@@ -243,7 +281,7 @@ aspect GenerationBackend {
                 IntrinsicTokenMethod tokenMethod = new IntrinsicTokenMethod();
                 tokenMethod.setMethod(method);
                 tokenMethod.setName(tokenName);
-                result.addAnalysedMethod(tokenMethod);
+                result.addOtherMethod(tokenMethod);
               }
               break;
             case "Attribute":
@@ -263,12 +301,12 @@ aspect GenerationBackend {
                     NTAListChildMethod ntaListChildMethod = new NTAListChildMethod();
                     ntaListChildMethod.setMethod(method);
                     ntaListChildMethod.setName(attributeName);
-                    result.addAnalysedMethod(ntaListChildMethod);
+                    result.addOtherMethod(ntaListChildMethod);
                   } else {
                     NTASingleChildMethod ntaSingleChildMethod = new NTASingleChildMethod();
                     ntaSingleChildMethod.setMethod(method);
                     ntaSingleChildMethod.setName(attributeName);
-                    result.addAnalysedMethod(ntaSingleChildMethod);
+                    result.addOtherMethod(ntaSingleChildMethod);
                   }
                 }
               } else if (isAttributeEnabled(clazzName, attributeName)) {
@@ -276,16 +314,53 @@ aspect GenerationBackend {
                 AttributeMethod attributeMethod = new AttributeMethod();
                 attributeMethod.setMethod(method);
                 attributeMethod.setName(attributeName);
-                result.addAnalysedMethod(attributeMethod);
+                result.addOtherMethod(attributeMethod);
               }
               break;
           }
+          if (containmentMethodToAdd != null) {
+            int indexOfContextInTarget = targetOrder.indexOf(contextNameToAdd);
+            if (indexOfContextInTarget == 0) {
+              result.getContainmentMethodList().insertChild(containmentMethodToAdd, 0);
+              continue;
+            }
+            if (indexOfContextInTarget == targetOrder.size() - 1) {
+              result.addContainmentMethod(containmentMethodToAdd);
+              continue;
+            }
+
+            for (int i = 0, size = result.getNumContainmentMethod(); i < size; i++) {
+              String currentContextName = result.getContainmentMethod(i).getName();
+              int indexOfCurrentInTarget = targetOrder.indexOf(currentContextName);
+              if (indexOfCurrentInTarget > indexOfContextInTarget) {
+                result.getContainmentMethodList().insertChild(containmentMethodToAdd, i);
+                break;
+              }
+            }
+            result.addContainmentMethod(containmentMethodToAdd);
+          }
         }
       }
     }
     return result;
   }
 
+  syn java.util.List<String> DumpAst.targetOrder(Class<?> clazz) {
+    for (java.lang.reflect.Constructor<?> method : clazz.getConstructors()) {
+      for (java.lang.annotation.Annotation annotation : method.getAnnotations()) {
+        String canonicalName = annotation.annotationType().getCanonicalName();
+        if (canonicalName.startsWith(astNodeAnnotationPrefix())) {
+          String simpleName = annotation.annotationType().getSimpleName();
+          if (simpleName.equals("Constructor")) {
+            return java.util.Arrays.asList((String[]) invokeMethod("name", annotation));
+          }
+        }
+      }
+    }
+    // there is no constructor with an annotation, iff the nonterminal has no children
+    return null;
+  }
+
   private String DumpAst.titleCase(String s) {
     if (s.isEmpty()) {
       return s;
@@ -468,7 +543,7 @@ aspect GenerationBackend {
     java.util.List<DumpNode> result = new java.util.ArrayList<>();
     for (DumpChildNode childNode : getDumpChildNodeList()) {
       for (DumpNode inner : childNode.innerNodes(false)) {
-        if (inner.getInvisible()) {
+        if (inner != null && inner.getInvisible()) {
           result.addAll(inner.reachableThroughInvisible());
         } else if (this.getInvisible()) {
           result.add(inner);
@@ -519,6 +594,7 @@ aspect GenerationBackend {
   class TransformationTransferInformation {
     java.util.Map<Object, DumpNode> transformed = new java.util.HashMap<>();
     java.util.Map<DumpNode, Boolean> relationTargetsUnprocessed = new java.util.HashMap<>();
+    int nodeCounter = 0;
   }
 
   syn String DumpAst.toYaml(boolean prependCreationComment) {
diff --git a/dumpAst/src/main/jastadd/Navigation.jrag b/dumpAst/src/main/jastadd/Navigation.jrag
index 3f04c9c..05f722d 100644
--- a/dumpAst/src/main/jastadd/Navigation.jrag
+++ b/dumpAst/src/main/jastadd/Navigation.jrag
@@ -5,18 +5,6 @@ aspect Navigation {
   syn boolean DumpRelation.isList() = false;
   eq DumpListRelation.isList() = true;
 
-  // --- isDumpValueToken ---
-  syn boolean DumpToken.isDumpValueToken() = false;
-  eq DumpValueToken.isDumpValueToken() = true;
-
-  // --- asDumpValueToken ---
-  syn DumpValueToken DumpToken.asDumpValueToken() = null;
-  eq DumpValueToken.asDumpValueToken() = this;
-
-  // --- asDumpReferenceToken ---
-  syn DumpReferenceToken DumpToken.asDumpReferenceToken() = null;
-  eq DumpReferenceToken.asDumpReferenceToken() = this;
-
   // --- buildConfig ---
   inh BuildConfig DumpNode.buildConfig();
   eq DumpAst.getChild().buildConfig() = getBuildConfig();
@@ -97,17 +85,4 @@ aspect Navigation {
 
   coll java.util.List<TokenMethod> ClassAnalysisResult.tokenMethods() [new java.util.ArrayList<>()] root ClassAnalysisResult;
   TokenMethod contributes this to ClassAnalysisResult.tokenMethods();
-
-  // --- isAttributeMethod ---
-  syn boolean TokenMethod.isAttributeMethod() = false;
-  eq AttributeMethod.isAttributeMethod() = true;
-
-  // --- isNTASingleChildMethod ---
-  syn boolean SingleChildMethod.isNTASingleChildMethod() = false;
-  eq NTASingleChildMethod.isNTASingleChildMethod() = true;
-
-  // --- isNTAListChildMethod ---
-  syn boolean ListChildMethod.isNTAListChildMethod() = false;
-  eq NTAListChildMethod.isNTAListChildMethod() = true;
-
 }
diff --git a/dumpAst/src/main/jastadd/Printing.jrag b/dumpAst/src/main/jastadd/Printing.jrag
index 362451e..9531e51 100644
--- a/dumpAst/src/main/jastadd/Printing.jrag
+++ b/dumpAst/src/main/jastadd/Printing.jrag
@@ -22,7 +22,10 @@ aspect Printing {
   syn String DumpNode.label() = getLabel();
 
   // --- bothVisible ---
-  syn boolean InnerDumpNode.bothVisible() = !containingDumpNode().getInvisible() && !getDumpNode().getInvisible();
-  syn boolean DumpNormalChildNode.bothVisible() = !containingDumpNode().getInvisible() && !getDumpNode().getInvisible();
-  syn boolean DumpNormalRelation.bothVisible() = !containingDumpNode().getInvisible() && !getDumpNode().getInvisible();
+  boolean ASTNode.bothVisible(DumpNode one, DumpNode two) {
+    return one != null && two != null && !one.getInvisible() && !two.getInvisible();
+  }
+  syn boolean InnerDumpNode.bothVisible() = bothVisible(containingDumpNode(), getDumpNode());
+  syn boolean DumpNormalChildNode.bothVisible() = bothVisible(containingDumpNode(), getDumpNode());
+  syn boolean DumpNormalRelation.bothVisible() = bothVisible(containingDumpNode(), getDumpNode());
 }
diff --git a/testDumper/src/test/java/de/tudresden/inf/st/jastadd/testDumper/TestDumperMain.java b/testDumper/src/test/java/de/tudresden/inf/st/jastadd/testDumper/TestDumperMain.java
index d7b1404..a031f27 100644
--- a/testDumper/src/test/java/de/tudresden/inf/st/jastadd/testDumper/TestDumperMain.java
+++ b/testDumper/src/test/java/de/tudresden/inf/st/jastadd/testDumper/TestDumperMain.java
@@ -1,12 +1,16 @@
 package de.tudresden.inf.st.jastadd.testDumper;
 
 import de.tudresden.inf.st.jastadd.dumpAst.ast.DumpAst;
+import de.tudresden.inf.st.jastadd.dumpAst.ast.DumpListChildNode;
 import de.tudresden.inf.st.jastadd.dumpAst.ast.DumpNode;
 import org.jastadd.testDumper.ast.A;
 import org.jastadd.testDumper.ast.B;
 import org.jastadd.testDumper.ast.C;
 import org.jastadd.testDumper.ast.Root;
 
+import java.util.Arrays;
+import java.util.List;
+import java.util.function.Function;
 import java.util.stream.Collectors;
 
 public class TestDumperMain {
@@ -41,9 +45,28 @@ public class TestDumperMain {
     DumpAst dumpAst = builder.build();
     System.out.println(dumpAst.toPlantUml());
 
+    String[] b = new String[]{"1"};
+    List<String> l = Arrays.asList(b);
+
     DumpNode node = dumpAst.getDumpNode(0);
     System.out.println(node.getName());
-    System.out.println(node.myChildren().stream().map(DumpNode::getName).collect(Collectors.joining(", ")));
+    Function<? super DumpNode, String> printInfo = d ->
+        d.getName() + "(" + d.getLabel() +
+            ", container: " + d.container() +
+            ", succ: " + (d.successor() == null ? "null" : d.successor().getName()) +
+            ", has_succ: " + d.hasSuccessor() + ")";
+    dumpAst.flushTreeCache();
+    dumpAst.treeResolveAll();
+    dumpAst.getRootNode().getDumpChildNodeList().forEach(d -> {
+      if (d instanceof DumpListChildNode) {
+        System.out.println("dump-list-node " + d.getName());
+        ((DumpListChildNode) d).getInnerDumpNodeList().forEach(inner -> {
+          System.out.println(" inner: " + inner.getDumpNode().getName());
+          System.out.println("  <- " + inner.getDumpNode().getContainerOfInner());
+        });
+      }
+    });
+    System.out.println(node.myChildren().stream().map(printInfo).collect(Collectors.joining(", ")));
 
 //    System.out.println(">> YAML begin");
 //    System.out.println(builder.build().toYaml(true));
diff --git a/testDumper/src/test/java/de/tudresden/inf/st/jastadd/testDumper/TestSimple.java b/testDumper/src/test/java/de/tudresden/inf/st/jastadd/testDumper/TestSimple.java
index 1f79691..0e6a927 100644
--- a/testDumper/src/test/java/de/tudresden/inf/st/jastadd/testDumper/TestSimple.java
+++ b/testDumper/src/test/java/de/tudresden/inf/st/jastadd/testDumper/TestSimple.java
@@ -11,8 +11,7 @@ import java.util.Optional;
 
 import static de.tudresden.inf.st.jastadd.testDumper.TestUtils.*;
 import static org.assertj.core.api.Assertions.*;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.*;
 
 public class TestSimple {
 
@@ -91,10 +90,11 @@ public class TestSimple {
 
   @Test
   public void testOrderedListChildren() {
-    Root root = createRoot(null, null, createB(B1_NAME), createB(B2_NAME), createB(B3_NAME));
+    Root root = createRoot(createA(A_NAME), createC(C_NAME), createB(B1_NAME), createB(B2_NAME), createB(B3_NAME));
 
     List<DumpNode> nodes = TestUtils.dumpModel(root, DumpBuilder::orderChildren);
-    assertThat(nodes).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(ROOT_NAME, B1_NAME, B2_NAME, B3_NAME);
+    assertThat(nodes).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(
+        ROOT_NAME, A_NAME, B1_NAME, B2_NAME, B3_NAME, C_NAME);
 
     DumpNode actualRoot = TestUtils.findByName(nodes, ROOT_NAME);
     // in grammar: DumpAst ::= [...] DumpNode* [...];
@@ -102,7 +102,30 @@ public class TestSimple {
     assertTrue(((DumpAst) actualRoot.getParent().getParent()).getPrintConfig().getOrderChildren());
 
     List<DumpNode> children = actualRoot.myChildren();
-    assertThat(children).extracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(B1_NAME, B2_NAME, B3_NAME);
+    assertThat(children).extracting(NAME_EXTRACTOR).containsExactly(
+        A_NAME, B1_NAME, B2_NAME, B3_NAME, C_NAME);
+
+    DumpNode actualA = TestUtils.findByName(nodes, A_NAME);
+    DumpNode actualB1 = TestUtils.findByName(nodes, B1_NAME);
+    DumpNode actualB2 = TestUtils.findByName(nodes, B2_NAME);
+    DumpNode actualB3 = TestUtils.findByName(nodes, B3_NAME);
+    DumpNode actualC = TestUtils.findByName(nodes, C_NAME);
+
+    for (DumpNode d : children) {
+      System.out.println(d.getName() + "/" + d.getLabel() + " = " + d);
+    }
+
+    assertEquals(actualB1, actualA.successor(), actualA.successor().getName());
+    assertEquals(actualB2, actualB1.successor(), actualB1.successor().getName());
+    assertEquals(actualB3, actualB2.successor(), actualB2.successor().getName());
+    assertEquals(actualC, actualB3.successor(), actualB3.successor().getName());
+    assertNull(actualC.successor());
+
+    assertTrue(actualA.hasSuccessor());
+    assertTrue(actualB1.hasSuccessor());
+    assertTrue(actualB2.hasSuccessor());
+    assertTrue(actualB3.hasSuccessor());
+    assertFalse(actualC.hasSuccessor());
   }
 
   @Test
diff --git a/testDumper/src/test/java/de/tudresden/inf/st/jastadd/testDumper/TestUtils.java b/testDumper/src/test/java/de/tudresden/inf/st/jastadd/testDumper/TestUtils.java
index 10a0859..3a8dbc3 100644
--- a/testDumper/src/test/java/de/tudresden/inf/st/jastadd/testDumper/TestUtils.java
+++ b/testDumper/src/test/java/de/tudresden/inf/st/jastadd/testDumper/TestUtils.java
@@ -187,7 +187,7 @@ public class TestUtils {
       if (!dumpChildNode.isList()) {
         // then it is a DumpNormalChildNode
         DumpNode target = ((DumpNormalChildNode) dumpChildNode).getDumpNode();
-        if (!target.getInvisible()) {
+        if (target != null && !target.getInvisible()) {
           result.put(dumpChildNode.getName(), target);
         }
       }
@@ -201,7 +201,7 @@ public class TestUtils {
       if (dumpChildNode.isList()) {
         // then it is a DumpListChildNode
         ((DumpListChildNode) dumpChildNode).getInnerDumpNodeList().forEach(inner -> {
-          if (!inner.getDumpNode().getInvisible()) {
+          if (inner.getDumpNode() != null && !inner.getDumpNode().getInvisible()) {
             result.computeIfAbsent(dumpChildNode.getName(), key -> new ArrayList<>()).add(inner.getDumpNode());
           }
         });
-- 
GitLab