diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 9b33d3f5b8f680bb60fc0cdbc9f3341f8074b89a..5c1e5c728453a0d42d04550490702f9ea7857a91 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,6 +1,14 @@
 variables:
   GIT_SUBMODULE_STRATEGY: recursive
 
+before_script:
+  - export GRADLE_USER_HOME=`pwd`/.gradle
+
+cache:
+  paths:
+    - .gradle/wrapper
+    - .gradle/caches
+
 stages:
   - build
   - test
diff --git a/.gitmodules b/.gitmodules
index a8b57b043c81bc4bd581b4c0dc3abe7e3ecc6ffe..cc5edadc2028bc1f4db584f19862852cda5830e8 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,6 +1,3 @@
-[submodule "relast.preprocessor"]
-	path = relast.preprocessor
-	url = ../relast-preprocessor.git
 [submodule "dumpAst/src/main/jastadd/mustache"]
 	path = dumpAst/src/main/jastadd/mustache
 	url = ../mustache
diff --git a/build.gradle b/build.gradle
index 2ecc97593fa23bab973d4411c2bda9636548da92..0379d158f230be7f18614c4f46cefd0213fac71b 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,3 +1,14 @@
 plugins {
   id 'com.github.ben-manes.versions' version '0.36.0'
+  id 'java'
+}
+
+java.toolchain.languageVersion = JavaLanguageVersion.of(11)
+
+if (JavaVersion.current().isJava8Compatible()) {
+  allprojects {
+    tasks.withType(Javadoc) {
+      options.addStringOption('Xdoclint:none', '-quiet')
+    }
+  }
 }
diff --git a/dumpAst/build.gradle b/dumpAst/build.gradle
index 8210eb050a316995f02a228956e5496afa244068..11c2716e20e3a033ab67f9f7bd8567489aaf5933 100644
--- a/dumpAst/build.gradle
+++ b/dumpAst/build.gradle
@@ -1,6 +1,5 @@
 // --- Buildscripts (must be at the top) ---
 buildscript {
-    repositories.mavenLocal()
     repositories.mavenCentral()
     dependencies {
         classpath group: 'org.jastadd', name: 'jastaddgradle', version: '1.13.3'
@@ -9,23 +8,30 @@ buildscript {
 
 // --- Plugin definitions ---
 plugins {
+    id 'com.github.ben-manes.versions'
     id 'java'
     id 'idea'
-    id 'com.github.ben-manes.versions'
+    id 'org.jastadd'
     id 'java-library'
     id 'maven-publish'
 }
 
-apply plugin: 'jastadd'
-
 // --- Dependencies ---
 repositories {
     mavenCentral()
+    maven {
+        name 'gitlab-maven'
+        url 'https://git-st.inf.tu-dresden.de/api/v4/groups/jastadd/-/packages/maven'
+    }
+}
+
+configurations {
+    relast
 }
 
 dependencies {
     jastadd2 "org.jastadd:jastadd:2.3.5"
-    // https://mvnrepository.com/artifact/net.sourceforge.plantuml/plantuml
+    relast group: 'org.jastadd', name: 'relast', version: "${relast_version}"
     implementation group: 'net.sourceforge.plantuml', name: 'plantuml', version: '1.2022.2'
     api group: 'net.sf.beaver', name: 'beaver-rt', version: '0.9.11'
     implementation group: 'com.github.spullara.mustache.java', name: 'compiler', version: "0.9.10"
@@ -42,18 +48,15 @@ File mustacheGrammar = file('./src/main/jastadd/mustache/Mustache.relast')
 
 task relast(type: JavaExec) {
     group = 'Build'
-    main = "-jar"
+    classpath = configurations.relast
+    mainClass = 'org.jastadd.relast.compiler.Compiler'
 
     doFirst {
-        delete "src/gen/jastadd/*.ast"
-        delete "src/gen/jastadd/DumpAst.jadd"
-        delete "src/gen/jastadd/DumpAstRefResolver.jadd"
-        delete "src/gen/jastadd/DumpAstResolverStubs.jrag"
+        delete "src/gen/jastadd/*"
         mkdir  "src/gen/jastadd/"
     }
 
     args = [
-            "../libs/relast.jar",
             dumpAstGrammar,
             mustacheGrammar,
             "--listClass=java.util.ArrayList",
@@ -63,14 +66,6 @@ task relast(type: JavaExec) {
             "--resolverHelper",
             "--grammarName=./src/gen/jastadd/DumpAst"
     ]
-
-    inputs.files(file("../libs/relast.jar"),
-            dumpAstGrammar,
-            mustacheGrammar)
-    outputs.files(file("./src/gen/jastadd/DumpAst.ast"),
-            file("./src/gen/jastadd/DumpAst.jadd"),
-            file("./src/gen/jastadd/DumpAstRefResolver.jadd"),
-            file('./src/gen/jastadd/DumpAstResolverStubs.jrag'))
 }
 
 // --- JastAdd ---
@@ -121,6 +116,16 @@ jastadd {
 // --- Versioning and Publishing ---
 group = 'de.tudresden.inf.st'
 
+task fatJar(type: Jar) {
+    dependsOn jar
+    group = "build"
+    archiveAppendix = "fatjar"
+    from sourceSets.main.output
+    from {
+        configurations.runtimeClasspath.collect {it.isDirectory() ? it : zipTree(it) }
+    }
+}
+
 def versionFile = "src/main/resources/${project.getName()}Version.properties"
 
 try {
@@ -154,6 +159,11 @@ task setDevVersionForCI() {
     }
 }
 
+java {
+    withJavadocJar()
+    withSourcesJar()
+}
+
 publishing {
     publications {
         maven(MavenPublication) {
diff --git a/dumpAst/src/main/jastadd/DumpAst.relast b/dumpAst/src/main/jastadd/DumpAst.relast
index 3d76a4b0c5fb31fd428b970c8248da607f3126a8..337c14cb1ddc8f2ba8312a5533e2db379dda320c 100644
--- a/dumpAst/src/main/jastadd/DumpAst.relast
+++ b/dumpAst/src/main/jastadd/DumpAst.relast
@@ -4,8 +4,11 @@ rel DumpAst.RootNode? -> DumpNode ;
 DumpNode ::=  DumpChildNode* DumpToken* DumpRelation*
  <Name> <Label> <BackgroundColor> <TextColor> <Object:Object> <Invisible:boolean> <Computed:boolean> <ManualStereotypes>
  /InvisiblePath/ ;
+
 InnerDumpNode ;
 rel InnerDumpNode.DumpNode <-> DumpNode.ContainerOfInner ;
+InnerRelationDumpNode;
+rel InnerRelationDumpNode.DumpNode -> DumpNode ; // .ContainerOfInner*
 
 abstract DumpChildNode ::= <Name> <Computed:boolean> ;
 DumpNormalChildNode : DumpChildNode ;
@@ -15,15 +18,16 @@ DumpListChildNode : DumpChildNode ::= InnerDumpNode* ;
 abstract DumpToken ::= <Name> <Computed:boolean> ;
 DumpReferenceToken : DumpToken ;
 rel DumpReferenceToken.Value -> DumpNode ;
+DumpReferenceListToken : DumpToken ::= InnerRelationDumpNode* ;
 DumpValueToken : DumpToken ::= <Value:Object> ;
 
 abstract DumpRelation ::= <Name> <Bidirectional:boolean> ;
 DumpNormalRelation : DumpRelation ;
 rel DumpNormalRelation.DumpNode -> DumpNode ;
-DumpListRelation : DumpRelation ::= InnerDumpNode* ;
+DumpListRelation : DumpRelation ::= InnerRelationDumpNode* ;
 
 // type of NTA
-InvisiblePath ::= InnerDumpNode* ;
+InvisiblePath ::= InnerRelationDumpNode* ;
 
 ClassAnalysisResult ::= ContainmentMethod:AnalysedMethod* OtherMethod:AnalysedMethod* ;
 abstract AnalysedMethod ::= <Method:java.lang.reflect.Method> <Name> ;
@@ -47,6 +51,10 @@ BuildConfig ::= StyleInformation
  GlobalPatternCollection:PatternCollection
  ExcludeTypePattern:TypePatternCollectionMapping*
  IncludeTypePattern:TypePatternCollectionMapping*
+ <IncludeRelationMethod:IncludeRelationMethod>
+ <IncludeChildMethod:IncludeChildMethod>
+ <IncludeAttributeMethod:IncludeAttributeMethod>
+ <IncludeTokenMethod:IncludeTokenMethod>
  <TypeIgnorePattern>
  <IncludeEmptyString:boolean>
  <ExcludeNullNodes:boolean>
@@ -58,5 +66,6 @@ StyleInformation ::= <NameMethod:StyleMethod> <BackgroundColorMethod:StyleMethod
 PrintConfig ::= Header*
  <Scale:double>
  <Version>
+ <RelationWithRank:boolean>
  <OrderChildren:boolean> ;
 Header ::= <Value> ;
diff --git a/dumpAst/src/main/jastadd/GenerationBackend.jadd b/dumpAst/src/main/jastadd/GenerationBackend.jadd
index be9aaff9334285645c3e4581838566d683879f5c..1c2a06fd4a98b30d804a7cd4281d8176e25c2932 100644
--- a/dumpAst/src/main/jastadd/GenerationBackend.jadd
+++ b/dumpAst/src/main/jastadd/GenerationBackend.jadd
@@ -11,7 +11,7 @@ aspect GenerationBackend {
       public boolean computed;
 
       public TransformationOptions asRelation() {
-        return fromSource(Source.RELATION, false);
+        return fromSource(Source.RELATION, false).allowNullObjectsOnce();
       }
 
       public TransformationOptions asRoot() {
@@ -119,12 +119,18 @@ aspect GenerationBackend {
       return node;
     }
     final ClassAnalysisResult car = analyzeClass(obj.getClass());
+    if (getBuildConfig().getDebug()) {
+      System.out.println("for node " + obj + ", analysis was:\n" + car.prettyPrint());
+    }
     for (AnalysedMethod containmentMethod : car.getContainmentMethodList()) {
       if (containmentMethod.isSingleChildMethod()) {
         // -- singleChild --
         Object target = containmentMethod.getMethod().invoke(obj);
         String childName = containmentMethod.getName();
-        DumpNode targetNode = transform(tti, target, options.asNormal(!isChildEnabled(objClassName, childName)).allowNullObjectsOnce());
+        if (!getBuildConfig().getIncludeChildMethod().shouldInclude(obj, target, childName)) {
+          continue;
+        }
+        DumpNode targetNode = transform(tti, target, options.asNormal(false).allowNullObjectsOnce());
         if (targetNode != null) {
           DumpNormalChildNode normalChild = new DumpNormalChildNode();
           normalChild.setName(childName);
@@ -138,10 +144,12 @@ aspect GenerationBackend {
         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, options.asNormal(shouldBeInvisisble));
+          if (!getBuildConfig().getIncludeChildMethod().shouldInclude(obj, target, childName)) {
+            continue;
+          }
+          DumpNode targetNode = transform(tti, target, options.asNormal(false));
           if (target != null && targetNode != null) {
             listChild.addInnerDumpNode(new InnerDumpNode().setDumpNode(targetNode));
           }
@@ -159,7 +167,13 @@ aspect GenerationBackend {
         Object target = otherMethod.getMethod().invoke(obj);
         String childName = otherMethod.getName();
         boolean computed = otherMethod.asSingleChildMethod().isNTASingleChildMethod();
-        DumpNode targetNode = transform(tti, target, options.asNormal(!isChildEnabled(objClassName, childName)).computed(computed).allowNullObjectsOnce());
+        boolean shouldInclude = computed ?
+            getBuildConfig().getIncludeAttributeMethod().shouldInclude(obj, childName, true, () -> catchedInvoke(otherMethod.getMethod(), obj)) :
+            getBuildConfig().getIncludeChildMethod().shouldInclude(obj, target, childName);
+        if (!shouldInclude) {
+          continue;
+        }
+        DumpNode targetNode = transform(tti, target, options.asNormal(false).computed(computed).allowNullObjectsOnce());
         if (targetNode != null) {
           DumpNormalChildNode normalChild = new DumpNormalChildNode();
           normalChild.setName(childName);
@@ -174,10 +188,15 @@ aspect GenerationBackend {
         boolean computed = otherMethod.asListChildMethod().isNTAListChildMethod();
         listChild.setComputed(computed);
         String childName = otherMethod.getName();
-        boolean shouldBeInvisisble = !isChildEnabled(objClassName, childName);
         listChild.setName(childName);
         for (Object target : targetList) {
-          DumpNode targetNode = transform(tti, target, options.asNormal(shouldBeInvisisble).computed(computed));
+          boolean shouldInclude = computed ?
+              getBuildConfig().getIncludeAttributeMethod().shouldInclude(obj, childName, true, () -> catchedInvoke(otherMethod.getMethod(), obj)) :
+              getBuildConfig().getIncludeChildMethod().shouldInclude(obj, target, childName);
+          if (!shouldInclude) {
+            continue;
+          }
+          DumpNode targetNode = transform(tti, target, options.asNormal(false).computed(computed));
           if (target != null && targetNode != null) {
             listChild.addInnerDumpNode(new InnerDumpNode().setDumpNode(targetNode));
           }
@@ -188,8 +207,11 @@ aspect GenerationBackend {
       } else if (otherMethod.isSingleRelationMethod()) {
         // -- singleRelation --
         Object target = otherMethod.getMethod().invoke(obj);
+        if (!getBuildConfig().getIncludeRelationMethod().shouldInclude(obj, target, otherMethod.getName())) {
+          continue;
+        }
         DumpNode targetNode = transform(tti, target, options.asRelation());
-        if (target != null && targetNode != null) {
+        if (targetNode != null) {
           DumpNormalRelation normalRelation = new DumpNormalRelation();
           normalRelation.setName(otherMethod.getName());
           normalRelation.setDumpNode(targetNode);
@@ -201,29 +223,59 @@ aspect GenerationBackend {
         DumpListRelation listRelation = new DumpListRelation();
         listRelation.setName(otherMethod.getName());
         for (Object target : targetList) {
+          if (!getBuildConfig().getIncludeRelationMethod().shouldInclude(obj, target, otherMethod.getName())) {
+            continue;
+          }
           DumpNode targetNode = transform(tti, target, options.asRelation());
           if (target != null && targetNode != null) {
-            listRelation.addInnerDumpNode(new InnerDumpNode(targetNode));
+            listRelation.addInnerRelationDumpNode(new InnerRelationDumpNode(targetNode));
           }
         }
-        if (listRelation.getNumInnerDumpNode() > 0) {
+        if (listRelation.getNumInnerRelationDumpNode() > 0) {
           node.addDumpRelation(listRelation);
         }
       } else if (otherMethod.isTokenMethod()) {
         // -- token --
+        TokenMethod tokenMethod = otherMethod.asTokenMethod();
+        boolean shouldInclude = tokenMethod.isAttributeMethod() ?
+            getBuildConfig().getIncludeAttributeMethod().shouldInclude(obj, otherMethod.getName(), false, () -> catchedInvoke(otherMethod.getMethod(), obj)) :
+            getBuildConfig().getIncludeTokenMethod().shouldInclude(obj, otherMethod.getName(), otherMethod.getMethod().invoke(obj));
+        if (!shouldInclude) {
+          continue;
+        }
         Object target = otherMethod.getMethod().invoke(obj);
         if (target != null) {
-          DumpNode targetNode = transform(tti, target, options.asRelation());
           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;
+          boolean atLeastOneASTNode = false;
+          if (Iterable.class.isAssignableFrom(target.getClass())) {
+            java.util.List<DumpNode> nodes = new java.util.ArrayList<>();
+            Iterable iterable = (Iterable) target;
+            for (Object element : iterable) {
+              // TODO check if isAstNode for first non-null. if yes, use DumpReferenceListToken, other DumpValueToken
+              DumpNode nodeForElement = transform(tti, element, options.asRelation());
+              nodes.add(nodeForElement);
+              if (nodeForElement != null && nodeForElement.isAstNode()) {
+                atLeastOneASTNode = true;
+              }
+            }
+            if (atLeastOneASTNode) {
+              DumpReferenceListToken listToken = new DumpReferenceListToken();
+              nodes.forEach(element -> {
+                listToken.addInnerRelationDumpNode(new InnerRelationDumpNode().setDumpNode(element));
+              });
+              token = listToken;
+            }
+          }
+          if (!atLeastOneASTNode) {
+            DumpNode targetNode = transform(tti, target, options.asRelation());
+            if (targetNode != null && targetNode.isAstNode()) {
+              token = new DumpReferenceToken().setValue(targetNode);
+            } else {
+              if (getBuildConfig().getIncludeEmptyString() || !target.toString().isEmpty()) {
+                DumpValueToken valueToken = new DumpValueToken();
+                valueToken.setValue(target);
+                token = valueToken;
+              }
             }
           }
           if (token != null) {
@@ -239,6 +291,16 @@ aspect GenerationBackend {
     return node;
   }
 
+  private Object DumpAst.catchedInvoke(java.lang.reflect.Method method, Object obj) {
+    try {
+      return method.invoke(obj);
+    }
+    catch (Exception e) {
+      e.printStackTrace();
+      return null;
+    }
+  }
+
   private void DumpAst.applyStyle(DumpNode node) {
     Object obj = node.getObject();
     node.setLabel(getBuildConfig().getStyleInformation().getNameMethod().get(obj));
@@ -251,7 +313,7 @@ aspect GenerationBackend {
     ClassAnalysisResult result = new ClassAnalysisResult();
     String clazzName = clazz.getSimpleName();
     java.util.List<String> targetOrder = targetOrder(clazz);
-    for (java.lang.reflect.Method method : clazz.getMethods()) {
+    methodLoop: 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())) {
@@ -296,12 +358,10 @@ aspect GenerationBackend {
                 try {
                   java.lang.reflect.Method relationMethod = clazz.getMethod("get" + relationName);
                   // normal get + token-name -> singleRelation
-                  if (isRelationEnabled(clazzName, relationName)) {
-                    SingleRelationMethod singleRelationMethod = new SingleRelationMethod();
-                    singleRelationMethod.setMethod(relationMethod);
-                    singleRelationMethod.setName(relationName);
-                    result.addOtherMethod(singleRelationMethod);
-                  }
+                  SingleRelationMethod singleRelationMethod = new SingleRelationMethod();
+                  singleRelationMethod.setMethod(relationMethod);
+                  singleRelationMethod.setName(relationName);
+                  result.addOtherMethod(singleRelationMethod);
                   continue;
                 } catch (NoSuchMethodException e) {
                   // ignore, but we know this is probably not a single relation
@@ -310,23 +370,19 @@ aspect GenerationBackend {
                 try {
                   java.lang.reflect.Method relationMethod = clazz.getMethod("get" + relationName + "List");
                   // normal get + token-name + "List" -> listRelation
-                  if (isRelationEnabled(clazzName, relationName)) {
-                    ListRelationMethod listRelationMethod = new ListRelationMethod();
-                    listRelationMethod.setMethod(relationMethod);
-                    listRelationMethod.setName(relationName);
-                    result.addOtherMethod(listRelationMethod);
-                  }
+                  ListRelationMethod listRelationMethod = new ListRelationMethod();
+                  listRelationMethod.setMethod(relationMethod);
+                  listRelationMethod.setName(relationName);
+                  result.addOtherMethod(listRelationMethod);
                   continue;
                 } catch (NoSuchMethodException e) {
                   // ignore, but we know this is probably not a relation at all
                 }
               }
-              if (isTokenEnabled(clazzName, tokenName)) {
-                IntrinsicTokenMethod tokenMethod = new IntrinsicTokenMethod();
-                tokenMethod.setMethod(method);
-                tokenMethod.setName(tokenName);
-                result.addOtherMethod(tokenMethod);
-              }
+              IntrinsicTokenMethod tokenMethod = new IntrinsicTokenMethod();
+              tokenMethod.setMethod(method);
+              tokenMethod.setName(tokenName);
+              result.addOtherMethod(tokenMethod);
               break;
             case "Attribute":
               String attributeName = method.getName();
@@ -340,20 +396,18 @@ aspect GenerationBackend {
                 if (attributeName.endsWith("List")) {
                   attributeName = attributeName.substring(0, attributeName.length() - 4);
                 }
-                if (isNonterminalAttributeEnabled(clazzName, attributeName)) {
-                  if (Iterable.class.isAssignableFrom(method.getReturnType())) {
-                    NTAListChildMethod ntaListChildMethod = new NTAListChildMethod();
-                    ntaListChildMethod.setMethod(method);
-                    ntaListChildMethod.setName(attributeName);
-                    result.addOtherMethod(ntaListChildMethod);
-                  } else {
-                    NTASingleChildMethod ntaSingleChildMethod = new NTASingleChildMethod();
-                    ntaSingleChildMethod.setMethod(method);
-                    ntaSingleChildMethod.setName(attributeName);
-                    result.addOtherMethod(ntaSingleChildMethod);
-                  }
+                if (Iterable.class.isAssignableFrom(method.getReturnType())) {
+                  NTAListChildMethod ntaListChildMethod = new NTAListChildMethod();
+                  ntaListChildMethod.setMethod(method);
+                  ntaListChildMethod.setName(attributeName);
+                  result.addOtherMethod(ntaListChildMethod);
+                } else {
+                  NTASingleChildMethod ntaSingleChildMethod = new NTASingleChildMethod();
+                  ntaSingleChildMethod.setMethod(method);
+                  ntaSingleChildMethod.setName(attributeName);
+                  result.addOtherMethod(ntaSingleChildMethod);
                 }
-              } else if (isAttributeEnabled(clazzName, attributeName)) {
+              } else {
                 // normal attribute
                 AttributeMethod attributeMethod = new AttributeMethod();
                 attributeMethod.setMethod(method);
@@ -366,11 +420,11 @@ aspect GenerationBackend {
             int indexOfContextInTarget = targetOrder.indexOf(contextNameToAdd);
             if (indexOfContextInTarget == 0) {
               result.getContainmentMethodList().insertChild(containmentMethodToAdd, 0);
-              continue;
+              continue methodLoop;
             }
             if (indexOfContextInTarget == targetOrder.size() - 1) {
               result.addContainmentMethod(containmentMethodToAdd);
-              continue;
+              continue methodLoop;
             }
 
             for (int i = 0, size = result.getNumContainmentMethod(); i < size; i++) {
@@ -378,7 +432,7 @@ aspect GenerationBackend {
               int indexOfCurrentInTarget = targetOrder.indexOf(currentContextName);
               if (indexOfCurrentInTarget > indexOfContextInTarget) {
                 result.getContainmentMethodList().insertChild(containmentMethodToAdd, i);
-                break;
+                continue methodLoop;
               }
             }
             result.addContainmentMethod(containmentMethodToAdd);
@@ -418,90 +472,6 @@ aspect GenerationBackend {
   syn boolean DumpAst.isTypeEnabled(String typeName) {
     return !matches(getBuildConfig().typeIgnorePattern(), typeName);
   }
-  // --- isTokenEnabled ---
-  syn boolean DumpAst.isTokenEnabled(String parentType, String tokenName) {
-    // level 4: excluded for type? -> return no
-    PatternCollection excludeOnType = getBuildConfig().matchExcludePatternCollection(parentType);
-    if (excludeOnType != null && matches(excludeOnType.tokenPattern(), tokenName)) {
-      return false;
-    }
-    // level 3: included for type? -> return yes
-    PatternCollection includeOnType = getBuildConfig().matchIncludePatternCollection(parentType);
-    if (includeOnType != null && matches(includeOnType.tokenPattern(), tokenName)) {
-      return true;
-    }
-    // level 2: globally excluded? -> return no
-    // level 1: otherwise return yes
-    return !matches(getBuildConfig().getGlobalPatternCollection().tokenPattern(), tokenName);
-  }
-
-  // --- isChildEnabled ---
-  syn boolean DumpAst.isChildEnabled(String parentType, String childName) {
-    // level 4: excluded for type? -> return no
-    PatternCollection excludeOnType = getBuildConfig().matchExcludePatternCollection(parentType);
-    if (excludeOnType != null && matches(excludeOnType.childPattern(), childName)) {
-      return false;
-    }
-    // level 3: included for type? -> return yes
-    PatternCollection includeOnType = getBuildConfig().matchIncludePatternCollection(parentType);
-    if (includeOnType != null && matches(includeOnType.childPattern(), childName)) {
-      return true;
-    }
-    // level 2: globally excluded? -> return no
-    // level 1: otherwise return yes
-    return !matches(getBuildConfig().getGlobalPatternCollection().childPattern(), childName);
-  }
-
-  // --- isRelationEnabled ---
-  syn boolean DumpAst.isRelationEnabled(String parentType, String relationName) {
-    // level 4: excluded for type? -> return no
-    PatternCollection excludeOnType = getBuildConfig().matchExcludePatternCollection(parentType);
-    if (excludeOnType != null && matches(excludeOnType.relationPattern(), relationName)) {
-      return false;
-    }
-    // level 3: included for type? -> return yes
-    PatternCollection includeOnType = getBuildConfig().matchIncludePatternCollection(parentType);
-    if (includeOnType != null && matches(includeOnType.relationPattern(), relationName)) {
-      return true;
-    }
-    // level 2: globally excluded? -> return no
-    // level 1: otherwise return yes
-    return !matches(getBuildConfig().getGlobalPatternCollection().relationPattern(), relationName);
-  }
-
-  // --- isAttributeEnabled ---
-  syn boolean DumpAst.isAttributeEnabled(String parentType, String attributeName) {
-    // level 4: included for type? -> return yes
-    PatternCollection includeOnType = getBuildConfig().matchIncludePatternCollection(parentType);
-    if (includeOnType != null && matches(includeOnType.attributePattern(), attributeName)) {
-      return true;
-    }
-    // level 3: excluded for type? -> return no
-    PatternCollection excludeOnType = getBuildConfig().matchExcludePatternCollection(parentType);
-    if (excludeOnType != null && matches(excludeOnType.attributePattern(), attributeName)) {
-      return false;
-    }
-    // level 2: globally included? -> return yes
-    // level 1: otherwise return no
-    return matches(getBuildConfig().getGlobalPatternCollection().attributePattern(), attributeName);
-  }
-
-  // --- isNonterminalAttributeEnabled ---
-  syn boolean DumpAst.isNonterminalAttributeEnabled(String parentType, String ntaName) {
-    // level 4: included for type? -> return yes
-    PatternCollection includeOnType = getBuildConfig().matchIncludePatternCollection(parentType);
-    if (includeOnType != null && matches(includeOnType.ntaPattern(), ntaName)) {
-      return true;
-    }
-    // level 3: excluded for type? -> return no
-    PatternCollection excludeOnType = getBuildConfig().matchExcludePatternCollection(parentType);
-    if (excludeOnType != null && matches(excludeOnType.ntaPattern(), ntaName)) {
-      return false;
-    }
-    // level 2: globally included? -> return yes
-    // level 1: otherwise return no
-    return matches(getBuildConfig().getGlobalPatternCollection().ntaPattern(), ntaName);
-  }
 
   // --- match{In,Ex}cludePatternCollection ---
   syn PatternCollection BuildConfig.matchIncludePatternCollection(String typeName) {
@@ -581,7 +551,7 @@ aspect GenerationBackend {
   syn InvisiblePath DumpNode.getInvisiblePath() {
     InvisiblePath result = new InvisiblePath();
     for (DumpNode successor : reachableThroughInvisible()) {
-      result.addInnerDumpNode(new InnerDumpNode(successor));
+      result.addInnerRelationDumpNode(new InnerRelationDumpNode(successor));
     }
     return result;
   }
@@ -686,4 +656,24 @@ aspect GenerationBackend {
   public interface StyleMethod<ASTNODE> {
     String get(ASTNODE node);
   }
+
+  @FunctionalInterface
+  public interface IncludeRelationMethod<ASTNODE> {
+    boolean shouldInclude(ASTNODE sourceNode, ASTNODE targetNode, String roleName);
+  }
+
+  @FunctionalInterface
+  public interface IncludeChildMethod<ASTNODE> {
+    boolean shouldInclude(ASTNODE parentNode, ASTNODE childNode, String contextName);
+  }
+
+  @FunctionalInterface
+  public interface IncludeAttributeMethod<ASTNODE> {
+    boolean shouldInclude(ASTNODE node, String attributeName, boolean isNTA, java.util.function.Supplier<Object> value);
+  }
+
+  @FunctionalInterface
+  public interface IncludeTokenMethod<ASTNODE> {
+    boolean shouldInclude(ASTNODE node, String tokenName, Object value);
+  }
 }
diff --git a/dumpAst/src/main/jastadd/GenerationFrontend.jadd b/dumpAst/src/main/jastadd/GenerationFrontend.jadd
deleted file mode 100644
index 3e6fcf0a93911c07bc48e6571a9764776da6c291..0000000000000000000000000000000000000000
--- a/dumpAst/src/main/jastadd/GenerationFrontend.jadd
+++ /dev/null
@@ -1,457 +0,0 @@
-aspect GenerationFrontend {
-  public class Dumper {
-    /**
-     * Prepare to read in the given object. Use the <code>dump*</code> methods to actually dump its content.
-     * @param obj the object to dump
-     * @return a builder to adjust dump options
-     */
-    public static DumpBuilder read(Object obj) {
-      return new DumpBuilder(obj);
-    }
-  }
-  public enum SkinParamBooleanSetting {
-    /** Print in grayscale? */
-    Monochrome,
-    /** Use shadows? */
-    Shadowing,
-    /** Use handwritten style? */
-    Handwritten
-  }
-  public enum SkinParamStringSetting {
-    /** Set color of background */
-    backgroundColor
-  }
-
-/**
- * Building a dump.
- * <p>
- *
- * <h3>Inclusion and Exclusion of Types</h3>
- * Types can be only be disabled, see {@link #disableTypes(String[])}.
- *
- * <h3>Inclusion and Exclusion of Childrens, tokens and relations</h3>
- * Childrens, tokens and relations are included by default.
- * This can be changed using exclusions and inclusion, both in general and per-type.
- * They are applied in the following order making later conditions take precedence over the first ones.
- * <ol>
- *   <li>Include everything as default.
- *   <li>Exclude general.
- *   <li>Include per type.
- *   <li>Exclude per type.
- * </ol>
- *
- * <h3>Inclusion and Exclusion of Attributes</h3>
- * Attributes are excluded by default, i.e., not shown.
- * This can be changed using inclusions and exclusions, both in general and per-type.
- * They are applied in the following order making later conditions take precedence over the first ones.
- * <ol>
- *   <li> Exclude everything as default.
- *   <li> Include general.
- *   <li> Exclude per type.
- *   <li> Include per type
- * </ol>
- */
-public class DumpBuilder {
-    private final Object target;
-    private String packageName;
-    private DumpAst result;
-    private final BuildConfig buildConfig;
-    private final PrintConfig printConfig;
-
-    protected DumpBuilder(Object target) {
-      this.target = target;
-      buildConfig = new BuildConfig();
-      buildConfig.setGlobalPatternCollection(new PatternCollection());
-      buildConfig.setStyleInformation(StyleInformation.createDefault());
-      printConfig = new PrintConfig();
-      printConfig.setScale(1);
-      printConfig.setVersion(readVersion());
-    }
-
-    /**
-     * Add debug information in dumped content, mainly version numbers.
-     * @return this
-     */
-    public DumpBuilder enableDebug() {
-      buildConfig.setDebug(true);
-      return this;
-    }
-
-    /**
-     * Include empty strings for all tokens
-     * @return this
-     */
-    public DumpBuilder includeEmptyStringsOnTokens() {
-      buildConfig.setIncludeEmptyString(true);
-      return this;
-    }
-
-    /**
-     * Disable all objects with types matching at least one of the given regex strings.
-     * Disabled objects won't be included in any output. However, their children are still processed.
-     * <p>
-     * See {@link DumpBuilder} for details on inclusion and exclusion precedence.
-     * @param regexes patterns to match type names
-     * @return this
-     * @see java.util.regex.Pattern#compile(java.lang.String)
-     */
-    public DumpBuilder disableTypes(String regex, String... moreRegexes) {
-      updateRegexes(() -> buildConfig.getTypeIgnorePattern(),
-                    s -> buildConfig.setTypeIgnorePattern(s),
-                    regex, moreRegexes);
-      return this;
-    }
-
-    /**
-     * Exclude tokens and their value if the token name matches at least one of the given regex strings.
-     * <p>
-     * See {@link DumpBuilder} for details on inclusion and exclusion precedence.
-     * @param regexes regex patterns to match token names
-     * @return this
-     * @see java.util.regex.Pattern#compile(java.lang.String)
-     */
-    public DumpBuilder excludeTokens(String regex, String... moreRegexes) {
-      updateRegexes(() -> buildConfig.getGlobalPatternCollection().getTokenPattern(),
-                    s -> buildConfig.getGlobalPatternCollection().setTokenPattern(s),
-                    regex, moreRegexes);
-      return this;
-    }
-
-    /** TODO: document, implement, sort */
-    public DumpBuilder excludeTokensFor(String typeRegex, String regex, String... moreRegexes) {
-      PatternCollection collection = findOrCreateExcludePatternCollection(typeRegex);
-      updateRegexes(() -> collection.getTokenPattern(),
-          s -> collection.setTokenPattern(s),
-          regex, moreRegexes);
-      return this;
-    }
-
-    /** TODO: document, implement, sort */
-    public DumpBuilder excludeChildrenFor(String typeRegex, String regex, String... moreRegexes) {
-      PatternCollection collection = findOrCreateExcludePatternCollection(typeRegex);
-      updateRegexes(() -> collection.getChildPattern(),
-          s -> collection.setChildPattern(s),
-          regex, moreRegexes);
-      return this;
-    }
-
-    /** TODO: document, implement, sort */
-    public DumpBuilder excludeRelationsFor(String typeRegex, String regex, String... moreRegexes) {
-      PatternCollection collection = findOrCreateExcludePatternCollection(typeRegex);
-      updateRegexes(() -> collection.getRelationPattern(),
-          s -> collection.setRelationPattern(s),
-          regex, moreRegexes);
-      return this;
-    }
-
-    /** TODO: document, implement, sort */
-    public DumpBuilder excludeAttributesFor(String typeRegex, String regex, String... moreRegexes) {
-      PatternCollection collection = findOrCreateExcludePatternCollection(typeRegex);
-      updateRegexes(() -> collection.getAttributePattern(),
-          s -> collection.setAttributePattern(s),
-          regex, moreRegexes);
-      return this;
-    }
-
-    /** TODO: document, implement, sort */
-    public DumpBuilder excludeNonterminalAttributesFor(String typeRegex, String regex, String... moreRegexes) {
-      PatternCollection collection = findOrCreateExcludePatternCollection(typeRegex);
-      updateRegexes(() -> collection.getNonterminalAttributePattern(),
-          s -> collection.setNonterminalAttributePattern(s),
-          regex, moreRegexes);
-      return this;
-    }
-
-    /** TODO: document, implement, sort */
-    public DumpBuilder includeTokensFor(String typeRegex, String regex, String... moreRegexes) {
-      PatternCollection collection = findOrCreateIncludePatternCollection(typeRegex);
-      updateRegexes(() -> collection.getTokenPattern(),
-          s -> collection.setTokenPattern(s),
-          regex, moreRegexes);
-      return this;
-    }
-
-    /** TODO: document, implement, sort */
-    public DumpBuilder includeChildrenFor(String typeRegex, String regex, String... moreRegexes) {
-      PatternCollection collection = findOrCreateIncludePatternCollection(typeRegex);
-      updateRegexes(() -> collection.getChildPattern(),
-          s -> collection.setChildPattern(s),
-          regex, moreRegexes);
-      return this;
-    }
-
-    /** TODO: document, implement, sort */
-    public DumpBuilder includeRelationsFor(String typeRegex, String regex, String... moreRegexes) {
-      PatternCollection collection = findOrCreateIncludePatternCollection(typeRegex);
-      updateRegexes(() -> collection.getRelationPattern(),
-          s -> collection.setRelationPattern(s),
-          regex, moreRegexes);
-      return this;
-    }
-
-    /** TODO: document, implement, sort */
-    public DumpBuilder includeAttributesFor(String typeRegex, String regex, String... moreRegexes) {
-      PatternCollection collection = findOrCreateIncludePatternCollection(typeRegex);
-      updateRegexes(() -> collection.getAttributePattern(),
-          s -> collection.setAttributePattern(s),
-          regex, moreRegexes);
-      return this;
-    }
-
-    /** TODO: document, implement, sort */
-    public DumpBuilder includeNonterminalAttributesFor(String typeRegex, String regex, String... moreRegexes) {
-      PatternCollection collection = findOrCreateIncludePatternCollection(typeRegex);
-      updateRegexes(() -> collection.getNonterminalAttributePattern(),
-          s -> collection.setNonterminalAttributePattern(s),
-          regex, moreRegexes);
-      return this;
-    }
-
-    /**
-     * Include attributes (as tokens) and their value if the attribute name matches at least on of the given regex strings.
-     * <p>
-     * See {@link DumpBuilder} for details on inclusion and exclusion precedence.
-     * @param regexes regex patterns to match token names
-     * @return this
-     * @see java.util.regex.Pattern#compile(java.lang.String)
-     */
-    public DumpBuilder includeAttributes(String regex, String... moreRegexes) {
-      updateRegexes(() -> buildConfig.getGlobalPatternCollection().getAttributePattern(),
-                    s -> buildConfig.getGlobalPatternCollection().setAttributePattern(s),
-                    regex, moreRegexes);
-      return this;
-    }
-
-    /**
-     * Includes nonterminal-attributes (as children) and their values if
-     * their attribute name matches at least on of the given regex strings.
-     * <br>
-     * <b>Note</b>: A leading "get" and a trailing "List" in the name will be removed prior to matching.
-     * Thus, it should not be contained in the regex either.
-     * <p>
-     * See {@link DumpBuilder} for details on inclusion and exclusion precedence.
-     * @param regexes regex patterns to match token names
-     * @return this
-     * @see java.util.regex.Pattern#compile(java.lang.String)
-     */
-    public DumpBuilder includeNonterminalAttributes(String regex, String... moreRegexes) {
-      updateRegexes(() -> buildConfig.getGlobalPatternCollection().getNonterminalAttributePattern(),
-                    s -> buildConfig.getGlobalPatternCollection().setNonterminalAttributePattern(s),
-                    regex, moreRegexes);
-      return this;
-    }
-
-    /**
-     * Exclude every child whose name (i.e., context) matches at least on of the given regex strings.
-     * This means, that the complete object and its (transitive) children will never be included in any output.
-     * <p>
-     * See {@link DumpBuilder} for details on inclusion and exclusion precedence.
-     * @param regexes regex patterns to match child names
-     * @return this
-     * @see java.util.regex.Pattern#compile(java.lang.String)
-     */
-    public DumpBuilder excludeChildren(String regex, String... moreRegexes) {
-      updateRegexes(() -> buildConfig.getGlobalPatternCollection().getChildPattern(),
-                    s -> buildConfig.getGlobalPatternCollection().setChildPattern(s),
-                    regex, moreRegexes);
-      return this;
-    }
-    /**
-     * Exclude every relation whose role-name matches at least on of the given regex strings.
-     * This means two things: a) the relation to any potential target object(s) is never shown, and b) the target
-     *  object(s) are not shown unless they are reachable by another relation or by containment.
-     * <p>
-     * See {@link DumpBuilder} for details on inclusion and exclusion precedence.
-     * @param regexes regex patterns to match child names
-     * @return this
-     * @see java.util.regex.Pattern#compile(java.lang.String)
-     */
-    public DumpBuilder excludeRelations(String regex, String... moreRegexes) {
-      updateRegexes(() -> buildConfig.getGlobalPatternCollection().getRelationPattern(),
-                    s -> buildConfig.getGlobalPatternCollection().setRelationPattern(s),
-                    regex, moreRegexes);
-      return this;
-    }
-
-    private PatternCollection findOrCreateIncludePatternCollection(String typeRegex) {
-      PatternCollection result = buildConfig.findIncludePatternCollection(typeRegex);
-      if (result == null) {
-        TypePatternCollectionMapping mapping = new TypePatternCollectionMapping();
-        mapping.setTypeRegex(typeRegex);
-        result = new PatternCollection();
-        mapping.setPatternCollection(result);
-        buildConfig.addIncludeTypePattern(mapping);
-      }
-      return result;
-    }
-
-    private PatternCollection findOrCreateExcludePatternCollection(String typeRegex) {
-      PatternCollection result = buildConfig.findExcludePatternCollection(typeRegex);
-      if (result == null) {
-        TypePatternCollectionMapping mapping = new TypePatternCollectionMapping();
-        mapping.setTypeRegex(typeRegex);
-        result = new PatternCollection();
-        mapping.setPatternCollection(result);
-        buildConfig.addExcludeTypePattern(mapping);
-      }
-      return result;
-    }
-
-    private void updateRegexes(java.util.function.Supplier<String> getter, java.util.function.Consumer<String> setter, String regex, String... moreRegexes) {
-      if (getter.get().isEmpty()) {
-        setter.accept(regex);
-      } else {
-        setter.accept(getter.get() + "|" + regex);
-      }
-      for (String value : moreRegexes) {
-        setter.accept(getter.get() + "|" + value);
-      }
-    }
-
-    public DumpBuilder excludeNullNodes() {
-      buildConfig.setExcludeNullNodes(true);
-      return this;
-    }
-
-    public DumpBuilder includeNullNodes() {
-      buildConfig.setExcludeNullNodes(false);
-      return this;
-    }
-
-    public DumpBuilder customPreamble(String option) {
-      printConfig.addHeader(new Header(option));
-      return this;
-    }
-
-    public DumpBuilder skinParam(SkinParamStringSetting setting, String value) {
-      customPreamble("skinparam " + setting.toString() + " " + value);
-      return this;
-    }
-    public DumpBuilder skinParam(SkinParamBooleanSetting setting, boolean value) {
-      customPreamble("skinparam " + setting.toString() + " " + value);
-      return this;
-    }
-
-    public <ASTNODE> DumpBuilder setNameMethod(StyleMethod<ASTNODE> nameMethod) {
-      buildConfig.getStyleInformation().setNameMethod(nameMethod);
-      return this;
-    }
-
-    public <ASTNODE> DumpBuilder setBackgroundColorMethod(StyleMethod<ASTNODE> colorMethod) {
-      buildConfig.getStyleInformation().setBackgroundColorMethod(colorMethod);
-      return this;
-    }
-
-    public <ASTNODE> DumpBuilder setTextColorMethod(StyleMethod<ASTNODE> colorMethod) {
-      buildConfig.getStyleInformation().setTextColorMethod(colorMethod);
-      return this;
-    }
-
-    public <ASTNODE> DumpBuilder setStereotypeMethod(StyleMethod<ASTNODE> stereotypeMethod) {
-      buildConfig.getStyleInformation().setStereotypeMethod(stereotypeMethod);
-      return this;
-    }
-
-    public DumpBuilder setComputedColor(String color) {
-      buildConfig.getStyleInformation().setComputedColor(color);
-      return this;
-    }
-
-    public DumpBuilder setScale(double value) {
-      printConfig.setScale(value);
-      return this;
-    }
-
-    public DumpBuilder orderChildren() {
-      printConfig.setOrderChildren(true);
-      return this;
-    }
-
-    private String readVersion() {
-      try {
-        java.util.ResourceBundle resources = java.util.ResourceBundle.getBundle("dumpAstVersion");
-        return resources.getString("version");
-      } catch (java.util.MissingResourceException e) {
-        return "version ?";
-      }
-    }
-
-    protected DumpAst build() {
-      if (result == null) {
-        result = new DumpAst();
-        final String packageNameToUse;
-        if (this.packageName != null) {
-          packageNameToUse = this.packageName;
-        } else {
-          if (this.target == null) {
-            packageNameToUse = null;
-          } else {
-            packageNameToUse = this.target.getClass().getPackage().getName();
-          }
-        }
-        result.setPackageName(packageNameToUse);
-        result.setBuildConfig(this.buildConfig);
-        result.setPrintConfig(this.printConfig);
-        try {
-          result.transform(new TransformationTransferInformation(), this.target);
-        } catch (java.lang.reflect.InvocationTargetException e) {
-          throw new RuntimeException("Could not transform :(", e);
-        } catch (IllegalAccessException e) {
-          throw new RuntimeException("Could not transform :(", e);
-        } catch (NoSuchMethodException e) {
-          throw new RuntimeException("Could not transform :(", e);
-        }
-      }
-      return result;
-    }
-
-    /**
-     * Write out content as plantuml source code.
-     * @param destination path of destination file
-     * @return this
-     * @throws java.io.IOException if an I/O error happend during opening or writing in that file
-     */
-    public DumpBuilder dumpAsSource(java.nio.file.Path destination) throws java.io.IOException {
-      String content = build().toPlantUml();
-      try (java.io.Writer writer = java.nio.file.Files.newBufferedWriter(destination)) {
-        writer.write(content);
-      }
-      return this;
-    }
-
-    public DumpBuilder dumpAsYaml(java.nio.file.Path destination, boolean prependCreationComment) throws java.io.IOException {
-      String content = build().printYaml(prependCreationComment);
-      try (java.io.Writer writer = java.nio.file.Files.newBufferedWriter(destination)) {
-        writer.write(content);
-      }
-      return this;
-    }
-
-    /**
-     * Write out content as PNG image generated by plantuml.
-     * @param destination path of destination file
-     * @return this
-     * @throws java.io.IOException if an I/O error happend during opening or writing in that file
-     */
-    public DumpBuilder dumpAsPNG(java.nio.file.Path destination) throws java.io.IOException {
-      String content = build().toPlantUml();
-      net.sourceforge.plantuml.SourceStringReader reader = new net.sourceforge.plantuml.SourceStringReader(content);
-      reader.outputImage(java.nio.file.Files.newOutputStream(destination));
-      return this;
-    }
-
-    /**
-     * Write out content as SVG image generated by plantuml.
-     * @param destination path of destination file
-     * @return this
-     * @throws java.io.IOException if an I/O error happend during opening or writing in that file
-     */
-    public DumpBuilder dumpAsSVG(java.nio.file.Path destination) throws java.io.IOException {
-      String content = build().toPlantUml();
-      net.sourceforge.plantuml.SourceStringReader reader = new net.sourceforge.plantuml.SourceStringReader(content);
-      reader.outputImage(java.nio.file.Files.newOutputStream(destination),
-          new net.sourceforge.plantuml.FileFormatOption(net.sourceforge.plantuml.FileFormat.SVG));
-      return this;
-    }
-  }
-}
diff --git a/dumpAst/src/main/jastadd/GenerationToYaml.jrag b/dumpAst/src/main/jastadd/GenerationToYaml.jrag
index 30a093c9f2495e68f4b552cedf9ce16d77e7ed06..5f3d3b26dac7c41a48564599cdf5bd80d158a0f4 100644
--- a/dumpAst/src/main/jastadd/GenerationToYaml.jrag
+++ b/dumpAst/src/main/jastadd/GenerationToYaml.jrag
@@ -26,14 +26,6 @@ aspect GenerationToYaml {
     return result;
   }
 
-  static void ASTNode.addYamledList(MappingElement base, String key, Iterable<? extends ASTNode<?>> iterable, boolean fromRelation) {
-    ListElement innerList = new ListElement();
-    for (ASTNode node : iterable) {
-      innerList.add(safeToYaml(node, fromRelation));
-    }
-    base.put(key, innerList);
-  }
-
   syn MappingElement PrintConfig.toYaml(boolean fromRelation) {
     MappingElement result = new MappingElement();
     // children
@@ -128,6 +120,7 @@ aspect GenerationToYaml {
     result.put("name", getName());
     result.put("computed", getComputed());
     // attributes
+    result.put("isList", isList());
     result.put("label", label());
     result.put("isDumpValueToken", isDumpValueToken());
     return result;
@@ -142,8 +135,20 @@ aspect GenerationToYaml {
 
   syn MappingElement DumpReferenceToken.toYaml(boolean fromRelation) {
     MappingElement result = super.toYaml(fromRelation);
-    // tokens
+    // attributes
     result.put("innerNodeName", innerNodeName());
+    result.put("innerNotNull", innerNotNull());
+    result.put("outerNodeName", outerNodeName());
+    return result;
+  }
+
+  syn MappingElement DumpReferenceListToken.toYaml(boolean fromRelation) {
+    MappingElement result = super.toYaml(fromRelation);
+
+    // children
+    addYamledList(result, "InnerRelationDumpNode", getInnerRelationDumpNodeList(), fromRelation);
+
+    // attributes
     result.put("outerNodeName", outerNodeName());
     return result;
   }
@@ -165,13 +170,14 @@ aspect GenerationToYaml {
     // attributes
     result.put("bothVisible", bothVisible());
     result.put("innerNodeName", innerNodeName());
+    result.put("innerNotNull", innerNotNull());
     return result;
   }
 
   syn MappingElement DumpListRelation.toYaml(boolean fromRelation) {
     MappingElement result = super.toYaml(fromRelation);
     // children
-    addYamledList(result, "InnerDumpNodes", getInnerDumpNodeList(), fromRelation);
+    addYamledList(result, "InnerRelationDumpNode", getInnerRelationDumpNodeList(), fromRelation);
     return result;
   }
 
@@ -185,13 +191,32 @@ aspect GenerationToYaml {
     return result;
   }
 
+  syn MappingElement InnerRelationDumpNode.toYaml(boolean fromRelation) {
+    MappingElement result = new MappingElement();
+    // attributes
+    result.put("bothVisible", bothVisible());
+    result.put("innerNodeName", innerNodeName());
+    result.put("innerNotNull", innerNotNull());
+    result.put("outerNodeName", outerNodeName());
+    result.put("label", label());
+    return result;
+  }
+
   syn MappingElement InvisiblePath.toYaml(boolean fromRelation) {
     MappingElement result = super.toYaml(fromRelation);
     // children
-    addYamledList(result, "InnerDumpNodes", getInnerDumpNodeList(), fromRelation);
+    addYamledList(result, "InnerRelationDumpNode", getInnerRelationDumpNodeList(), fromRelation);
     return result;
   }
 
+  static void ASTNode.addYamledList(MappingElement base, String key, Iterable<? extends ASTNode<?>> iterable, boolean fromRelation) {
+    ListElement innerList = new ListElement();
+    for (ASTNode node : iterable) {
+      innerList.add(safeToYaml(node, fromRelation));
+    }
+    base.put(key, innerList);
+  }
+
   // extension for mustache
   public static ValueElement ValueElement.of(double value) {
     return new ValueElement(false, String.valueOf(value));
diff --git a/dumpAst/src/main/jastadd/Navigation.jrag b/dumpAst/src/main/jastadd/Navigation.jrag
index c69141d3208bc5f99ee866d6417b043daaac9a03..dc4ce9a3e857777d7305d07f48a436fb18c0bfc7 100644
--- a/dumpAst/src/main/jastadd/Navigation.jrag
+++ b/dumpAst/src/main/jastadd/Navigation.jrag
@@ -4,6 +4,8 @@ aspect Navigation {
   eq DumpListChildNode.isList() = true;
   syn boolean DumpRelation.isList() = false;
   eq DumpListRelation.isList() = true;
+  syn boolean DumpToken.isList() = false;
+  eq DumpReferenceListToken.isList() = true;
 
   // --- buildConfig ---
   inh BuildConfig DumpNode.buildConfig();
@@ -12,14 +14,20 @@ aspect Navigation {
 
   // --- printConfig ---
   inh PrintConfig BuildConfig.printConfig();
+  inh PrintConfig DumpNode.printConfig();
+  inh PrintConfig DumpNormalRelation.printConfig();
+  inh PrintConfig InnerRelationDumpNode.printConfig();
+  inh PrintConfig DumpReferenceToken.printConfig();
   eq DumpAst.getChild().printConfig() = getPrintConfig();
 
   // --- containingDumpNode ---
   inh DumpNode InnerDumpNode.containingDumpNode();
+  inh DumpNode InnerRelationDumpNode.containingDumpNode();
   inh DumpNode DumpChildNode.containingDumpNode();
   inh DumpNode DumpRelation.containingDumpNode();
   eq DumpNode.getDumpChildNode().containingDumpNode() = this;
   eq DumpNode.getDumpRelation().containingDumpNode() = this;
+  eq DumpNode.getDumpToken().containingDumpNode() = this;
   eq DumpNode.getInvisiblePath().containingDumpNode() = this;
 
   // --- container ---
@@ -69,7 +77,7 @@ aspect Navigation {
       return java.util.Collections.emptyList();
     }
     java.util.List<DumpNode> result = new java.util.ArrayList<>();
-    getInnerDumpNodeList().forEach(inner -> {
+    getInnerRelationDumpNodeList().forEach(inner -> {
       if (!onlyVisible || !inner.getDumpNode().getInvisible()) {
         result.add(inner.getDumpNode());
       }
@@ -77,6 +85,11 @@ aspect Navigation {
     return result;
   }
 
+  // --- innerNotNull ---
+  syn boolean DumpNormalRelation.innerNotNull() = !printConfig().getRelationWithRank() && getDumpNode() != null && getDumpNode().getObject() != null;
+  syn boolean DumpReferenceToken.innerNotNull() = !printConfig().getRelationWithRank() && getValue() != null && getValue().getObject() != null;
+  syn boolean InnerRelationDumpNode.innerNotNull() = !printConfig().getRelationWithRank() && getDumpNode() != null && getDumpNode().getObject() != null;
+
   // === Method naviagtion ===
   coll java.util.List<SingleChildMethod> ClassAnalysisResult.singleChildMethods() [new java.util.ArrayList<>()] root ClassAnalysisResult;
   SingleChildMethod contributes this to ClassAnalysisResult.singleChildMethods();
diff --git a/dumpAst/src/main/jastadd/Printing.jrag b/dumpAst/src/main/jastadd/Printing.jrag
index e4565af809152de0c654dd6bc613dd5aab2c3e02..6ae0af30b8273ed64d20e486035ef03b912f4844 100644
--- a/dumpAst/src/main/jastadd/Printing.jrag
+++ b/dumpAst/src/main/jastadd/Printing.jrag
@@ -1,13 +1,16 @@
 aspect Printing {
   // --- outerNodeName ---
   inh String InnerDumpNode.outerNodeName();
+  inh String InnerRelationDumpNode.outerNodeName();
   inh String DumpChildNode.outerNodeName();
   inh String DumpRelation.outerNodeName();
+  inh String DumpToken.outerNodeName();
   inh String DumpReferenceToken.outerNodeName();
   eq DumpNode.getChild().outerNodeName() = name();
 
   // --- innerNodeName ---
   syn String InnerDumpNode.innerNodeName() = getDumpNode() != null ? getDumpNode().name() : null;
+  syn String InnerRelationDumpNode.innerNodeName() = getDumpNode() != null ? getDumpNode().name() : null;
   syn String DumpNormalChildNode.innerNodeName() = getDumpNode() != null ? getDumpNode().name() : null;
   syn String DumpNormalRelation.innerNodeName() = getDumpNode() != null ? getDumpNode().name() : null;
   syn String DumpReferenceToken.innerNodeName() = getValue().name();
@@ -21,15 +24,46 @@ aspect Printing {
   syn String DumpToken.label() = getName() + (getComputed() ? "()" : "");
   syn String DumpNode.label() = getLabel();
   inh String InnerDumpNode.label();
+  inh String InnerRelationDumpNode.label();
   eq DumpListChildNode.getInnerDumpNode(int index).label() = label() + "[" + index + "]";
-  eq DumpListRelation.getInnerDumpNode(int index).label() = label() + "[" + index + "]";
-  eq InvisiblePath.getInnerDumpNode(int index).label() = null;
+  eq DumpListRelation.getInnerRelationDumpNode(int index).label() = label() + "[" + index + "]";
+  eq DumpReferenceListToken.getInnerRelationDumpNode(int index).label() = label() + "[" + index + "]";
+  eq InvisiblePath.getInnerRelationDumpNode(int index).label() = null;
 
   // --- bothVisible ---
   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 InnerRelationDumpNode.bothVisible() = bothVisible(containingDumpNode(), getDumpNode());
   syn boolean DumpNormalChildNode.bothVisible() = bothVisible(containingDumpNode(), getDumpNode());
   syn boolean DumpNormalRelation.bothVisible() = bothVisible(containingDumpNode(), getDumpNode());
 }
+
+aspect Debugging {
+  syn String ClassAnalysisResult.prettyPrint() {
+    StringBuilder sb = new StringBuilder();
+    sb.append("ContainmentMethods:");
+    for (AnalysedMethod method : getContainmentMethodList()) {
+      sb.append(method.prettyPrint()).append(",");
+    }
+    if (getNumContainmentMethod() == 0) {
+      sb.append("none. ");
+    }
+    sb.append("other methods:");
+    for (AnalysedMethod method : getOtherMethodList()) {
+      sb.append(method.prettyPrint()).append(",");
+    }
+    if (getNumOtherMethod() == 0) {
+      sb.append("none.");
+    }
+    return sb.toString();
+  }
+  syn String AnalysedMethod.prettyPrint() {
+    String methodString = "?";
+    try {
+      methodString = getMethod().toString();
+    } catch (Exception ignore) {}
+    return this.getClass().getSimpleName() + "[Method: " + methodString + ", Name: " + getName() + "]";
+  }
+}
diff --git a/dumpAst/src/main/java/de/tudresden/inf/st/jastadd/dumpAst/ast/DumpBuilder.java b/dumpAst/src/main/java/de/tudresden/inf/st/jastadd/dumpAst/ast/DumpBuilder.java
new file mode 100644
index 0000000000000000000000000000000000000000..9fdc7b02648849f89395c98bd989c36874266d94
--- /dev/null
+++ b/dumpAst/src/main/java/de/tudresden/inf/st/jastadd/dumpAst/ast/DumpBuilder.java
@@ -0,0 +1,789 @@
+package de.tudresden.inf.st.jastadd.dumpAst.ast;
+
+import java.lang.reflect.InvocationTargetException;
+
+import static de.tudresden.inf.st.jastadd.dumpAst.ast.ASTNode.matches;
+
+/**
+ * Building a dump.
+ * <p>
+ *
+ * <h3>Inclusion and Exclusion of Types</h3>
+ * Types can be only be disabled, see {@link #disableTypes(String, String...)}.
+ *
+ * <h3>Inclusion and Exclusion of children, tokens and relations</h3>
+ * Children, tokens and relations are included by default.
+ * This can be changed using exclusions and inclusion, both in general and per-type.
+ * They are applied in the following order making later conditions take precedence over the first ones.
+ * <ol>
+ *   <li>Include everything as default.
+ *   <li>Exclude general.
+ *   <li>Include per type.
+ *   <li>Exclude per type.
+ * </ol>
+ *
+ * <h3>Inclusion and Exclusion of Attributes</h3>
+ * Attributes are excluded by default, i.e., not shown.
+ * This can be changed using inclusions and exclusions, both in general and per-type.
+ * They are applied in the following order making later conditions take precedence over the first ones.
+ * <ol>
+ *   <li> Exclude everything as default.
+ *   <li> Include general.
+ *   <li> Exclude per type.
+ *   <li> Include per type
+ * </ol>
+ */
+@SuppressWarnings("UnusedReturnValue")
+public class DumpBuilder {
+  private final Object target;
+  private String packageName;
+  private DumpAst result;
+  private final BuildConfig buildConfig;
+  private final PrintConfig printConfig;
+
+  protected DumpBuilder(Object target) {
+    this.target = target;
+    buildConfig = new BuildConfig();
+    buildConfig.setIncludeChildMethod((parentNode, childNode, contextName) -> {
+      System.out.printf("child: %s, %s, %s%n", parentNode, childNode, contextName);
+      // level 4: excluded for type? -> return no
+      PatternCollection excludeOnType = buildConfig.matchExcludePatternCollection(parentNode.getClass().getSimpleName());
+      if (excludeOnType != null && matches(excludeOnType.childPattern(), contextName)) {
+        return false;
+      }
+      // level 3: included for type? -> return yes
+      PatternCollection includeOnType = buildConfig.matchIncludePatternCollection(parentNode.getClass().getSimpleName());
+      if (includeOnType != null && matches(includeOnType.childPattern(), contextName)) {
+        return true;
+      }
+      // level 2: globally excluded? -> return no
+      // level 1: otherwise return yes
+      return !matches(buildConfig.getGlobalPatternCollection().childPattern(), contextName);
+    });
+    buildConfig.setIncludeRelationMethod((sourceNode, targetNode, roleName) -> {
+      // level 4: excluded for type? -> return no
+      PatternCollection excludeOnType = buildConfig.matchExcludePatternCollection(sourceNode.getClass().getSimpleName());
+      if (excludeOnType != null && matches(excludeOnType.relationPattern(), roleName)) {
+        return false;
+      }
+      // level 3: included for type? -> return yes
+      PatternCollection includeOnType = buildConfig.matchIncludePatternCollection(sourceNode.getClass().getSimpleName());
+      if (includeOnType != null && matches(includeOnType.relationPattern(), roleName)) {
+        return true;
+      }
+      // level 2: globally excluded? -> return no
+      // level 1: otherwise return yes
+      return !matches(buildConfig.getGlobalPatternCollection().relationPattern(), roleName);
+    });
+    buildConfig.setIncludeTokenMethod((node, tokenName, value) -> {
+      // level 4: excluded for type? -> return no
+      PatternCollection excludeOnType = buildConfig.matchExcludePatternCollection(node.getClass().getSimpleName());
+      if (excludeOnType != null && matches(excludeOnType.tokenPattern(), tokenName)) {
+        return false;
+      }
+      // level 3: included for type? -> return yes
+      PatternCollection includeOnType = buildConfig.matchIncludePatternCollection(node.getClass().getSimpleName());
+      if (includeOnType != null && matches(includeOnType.tokenPattern(), tokenName)) {
+        return true;
+      }
+      // level 2: globally excluded? -> return no
+      // level 1: otherwise return yes
+      return !matches(buildConfig.getGlobalPatternCollection().tokenPattern(), tokenName);
+    });
+    buildConfig.setIncludeAttributeMethod((node, attributeName, isNTA, supplier) -> {
+      // level 4: included for type? -> return yes
+      PatternCollection includeOnType = buildConfig.matchIncludePatternCollection(node.getClass().getSimpleName());
+      if (includeOnType != null && matches(isNTA ? includeOnType.ntaPattern() : includeOnType.attributePattern(), attributeName)) {
+        return true;
+      }
+      // level 3: excluded for type? -> return no
+      PatternCollection excludeOnType = buildConfig.matchExcludePatternCollection(node.getClass().getSimpleName());
+      if (excludeOnType != null && matches(isNTA ? excludeOnType.ntaPattern() : excludeOnType.attributePattern(), attributeName)) {
+        return false;
+      }
+      // level 2: globally included? -> return yes
+      // level 1: otherwise return no
+      PatternCollection global = buildConfig.getGlobalPatternCollection();
+      return matches(isNTA ? global.ntaPattern() : global.attributePattern(), attributeName);
+    });
+    buildConfig.setGlobalPatternCollection(new PatternCollection());
+    buildConfig.setStyleInformation(StyleInformation.createDefault());
+    printConfig = new PrintConfig();
+    printConfig.setScale(1);
+    printConfig.setVersion(readVersion());
+  }
+
+  /**
+   * Add debug information in dumped content, mainly version numbers.
+   * Also dump debugging information while reading in object.
+   *
+   * @return this
+   */
+  public DumpBuilder enableDebug() {
+    buildConfig.setDebug(true);
+    return this;
+  }
+
+  /**
+   * Set the name of the package, where all AST classes are located.
+   * @param packageName name of the package
+   * @return this
+   */
+  public DumpBuilder setPackageName(String packageName) {
+    this.packageName = packageName;
+    return this;
+  }
+
+  /**
+   * Include empty strings for all tokens
+   *
+   * @return this
+   */
+  public DumpBuilder includeEmptyStringsOnTokens() {
+    buildConfig.setIncludeEmptyString(true);
+    return this;
+  }
+
+  /**
+   * Let all relations (and reference attributes) influence layouting of nodes (disabled by default).
+   * @return this
+   */
+  public DumpBuilder enableRelationWithRank() {
+    printConfig.setRelationWithRank(true);
+    return this;
+  }
+
+  // --- Types ---
+
+  /**
+   * Disable all objects with types matching at least one of the given regex strings.
+   * Disabled objects won't be included in any output. However, their children are still processed.
+   * <p>
+   * See {@link DumpBuilder} for details on inclusion and exclusion precedence.
+   *
+   * @param regex first pattern to match type names
+   * @param moreRegexes more patterns to match type names
+   * @return this
+   * @see java.util.regex.Pattern#compile(String)
+   */
+  public DumpBuilder disableTypes(String regex, String... moreRegexes) {
+    updateRegexes(buildConfig::getTypeIgnorePattern,
+        buildConfig::setTypeIgnorePattern,
+        regex, moreRegexes);
+    return this;
+  }
+
+  // --- Tokens ---
+
+  public <ASTNODE> DumpBuilder includeTokensWhen(IncludeTokenMethod<ASTNODE> spec) {
+    buildConfig.setIncludeTokenMethod(spec);
+    if (!buildConfig.getGlobalPatternCollection().getTokenPattern().isEmpty()) {
+      System.err.println("Overriding previous filters for tokens");
+    }
+    return this;
+  }
+
+  /**
+   * Exclude tokens and their value if the token name matches at least one of the given regex strings.
+   * <p>
+   * See {@link DumpBuilder} for details on inclusion and exclusion precedence.
+   *
+   * @param regex first pattern to match token names
+   * @param moreRegexes more patterns to match token names
+   * @return this
+   * @see java.util.regex.Pattern#compile(String)
+   */
+  public DumpBuilder excludeTokens(String regex, String... moreRegexes) {
+    updateRegexes(() -> buildConfig.getGlobalPatternCollection().getTokenPattern(),
+        s -> buildConfig.getGlobalPatternCollection().setTokenPattern(s),
+        regex, moreRegexes);
+    return this;
+  }
+
+  /**
+   * Exclude tokens and their value within a type if the token name matches at least one of the given regex strings.
+   * <p>
+   * See {@link DumpBuilder} for details on inclusion and exclusion precedence.
+   *
+   * @param typeRegex pattern to match a nonterminal name
+   * @param regex first pattern to match token names
+   * @param moreRegexes more patterns to match token names
+   * @return this
+   * @see java.util.regex.Pattern#compile(String)
+   */
+  public DumpBuilder excludeTokensFor(String typeRegex, String regex, String... moreRegexes) {
+    PatternCollection collection = findOrCreateExcludePatternCollection(typeRegex);
+    updateRegexes(collection::getTokenPattern,
+        collection::setTokenPattern,
+        regex, moreRegexes);
+    return this;
+  }
+
+  /**
+   * Include tokens (again) and their value within a type if the token name matches at least one of the given regex strings.
+   * <p>
+   * See {@link DumpBuilder} for details on inclusion and exclusion precedence.
+   *
+   * @param typeRegex pattern to match a nonterminal name
+   * @param regex first pattern to match token names
+   * @param moreRegexes more patterns to match token names
+   * @return this
+   * @see java.util.regex.Pattern#compile(String)
+   */
+  public DumpBuilder includeTokensFor(String typeRegex, String regex, String... moreRegexes) {
+    PatternCollection collection = findOrCreateIncludePatternCollection(typeRegex);
+    updateRegexes(collection::getTokenPattern,
+        collection::setTokenPattern,
+        regex, moreRegexes);
+    return this;
+  }
+
+  // --- Children ---
+
+  public <ASTNODE> DumpBuilder includeChildWhen(IncludeChildMethod<ASTNODE> spec) {
+    buildConfig.setIncludeChildMethod(spec);
+    if (!buildConfig.getGlobalPatternCollection().getChildPattern().isEmpty()) {
+      System.err.println("Overriding previous filters for children");
+    }
+    return this;
+  }
+
+  /**
+   * Exclude every child whose name (i.e., context) matches at least on of the given regex strings.
+   * This means, that the complete object and its (transitive) children will never be included in any output.
+   * <p>
+   * See {@link DumpBuilder} for details on inclusion and exclusion precedence.
+   *
+   * @param regex first pattern to match child name
+   * @param moreRegexes more patterns to match child names
+   * @return this
+   * @see java.util.regex.Pattern#compile(String)
+   */
+  public DumpBuilder excludeChildren(String regex, String... moreRegexes) {
+    updateRegexes(() -> buildConfig.getGlobalPatternCollection().getChildPattern(),
+        s -> buildConfig.getGlobalPatternCollection().setChildPattern(s),
+        regex, moreRegexes);
+    return this;
+  }
+
+  /**
+   * Exclude every child within a type whose name (i.e., context) matches at least on of the given regex strings.
+   * This means, that the complete object and its (transitive) children will never be included in any output.
+   * <p>
+   * See {@link DumpBuilder} for details on inclusion and exclusion precedence.
+   *
+   * @param typeRegex pattern to match a nonterminal name
+   * @param regex first pattern to match child name
+   * @param moreRegexes more patterns to match child names
+   * @return this
+   * @see java.util.regex.Pattern#compile(String)
+   */
+  public DumpBuilder excludeChildrenFor(String typeRegex, String regex, String... moreRegexes) {
+    PatternCollection collection = findOrCreateExcludePatternCollection(typeRegex);
+    updateRegexes(collection::getChildPattern,
+        collection::setChildPattern,
+        regex, moreRegexes);
+    return this;
+  }
+
+  /**
+   * Include every child (again) within a type whose name (i.e., context) matches at least on of the given regex strings.
+   * This means, that the complete object and its (transitive) children will never be included in any output.
+   * <p>
+   * See {@link DumpBuilder} for details on inclusion and exclusion precedence.
+   *
+   * @param typeRegex pattern to match a nonterminal name
+   * @param regex first pattern to match child name
+   * @param moreRegexes more patterns to match child names
+   * @return this
+   * @see java.util.regex.Pattern#compile(String)
+   */
+  public DumpBuilder includeChildrenFor(String typeRegex, String regex, String... moreRegexes) {
+    PatternCollection collection = findOrCreateIncludePatternCollection(typeRegex);
+    updateRegexes(collection::getChildPattern,
+        collection::setChildPattern,
+        regex, moreRegexes);
+    return this;
+  }
+
+  // --- Attributes ---
+
+  public <ASTNODE> DumpBuilder includeAttributeWhen(IncludeAttributeMethod<ASTNODE> spec) {
+    buildConfig.setIncludeAttributeMethod(spec);
+    if (!buildConfig.getGlobalPatternCollection().getAttributePattern().isEmpty()) {
+      System.err.println("Overriding previous filters for attributes");
+    }
+    return this;
+  }
+
+  /**
+   * Include attributes (as tokens) and their value if the attribute name matches at least on of the given regex strings.
+   * <p>
+   * See {@link DumpBuilder} for details on inclusion and exclusion precedence.
+   *
+   * @param regex first pattern to match attribute name
+   * @param moreRegexes more patterns to match attribute names
+   * @return this
+   * @see java.util.regex.Pattern#compile(String)
+   */
+  public DumpBuilder includeAttributes(String regex, String... moreRegexes) {
+    updateRegexes(() -> buildConfig.getGlobalPatternCollection().getAttributePattern(),
+        s -> buildConfig.getGlobalPatternCollection().setAttributePattern(s),
+        regex, moreRegexes);
+    return this;
+  }
+
+  /**
+   * Include attributes within a type (as tokens) and their value if the attribute name matches at least on of the given regex strings.
+   * <p>
+   * See {@link DumpBuilder} for details on inclusion and exclusion precedence.
+   *
+   * @param typeRegex pattern to match a nonterminal name
+   * @param regex first pattern to match attribute name
+   * @param moreRegexes more patterns to match attribute names
+   * @return this
+   * @see java.util.regex.Pattern#compile(String)
+   */
+  public DumpBuilder includeAttributesFor(String typeRegex, String regex, String... moreRegexes) {
+    PatternCollection collection = findOrCreateIncludePatternCollection(typeRegex);
+    updateRegexes(collection::getAttributePattern,
+        collection::setAttributePattern,
+        regex, moreRegexes);
+    return this;
+  }
+
+  /**
+   * Exclude attributes within a type (as tokens) and their value (again) if the attribute name matches at least on of the given regex strings.
+   * <p>
+   * See {@link DumpBuilder} for details on inclusion and exclusion precedence.
+   *
+   * @param typeRegex pattern to match a nonterminal name
+   * @param regex first pattern to match attribute name
+   * @param moreRegexes more patterns to match attribute names
+   * @return this
+   * @see java.util.regex.Pattern#compile(String)
+   */
+  public DumpBuilder excludeAttributesFor(String typeRegex, String regex, String... moreRegexes) {
+    PatternCollection collection = findOrCreateExcludePatternCollection(typeRegex);
+    updateRegexes(collection::getAttributePattern,
+        collection::setAttributePattern,
+        regex, moreRegexes);
+    return this;
+  }
+
+  // --- Nonterminal-Attributes ---
+
+  /**
+   * Includes nonterminal-attributes (as children) and their values if
+   * their attribute name matches at least on of the given regex strings.
+   * <br>
+   * <b>Note</b>: A leading "get" and a trailing "List" in the name will be removed prior to matching.
+   * Thus, it should not be contained in the regex either.
+   * <p>
+   * See {@link DumpBuilder} for details on inclusion and exclusion precedence.
+   *
+   * @param regex first pattern to match attribute name
+   * @param moreRegexes more patterns to match attribute names
+   * @return this
+   * @see java.util.regex.Pattern#compile(String)
+   */
+  public DumpBuilder includeNonterminalAttributes(String regex, String... moreRegexes) {
+    updateRegexes(() -> buildConfig.getGlobalPatternCollection().getNonterminalAttributePattern(),
+        s -> buildConfig.getGlobalPatternCollection().setNonterminalAttributePattern(s),
+        regex, moreRegexes);
+    return this;
+  }
+
+  /**
+   * Includes nonterminal-attributes (as children) and their values within a type if
+   * their attribute name matches at least on of the given regex strings.
+   * <br>
+   * <b>Note</b>: A leading "get" and a trailing "List" in the name will be removed prior to matching.
+   * Thus, it should not be contained in the regex either.
+   * <p>
+   * See {@link DumpBuilder} for details on inclusion and exclusion precedence.
+   *
+   * @param typeRegex pattern to match a nonterminal name
+   * @param regex first pattern to match attribute name
+   * @param moreRegexes more patterns to match attribute names
+   * @return this
+   * @see java.util.regex.Pattern#compile(String)
+   */
+  public DumpBuilder includeNonterminalAttributesFor(String typeRegex, String regex, String... moreRegexes) {
+    PatternCollection collection = findOrCreateIncludePatternCollection(typeRegex);
+    updateRegexes(collection::getNonterminalAttributePattern,
+        collection::setNonterminalAttributePattern,
+        regex, moreRegexes);
+    return this;
+  }
+
+  /**
+   * Excludes nonterminal-attributes (as children) and their values (again) within a type if
+   * their attribute name matches at least on of the given regex strings.
+   * <br>
+   * <b>Note</b>: A leading "get" and a trailing "List" in the name will be removed prior to matching.
+   * Thus, it should not be contained in the regex either.
+   * <p>
+   * See {@link DumpBuilder} for details on inclusion and exclusion precedence.
+   *
+   * @param typeRegex pattern to match a nonterminal name
+   * @param regex first pattern to match attribute name
+   * @param moreRegexes more patterns to match attribute names
+   * @return this
+   * @see java.util.regex.Pattern#compile(String)
+   */
+  public DumpBuilder excludeNonterminalAttributesFor(String typeRegex, String regex, String... moreRegexes) {
+    PatternCollection collection = findOrCreateExcludePatternCollection(typeRegex);
+    updateRegexes(collection::getNonterminalAttributePattern,
+        collection::setNonterminalAttributePattern,
+        regex, moreRegexes);
+    return this;
+  }
+
+  // --- Relations ---
+
+  public <ASTNODE> DumpBuilder includeRelationsWhen(IncludeRelationMethod<ASTNODE> spec) {
+    buildConfig.setIncludeRelationMethod(spec);
+    if (!buildConfig.getGlobalPatternCollection().getRelationPattern().isEmpty()) {
+      System.err.println("Overriding previous filters for relations");
+    }
+    return this;
+  }
+
+  /**
+   * Exclude every relation whose role-name matches at least on of the given regex strings.
+   * This means two things: a) the relation to any potential target object(s) is never shown, and b) the target
+   * object(s) are not shown unless they are reachable by another relation or by containment.
+   * <p>
+   * See {@link DumpBuilder} for details on inclusion and exclusion precedence.
+   *
+   * @param regex first pattern to match child name
+   * @param moreRegexes more patterns to match child names
+   * @return this
+   * @see java.util.regex.Pattern#compile(String)
+   */
+  public DumpBuilder excludeRelations(String regex, String... moreRegexes) {
+    updateRegexes(() -> buildConfig.getGlobalPatternCollection().getRelationPattern(),
+        s -> buildConfig.getGlobalPatternCollection().setRelationPattern(s),
+        regex, moreRegexes);
+    return this;
+  }
+
+  /**
+   * Exclude every relation within a type whose role-name matches at least on of the given regex strings.
+   * This means two things: a) the relation to any potential target object(s) is never shown, and b) the target
+   * object(s) are not shown unless they are reachable by another relation or by containment.
+   * <p>
+   * See {@link DumpBuilder} for details on inclusion and exclusion precedence.
+   *
+   * @param typeRegex pattern to match a nonterminal name
+   * @param regex first pattern to match child name
+   * @param moreRegexes more patterns to match child names
+   * @return this
+   * @see java.util.regex.Pattern#compile(String)
+   */
+  public DumpBuilder excludeRelationsFor(String typeRegex, String regex, String... moreRegexes) {
+    PatternCollection collection = findOrCreateExcludePatternCollection(typeRegex);
+    updateRegexes(collection::getRelationPattern,
+        collection::setRelationPattern,
+        regex, moreRegexes);
+    return this;
+  }
+
+  /**
+   * Include every relation (again) within a type whose role-name matches at least on of the given regex strings.
+   * This means two things: a) the relation to any potential target object(s) is never shown, and b) the target
+   * object(s) are not shown unless they are reachable by another relation or by containment.
+   * <p>
+   * See {@link DumpBuilder} for details on inclusion and exclusion precedence.
+   *
+   * @param typeRegex pattern to match a nonterminal name
+   * @param regex first pattern to match child name
+   * @param moreRegexes more patterns to match child names
+   * @return this
+   * @see java.util.regex.Pattern#compile(String)
+   */
+  public DumpBuilder includeRelationsFor(String typeRegex, String regex, String... moreRegexes) {
+    PatternCollection collection = findOrCreateIncludePatternCollection(typeRegex);
+    updateRegexes(collection::getRelationPattern,
+        collection::setRelationPattern,
+        regex, moreRegexes);
+    return this;
+  }
+
+  // --- Settings ---
+
+
+  /**
+   * Omit children that are <code>null</code> (in a full AST, there are no such nodes).
+   *
+   * Normally, those nodes are shown.
+   * @return this
+   */
+  public DumpBuilder excludeNullNodes() {
+    buildConfig.setExcludeNullNodes(true);
+    return this;
+  }
+
+  /**
+   * Include children that are <code>null</code> (in a full AST, there are no such nodes).
+   *
+   * This is the default.
+   * @return this
+   */
+  public DumpBuilder includeNullNodes() {
+    buildConfig.setExcludeNullNodes(false);
+    return this;
+  }
+
+  /**
+   * Add custom preamble put before diagram content.
+   * @param option arbitrary plantuml content
+   * @return this
+   */
+  public DumpBuilder customPreamble(String option) {
+    printConfig.addHeader(new Header(option));
+    return this;
+  }
+
+  /**
+   * Add skin param setting
+   * @param setting the setting
+   * @param value   value of the setting
+   * @return this
+   */
+  public DumpBuilder skinParam(SkinParamStringSetting setting, String value) {
+    customPreamble("skinparam " + setting.toString() + " " + value);
+    return this;
+  }
+
+  /**
+   * Add skin param setting
+   * @param setting the setting
+   * @param value   value of the setting
+   * @return this
+   */
+  public DumpBuilder skinParam(SkinParamBooleanSetting setting, boolean value) {
+    customPreamble("skinparam " + setting.toString() + " " + value);
+    return this;
+  }
+
+  /**
+   * Set the method defining, what name a node has (default: n -> n == null ? "null" : n.getClass().getSimpleName() + "@" + Integer.toHexString(n.hashCode())).
+   *
+   * <p>Example:<br>
+   * {@code builder.<ASTNode<?>>setNameMethod(n -> n.isA() ? "A" : "Not A")}
+   * @param nameMethod a style method
+   * @param <ASTNODE> the type of ASTNode
+   * @return this
+   */
+  public <ASTNODE> DumpBuilder setNameMethod(StyleMethod<ASTNODE> nameMethod) {
+    buildConfig.getStyleInformation().setNameMethod(nameMethod);
+    return this;
+  }
+
+  /**
+   * Set the method defining, what background color a node has (default: n -> "").
+   *
+   * <p>Example:<br>
+   * {@code builder.<ASTNode<?>>setBackgroundColorMethod(n -> n.isA() ? "red" : "blue")}
+   * @param colorMethod a style method
+   * @param <ASTNODE> the type of ASTNode
+   * @return this
+   */
+  public <ASTNODE> DumpBuilder setBackgroundColorMethod(StyleMethod<ASTNODE> colorMethod) {
+    buildConfig.getStyleInformation().setBackgroundColorMethod(colorMethod);
+    return this;
+  }
+
+  /**
+   * Set the method defining, what text color a node has (default: n -> "").
+   *
+   * <p>Example:<br>
+   * {@code builder.<ASTNode<?>>setTextColorMethod(n -> n.isA() ? "black" : "white")}
+   * @param colorMethod a style method
+   * @param <ASTNODE> the type of ASTNode
+   * @return this
+   */
+  public <ASTNODE> DumpBuilder setTextColorMethod(StyleMethod<ASTNODE> colorMethod) {
+    buildConfig.getStyleInformation().setTextColorMethod(colorMethod);
+    return this;
+  }
+
+  /**
+   * Set the method defining, what stereotype a node has (default: n -> "").
+   *
+   * <p>Example:<br>
+   * {@code builder.<ASTNode<?>>setStereotypeMethod(n -> n.isA() ? "MyStereoType" : "")}
+   * @param stereotypeMethod a style method
+   * @param <ASTNODE> the type of ASTNode
+   * @return this
+   */
+  public <ASTNODE> DumpBuilder setStereotypeMethod(StyleMethod<ASTNODE> stereotypeMethod) {
+    buildConfig.getStyleInformation().setStereotypeMethod(stereotypeMethod);
+    return this;
+  }
+
+  /**
+   * Set color for computed parts of the AST (default: "blue").
+   * @param color some color understood by plantuml
+   * @return this
+   * @see <a href="https://plantuml.com/en/color">https://plantuml.com/en/color</a>
+   */
+  public DumpBuilder setComputedColor(String color) {
+    buildConfig.getStyleInformation().setComputedColor(color);
+    return this;
+  }
+
+  /**
+   * Set scale of diagram.
+   * @param value the new scale (default: 1.0).
+   * @return this
+   */
+  public DumpBuilder setScale(double value) {
+    printConfig.setScale(value);
+    return this;
+  }
+
+  /**
+   * Enable enforced ordering of children nodes (may not always work due to dynamic placement by plantuml).
+   * @return this
+   */
+  public DumpBuilder orderChildren() {
+    printConfig.setOrderChildren(true);
+    return this;
+  }
+
+  // --- Dump methods ---
+
+  /**
+   * Write out content as plantuml source code.
+   *
+   * @param destination path of destination file
+   * @return this
+   * @throws java.io.IOException if an I/O error happend during opening or writing in that file
+   */
+  public DumpBuilder dumpAsSource(java.nio.file.Path destination) throws java.io.IOException {
+    String content = build().toPlantUml();
+    try (java.io.Writer writer = java.nio.file.Files.newBufferedWriter(destination)) {
+      writer.write(content);
+    }
+    return this;
+  }
+
+  /**
+   * Write out content as intermediate data structure used by the template engine.
+   *
+   * @param destination path of destination file
+   * @param prependCreationComment whether to add the creation date as first line
+   * @return this
+   * @throws java.io.IOException if an I/O error happend during opening or writing in that file
+   */
+  public DumpBuilder dumpAsYaml(java.nio.file.Path destination, boolean prependCreationComment) throws java.io.IOException {
+    String content = build().printYaml(prependCreationComment);
+    try (java.io.Writer writer = java.nio.file.Files.newBufferedWriter(destination)) {
+      writer.write(content);
+    }
+    return this;
+  }
+
+  /**
+   * Write out content as PNG image generated by plantuml.
+   *
+   * @param destination path of destination file
+   * @return this
+   * @throws java.io.IOException if an I/O error happend during opening or writing in that file
+   */
+  public DumpBuilder dumpAsPNG(java.nio.file.Path destination) throws java.io.IOException {
+    String content = build().toPlantUml();
+    net.sourceforge.plantuml.SourceStringReader reader = new net.sourceforge.plantuml.SourceStringReader(content);
+    reader.outputImage(java.nio.file.Files.newOutputStream(destination));
+    return this;
+  }
+
+  /**
+   * Write out content as SVG image generated by plantuml.
+   *
+   * @param destination path of destination file
+   * @return this
+   * @throws java.io.IOException if an I/O error happend during opening or writing in that file
+   */
+  public DumpBuilder dumpAsSVG(java.nio.file.Path destination) throws java.io.IOException {
+    String content = build().toPlantUml();
+    net.sourceforge.plantuml.SourceStringReader reader = new net.sourceforge.plantuml.SourceStringReader(content);
+    reader.outputImage(java.nio.file.Files.newOutputStream(destination),
+        new net.sourceforge.plantuml.FileFormatOption(net.sourceforge.plantuml.FileFormat.SVG));
+    return this;
+  }
+
+  // --- Helper methods ---
+
+  protected DumpAst build() {
+    if (result == null) {
+      result = new DumpAst();
+      final String packageNameToUse;
+      if (this.packageName != null) {
+        packageNameToUse = this.packageName;
+      } else {
+        if (this.target == null) {
+          packageNameToUse = null;
+        } else {
+          packageNameToUse = this.target.getClass().getPackage().getName();
+        }
+      }
+      result.setPackageName(packageNameToUse);
+      result.setBuildConfig(this.buildConfig);
+      result.setPrintConfig(this.printConfig);
+      try {
+        result.transform(new TransformationTransferInformation(), this.target);
+      } catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) {
+        throw new RuntimeException("Could not transform :(", e);
+      }
+    }
+    return result;
+  }
+
+  private PatternCollection findOrCreateIncludePatternCollection(String typeRegex) {
+    PatternCollection result = buildConfig.findIncludePatternCollection(typeRegex);
+    if (result == null) {
+      TypePatternCollectionMapping mapping = new TypePatternCollectionMapping();
+      mapping.setTypeRegex(typeRegex);
+      result = new PatternCollection();
+      mapping.setPatternCollection(result);
+      buildConfig.addIncludeTypePattern(mapping);
+    }
+    return result;
+  }
+
+  private PatternCollection findOrCreateExcludePatternCollection(String typeRegex) {
+    PatternCollection result = buildConfig.findExcludePatternCollection(typeRegex);
+    if (result == null) {
+      TypePatternCollectionMapping mapping = new TypePatternCollectionMapping();
+      mapping.setTypeRegex(typeRegex);
+      result = new PatternCollection();
+      mapping.setPatternCollection(result);
+      buildConfig.addExcludeTypePattern(mapping);
+    }
+    return result;
+  }
+
+  private void updateRegexes(java.util.function.Supplier<String> getter, java.util.function.Consumer<String> setter, String regex, String... moreRegexes) {
+    if (getter.get().isEmpty()) {
+      setter.accept(regex);
+    } else {
+      setter.accept(getter.get() + "|" + regex);
+    }
+    for (String value : moreRegexes) {
+      setter.accept(getter.get() + "|" + value);
+    }
+  }
+
+  private String readVersion() {
+    try {
+      java.util.ResourceBundle resources = java.util.ResourceBundle.getBundle("dumpAstVersion");
+      return resources.getString("version");
+    } catch (java.util.MissingResourceException e) {
+      return "version ?";
+    }
+  }
+}
diff --git a/dumpAst/src/main/java/de/tudresden/inf/st/jastadd/dumpAst/ast/Dumper.java b/dumpAst/src/main/java/de/tudresden/inf/st/jastadd/dumpAst/ast/Dumper.java
new file mode 100644
index 0000000000000000000000000000000000000000..a5a7d1f74723e47901c1e7d0c549153c428b4486
--- /dev/null
+++ b/dumpAst/src/main/java/de/tudresden/inf/st/jastadd/dumpAst/ast/Dumper.java
@@ -0,0 +1,17 @@
+package de.tudresden.inf.st.jastadd.dumpAst.ast;
+
+/**
+ * Entrypoint for dumpAst.
+ *
+ * @author rschoene - Initial contribution
+ */
+public class Dumper {
+  /**
+   * Prepare to read in the given object. Use the <code>dump*</code> methods to actually dump its content.
+   * @param obj the object to dump
+   * @return a builder to adjust dump options
+   */
+  public static DumpBuilder read(Object obj) {
+    return new DumpBuilder(obj);
+  }
+}
diff --git a/dumpAst/src/main/java/de/tudresden/inf/st/jastadd/dumpAst/ast/SkinParamBooleanSetting.java b/dumpAst/src/main/java/de/tudresden/inf/st/jastadd/dumpAst/ast/SkinParamBooleanSetting.java
new file mode 100644
index 0000000000000000000000000000000000000000..29770781f057185548a458792b46e02af9c33eaf
--- /dev/null
+++ b/dumpAst/src/main/java/de/tudresden/inf/st/jastadd/dumpAst/ast/SkinParamBooleanSetting.java
@@ -0,0 +1,16 @@
+package de.tudresden.inf.st.jastadd.dumpAst.ast;
+
+public enum SkinParamBooleanSetting {
+  /**
+   * Print in grayscale?
+   */
+  Monochrome,
+  /**
+   * Use shadows?
+   */
+  Shadowing,
+  /**
+   * Use handwritten style?
+   */
+  Handwritten
+}
diff --git a/dumpAst/src/main/java/de/tudresden/inf/st/jastadd/dumpAst/ast/SkinParamStringSetting.java b/dumpAst/src/main/java/de/tudresden/inf/st/jastadd/dumpAst/ast/SkinParamStringSetting.java
new file mode 100644
index 0000000000000000000000000000000000000000..5f7ca1b0a7162e854e495f94b6b843075844ddf3
--- /dev/null
+++ b/dumpAst/src/main/java/de/tudresden/inf/st/jastadd/dumpAst/ast/SkinParamStringSetting.java
@@ -0,0 +1,8 @@
+package de.tudresden.inf.st.jastadd.dumpAst.ast;
+
+public enum SkinParamStringSetting {
+  /**
+   * Set color of background
+   */
+  backgroundColor
+}
diff --git a/dumpAst/src/main/resources/dumpAst.mustache b/dumpAst/src/main/resources/dumpAst.mustache
index a4c15afde2619529e104c4288b5d5fe80cf9244e..da993c1d4d995ea2f8b15f4129207be3bc14d08a 100644
--- a/dumpAst/src/main/resources/dumpAst.mustache
+++ b/dumpAst/src/main/resources/dumpAst.mustache
@@ -37,9 +37,18 @@ object "{{{labelAndTextColor}}}" as {{{name}}} {{{stereotypeList}}} {{#backgroun
 {{#DumpNodes}}
   {{#DumpTokens}}
     {{^invisible}}
-      {{^isDumpValueToken}}
-{{{outerNodeName}}} .{{#computed}}[#{{{computedColor}}}]{{/computed}}.> {{{innerNodeName}}} : {{{label}}}
-      {{/isDumpValueToken}}
+      {{#isList}}
+        {{#InnerRelationDumpNode}}
+          {{#bothVisible}}
+{{{outerNodeName}}} .[#black{{#computed}},#{{{computedColor}}}{{/computed}}{{#innerNotNull}},norank{{/innerNotNull}}].> {{{innerNodeName}}} : {{{label}}}
+          {{/bothVisible}}
+        {{/InnerRelationDumpNode}}
+      {{/isList}}
+      {{^isList}}
+        {{^isDumpValueToken}}
+{{{outerNodeName}}} .[#black{{#computed}},#{{{computedColor}}}{{/computed}}{{#innerNotNull}},norank{{/innerNotNull}}].> {{{innerNodeName}}} : {{{label}}}
+        {{/isDumpValueToken}}
+      {{/isList}}
     {{/invisible}}
   {{/DumpTokens}}
   {{#DumpChildNodes}}
@@ -58,23 +67,23 @@ object "{{{labelAndTextColor}}}" as {{{name}}} {{{stereotypeList}}} {{#backgroun
   {{/DumpChildNodes}}
   {{#DumpRelations}}
     {{#isList}}
-      {{#InnerDumpNodes}}
+      {{#InnerRelationDumpNode}}
         {{#bothVisible}}
-{{{outerNodeName}}} {{#bidirectional}}<{{/bidirectional}}--> {{{innerNodeName}}} : {{{label}}}
+{{{outerNodeName}}} {{#bidirectional}}<{{/bidirectional}}-{{#innerNotNull}}[norank]{{/innerNotNull}}-> {{{innerNodeName}}} : {{{label}}}
         {{/bothVisible}}
-      {{/InnerDumpNodes}}
+      {{/InnerRelationDumpNode}}
     {{/isList}}
     {{^isList}}
       {{#bothVisible}}
-{{{outerNodeName}}} {{#bidirectional}}<{{/bidirectional}}--> {{{innerNodeName}}} : {{{label}}}
+{{{outerNodeName}}} {{#bidirectional}}<{{/bidirectional}}-{{#innerNotNull}}[norank]{{/innerNotNull}}-> {{{innerNodeName}}} : {{{label}}}
       {{/bothVisible}}
     {{/isList}}
   {{/DumpRelations}}
   {{^invisible}}
     {{#InvisiblePath}}
-      {{#InnerDumpNodes}}
+      {{#InnerRelationDumpNode}}
 {{{outerNodeName}}} o.. {{{innerNodeName}}}
-      {{/InnerDumpNodes}}
+      {{/InnerRelationDumpNode}}
     {{/InvisiblePath}}
   {{/invisible}}
   {{#PrintConfig}}{{#orderChildren}}
diff --git a/dumpAst/src/main/resources/dumpAstVersion.properties b/dumpAst/src/main/resources/dumpAstVersion.properties
index 4f787e6a9ba7ce9674d053068c4f55768e8c5294..086530a8564c12127715c686c9ab5e5a4f2b56ee 100644
--- a/dumpAst/src/main/resources/dumpAstVersion.properties
+++ b/dumpAst/src/main/resources/dumpAstVersion.properties
@@ -1,2 +1,2 @@
-#Fri Mar 11 12:41:31 CET 2022
-version=1.0.0
+#Thu Jun 23 16:13:20 CEST 2022
+version=1.1.0
diff --git a/featureTest/.gitignore b/featureTest/.gitignore
index 7609b2344767a4c3fd8857201dac2c4d1dff200c..464bb11e342becdcd6a5d6dde4a764c4ea13111a 100644
--- a/featureTest/.gitignore
+++ b/featureTest/.gitignore
@@ -5,3 +5,4 @@ out/
 *.class
 /featureTest.png
 /featureTest.yml
+/featureTest.puml
diff --git a/featureTest/build.gradle b/featureTest/build.gradle
index ee343385c7fc3719b4420d063d34db968fb346bb..21ca23ab47a643426503e7d188a03ac2add312b9 100644
--- a/featureTest/build.gradle
+++ b/featureTest/build.gradle
@@ -20,16 +20,22 @@ apply plugin: 'jastadd'
 // --- Dependencies ---
 repositories {
     mavenCentral()
+    maven {
+        name 'gitlab-maven'
+        url 'https://git-st.inf.tu-dresden.de/api/v4/groups/jastadd/-/packages/maven'
+    }
+}
+
+configurations {
+    relast
 }
 
 dependencies {
     implementation project(":dumpAst")
+    jastadd2 group: 'org.jastadd', name: 'jastadd2', version: '2.3.5-dresden'
+    relast group: 'org.jastadd', name: 'relast', version: "${relast_version}"
 
-    api group: 'net.sf.beaver', name: 'beaver-rt', version: '0.9.11'
-
-    testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: "${jupiter_version}"
-    testImplementation group: 'org.assertj', name: 'assertj-core', version: '3.18.1'
-    testRuntimeOnly group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: "${jupiter_version}"
+    implementation group: 'net.sf.beaver', name: 'beaver-rt', version: '0.9.11'
 }
 
 // --- Preprocessors ---
@@ -41,20 +47,16 @@ File testingGrammar = file('./src/main/jastadd/featureTest.relast')
 
 task relast(type: JavaExec) {
     group = 'Build'
-    main = "-jar"
+    classpath = configurations.relast
+    mainClass = 'org.jastadd.relast.compiler.Compiler'
 
     doFirst {
-        delete "src/gen/jastadd/*.ast"
-        delete "src/gen/jastadd/featureTest.jadd"
-        delete "src/gen/jastadd/featureTestRefResolver.jadd"
-        delete "src/gen/jastadd/featureTestResolverStubs.jrag"
+        delete "src/gen/jastadd/*"
         mkdir  "src/gen/jastadd/"
     }
 
     args = [
-            "../libs/relast.jar",
             testingGrammar,
-//            "./src/main/jastadd/MustacheNodes.relast",
             "--listClass=java.util.ArrayList",
             "--jastAddList=JastAddList",
             "--useJastAddNames",
@@ -62,13 +64,6 @@ task relast(type: JavaExec) {
             "--resolverHelper",
             "--grammarName=./src/gen/jastadd/featureTest"
     ]
-
-    inputs.files(file("../libs/relast.jar"),
-            testingGrammar)
-    outputs.files(file("./src/gen/jastadd/featureTest.ast"),
-            file("./src/gen/jastadd/featureTest.jadd"),
-            file("./src/gen/jastadd/featureTestRefResolver.jadd"),
-            file('./src/gen/jastadd/featureTestResolverStubs.jrag'))
 }
 
 // --- JastAdd ---
@@ -102,7 +97,6 @@ jastadd {
 }
 
 // --- Tests ---
-test.useJUnitPlatform()
 
 // --- Versioning and Publishing ---
 mainClassName = 'de.tudresden.inf.st.jastadd.featureTest.FeatureTestMain'
diff --git a/featureTest/src/main/jastadd/featureTest.jrag b/featureTest/src/main/jastadd/featureTest.jrag
index 4001126135dcb55faee01ca8edfc8c8f959f019f..9a158c1b6d03df5db880aa7ab63b373bc798e6a4 100644
--- a/featureTest/src/main/jastadd/featureTest.jrag
+++ b/featureTest/src/main/jastadd/featureTest.jrag
@@ -2,6 +2,7 @@ aspect GrammarGlobal {
   syn A C.getCalculated() {
     A result = new A();
     result.setName("Calculated-" + getName());
+    result.setB(new B().setName("B" + getName()));
     D innerD = new D();
     result.setD(innerD);
     return result;
@@ -18,6 +19,7 @@ aspect GrammarGlobal {
   syn nta A C.getCalculatedNewSyntax() {
     A result = new A();
     result.setName("Calculated-" + getName());
+    result.setB(new B().setName("B" + getName()));
     return result;
   }
 
@@ -34,6 +36,11 @@ aspect GrammarGlobal {
 
   syn boolean ASTNode.isA() = false;
   eq A.isA() = true;
+
+  coll java.util.Set<B> Root.collectBs() [new java.util.HashSet<>()] root Root ;
+  B contributes this to Root.collectBs();
+  C contributes nta getAlsoCalculatedList() to Root.collectBs();
+  C contributes nta getAlsoCalculatedListNewSyntax() to Root.collectBs();
 }
 
 aspect GrammarTypeLevel {
diff --git a/featureTest/src/main/jastadd/featureTest.relast b/featureTest/src/main/jastadd/featureTest.relast
index 7b66cc96e3e0af88dd7c124928fca2ae86fa5e53..fa1c50ee8d1848f9aaa64306bc75ef70c89f8587 100644
--- a/featureTest/src/main/jastadd/featureTest.relast
+++ b/featureTest/src/main/jastadd/featureTest.relast
@@ -1,7 +1,7 @@
 // testcases with global inclusion/exclusion
 Nameable ::= <Name> ;
 Root : Nameable ::= A B* [C];
-A : Nameable ::= B MyC:C D;
+A : Nameable ::= B [MyC:C] [D];
 B : Nameable ::= <OtherValue> ;
 C : Nameable ::= [A] <Unwanted:int> <RawReference:A> /Calculated:A/ /AlsoCalculated:B*/ ;
 SubC : C ::= <RawReference:A> <Unwanted:int> [A] /Calculated:A/ /AlsoCalculated:B*/ ;
diff --git a/featureTest/src/main/java/de/tudresden/inf/st/jastadd/featureTest/FeatureTestMain.java b/featureTest/src/main/java/de/tudresden/inf/st/jastadd/featureTest/FeatureTestMain.java
index 298298c64c1ce5ff4f1a646c9aaacc2c465cf70f..99cd85d0c2ef68db861d77ba164dbc00f3bd40ab 100644
--- a/featureTest/src/main/java/de/tudresden/inf/st/jastadd/featureTest/FeatureTestMain.java
+++ b/featureTest/src/main/java/de/tudresden/inf/st/jastadd/featureTest/FeatureTestMain.java
@@ -2,8 +2,10 @@ package de.tudresden.inf.st.jastadd.featureTest;
 
 import de.tudresden.inf.st.jastadd.dumpAst.ast.Dumper;
 import de.tudresden.inf.st.jastadd.dumpAst.ast.SkinParamBooleanSetting;
-import de.tudresden.inf.st.jastadd.dumpAst.ast.SkinParamStringSetting;
-import org.jastadd.featureTest.ast.*;
+import org.jastadd.featureTest.ast.A;
+import org.jastadd.featureTest.ast.B;
+import org.jastadd.featureTest.ast.C;
+import org.jastadd.featureTest.ast.Root;
 
 import java.io.IOException;
 import java.nio.file.Path;
@@ -19,18 +21,17 @@ public class FeatureTestMain {
   public static void main(String[] args) throws IOException {
     Root root = new Root();
     root.setName("Root1");
-    A a = new A();
-    a.setName("A2");
-    B b1 = new B();
-    b1.setName("B3");
-    C c = new C();
-    c.setName("C4");
+    A a = new A().setName("A2");
+    a.setB(new B().setName("B2.1"));
+    a.setMyC(new C().setName("C2.1"));
+    B b1 = new B().setName("B3");
+    C c = new C().setName("C4");
+    c.setA(new A().setName("A4.1").setB(new B().setName("B4.1.1")));
     c.setRawReference(a);
     b1.setOneA(a);
-    B b2 = new B();
-    b2.setName("B5");
-    C myC = new C();
-    myC.setName("C6");
+    B b2 = new B().setName("B5");
+    C myC = new C().setName("C6");
+    c.setA(new A().setName("A6.1").setB(new B().setName("B6.1.1")));
     a.setMyC(myC);
     root.setA(a);
     root.addB(b1);
@@ -39,21 +40,35 @@ public class FeatureTestMain {
 
     Path pathToYaml = Paths.get("featureTest.yml");
     Path pathToPng = Paths.get("featureTest.png");
-//    Dumper.read(null).dumpAsPNG(pathToPng);
     Dumper
 //        .read(null)
         .read(root)
 //        .customPreamble("hide empty members")
+        .customPreamble("title My fancy title")
+        .includeChildWhen((parentNode, childNode, contextName) -> {
+          if (parentNode instanceof A && ((A) parentNode).getName().equals("A2")) {
+            return false;
+          }
+          return !contextName.equals("MyC");
+        })
+        .includeRelationsWhen((sourceNode, targetNode, roleName) ->
+            !(sourceNode instanceof B) || !((B) sourceNode).getName().equals("B6.1.1"))
+        .includeAttributeWhen((node, attributeName, isNTA, supplier) -> {
+          switch (attributeName) {
+            case "referenceAttr":
+            case "collectBs":
+            case "Calculated":
+            case "AlsoCalculatedListNewSyntax":
+              return true;
+            default:
+              return false;
+          }
+        })
         .skinParam(SkinParamBooleanSetting.Shadowing, false)
-        .includeAttributes("referenceAttr")
-        .includeNonterminalAttributes("Calculated")
-        .includeNonterminalAttributes("AlsoCalculatedListNewSyntax")
+//        .enableRelationWithRank()
         .dumpAsYaml(pathToYaml, true)
-        .dumpAsPNG(pathToPng);
-  }
-
-  private void m() {
-    //noinspection UnnecessaryReturnStatement
-    return;
+        .dumpAsPNG(pathToPng)
+        .dumpAsSource(Paths.get("featureTest.puml"))
+    ;
   }
 }
diff --git a/gradle.properties b/gradle.properties
index 0b9ca1a9e095602aa642d4f103b301728a005d3c..fb14289849cf57bfede946ba89e884553c059238 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,3 +1,4 @@
 log4j_version = 2.13.3
 jupiter_version = 5.7.0
 mustache_java_version = 0.9.7
+relast_version = 0.3.0-137
diff --git a/libs/relast.jar b/libs/relast.jar
deleted file mode 100644
index f71563319c89f149ab48a7f4d20bc4f663dafe9b..0000000000000000000000000000000000000000
Binary files a/libs/relast.jar and /dev/null differ
diff --git a/pages/docs/adding.md b/pages/docs/adding.md
index 8bfc64a25902b465da7c07f6da1a73e7979258b2..cca2cbfd1e2aed137e5772a1a84c357aa610abc5 100644
--- a/pages/docs/adding.md
+++ b/pages/docs/adding.md
@@ -1,12 +1,10 @@
 # Add DumpAst to your project
 
-If you want to use `DumpAst`, either use the latest [pre-build version](#use-packaged-version) or clone the repository and [build it yourself](#build-from-source).
-
-## Use packaged version
-
 Check the [package overview page](https://git-st.inf.tu-dresden.de/jastadd/relast2uml/-/packages) to find the latest versions of the individual packages.
 
-Add this GitLab as a repository, and `dumpAst` as a dependency in your `build.gradle`:
+To use `dumpAst`, adjust your `build.gradle` as follows.
+
+Set up the maven package source as repository:
 
 ```
 repositories {
@@ -15,37 +13,41 @@ repositories {
         url "https://git-st.inf.tu-dresden.de/api/v4/groups/jastadd/-/packages/maven"
     }
 }
+```
 
+Add `dumpAst` as a dependency:
+
+```
 dependencies {
-    implementation group: 'de.tudresden.inf.st', name: 'dumpAst', version: '0.3.7'
+    implementation group: 'de.tudresden.inf.st', name: 'dumpAst', version: '1.0.1'
 }
 ```
 
-## Build from source
+## Build from source (not recommended)
 
-If you want to build the tools of `DumpAst` from source, first build the jar from the [repository](https://git-st.inf.tu-dresden.de/jastadd/relast2uml):
+If you want to build the tools of `DumpAst` from source, first build the jar from the [repository](https://git-st.inf.tu-dresden.de/jastadd/relast2uml).
+The normal jar does not suffice, as it lacks the information on needed dependencies.
 
 ```bash
 git clone https://git-st.inf.tu-dresden.de/jastadd/relast2uml.git
 cd relast2uml
-./gradlew jar
+./gradlew fatJar
 ls dumpAst/build/libs/
 ```
 
-This JAR can then be copied to your project.
+This jar can then be copied to your project.
 
 ```bash
 cp dumpAst/build/libs/dumpAst-<version>.jar ../your-project/libs/dumpAst.jar
 cd ../your-project/
 ```
 
-Finally, this JAR has to be integrated into your build process.
-In case [Gradle](https://gradle.org/) is used, the JAR file needs to be added as dependency using:
+Finally, this jar has to be integrated into your build process.
 
-```groovy
+```
 dependencies {
     implementation fileTree(include: ['dumpAst.jar'], dir: 'libs')
 }
 ```
 
-The path to the JAR file may need to be changed according to your project structure.
+The path to the jar file may need to be changed according to your project structure.
diff --git a/pages/docs/index.md b/pages/docs/index.md
index 80e8008f7e47b4ebc8676959db78292d306c4e76..e8f11ced151c84d6105f804427ae7f40cd6901ac 100644
--- a/pages/docs/index.md
+++ b/pages/docs/index.md
@@ -11,60 +11,12 @@ First, import the entry point class `Dumper`
 import de.tudresden.inf.st.jastadd.dumpAst.ast.Dumper;
 ```
 
-Then, read in the ASTnode in question:
+Then, read in the ASTNode in question:
 
 ```java
 Dumper.read(astNode)
 ```
 
-Using the return value, the following methods are supported and can be chained together.
-
-|               Name              |                                                                                                                                        Description                                                                                                                                         |
-|---------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| customPreamble                  | Add the given string as preamble to the visualization.                                                                                                                                                                                                                                     |
-| disableTypes                    | Disable all objects with types matching at least one of the given regex strings. Disabled objects won't be included in any output. However, their children are still processed.                                                                                                            |
-| dumpAsPNG                       | Write out content as PNG image generated by plantuml.                                                                                                                                                                                    |
-| dumpAsSource                    | Write out content as plantuml source code                                                                                                                                                                                                                                                  |
-| dumpAsSVG                       | Write out content as SVG image generated by plantuml.                                                                                                                                                                                          |
-| dumpAsYaml                      | Write out content as YAML representation of the ASTNode                                                                                                                                                                                                                                    |
-| enableDebug                     | Add debug information in dumped content, mainly version numbers.                                                                                                                                                                                                                           |
-| excludeAttributesFor            | *experimental, documentation missing*                                                                                                                                                                                                                                                      |
-| excludeChildren                 | Exclude every child whose name (i.e., context) matches at least on of the given regex strings. This means, that the complete object and its (transitive) children will never be included in any output.                                                                                    |
-| excludeChildrenFor              | *experimental, documentation missing*                                                                                                                                                                                                                                                      |
-| excludeNonterminalAttributesFor | *experimental, documentation missing*                                                                                                                                                                                                                                                      |
-| excludeRelations                | Exclude every relation whose role-name matches at least on of the given regex strings. This means two things: a) the relation to any potential target object(s) is never shown, and b) the target object(s) are not shown unless they are reachable by another relation or by containment. |
-| excludeRelationsFor             | *experimental, documentation missing*                                                                                                                                                                                                                                                      |
-| excludeTokens                   | Exclude tokens and their value if the token name matches at least one of the given regex strings.                                                                                                                                                                                          |
-| excludeTokensFor                | *experimental, documentation missing*                                                                                                                                                                                                                                                      |
-| includeAttributes               | Include attributes (as tokens) and their value if the attribute name matches at least on of the given regex strings.                                                                                                                                                                       |
-| includeAttributesFor            | *experimental, documentation missing*                                                                                                                                                                                                                                                      |
-| includeChildrenFor              | *experimental, documentation missing*                                                                                                                                                                                                                                                      |
-| includeEmptyStringsOnTokens     | Include empty strings for all tokens                                                                                                                                                                                                                                                       |
-| includeNonterminalAttributes    | Includes nonterminal-attributes (as children) and their values if their attribute name matches at least on of the given regex strings. Note: A leading "get" and a trailing "List" in the name will be removed prior to matching. Thus, it should not be contained in the regex either.    |
-| includeNonterminalAttributesFor | *experimental, documentation missing*                                                                                                                                                                                                                                                      |
-| includeRelationsFor             | *experimental, documentation missing*                                                                                                                                                                                                                                                      |
-| includeTokensFor                | *experimental, documentation missing*                                                                                                                                                                                                                                                      |
-| setScale                        | Set plantuml scale setting                                                                                                                                                                                                                                                                 |
-| skinParam                       | Add plantuml skinParam setting                                                                                                                                                                                                                                                             |
-
-## Inclusion and Exclusion of Types
-
-Types can be only be disabled, see `disableTypes`.
-
-### Inclusion and Exclusion of Childrens, tokens and relations
-
-Childrens, tokens and relations are included by default. This can be changed using exclusions and inclusion, both in general and per-type. They are applied in the following order making later conditions take precedence over the first ones.
-
-1. Include everything as default.
-2. Exclude general.
-3. Include per type.
-4. Exclude per type.
-
-### Inclusion and Exclusion of Attributes
-
-Attributes are excluded by default, i.e., not shown. This can be changed using inclusions and exclusions, both in general and per-type. They are applied in the following order making later conditions take precedence over the first ones.
-
-1. Exclude everything as default.
-2. Include general.
-3. Exclude per type.
-4. Include per type
+Using the return value (a `DumpBuilder`), use methods to filter out unwanted parts, add styling or other settings.
+All methods can be chained together.
+Please see the [API documentation](ragdoc/index.html) for more details.
diff --git a/relast.preprocessor b/relast.preprocessor
deleted file mode 160000
index c00441c03dc6723a08de0fcb041254a99497774f..0000000000000000000000000000000000000000
--- a/relast.preprocessor
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit c00441c03dc6723a08de0fcb041254a99497774f
diff --git a/settings.gradle b/settings.gradle
index 3eb76f1e946e81f5037864327765fdf452bf8c5c..b5b5e2aba025ed461a508cd5fb5846993f43d7f7 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1,7 +1,11 @@
+pluginManagement {
+    plugins {
+        id 'org.jastadd' version '1.13.3'
+    }
+}
+
 rootProject.name = 'relast2uml'
 
-include 'relast.preprocessor'
 include 'dumpAst'
 include 'testDumper'
 include 'featureTest'
-
diff --git a/testDumper/build.gradle b/testDumper/build.gradle
index 34cd313f4905be14e54079c763fab87a07460f95..1c589314ae9434afb054c922504521320a64174e 100644
--- a/testDumper/build.gradle
+++ b/testDumper/build.gradle
@@ -19,12 +19,21 @@ apply plugin: 'jastadd'
 // --- Dependencies ---
 repositories {
     mavenCentral()
+    maven {
+        name 'gitlab-maven'
+        url 'https://git-st.inf.tu-dresden.de/api/v4/groups/jastadd/-/packages/maven'
+    }
+}
+
+configurations {
+    relast
 }
 
 dependencies {
     implementation project(':dumpAst')
+    relast group: 'org.jastadd', name: 'relast', version: "${relast_version}"
 
-    api group: 'net.sf.beaver', name: 'beaver-rt', version: '0.9.11'
+    implementation group: 'net.sf.beaver', name: 'beaver-rt', version: '0.9.11'
 
     testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: "${jupiter_version}"
     testImplementation group: 'org.assertj', name: 'assertj-core', version: '3.18.1'
@@ -41,20 +50,16 @@ File testingGrammar = file('./src/main/jastadd/testDumper.relast')
 
 task relast(type: JavaExec) {
     group = 'Build'
-    main = "-jar"
+    classpath = configurations.relast
+    mainClass = 'org.jastadd.relast.compiler.Compiler'
 
     doFirst {
-        delete "src/gen/jastadd/*.ast"
-        delete "src/gen/jastadd/testDumper.jadd"
-        delete "src/gen/jastadd/testDumperRefResolver.jadd"
-        delete "src/gen/jastadd/testDumperResolverStubs.jrag"
+        delete "src/gen/jastadd/*"
         mkdir  "src/gen/jastadd/"
     }
 
     args = [
-            "../libs/relast.jar",
             testingGrammar,
-//            "./src/main/jastadd/MustacheNodes.relast",
             "--listClass=java.util.ArrayList",
             "--jastAddList=JastAddList",
             "--useJastAddNames",
@@ -62,13 +67,6 @@ task relast(type: JavaExec) {
             "--resolverHelper",
             "--grammarName=./src/gen/jastadd/testDumper"
     ]
-
-    inputs.files(file("../libs/relast.jar"),
-            testingGrammar)
-    outputs.files(file("./src/gen/jastadd/testDumper.ast"),
-            file("./src/gen/jastadd/testDumper.jadd"),
-            file("./src/gen/jastadd/testDumperRefResolver.jadd"),
-            file('./src/gen/jastadd/testDumperResolverStubs.jrag'))
 }
 
 // --- JastAdd ---
diff --git a/testDumper/src/main/jastadd/testDumper.jrag b/testDumper/src/main/jastadd/testDumper.jrag
index 4cedce8cfa75749d2fb90adcb0ef5766e36e0ca1..a4e17a122827b1b54a814ddd453014a9b8beda0f 100644
--- a/testDumper/src/main/jastadd/testDumper.jrag
+++ b/testDumper/src/main/jastadd/testDumper.jrag
@@ -32,6 +32,12 @@ aspect GrammarGlobal {
 
   syn boolean ASTNode.isA() = false;
   eq A.isA() = true;
+
+  syn Object Root.setOfBs() {
+    java.util.Set<B> result = new java.util.HashSet<>();
+    getBList().forEach(result::add);
+    return result;
+  }
 }
 
 aspect GrammarTypeLevel {
diff --git a/testDumper/src/main/jastadd/testDumper.relast b/testDumper/src/main/jastadd/testDumper.relast
index 7b66cc96e3e0af88dd7c124928fca2ae86fa5e53..cf9d37f4899b1af59099341292e3ecd111200e4d 100644
--- a/testDumper/src/main/jastadd/testDumper.relast
+++ b/testDumper/src/main/jastadd/testDumper.relast
@@ -24,3 +24,9 @@ T3 : AbstractT ;
 rel AbstractT.oneA -> A ;
 rel AbstractT.maybeA? -> A ;
 rel AbstractT.manyA* -> A ;
+
+Position : Nameable ::= <X:double> <Y:double> <Z:double>;
+Size : Nameable ::= <Length:double> <Width:double> <Height:double>;
+Orientation : Nameable ::= <X:double> <Y:double> <Z:double> <W:double>;
+ObjectOfInterest : Nameable ::= Position Size Orientation;
+DropOffLocation : ObjectOfInterest;
diff --git a/testDumper/src/test/java/de/tudresden/inf/st/jastadd/testDumper/TestComplex.java b/testDumper/src/test/java/de/tudresden/inf/st/jastadd/testDumper/TestComplex.java
new file mode 100644
index 0000000000000000000000000000000000000000..856bab384ff5d2ade9b7dacf4704a0a5e90cc648
--- /dev/null
+++ b/testDumper/src/test/java/de/tudresden/inf/st/jastadd/testDumper/TestComplex.java
@@ -0,0 +1,37 @@
+package de.tudresden.inf.st.jastadd.testDumper;
+
+import de.tudresden.inf.st.jastadd.dumpAst.ast.DumpNode;
+import org.jastadd.testDumper.ast.DropOffLocation;
+import org.jastadd.testDumper.ast.Orientation;
+import org.jastadd.testDumper.ast.Position;
+import org.jastadd.testDumper.ast.Size;
+import org.junit.jupiter.api.Test;
+
+import java.util.List;
+
+import static de.tudresden.inf.st.jastadd.testDumper.TestUtils.*;
+import static org.assertj.core.api.Assertions.*;
+
+/**
+ * More complex test cases.
+ *
+ * @author rschoene - Initial contribution
+ */
+public class TestComplex {
+
+  @Test
+  public void testRegressionIssue16() {
+    DropOffLocation location = new DropOffLocation();
+    location.setName(ROOT_NAME);
+    location.setPosition(new Position(T1_NAME, 1, 2, 3));
+    location.setOrientation(new Orientation(T2_NAME, 4, 5, 6, 7));
+    location.setSize(new Size(T3_NAME, 8, 9, 10));
+
+    List<DumpNode> nodes = TestUtils.dumpModel(location);
+    assertThat(valueTokens(findByName(nodes, T1_NAME))).containsOnly(
+        entry("Name", T1_NAME),
+        entry("X", 1.0),
+        entry("Y", 2.0),
+        entry("Z", 3.0));
+  }
+}
diff --git a/testDumper/src/test/java/de/tudresden/inf/st/jastadd/testDumper/TestExcluded.java b/testDumper/src/test/java/de/tudresden/inf/st/jastadd/testDumper/TestExcluded.java
index 87588648dad540b88ba856b349c57c4a95d84cd3..a122a05519a8a68e5b3963b3ff30da13b2cca86a 100644
--- a/testDumper/src/test/java/de/tudresden/inf/st/jastadd/testDumper/TestExcluded.java
+++ b/testDumper/src/test/java/de/tudresden/inf/st/jastadd/testDumper/TestExcluded.java
@@ -312,7 +312,8 @@ public class TestExcluded {
     root.getA().getB().setOneA(root.getA().getMyC().getA());
 
     List<DumpNode> nodes = TestUtils.dumpModel(root, db -> db.excludeChildren("MyC"));
-    assertThat(nodes).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(ROOT_NAME, A_NAME, B_NAME);
+    assertThat(nodes).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(
+        ROOT_NAME, A_NAME, B_NAME, A2_Name, B2_NAME);
     DumpNode actualA = findByName(nodes, A_NAME);
     assertThatMapOf(normalChildren(actualA)).containsExactlyInAnyOrder(tuple("B", B_NAME));
   }
diff --git a/testDumper/src/test/java/de/tudresden/inf/st/jastadd/testDumper/TestIncluded.java b/testDumper/src/test/java/de/tudresden/inf/st/jastadd/testDumper/TestIncluded.java
index cd6e5c83fc191d797b0b3cb8901749f28c840773..0d5dce44733c11890fa8d345c310c3fcbe1ead61 100644
--- a/testDumper/src/test/java/de/tudresden/inf/st/jastadd/testDumper/TestIncluded.java
+++ b/testDumper/src/test/java/de/tudresden/inf/st/jastadd/testDumper/TestIncluded.java
@@ -31,6 +31,18 @@ public class TestIncluded {
     assertThat(valueTokens(actualRoot)).containsOnly(entry("Name", ROOT_NAME), entry("simpleAttr", 42));
   }
 
+  @Test
+  public void testReferenceListAttributeIncluded() {
+    Root root = createRoot(null, null, createB(B1_NAME), createB(B2_NAME), createB(B3_NAME));
+
+    List<DumpNode> nodes = TestUtils.dumpModel(root, db -> db.includeAttributes("setOfBs"));
+    assertThat(nodes).flatExtracting(NAME_EXTRACTOR).containsExactly(ROOT_NAME, B1_NAME, B2_NAME, B3_NAME);
+
+    DumpNode actualRoot = TestUtils.findByName(nodes, ROOT_NAME);
+    assertThatMapOf(referenceListTokens(actualRoot), "setOfBs").containsOnly(
+        B1_NAME, B2_NAME, B3_NAME);
+  }
+
   @Test
   public void testReferenceAttributeDefault() {
     Root root = createRoot(createA(A_NAME), null);
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 1b9be71cc6a27b381a94127d8cab991f06a87479..8a10b40c290454c154ea0c1ab9d978d279e96952 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
@@ -74,14 +74,24 @@ public class TestSimple {
 
     List<DumpNode> nodes = TestUtils.dumpModel(root, DumpBuilder::includeNullNodes);
     assertThat(nodes).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(
-        ROOT_NAME, A_NAME, null, null, null, null);
+        ROOT_NAME, A_NAME,
+        null,  // C in Root
+        null,  // B in A
+        null,  // MyC in A
+        null,  // D in A
+        null,  // BiC2 in A
+        null,  // BiC1 in A
+        null   // BiC3 in A
+    );
     DumpNode actualRoot = TestUtils.findByName(nodes, ROOT_NAME);
     assertEquals(1, actualRoot.getNumDumpToken());
     assertEquals(2, actualRoot.getNumDumpChildNode());
     assertEquals(0, actualRoot.getNumDumpRelation());
 
     DumpNode actualA = TestUtils.findByName(nodes, A_NAME);
+    assertEquals(1, actualA.getNumDumpToken());
     assertEquals(3, actualA.getNumDumpChildNode());
+    assertEquals(3, actualA.getNumDumpRelation());
   }
 
   @Test
@@ -120,10 +130,6 @@ public class TestSimple {
     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());
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 b3e8fa92d7ddfcefc0331facc0d27e228f6363b8..9d76fca579ef4856ee7279949de341c7a48a4afc 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
@@ -229,7 +229,7 @@ public class TestUtils {
     for (DumpRelation dumpRelation : node.getDumpRelationList()) {
       if (dumpRelation.isList()) {
         // then it is a DumpListRelation
-        ((DumpListRelation) dumpRelation).getInnerDumpNodeList().forEach(inner -> {
+        ((DumpListRelation) dumpRelation).getInnerRelationDumpNodeList().forEach(inner -> {
           if (!inner.getDumpNode().getInvisible()) {
             result.computeIfAbsent(dumpRelation.getName(), key -> new ArrayList<>()).add(inner.getDumpNode());
           }
@@ -242,7 +242,7 @@ public class TestUtils {
   public static Map<String, DumpNode> referenceTokens(DumpNode node) {
     Map<String, DumpNode> result = new HashMap<>();
     for (DumpToken dumpToken : node.getDumpTokenList()) {
-      if (!dumpToken.isDumpValueToken()) {
+      if (!dumpToken.isDumpValueToken() && !dumpToken.isList()) {
         // then it is a DumpReferenceToken
         DumpNode target = ((DumpReferenceToken) dumpToken).getValue();
         if (!target.getInvisible()) {
@@ -253,6 +253,21 @@ public class TestUtils {
     return result;
   }
 
+  public static Map<String, List<DumpNode>> referenceListTokens(DumpNode node) {
+    Map<String, List<DumpNode>> result = new HashMap<>();
+    for (DumpToken dumpToken : node.getDumpTokenList()) {
+      if (!dumpToken.isDumpValueToken() && dumpToken.isList()) {
+        // then it is a DumpReferenceListToken
+        ((DumpReferenceListToken) dumpToken).getInnerRelationDumpNodeList().forEach(inner -> {
+          if (!inner.getDumpNode().getInvisible()) {
+            result.computeIfAbsent(dumpToken.getName(), key -> new ArrayList<>()).add(inner.getDumpNode());
+          }
+        });
+      }
+    }
+    return result;
+  }
+
   public static Map<String, Object> valueTokens(DumpNode node) {
     Map<String, Object> result = new HashMap<>();
     for (DumpToken dumpToken : node.getDumpTokenList()) {
@@ -267,7 +282,7 @@ public class TestUtils {
 
   public static List<DumpNode> invisiblePath(DumpNode node) {
     List<DumpNode> result = new ArrayList<>();
-    for (InnerDumpNode inner : node.getInvisiblePath().getInnerDumpNodeList()) {
+    for (InnerRelationDumpNode inner : node.getInvisiblePath().getInnerRelationDumpNodeList()) {
       DumpNode target = inner.getDumpNode();
       assertFalse(target.getInvisible());
       result.add(target);