diff --git a/dumpAst2uml/.gitignore b/dumpAst/.gitignore
similarity index 100%
rename from dumpAst2uml/.gitignore
rename to dumpAst/.gitignore
diff --git a/dumpAst2uml/build.gradle b/dumpAst/build.gradle
similarity index 78%
rename from dumpAst2uml/build.gradle
rename to dumpAst/build.gradle
index f4badc1b73afbdf0e044ccd8faf7b71943c00022..0c1159eba4f504d5795d437d98f5e81853c7b4ea 100644
--- a/dumpAst2uml/build.gradle
+++ b/dumpAst/build.gradle
@@ -4,7 +4,7 @@ apply plugin: 'idea'
 
 sourceCompatibility = 1.8
 
-mainClassName = 'org.jastadd.dumpAst2uml.compiler.Compiler'
+mainClassName = 'de.tudresden.inf.st.jastadd.dumpAst.compiler.Compiler'
 
 repositories {
     jcenter()
@@ -82,9 +82,9 @@ task relast(type: JavaExec) {
 
     doFirst {
         delete "src/gen/jastadd/*.ast"
-        delete "src/gen/jastadd/DumpAst2.jadd"
-        delete "src/gen/jastadd/DumpAstlRefResolver.jadd"
-        delete "src/gen/jastadd/DumpAstlResolverStubs.jrag"
+        delete "src/gen/jastadd/DumpAst.jadd"
+        delete "src/gen/jastadd/DumpAstRefResolver.jadd"
+        delete "src/gen/jastadd/DumpAstResolverStubs.jrag"
         mkdir  "src/gen/jastadd/"
     }
 
@@ -97,12 +97,12 @@ task relast(type: JavaExec) {
             "--useJastAddNames",
             "--file",
             "--resolverHelper",
-            "--grammarName=./src/gen/jastadd/DumpAst2"
+            "--grammarName=./src/gen/jastadd/DumpAst"
     ]
 
     inputs.files(file("../libs/relast.jar"),
             dumpAstGrammar)
-    outputs.files(file("./src/gen/jastadd/DumpAst2.ast"),
+    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'))
@@ -112,28 +112,28 @@ jastadd {
     configureModuleBuild()
     modules {
         //noinspection GroovyAssignabilityCheck
-        module("DumpAst2Uml") {
+        module("DumpAst") {
 
             java {
                 basedir ".."
-                include "dumpAst2uml/src/main/**/*.java"
-                include "dumpAst2uml/src/gen/**/*.java"
+                include "dumpAst/src/main/**/*.java"
+                include "dumpAst/src/gen/**/*.java"
             }
 
             jastadd {
                 basedir ".."
-                include "dumpAst2uml/src/main/jastadd/**/*.ast"
-                include "dumpAst2uml/src/main/jastadd/**/*.jadd"
-                include "dumpAst2uml/src/main/jastadd/**/*.jrag"
-                include "dumpAst2uml/src/gen/jastadd/**/*.ast"
-                include "dumpAst2uml/src/gen/jastadd/**/*.jadd"
-                include "dumpAst2uml/src/gen/jastadd/**/*.jrag"
+                include "dumpAst/src/main/jastadd/**/*.ast"
+                include "dumpAst/src/main/jastadd/**/*.jadd"
+                include "dumpAst/src/main/jastadd/**/*.jrag"
+                include "dumpAst/src/gen/jastadd/**/*.ast"
+                include "dumpAst/src/gen/jastadd/**/*.jadd"
+                include "dumpAst/src/gen/jastadd/**/*.jrag"
             }
         }
     }
 
     cleanGen.doFirst {
-        delete "src/gen/java/org"
+        delete "src/gen/java/de"
         delete "src/gen-res/BuildInfo.properties"
     }
 
@@ -143,8 +143,8 @@ jastadd {
 
     }
 
-    module = "DumpAst2Uml"
-    astPackage = 'org.jastadd.dumpAst2uml.ast'
+    module = "DumpAst"
+    astPackage = 'de.tudresden.inf.st.jastadd.dumpAst.ast'
     genDir = 'src/gen/java'
     buildInfoDir = 'src/gen-res'
     jastaddOptions = ["--lineColumnNumbers", "--List=JastAddList", "--safeLazy", "--visitCheck=true", "--rewrite=cnta", "--cache=all"]
diff --git a/dumpAst2uml/src/main/jastadd/DumpAst.relast b/dumpAst/src/main/jastadd/DumpAst.relast
similarity index 83%
rename from dumpAst2uml/src/main/jastadd/DumpAst.relast
rename to dumpAst/src/main/jastadd/DumpAst.relast
index 42c18fddf417756538ebd9739877556667c6fa74..e7acd5daa36734376fa0858fbd1cd026b857f152 100644
--- a/dumpAst2uml/src/main/jastadd/DumpAst.relast
+++ b/dumpAst/src/main/jastadd/DumpAst.relast
@@ -1,26 +1,22 @@
-DumpAst ::= DumpNode* <PackageName> BuildConfig PrintConfig ;
-BuildConfig ::= <TypeIgnore> <TokenIgnore> <ChildIgnore> <AttributeIgnore> <RelationIgnore> <IgnoreEmptyString:boolean> <Debug:boolean> ;
-PrintConfig ::= <Scale:double> <Version> Header* ;
-Header ::= <Value> ;
-DumpNode ::= <Name> <Label> <Object:Object> DumpChildNode* DumpToken* DumpRelation* ;
-InnerDumpNode ;
-rel InnerDumpNode.DumpNode -> DumpNode ;
-
-abstract DumpChildNode ::= <Name> ;
-DumpNormalChildNode : DumpChildNode ;
-rel DumpNormalChildNode.DumpNode -> DumpNode ;
-DumpListChildNode : DumpChildNode ::= InnerDumpNode* ;
-//DumpOptChildNode : DumpChildNode ;
-//rel DumpOptChildNode.DumpNode? -> DumpNode ;
-
-abstract DumpToken ::= <Name> ;
-DumpReferenceToken : DumpToken ;
-rel DumpReferenceToken.Value -> DumpNode ;
-DumpValueToken : DumpToken ::= <Value:Object> ;
-
-abstract DumpRelation ::= <Name> <Bidirectional:boolean> ;
-DumpNormalRelation : DumpRelation ;
-rel DumpNormalRelation.DumpNode -> DumpNode ;
-DumpListRelation : DumpRelation ::= InnerDumpNode* ;
-//DumpOptRelation : DumpRelation ;
-//rel DumpOptRelation.DumpNode? -> DumpNode ;
+DumpAst ::= DumpNode* <PackageName> BuildConfig PrintConfig ;
+BuildConfig ::= <TypeIgnore> <TokenIgnore> <ChildIgnore> <AttributeIgnore> <RelationIgnore> <IgnoreEmptyString:boolean> <Debug:boolean> ;
+PrintConfig ::= <Scale:double> <Version> Header* ;
+Header ::= <Value> ;
+DumpNode ::= <Name> <Label> <Object:Object> DumpChildNode* DumpToken* DumpRelation* ;
+InnerDumpNode ;
+rel InnerDumpNode.DumpNode -> DumpNode ;
+
+abstract DumpChildNode ::= <Name> ;
+DumpNormalChildNode : DumpChildNode ;
+rel DumpNormalChildNode.DumpNode -> DumpNode ;
+DumpListChildNode : DumpChildNode ::= InnerDumpNode* ;
+
+abstract DumpToken ::= <Name> ;
+DumpReferenceToken : DumpToken ;
+rel DumpReferenceToken.Value -> DumpNode ;
+DumpValueToken : DumpToken ::= <Value:Object> ;
+
+abstract DumpRelation ::= <Name> <Bidirectional:boolean> ;
+DumpNormalRelation : DumpRelation ;
+rel DumpNormalRelation.DumpNode -> DumpNode ;
+DumpListRelation : DumpRelation ::= InnerDumpNode* ;
diff --git a/dumpAst2uml/src/main/jastadd/Generation.jadd b/dumpAst/src/main/jastadd/Generation.jadd
similarity index 91%
rename from dumpAst2uml/src/main/jastadd/Generation.jadd
rename to dumpAst/src/main/jastadd/Generation.jadd
index 8cf0aba63e98680cdcfe0ad7d5a8ea8be4d45d45..692220c3af5bec01d169da9279466d8961f85438 100644
--- a/dumpAst2uml/src/main/jastadd/Generation.jadd
+++ b/dumpAst/src/main/jastadd/Generation.jadd
@@ -1,393 +1,424 @@
-aspect Generation {
-  public class Dumper {
-    public static DumpBuilder read(Object obj) {
-      return new DumpBuilder(obj);
-    }
-  }
-  public enum SkinParamBooleanSetting {
-    Monochrome, Shadowing, Handwritten
-  }
-  public enum SkinParamStringSetting {
-    backgroundColor
-  }
-  public class DumpBuilder {
-    private Object target;
-    private String packageName;
-    private DumpAst result;
-    private BuildConfig buildConfig = new BuildConfig();
-    private PrintConfig printConfig = new PrintConfig();
-
-    protected DumpBuilder(Object target) {
-      this.target = target;
-      printConfig.setScale(1);
-      printConfig.setVersion(readVersion());
-      ignoreEmptyStrings(true);
-    }
-
-    public DumpBuilder setDebug(boolean debug) {
-      buildConfig.setDebug(debug);
-      return this;
-    }
-    public DumpBuilder ignoreEmptyStrings(boolean ignoreThem) {
-      buildConfig.setIgnoreEmptyString(ignoreThem);
-      return this;
-    }
-    public DumpBuilder ignoreTypes(String... regexes) {
-      updateIgnored(() -> buildConfig.getTypeIgnore(), s -> buildConfig.setTypeIgnore(s), regexes);
-      return this;
-    }
-    public DumpBuilder ignoreTokens(String... regexes) {
-      updateIgnored(() -> buildConfig.getTokenIgnore(), s -> buildConfig.setTokenIgnore(s), regexes);
-      return this;
-    }
-    public DumpBuilder ignoreAttributes(String... regexes) {
-      updateIgnored(() -> buildConfig.getAttributeIgnore(), s -> buildConfig.setAttributeIgnore(s), regexes);
-      return this;
-    }
-    public DumpBuilder ignoreChildren(String... regexes) {
-      updateIgnored(() -> buildConfig.getChildIgnore(), s -> buildConfig.setChildIgnore(s), regexes);
-      return this;
-    }
-    public DumpBuilder ignoreRelations(String... regexes) {
-      updateIgnored(() -> buildConfig.getRelationIgnore(), s -> buildConfig.setRelationIgnore(s), regexes);
-      return this;
-    }
-    private void updateIgnored(java.util.function.Supplier<String> getter, java.util.function.Consumer<String> setter, String... values) {
-      for (String value : values) {
-        if (getter.get().isEmpty()) {
-          setter.accept(value);
-        } else {
-          setter.accept(getter.get() + "|" + value);
-        }
-      }
-    }
-
-    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 DumpBuilder setScale(double value) {
-      printConfig.setScale(value);
-      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();
-        result.setPackageName(this.packageName == null ? this.target.getClass().getPackage().getName() : this.packageName);
-        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;
-    }
-    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 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;
-    }
-    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;
-    }
-  }
-  // --- transform --- (need to be a method, because it alters the AST while traversing the object structure)
-  // maybe return type is unncessary
-  protected DumpNode DumpAst.transform(TransformationTransferInformation tti, Object obj)
-      throws java.lang.reflect.InvocationTargetException, IllegalAccessException, NoSuchMethodException {
-    if (obj == null) {
-      return null;
-    }
-    if (tti.transformed.containsKey(obj)) {
-      return tti.transformed.get(obj);
-    }
-    if (matches(getBuildConfig().typeIgnorePattern(), obj.getClass().getSimpleName())) {
-      tti.transformed.put(obj, null);
-      return null;
-    }
-    DumpNode node = new DumpNode();
-    node.setObject(obj);
-    node.setLabel(obj.getClass().getSimpleName() + "@" + obj.hashCode());
-    node.setName("node" + tti.transformed.size());
-    tti.transformed.put(obj, node);
-    this.addDumpNode(node);
-    if (node.isAstNode()) {
-      // only caching node.analyseClass does not help, since we want to do this only once per class of a node
-      final ClassAnalysisResult car;
-      Class<?> clazz = obj.getClass();
-      if (tti.classAnalysisResults.containsKey(clazz)) {
-        car = tti.classAnalysisResults.get(clazz);
-      } else {
-        car = node.analyzeClass();
-        tti.classAnalysisResults.put(clazz, car);
-      }
-      // -- singleChild --
-      for (java.lang.reflect.Method method : car.singleChildMethods) {
-        Object target = method.invoke(obj);
-        DumpNode targetNode = transform(tti, target);
-        if (target != null && targetNode != null) {
-          DumpNormalChildNode normalChild = new DumpNormalChildNode();
-          normalChild.setName(car.names.get(method));
-          normalChild.setDumpNode(targetNode);
-          node.addDumpChildNode(normalChild);
-        }
-      }
-      // -- listChild --
-      for (java.lang.reflect.Method method : car.listChildMethods) {
-        Iterable<?> targetList = (Iterable<?>) method.invoke(obj);
-        DumpListChildNode listChild = new DumpListChildNode();
-        listChild.setName(car.names.get(method));
-        for (Object target : targetList) {
-          DumpNode targetNode = transform(tti, target);
-          if (target != null && targetNode != null) {
-            listChild.addInnerDumpNode(new InnerDumpNode(targetNode));
-          }
-        }
-        node.addDumpChildNode(listChild);
-      }
-      // -- singleRelation --
-      for (java.lang.reflect.Method method : car.singleRelationMethods) {
-        Object target = method.invoke(obj);
-        DumpNode targetNode = transform(tti, target);
-        if (target != null && targetNode != null) {
-          DumpNormalRelation normalRelation = new DumpNormalRelation();
-          normalRelation.setName(car.names.get(method));
-          normalRelation.setDumpNode(targetNode);
-          node.addDumpRelation(normalRelation);
-        }
-      }
-      // -- listRelation --
-      for (java.lang.reflect.Method method : car.listRelationMethods) {
-        Iterable<?> targetList = (Iterable<?>) method.invoke(obj);
-        DumpListRelation listRelation = new DumpListRelation();
-        listRelation.setName(car.names.get(method));
-        for (Object target : targetList) {
-          DumpNode targetNode = transform(tti, target);
-          if (target != null && targetNode != null) {
-            listRelation.addInnerDumpNode(new InnerDumpNode(targetNode));
-          }
-        }
-        node.addDumpRelation(listRelation);
-      }
-      // -- token --
-      for (java.lang.reflect.Method method : car.tokenMethods) {
-        Object target = method.invoke(obj);
-        if (target != null) {
-          DumpNode targetNode = transform(tti, target);
-          DumpToken token = null;
-          if (targetNode != null && targetNode.isAstNode()) {
-            token = new DumpReferenceToken().setValue(targetNode);
-          } else {
-            if (target != null && (!getBuildConfig().getIgnoreEmptyString() || !target.toString().isEmpty())) {
-              DumpValueToken valueToken = new DumpValueToken();
-              valueToken.setValue(target);
-              token = valueToken;
-            }
-          }
-          if (token != null) {
-            token.setName(car.names.get(method));
-            node.addDumpToken(token);
-          }
-        }
-      }
-    }
-    return node;
-  }
-
-  ClassAnalysisResult DumpNode.analyzeClass()
-      throws java.lang.reflect.InvocationTargetException, IllegalAccessException, NoSuchMethodException {
-    ClassAnalysisResult result = new ClassAnalysisResult();
-    Class<?> clazz = getObject().getClass();
-    for (java.lang.reflect.Method method : clazz.getMethods()) {
-      for (java.lang.annotation.Annotation annotation : method.getAnnotations()) {
-        String canonicalName = annotation.annotationType().getCanonicalName();
-        if (canonicalName.startsWith(astNodeAnnotationPrefix())) {
-          String simpleName = annotation.annotationType().getSimpleName();
-          switch (simpleName) {
-            case "Child":
-            case "OptChild":
-              String singleChildName = invokeName(annotation);
-              if (!matches(buildConfig().childIgnorePattern(), singleChildName)) {
-                result.singleChildMethods.add(method);
-                result.names.put(method, singleChildName);
-              }
-              break;
-            case "ListChild":
-              String listChildName = invokeName(annotation);
-              if (!matches(buildConfig().childIgnorePattern(), listChildName)) {
-                result.listChildMethods.add(method);
-                result.names.put(method, listChildName);
-              }
-              break;
-            case "Token":
-              // heuristic for relations
-              String tokenName = invokeName(annotation);
-              if (tokenName.startsWith("_impl_")) {
-                String relationName = tokenName.substring(6);
-                try {
-                  java.lang.reflect.Method relationMethod = clazz.getMethod("get" + relationName);
-                  // normal get + token-name -> singleRelation
-                  if (!matches(buildConfig().relationIgnorePattern(), relationName)) {
-                    result.singleRelationMethods.add(relationMethod);
-                    result.names.put(relationMethod, relationName);
-                  }
-                  continue;
-                } catch (NoSuchMethodException e) {
-                  // ignore, but we know this is probably not a single relation
-                }
-                // try list-relation next
-                try {
-                  java.lang.reflect.Method relationMethod = clazz.getMethod("get" + relationName + "List");
-                  // normal get + token-name + "List" -> listRelation
-                  if (!matches(buildConfig().relationIgnorePattern(), relationName)) {
-                    result.listRelationMethods.add(relationMethod);
-                    result.names.put(relationMethod, relationName);
-                  }
-                  continue;
-                } catch (NoSuchMethodException e) {
-                  // ignore, but we know this is probably not a relation at all
-                }
-              }
-              if (!matches(buildConfig().tokenIgnorePattern(), tokenName)) {
-                result.tokenMethods.add(method);
-                result.names.put(method, tokenName);
-              }
-              break;
-          }
-        }
-      }
-    }
-    return result;
-  }
-
-  syn java.util.regex.Pattern BuildConfig.typeIgnorePattern() = java.util.regex.Pattern.compile(getTypeIgnore());
-  syn java.util.regex.Pattern BuildConfig.childIgnorePattern() = java.util.regex.Pattern.compile(getChildIgnore());
-  syn java.util.regex.Pattern BuildConfig.tokenIgnorePattern() = java.util.regex.Pattern.compile(getTokenIgnore());
-  syn java.util.regex.Pattern BuildConfig.attributeIgnorePattern() = java.util.regex.Pattern.compile(getAttributeIgnore());
-  syn java.util.regex.Pattern BuildConfig.relationIgnorePattern() = java.util.regex.Pattern.compile(getRelationIgnore());
-  static boolean ASTNode.matches(java.util.regex.Pattern p, String input) {
-    return p.matcher(input).matches();
-  }
-
-  // --- version --- (mustache has buildConfig as context)
-  syn String BuildConfig.version() = printConfig().getVersion();
-
-  private static String DumpNode.invokeName(java.lang.annotation.Annotation annotation)
-      throws java.lang.reflect.InvocationTargetException, IllegalAccessException, NoSuchMethodException {
-    return (String) annotation.annotationType().getMethod("name").invoke(annotation);
-  }
-
-  syn boolean DumpNode.isAstNode() {
-    Class<?> clazz = getObject().getClass();
-    for (java.lang.reflect.Constructor<?> constructor : clazz.getConstructors()) {
-      for (java.lang.annotation.Annotation annotation : constructor.getAnnotations()) {
-        if (annotation.annotationType().getCanonicalName().startsWith(astNodeAnnotationPrefix()) &&
-              annotation.annotationType().getSimpleName().equals("Constructor")) {
-          return true;
-        }
-      }
-    }
-    return false;
-  }
-
-  inh String DumpNode.astNodeAnnotationPrefix();
-  eq DumpAst.getDumpNode().astNodeAnnotationPrefix() = getPackageName() + ".ASTNodeAnnotation";
-
-  class TransformationTransferInformation {
-    java.util.Map<Object, DumpNode> transformed = new java.util.HashMap<>();
-    java.util.Map<Class<?>, ClassAnalysisResult> classAnalysisResults = new java.util.HashMap<>();
-  }
-
-  class ClassAnalysisResult {
-    java.util.List<java.lang.reflect.Method> singleChildMethods = new java.util.ArrayList<>();
-    java.util.List<java.lang.reflect.Method> listChildMethods = new java.util.ArrayList<>();
-    java.util.List<java.lang.reflect.Method> singleRelationMethods = new java.util.ArrayList<>();
-    java.util.List<java.lang.reflect.Method> listRelationMethods = new java.util.ArrayList<>();
-    java.util.List<java.lang.reflect.Method> tokenMethods = new java.util.ArrayList<>();
-    java.util.Map<java.lang.reflect.Method, String> names = new java.util.HashMap<>();
-  }
-
-  syn String DumpAst.toPlantUml() {
-    StringBuilder sb = new StringBuilder();
-    com.github.mustachejava.reflect.ReflectionObjectHandler roh = new com.github.mustachejava.reflect.ReflectionObjectHandler() {
-      @Override
-      public com.github.mustachejava.Binding createBinding(String name, final com.github.mustachejava.TemplateContext tc, com.github.mustachejava.Code code) {
-        return new com.github.mustachejava.reflect.GuardedBinding(this, name, tc, code) {
-          @Override
-          protected synchronized com.github.mustachejava.util.Wrapper getWrapper(String name, java.util.List<Object> scopes) {
-            com.github.mustachejava.util.Wrapper wrapper = super.getWrapper(name, scopes);
-            if (wrapper instanceof com.github.mustachejava.reflect.MissingWrapper) {
-              throw new com.github.mustachejava.MustacheException(name + " not found in " + tc);
-            }
-            return wrapper;
-          }
-        };
-      }
-    };
-    com.github.mustachejava.DefaultMustacheFactory mf = new com.github.mustachejava.DefaultMustacheFactory();
-    mf.setObjectHandler(roh);
-    com.github.mustachejava.Mustache m = mf.compile("dumpAst.mustache");
-    m.execute(new java.io.PrintWriter(new AppendableWriter(sb)), this);
-    return sb.toString();
-  }
-
-  public class AppendableWriter extends java.io.Writer {
-    private final StringBuilder sb;
-
-    public AppendableWriter(StringBuilder sb) {
-      this.sb = sb;
-    }
-
-    @Override
-    public void write(char[] chars, int off, int len) {
-      sb.append(chars, off, len);
-    }
-
-    @Override
-    public void write(String str) {
-      sb.append(str);
-    }
-
-    @Override
-    public void flush() {}
-
-    @Override
-    public void close() {}
-  }
-}
\ No newline at end of file
+aspect GenerationFrontend {
+  public class Dumper {
+    public static DumpBuilder read(Object obj) {
+      return new DumpBuilder(obj);
+    }
+  }
+  public enum SkinParamBooleanSetting {
+    Monochrome, Shadowing, Handwritten
+  }
+  public enum SkinParamStringSetting {
+    backgroundColor
+  }
+  public class DumpBuilder {
+    private Object target;
+    private String packageName;
+    private DumpAst result;
+    private BuildConfig buildConfig = new BuildConfig();
+    private PrintConfig printConfig = new PrintConfig();
+
+    protected DumpBuilder(Object target) {
+      this.target = target;
+      printConfig.setScale(1);
+      printConfig.setVersion(readVersion());
+      ignoreEmptyStrings(true);
+    }
+
+    public DumpBuilder setDebug(boolean debug) {
+      buildConfig.setDebug(debug);
+      return this;
+    }
+    public DumpBuilder ignoreEmptyStrings(boolean ignoreThem) {
+      buildConfig.setIgnoreEmptyString(ignoreThem);
+      return this;
+    }
+    public DumpBuilder ignoreTypes(String... regexes) {
+      updateIgnored(() -> buildConfig.getTypeIgnore(), s -> buildConfig.setTypeIgnore(s), regexes);
+      return this;
+    }
+    public DumpBuilder ignoreTokens(String... regexes) {
+      updateIgnored(() -> buildConfig.getTokenIgnore(), s -> buildConfig.setTokenIgnore(s), regexes);
+      return this;
+    }
+    public DumpBuilder ignoreAttributes(String... regexes) {
+      updateIgnored(() -> buildConfig.getAttributeIgnore(), s -> buildConfig.setAttributeIgnore(s), regexes);
+      return this;
+    }
+    public DumpBuilder ignoreChildren(String... regexes) {
+      updateIgnored(() -> buildConfig.getChildIgnore(), s -> buildConfig.setChildIgnore(s), regexes);
+      return this;
+    }
+    public DumpBuilder ignoreRelations(String... regexes) {
+      updateIgnored(() -> buildConfig.getRelationIgnore(), s -> buildConfig.setRelationIgnore(s), regexes);
+      return this;
+    }
+    private void updateIgnored(java.util.function.Supplier<String> getter, java.util.function.Consumer<String> setter, String... values) {
+      for (String value : values) {
+        if (getter.get().isEmpty()) {
+          setter.accept(value);
+        } else {
+          setter.accept(getter.get() + "|" + value);
+        }
+      }
+    }
+
+    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 DumpBuilder setScale(double value) {
+      printConfig.setScale(value);
+      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();
+        result.setPackageName(this.packageName == null ? this.target.getClass().getPackage().getName() : this.packageName);
+        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;
+    }
+    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 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;
+    }
+    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;
+    }
+  }
+}
+
+aspect GenerationBackend {
+  // --- transform --- (need to be a method, because it alters the AST while traversing the object structure)
+  // maybe return type is unncessary
+  protected DumpNode DumpAst.transform(TransformationTransferInformation tti, Object obj)
+      throws java.lang.reflect.InvocationTargetException, IllegalAccessException, NoSuchMethodException {
+    if (obj == null) {
+      return null;
+    }
+    if (tti.transformed.containsKey(obj)) {
+      return tti.transformed.get(obj);
+    }
+    if (matches(getBuildConfig().typeIgnorePattern(), obj.getClass().getSimpleName())) {
+      tti.transformed.put(obj, null);
+      return null;
+    }
+    DumpNode node = new DumpNode();
+    node.setObject(obj);
+    node.setLabel(obj.getClass().getSimpleName() + "@" + obj.hashCode());
+    node.setName("node" + tti.transformed.size());
+    tti.transformed.put(obj, node);
+    this.addDumpNode(node);
+    if (node.isAstNode()) {
+      // only caching node.analyseClass does not help, since we want to do this only once per class of a node
+      final ClassAnalysisResult car;
+      Class<?> clazz = obj.getClass();
+      if (tti.classAnalysisResults.containsKey(clazz)) {
+        car = tti.classAnalysisResults.get(clazz);
+      } else {
+        car = node.analyzeClass();
+        tti.classAnalysisResults.put(clazz, car);
+      }
+      // -- singleChild --
+      for (java.lang.reflect.Method method : car.singleChildMethods) {
+        Object target = method.invoke(obj);
+        DumpNode targetNode = transform(tti, target);
+        if (target != null && targetNode != null) {
+          DumpNormalChildNode normalChild = new DumpNormalChildNode();
+          normalChild.setName(car.names.get(method));
+          normalChild.setDumpNode(targetNode);
+          node.addDumpChildNode(normalChild);
+        }
+      }
+      // -- listChild --
+      for (java.lang.reflect.Method method : car.listChildMethods) {
+        Iterable<?> targetList = (Iterable<?>) method.invoke(obj);
+        DumpListChildNode listChild = new DumpListChildNode();
+        listChild.setName(car.names.get(method));
+        for (Object target : targetList) {
+          DumpNode targetNode = transform(tti, target);
+          if (target != null && targetNode != null) {
+            listChild.addInnerDumpNode(new InnerDumpNode(targetNode));
+          }
+        }
+        if (listChild.getNumInnerDumpNode() > 0) {
+          node.addDumpChildNode(listChild);
+        }
+      }
+      // -- singleRelation --
+      for (java.lang.reflect.Method method : car.singleRelationMethods) {
+        Object target = method.invoke(obj);
+        DumpNode targetNode = transform(tti, target);
+        if (target != null && targetNode != null) {
+          DumpNormalRelation normalRelation = new DumpNormalRelation();
+          normalRelation.setName(car.names.get(method));
+          normalRelation.setDumpNode(targetNode);
+          node.addDumpRelation(normalRelation);
+        }
+      }
+      // -- listRelation --
+      for (java.lang.reflect.Method method : car.listRelationMethods) {
+        Iterable<?> targetList = (Iterable<?>) method.invoke(obj);
+        DumpListRelation listRelation = new DumpListRelation();
+        listRelation.setName(car.names.get(method));
+        for (Object target : targetList) {
+          DumpNode targetNode = transform(tti, target);
+          if (target != null && targetNode != null) {
+            listRelation.addInnerDumpNode(new InnerDumpNode(targetNode));
+          }
+        }
+        if (listRelation.getNumInnerDumpNode() > 0) {
+          node.addDumpRelation(listRelation);
+        }
+      }
+      // -- token --
+      for (java.lang.reflect.Method method : car.tokenMethods) {
+        Object target = method.invoke(obj);
+        if (target != null) {
+          DumpNode targetNode = transform(tti, target);
+          DumpToken token = null;
+          if (targetNode != null && targetNode.isAstNode()) {
+            token = new DumpReferenceToken().setValue(targetNode);
+          } else {
+            if (target != null && (!getBuildConfig().getIgnoreEmptyString() || !target.toString().isEmpty())) {
+              DumpValueToken valueToken = new DumpValueToken();
+              valueToken.setValue(target);
+              token = valueToken;
+            }
+          }
+          if (token != null) {
+            token.setName(car.names.get(method));
+            node.addDumpToken(token);
+          }
+        }
+      }
+    }
+    return node;
+  }
+
+  ClassAnalysisResult DumpNode.analyzeClass()
+      throws java.lang.reflect.InvocationTargetException, IllegalAccessException, NoSuchMethodException {
+    ClassAnalysisResult result = new ClassAnalysisResult();
+    Class<?> clazz = getObject().getClass();
+    for (java.lang.reflect.Method method : clazz.getMethods()) {
+      for (java.lang.annotation.Annotation annotation : method.getAnnotations()) {
+        String canonicalName = annotation.annotationType().getCanonicalName();
+        if (canonicalName.startsWith(astNodeAnnotationPrefix())) {
+          String simpleName = annotation.annotationType().getSimpleName();
+          switch (simpleName) {
+            case "Child":
+              String singleChildName = invokeName(annotation);
+              if (!matches(buildConfig().childIgnorePattern(), singleChildName)) {
+                result.singleChildMethods.add(method);
+                result.names.put(method, singleChildName);
+              }
+              break;
+            case "OptChild":
+              String optChildName = invokeName(annotation);
+              if (!matches(buildConfig().childIgnorePattern(), optChildName)) {
+                try {
+                  // the annotated method is "get???Opt", but we want "get???"
+                  java.lang.reflect.Method realGetter = clazz.getMethod("get" + optChildName);
+                  result.singleChildMethods.add(realGetter);
+                  result.names.put(realGetter, optChildName);
+                } catch (NoSuchMethodException e) {
+                  System.err.println("Could not find getter for Opt-child " + optChildName + " in " + clazz.getName());
+                  throw new RuntimeException(e);
+                }
+              }
+              break;
+            case "ListChild":
+              String listChildName = invokeName(annotation);
+              if (!matches(buildConfig().childIgnorePattern(), listChildName)) {
+                result.listChildMethods.add(method);
+                result.names.put(method, listChildName);
+              }
+              break;
+            case "Token":
+              // heuristic for relations
+              String tokenName = invokeName(annotation);
+              if (tokenName.startsWith("_impl_")) {
+                String relationName = titleCase(tokenName.substring(6));
+                try {
+                  java.lang.reflect.Method relationMethod = clazz.getMethod("get" + relationName);
+                  // normal get + token-name -> singleRelation
+                  if (!matches(buildConfig().relationIgnorePattern(), relationName)) {
+                    result.singleRelationMethods.add(relationMethod);
+                    result.names.put(relationMethod, relationName);
+                  }
+                  continue;
+                } catch (NoSuchMethodException e) {
+                  // ignore, but we know this is probably not a single relation
+                }
+                // try list-relation next
+                try {
+                  java.lang.reflect.Method relationMethod = clazz.getMethod("get" + relationName + "List");
+                  // normal get + token-name + "List" -> listRelation
+                  if (!matches(buildConfig().relationIgnorePattern(), relationName)) {
+                    result.listRelationMethods.add(relationMethod);
+                    result.names.put(relationMethod, relationName);
+                  }
+                  continue;
+                } catch (NoSuchMethodException e) {
+                  // ignore, but we know this is probably not a relation at all
+                }
+              }
+              if (!matches(buildConfig().tokenIgnorePattern(), tokenName)) {
+                result.tokenMethods.add(method);
+                result.names.put(method, tokenName);
+              }
+              break;
+            case "Attribute":
+              // TODO. check for isNTA=true. then check for whether target is instance of iterable. if so, add to listChildren, otherwise to singelChild
+              // TODO. if isNTA=false, then handle attribute
+              break
+          }
+        }
+      }
+    }
+    return result;
+  }
+
+  private String DumpNode.titleCase(String s) {
+    if (s.isEmpty()) {
+      return s;
+    }
+    return Character.toUpperCase(s.charAt(0)) + s.substring(1);
+  }
+
+  syn java.util.regex.Pattern BuildConfig.typeIgnorePattern() = java.util.regex.Pattern.compile(getTypeIgnore());
+  syn java.util.regex.Pattern BuildConfig.childIgnorePattern() = java.util.regex.Pattern.compile(getChildIgnore());
+  syn java.util.regex.Pattern BuildConfig.tokenIgnorePattern() = java.util.regex.Pattern.compile(getTokenIgnore());
+  syn java.util.regex.Pattern BuildConfig.attributeIgnorePattern() = java.util.regex.Pattern.compile(getAttributeIgnore());
+  syn java.util.regex.Pattern BuildConfig.relationIgnorePattern() = java.util.regex.Pattern.compile(getRelationIgnore());
+  static boolean ASTNode.matches(java.util.regex.Pattern p, String input) {
+    return p.matcher(input).matches();
+  }
+
+  // --- version --- (mustache has buildConfig as context)
+  syn String BuildConfig.version() = printConfig().getVersion();
+
+  private static String DumpNode.invokeName(java.lang.annotation.Annotation annotation)
+      throws java.lang.reflect.InvocationTargetException, IllegalAccessException, NoSuchMethodException {
+    return (String) annotation.annotationType().getMethod("name").invoke(annotation);
+  }
+
+  syn boolean DumpNode.isAstNode() {
+    Class<?> clazz = getObject().getClass();
+    for (java.lang.reflect.Constructor<?> constructor : clazz.getConstructors()) {
+      for (java.lang.annotation.Annotation annotation : constructor.getAnnotations()) {
+        if (annotation.annotationType().getCanonicalName().startsWith(astNodeAnnotationPrefix()) &&
+              annotation.annotationType().getSimpleName().equals("Constructor")) {
+          return true;
+        }
+      }
+    }
+    return false;
+  }
+
+  inh String DumpNode.astNodeAnnotationPrefix();
+  eq DumpAst.getDumpNode().astNodeAnnotationPrefix() = getPackageName() + ".ASTNodeAnnotation";
+
+  class TransformationTransferInformation {
+    java.util.Map<Object, DumpNode> transformed = new java.util.HashMap<>();
+    java.util.Map<Class<?>, ClassAnalysisResult> classAnalysisResults = new java.util.HashMap<>();
+  }
+
+  class ClassAnalysisResult {
+    java.util.List<java.lang.reflect.Method> singleChildMethods = new java.util.ArrayList<>();
+    java.util.List<java.lang.reflect.Method> listChildMethods = new java.util.ArrayList<>();
+    java.util.List<java.lang.reflect.Method> singleRelationMethods = new java.util.ArrayList<>();
+    java.util.List<java.lang.reflect.Method> listRelationMethods = new java.util.ArrayList<>();
+    java.util.List<java.lang.reflect.Method> tokenMethods = new java.util.ArrayList<>();
+    java.util.Map<java.lang.reflect.Method, String> names = new java.util.HashMap<>();
+  }
+
+  syn String DumpAst.toPlantUml() {
+    StringBuilder sb = new StringBuilder();
+    com.github.mustachejava.reflect.ReflectionObjectHandler roh = new com.github.mustachejava.reflect.ReflectionObjectHandler() {
+      @Override
+      public com.github.mustachejava.Binding createBinding(String name, final com.github.mustachejava.TemplateContext tc, com.github.mustachejava.Code code) {
+        return new com.github.mustachejava.reflect.GuardedBinding(this, name, tc, code) {
+          @Override
+          protected synchronized com.github.mustachejava.util.Wrapper getWrapper(String name, java.util.List<Object> scopes) {
+            com.github.mustachejava.util.Wrapper wrapper = super.getWrapper(name, scopes);
+            if (wrapper instanceof com.github.mustachejava.reflect.MissingWrapper) {
+              throw new com.github.mustachejava.MustacheException(name + " not found in " + tc);
+            }
+            return wrapper;
+          }
+        };
+      }
+    };
+    com.github.mustachejava.DefaultMustacheFactory mf = new com.github.mustachejava.DefaultMustacheFactory();
+    mf.setObjectHandler(roh);
+    com.github.mustachejava.Mustache m = mf.compile("dumpAst.mustache");
+    m.execute(new java.io.PrintWriter(new AppendableWriter(sb)), this);
+    return sb.toString();
+  }
+
+  public class AppendableWriter extends java.io.Writer {
+    private final StringBuilder sb;
+
+    public AppendableWriter(StringBuilder sb) {
+      this.sb = sb;
+    }
+
+    @Override
+    public void write(char[] chars, int off, int len) {
+      sb.append(chars, off, len);
+    }
+
+    @Override
+    public void write(String str) {
+      sb.append(str);
+    }
+
+    @Override
+    public void flush() {}
+
+    @Override
+    public void close() {}
+  }
+}
diff --git a/dumpAst2uml/src/main/jastadd/Navigation.jrag b/dumpAst/src/main/jastadd/Navigation.jrag
similarity index 74%
rename from dumpAst2uml/src/main/jastadd/Navigation.jrag
rename to dumpAst/src/main/jastadd/Navigation.jrag
index eb85ca21852c8e7e31626b0ecacc2cb46e36de2f..a8adbd2246cb45d06fb74cf1f0ed8e260afc8f42 100644
--- a/dumpAst2uml/src/main/jastadd/Navigation.jrag
+++ b/dumpAst/src/main/jastadd/Navigation.jrag
@@ -1,17 +1,23 @@
-aspect Navigation {
-  // --- isList ---
-  syn boolean DumpChildNode.isList() = false;
-  eq DumpListChildNode.isList() = true;
-  syn boolean DumpRelation.isList() = false;
-  eq DumpListRelation.isList() = true;
-
-  // --- isDumpValueToken ---
-  syn boolean DumpToken.isDumpValueToken() = false;
-  eq DumpValueToken.isDumpValueToken() = true;
-
-  inh BuildConfig DumpNode.buildConfig();
-  eq DumpAst.getChild().buildConfig() = getBuildConfig();
-
-  inh PrintConfig BuildConfig.printConfig();
-  eq DumpAst.getChild().printConfig() = getPrintConfig();
-}
+aspect Navigation {
+  // --- isList ---
+  syn boolean DumpChildNode.isList() = false;
+  eq DumpListChildNode.isList() = true;
+  syn boolean DumpRelation.isList() = false;
+  eq DumpListRelation.isList() = true;
+
+  // --- isDumpValueToken ---
+  syn boolean DumpToken.isDumpValueToken() = false;
+  eq DumpValueToken.isDumpValueToken() = true;
+
+  // --- asDumpValueToken ---
+  syn DumpValueToken DumpToken.asDumpValueToken() = null;
+  eq DumpValueToken.asDumpValueToken() = this;
+
+  // --- buildConfig ---
+  inh BuildConfig DumpNode.buildConfig();
+  eq DumpAst.getChild().buildConfig() = getBuildConfig();
+
+  // --- printConfig ---
+  inh PrintConfig BuildConfig.printConfig();
+  eq DumpAst.getChild().printConfig() = getPrintConfig();
+}
diff --git a/dumpAst2uml/src/main/jastadd/Printing.jrag b/dumpAst/src/main/jastadd/Printing.jrag
similarity index 100%
rename from dumpAst2uml/src/main/jastadd/Printing.jrag
rename to dumpAst/src/main/jastadd/Printing.jrag
diff --git a/dumpAst2uml/src/main/java/org/jastadd/dumpAst2uml/compiler/SimpleMain.java b/dumpAst/src/main/java/de/tudresden/inf/st/jastadd/dumpAst/SimpleMain.java
similarity index 96%
rename from dumpAst2uml/src/main/java/org/jastadd/dumpAst2uml/compiler/SimpleMain.java
rename to dumpAst/src/main/java/de/tudresden/inf/st/jastadd/dumpAst/SimpleMain.java
index c85abf731723a81200b7e21f1843881570407818..812d1a1881aa84804f3fe28fc4852de64ab48056 100644
--- a/dumpAst2uml/src/main/java/org/jastadd/dumpAst2uml/compiler/SimpleMain.java
+++ b/dumpAst/src/main/java/de/tudresden/inf/st/jastadd/dumpAst/SimpleMain.java
@@ -1,12 +1,13 @@
-package org.jastadd.dumpAst2uml.compiler;
+package de.tudresden.inf.st.jastadd.dumpAst;
 
 import beaver.Parser;
-import org.jastadd.dumpAst2uml.ast.*;
-import org.jastadd.grammar2uml.ast.*;
-import org.jastadd.grammar2uml.parser.Grammar2UmlParser;
-import org.jastadd.grammar2uml.scanner.Grammar2UmlScanner;
+import de.tudresden.inf.st.jastadd.dumpAst.ast.*;
+import de.tudresden.inf.st.jastadd.grammar2uml.ast.*;
+import de.tudresden.inf.st.jastadd.grammar2uml.parser.Grammar2UmlParser;
+import de.tudresden.inf.st.jastadd.grammar2uml.scanner.Grammar2UmlScanner;
 
-import java.io.*;
+import java.io.BufferedReader;
+import java.io.IOException;
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationTargetException;
@@ -14,12 +15,10 @@ import java.lang.reflect.Method;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
-import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Set;
-import java.util.regex.Pattern;
 
-import static org.jastadd.dumpAst2uml.compiler.SimpleMain.Kind.*;
+import static de.tudresden.inf.st.jastadd.dumpAst.SimpleMain.Kind.*;
 
 /**
  * Testing Relast2Uml without parser.
diff --git a/dumpAst2uml/src/main/resources/dumpAst.mustache b/dumpAst/src/main/resources/dumpAst.mustache
similarity index 95%
rename from dumpAst2uml/src/main/resources/dumpAst.mustache
rename to dumpAst/src/main/resources/dumpAst.mustache
index 1a72eeec32b6c3f1867d50cd542de5ecec39e547..6b375b3706e4223af9143e095664265950d7ac2e 100644
--- a/dumpAst2uml/src/main/resources/dumpAst.mustache
+++ b/dumpAst/src/main/resources/dumpAst.mustache
@@ -1,57 +1,57 @@
-@startuml
-{{#PrintConfig}}
-scale {{Scale}}
-{{#Headers}}
-{{Value}}
-{{/Headers}}
-{{/PrintConfig}}
-
-{{#DumpNodes}}
-{{#isAstNode}}
-object "{{label}}" as {{name}} {
-{{#DumpTokens}}
-{{#isDumpValueToken}}
-  {{label}} = {{{Value}}}
-{{/isDumpValueToken}}
-{{/DumpTokens}}
-}
-{{/isAstNode}}
-{{/DumpNodes}}
-
-{{#DumpNodes}}
-{{#DumpTokens}}
-{{^isDumpValueToken}}
-{{outerNodeName}} ..> {{innerNodeName}} : {{label}}
-{{/isDumpValueToken}}
-{{/DumpTokens}}
-{{#DumpChildNodes}}
-{{#isList}}
-{{#InnerDumpNodes}}
-{{outerNodeName}} *-- {{innerNodeName}} : {{label}}
-{{/InnerDumpNodes}}
-{{/isList}}
-{{^isList}}
-{{outerNodeName}} *-- {{innerNodeName}} : {{label}}
-{{/isList}}
-{{/DumpChildNodes}}
-{{#DumpRelations}}
-{{#isList}}
-{{#InnerDumpNodes}}
-{{outerNodeName}} {{#Bidirectional}}<{{/Bidirectional}}--> {{innerNodeName}} : {{label}}
-{{/InnerDumpNodes}}
-{{/isList}}
-{{^isList}}
-{{outerNodeName}} {{#Bidirectional}}<{{/Bidirectional}}--> {{innerNodeName}} : {{label}}
-{{/isList}}
-{{/DumpRelations}}
-{{/DumpNodes}}
-{{#BuildConfig}}
-{{#Debug}}
-legend right
-  %date()
-  dumpAst: {{version}}
-  plantuml: %version()
-endlegend
-{{/Debug}}
-{{/BuildConfig}}
-@enduml
+@startuml
+{{#PrintConfig}}
+scale {{Scale}}
+{{#Headers}}
+{{Value}}
+{{/Headers}}
+{{/PrintConfig}}
+
+{{#DumpNodes}}
+{{#isAstNode}}
+object "{{label}}" as {{name}} {
+{{#DumpTokens}}
+{{#isDumpValueToken}}
+  {{label}} = {{{Value}}}
+{{/isDumpValueToken}}
+{{/DumpTokens}}
+}
+{{/isAstNode}}
+{{/DumpNodes}}
+
+{{#DumpNodes}}
+{{#DumpTokens}}
+{{^isDumpValueToken}}
+{{outerNodeName}} ..> {{innerNodeName}} : {{label}}
+{{/isDumpValueToken}}
+{{/DumpTokens}}
+{{#DumpChildNodes}}
+{{#isList}}
+{{#InnerDumpNodes}}
+{{outerNodeName}} *-- {{innerNodeName}} : {{label}}
+{{/InnerDumpNodes}}
+{{/isList}}
+{{^isList}}
+{{outerNodeName}} *-- {{innerNodeName}} : {{label}}
+{{/isList}}
+{{/DumpChildNodes}}
+{{#DumpRelations}}
+{{#isList}}
+{{#InnerDumpNodes}}
+{{outerNodeName}} {{#Bidirectional}}<{{/Bidirectional}}--> {{innerNodeName}} : {{label}}
+{{/InnerDumpNodes}}
+{{/isList}}
+{{^isList}}
+{{outerNodeName}} {{#Bidirectional}}<{{/Bidirectional}}--> {{innerNodeName}} : {{label}}
+{{/isList}}
+{{/DumpRelations}}
+{{/DumpNodes}}
+{{#BuildConfig}}
+{{#Debug}}
+legend right
+  %date()
+  dumpAst: {{version}}
+  plantuml: %version()
+endlegend
+{{/Debug}}
+{{/BuildConfig}}
+@enduml
diff --git a/dumpAst/src/main/resources/dumpAstVersion.properties b/dumpAst/src/main/resources/dumpAstVersion.properties
new file mode 100644
index 0000000000000000000000000000000000000000..ea4c6af67e252847ff32363384187c7dccdd61f4
--- /dev/null
+++ b/dumpAst/src/main/resources/dumpAstVersion.properties
@@ -0,0 +1,2 @@
+#Fri Sep 11 23:37:05 CEST 2020
+version=0.3.1
diff --git a/dumpAst2uml/src/main/resources/dumpAstVersion.properties b/dumpAst2uml/src/main/resources/dumpAstVersion.properties
deleted file mode 100644
index cc8ed0e4a564b093ec353594cbc9df7795c5a5aa..0000000000000000000000000000000000000000
--- a/dumpAst2uml/src/main/resources/dumpAstVersion.properties
+++ /dev/null
@@ -1,2 +0,0 @@
-#Sat Sep 05 23:54:45 CEST 2020
-version=0.3
diff --git a/grammar2uml/build.gradle b/grammar2uml/build.gradle
index 578aaa1664d74c94a9d2adecb49cd5a78ae24370..d4e998bd3c5ee2c284c38da757f8e7f61d45383c 100644
--- a/grammar2uml/build.gradle
+++ b/grammar2uml/build.gradle
@@ -4,7 +4,7 @@ apply plugin: 'idea'
 
 sourceCompatibility = 1.8
 
-mainClassName = 'org.jastadd.grammar2uml.compiler.Compiler'
+mainClassName = 'de.tudresden.inf.st.jastadd.grammar2uml.compiler.Compiler'
 
 repositories {
     jcenter()
@@ -162,7 +162,7 @@ jastadd {
     }
 
     cleanGen.doFirst {
-        delete "src/gen/java/org"
+        delete "src/gen/java/de"
         delete "src/gen-res/BuildInfo.properties"
     }
 
@@ -174,7 +174,7 @@ jastadd {
 
     module = "Grammar2Uml"
 
-    astPackage = 'org.jastadd.grammar2uml.ast'
+    astPackage = 'de.tudresden.inf.st.jastadd.grammar2uml.ast'
 
     parser.name = 'Grammar2UmlParser'
 
@@ -182,8 +182,8 @@ jastadd {
 
     buildInfoDir = 'src/gen-res'
 
-    scanner.genDir = "src/gen/java/org/jastadd/grammar2uml/scanner"
-    parser.genDir = "src/gen/java/org/jastadd/grammar2uml/parser"
+    scanner.genDir = "src/gen/java/de/tudresden/inf/st/jastadd/grammar2uml/scanner"
+    parser.genDir = "src/gen/java/de/tudresden/inf/st/jastadd/grammar2uml/parser"
 
     jastaddOptions = ["--lineColumnNumbers", "--List=JastAddList", "--safeLazy", "--visitCheck=true", "--rewrite=cnta", "--cache=all"]
 }
diff --git a/grammar2uml/src/main/jastadd/backend/Generation.jadd b/grammar2uml/src/main/jastadd/backend/Generation.jadd
index 7001c2f290bd9d8dd5a132f3c1d248c941055747..45276bd4aa3744de020a75512bc9ef62e7e130fb 100644
--- a/grammar2uml/src/main/jastadd/backend/Generation.jadd
+++ b/grammar2uml/src/main/jastadd/backend/Generation.jadd
@@ -186,7 +186,30 @@ aspect AspectGeneration {
     com.github.mustachejava.DefaultMustacheFactory mf = new com.github.mustachejava.DefaultMustacheFactory();
 //    mf.setObjectHandler(roh);
     com.github.mustachejava.Mustache m = mf.compile("grammar2uml.mustache");
-    m.execute(new java.io.PrintWriter(new org.jastadd.grammar2uml.compiler.AppendableWriter(sb)), this);
+    m.execute(new java.io.PrintWriter(new AppendableWriter(sb)), this);
     return sb.toString();
   }
+  public class AppendableWriter extends java.io.Writer {
+    private final StringBuilder sb;
+
+    public AppendableWriter(StringBuilder sb) {
+      this.sb = sb;
+    }
+
+    @Override
+    public void write(char[] chars, int off, int len) {
+      sb.append(chars, off, len);
+    }
+
+    @Override
+    public void write(String str) {
+      sb.append(str);
+    }
+
+    @Override
+    public void flush() {}
+
+    @Override
+    public void close() {}
+  }
 }
diff --git a/grammar2uml/src/main/jastadd/parser/Preamble.parser b/grammar2uml/src/main/jastadd/parser/Preamble.parser
index 6d8af56847d11654ce2e75845dd5a27c317d273d..b65eedbcac607a2495d8d514d48ba2eb7d415ed2 100644
--- a/grammar2uml/src/main/jastadd/parser/Preamble.parser
+++ b/grammar2uml/src/main/jastadd/parser/Preamble.parser
@@ -1,6 +1,6 @@
 %header {:
-package org.jastadd.grammar2uml.parser;
-import org.jastadd.grammar2uml.ast.*;
+package de.tudresden.inf.st.jastadd.grammar2uml.parser;
+import de.tudresden.inf.st.jastadd.grammar2uml.ast.*;
 :};
 
 %goal goal;
diff --git a/grammar2uml/src/main/jastadd/scanner/Header.flex b/grammar2uml/src/main/jastadd/scanner/Header.flex
index 5050ac3e25313c07411bc53ed2b49a3c66bc3c86..1fa63001776ed524713f182197ccb097127394a8 100644
--- a/grammar2uml/src/main/jastadd/scanner/Header.flex
+++ b/grammar2uml/src/main/jastadd/scanner/Header.flex
@@ -1,6 +1,6 @@
-package org.jastadd.grammar2uml.scanner;
+package de.tudresden.inf.st.jastadd.grammar2uml.scanner;
 
-import org.jastadd.grammar2uml.parser.Grammar2UmlParser.Terminals;
+import de.tudresden.inf.st.jastadd.grammar2uml.parser.Grammar2UmlParser.Terminals;
 %%
 
 %public
diff --git a/grammar2uml/src/main/java/org/jastadd/grammar2uml/compiler/Compiler.java b/grammar2uml/src/main/java/de/tudresden/inf/st/jastadd/grammar2uml/compiler/Compiler.java
similarity index 93%
rename from grammar2uml/src/main/java/org/jastadd/grammar2uml/compiler/Compiler.java
rename to grammar2uml/src/main/java/de/tudresden/inf/st/jastadd/grammar2uml/compiler/Compiler.java
index 7b96d64fcd8c3504071992af6062ab24ef312fd5..fcb3b16d4378d64052ea88df14d63d755c988b70 100644
--- a/grammar2uml/src/main/java/org/jastadd/grammar2uml/compiler/Compiler.java
+++ b/grammar2uml/src/main/java/de/tudresden/inf/st/jastadd/grammar2uml/compiler/Compiler.java
@@ -1,16 +1,16 @@
-package org.jastadd.grammar2uml.compiler;
+package de.tudresden.inf.st.jastadd.grammar2uml.compiler;
 
 import beaver.Parser;
 import org.jastadd.option.BooleanOption;
 import org.jastadd.option.ValueOption;
 import org.jastadd.relast.compiler.AbstractCompiler;
 import org.jastadd.relast.compiler.CompilerException;
-import org.jastadd.grammar2uml.ast.ErrorMessage;
-import org.jastadd.grammar2uml.ast.GrammarFile;
-import org.jastadd.grammar2uml.ast.Program;
-import org.jastadd.grammar2uml.ast.Grammar2Uml;
-import org.jastadd.grammar2uml.parser.Grammar2UmlParser;
-import org.jastadd.grammar2uml.scanner.Grammar2UmlScanner;
+import de.tudresden.inf.st.jastadd.grammar2uml.ast.ErrorMessage;
+import de.tudresden.inf.st.jastadd.grammar2uml.ast.GrammarFile;
+import de.tudresden.inf.st.jastadd.grammar2uml.ast.Program;
+import de.tudresden.inf.st.jastadd.grammar2uml.ast.Grammar2Uml;
+import de.tudresden.inf.st.jastadd.grammar2uml.parser.Grammar2UmlParser;
+import de.tudresden.inf.st.jastadd.grammar2uml.scanner.Grammar2UmlScanner;
 
 import java.io.BufferedReader;
 import java.io.IOException;
diff --git a/grammar2uml/src/main/java/org/jastadd/grammar2uml/compiler/SimpleMain.java b/grammar2uml/src/main/java/de/tudresden/inf/st/jastadd/grammar2uml/compiler/SimpleMain.java
similarity index 89%
rename from grammar2uml/src/main/java/org/jastadd/grammar2uml/compiler/SimpleMain.java
rename to grammar2uml/src/main/java/de/tudresden/inf/st/jastadd/grammar2uml/compiler/SimpleMain.java
index ff422991c640c7308c89a990c9baa71e031cd9e6..b8313b8bf15297cd1e2ab1844c02277ea5b8c1f3 100644
--- a/grammar2uml/src/main/java/org/jastadd/grammar2uml/compiler/SimpleMain.java
+++ b/grammar2uml/src/main/java/de/tudresden/inf/st/jastadd/grammar2uml/compiler/SimpleMain.java
@@ -1,9 +1,9 @@
-package org.jastadd.grammar2uml.compiler;
+package de.tudresden.inf.st.jastadd.grammar2uml.compiler;
 
 import beaver.Parser;
-import org.jastadd.grammar2uml.ast.*;
-import org.jastadd.grammar2uml.parser.Grammar2UmlParser;
-import org.jastadd.grammar2uml.scanner.Grammar2UmlScanner;
+import de.tudresden.inf.st.jastadd.grammar2uml.ast.*;
+import de.tudresden.inf.st.jastadd.grammar2uml.parser.Grammar2UmlParser;
+import de.tudresden.inf.st.jastadd.grammar2uml.scanner.Grammar2UmlScanner;
 
 import java.io.BufferedReader;
 import java.io.IOException;
diff --git a/grammar2uml/src/main/java/org/jastadd/grammar2uml/compiler/Utils.java b/grammar2uml/src/main/java/de/tudresden/inf/st/jastadd/grammar2uml/compiler/Utils.java
similarity index 87%
rename from grammar2uml/src/main/java/org/jastadd/grammar2uml/compiler/Utils.java
rename to grammar2uml/src/main/java/de/tudresden/inf/st/jastadd/grammar2uml/compiler/Utils.java
index e10374b3155f7a394cf44a32a1dc06315d455382..496daa3bbe6f1745e2b69c6b0b412e0b1b62d29b 100644
--- a/grammar2uml/src/main/java/org/jastadd/grammar2uml/compiler/Utils.java
+++ b/grammar2uml/src/main/java/de/tudresden/inf/st/jastadd/grammar2uml/compiler/Utils.java
@@ -1,4 +1,4 @@
-package org.jastadd.grammar2uml.compiler;
+package de.tudresden.inf.st.jastadd.grammar2uml.compiler;
 
 import java.util.*;
 import java.util.function.Predicate;
diff --git a/grammar2uml/src/main/java/org/jastadd/grammar2uml/compiler/AppendableWriter.java b/grammar2uml/src/main/java/org/jastadd/grammar2uml/compiler/AppendableWriter.java
deleted file mode 100644
index 318f67fa93eccbdf25a30ced491e59115235f9da..0000000000000000000000000000000000000000
--- a/grammar2uml/src/main/java/org/jastadd/grammar2uml/compiler/AppendableWriter.java
+++ /dev/null
@@ -1,37 +0,0 @@
-package org.jastadd.grammar2uml.compiler;
-
-import java.io.IOException;
-import java.io.Writer;
-
-/**
- * Writer appending to a StringBuilder.
- *
- * @author rschoene - Initial contribution
- */
-public class AppendableWriter extends Writer {
-  private final StringBuilder sb;
-
-  public AppendableWriter(StringBuilder sb) {
-    this.sb = sb;
-  }
-
-  @Override
-  public void write(char[] chars, int off, int len) throws IOException {
-    sb.append(chars, off, len);
-  }
-
-  @Override
-  public void write(String str) throws IOException {
-    sb.append(str);
-  }
-
-  @Override
-  public void flush() {
-
-  }
-
-  @Override
-  public void close() {
-
-  }
-}
diff --git a/grammar2uml/src/test/java/org/jastadd/grammar2uml/test/CompilerTest.java b/grammar2uml/src/test/java/de/tudresden/inf/st/jastadd/grammar2uml/test/CompilerTest.java
similarity index 92%
rename from grammar2uml/src/test/java/org/jastadd/grammar2uml/test/CompilerTest.java
rename to grammar2uml/src/test/java/de/tudresden/inf/st/jastadd/grammar2uml/test/CompilerTest.java
index bf5af06acffe0e527a2f13e7890a6a8eee6bc683..a622034ad0cead21b95749243a0b18047ab55374 100644
--- a/grammar2uml/src/test/java/org/jastadd/grammar2uml/test/CompilerTest.java
+++ b/grammar2uml/src/test/java/de/tudresden/inf/st/jastadd/grammar2uml/test/CompilerTest.java
@@ -1,7 +1,7 @@
-package org.jastadd.grammar2uml.test;
+package de.tudresden.inf.st.jastadd.grammar2uml.test;
 
 import org.jastadd.relast.compiler.CompilerException;
-import org.jastadd.grammar2uml.compiler.Compiler;
+import de.tudresden.inf.st.jastadd.grammar2uml.compiler.Compiler;
 import org.junit.jupiter.api.Test;
 
 import java.io.File;
diff --git a/settings.gradle b/settings.gradle
index 3278510c64b72f36f4d307f08eea5f134a2d99d9..5f2b84d4f102e477be734afb63cc5dd074c20dfe 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -2,4 +2,5 @@ rootProject.name = 'relast2uml'
 
 include 'relast.preprocessor'
 include 'grammar2uml'
-include 'dumpAst2uml'
+include 'dumpAst'
+include 'testDumper'
diff --git a/testDumper/.gitignore b/testDumper/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..87b4cdd3d7c6a41502ca98703abeeb69a1d536fb
--- /dev/null
+++ b/testDumper/.gitignore
@@ -0,0 +1,5 @@
+build
+src/gen-res/
+src/gen/
+out/
+*.class
diff --git a/testDumper/build.gradle b/testDumper/build.gradle
new file mode 100644
index 0000000000000000000000000000000000000000..9980fef2d08a4422fc26a598b495b1dc16d18ce1
--- /dev/null
+++ b/testDumper/build.gradle
@@ -0,0 +1,110 @@
+apply plugin: 'jastadd'
+apply plugin: 'idea'
+
+repositories {
+    mavenCentral()
+}
+
+buildscript {
+    repositories.jcenter()
+    dependencies {
+        classpath 'org.jastadd:jastaddgradle:1.13.3'
+    }
+}
+
+dependencies {
+    runtime group: 'org.jastadd', name: 'jastadd', version: '2.3.4'
+
+    testImplementation project(':dumpAst')
+    testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.4.0'
+    testRuntimeOnly group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.4.0'
+    testImplementation group: 'org.assertj', name: 'assertj-core', version: '3.12.1'
+}
+
+File genSrc = file("src/gen/java")
+sourceSets.main.java.srcDir genSrc
+idea.module.generatedSourceDirs += genSrc
+
+test {
+    useJUnitPlatform()
+
+    maxHeapSize = '1G'
+}
+
+File testingGrammar = file('./src/main/jastadd/testDumper.relast')
+
+task relast(type: JavaExec) {
+    group = 'Build'
+    main = "-jar"
+
+    doFirst {
+        delete "src/gen/jastadd/*.ast"
+        delete "src/gen/jastadd/testDumper.jadd"
+        delete "src/gen/jastadd/testDumperRefResolver.jadd"
+        delete "src/gen/jastadd/testDumperResolverStubs.jrag"
+        mkdir  "src/gen/jastadd/"
+    }
+
+    args = [
+            "../libs/relast.jar",
+            testingGrammar,
+//            "./src/main/jastadd/MustacheNodes.relast",
+            "--listClass=java.util.ArrayList",
+            "--jastAddList=JastAddList",
+            "--useJastAddNames",
+            "--file",
+            "--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 {
+    configureModuleBuild()
+    modules {
+        //noinspection GroovyAssignabilityCheck
+        module("testDumper") {
+
+            java {
+                basedir ".."
+                include "testDumper/src/main/**/*.java"
+                include "testDumper/src/gen/**/*.java"
+            }
+
+            jastadd {
+                basedir ".."
+                include "testDumper/src/main/jastadd/**/*.ast"
+                include "testDumper/src/main/jastadd/**/*.jadd"
+                include "testDumper/src/main/jastadd/**/*.jrag"
+                include "testDumper/src/gen/jastadd/**/*.ast"
+                include "testDumper/src/gen/jastadd/**/*.jadd"
+                include "testDumper/src/gen/jastadd/**/*.jrag"
+            }
+        }
+    }
+
+    cleanGen.doFirst {
+        delete "src/gen/java/org"
+        delete "src/gen-res/BuildInfo.properties"
+    }
+
+    preprocessParser.doFirst {
+
+        args += ["--no-beaver-symbol"]
+
+    }
+
+    module = "testDumper"
+    astPackage = 'org.jastadd.testDumper.ast'
+    genDir = 'src/gen/java'
+    buildInfoDir = 'src/gen-res'
+    jastaddOptions = ["--lineColumnNumbers", "--List=JastAddList", "--safeLazy", "--visitCheck=true", "--rewrite=cnta", "--cache=all"]
+}
+
+generateAst.dependsOn relast
diff --git a/testDumper/src/main/jastadd/testDumper.jrag b/testDumper/src/main/jastadd/testDumper.jrag
new file mode 100644
index 0000000000000000000000000000000000000000..c27464bea4ff4586b04c35efdb77a20412b3e36e
--- /dev/null
+++ b/testDumper/src/main/jastadd/testDumper.jrag
@@ -0,0 +1,29 @@
+aspect Grammar {
+  syn A C.getCalculated() {
+    A result = new A();
+    result.setName("Calculated-" + getName());
+    return result;
+  }
+
+  syn JastAddList<B> C.getAlsoCalculatedList() {
+    JastAddList<B> result = new JastAddList<>();
+    B inner = new B();
+    inner.setName("AlsoCalculated-" + getName());
+    result.add(inner);
+    return result;
+  }
+
+  syn nta A C.getCalculatedNewSyntax() {
+    A result = new A();
+    result.setName("Calculated-" + getName());
+    return result;
+  }
+
+  syn nta JastAddList<B> C.getAlsoCalculatedListNewSyntax() {
+    JastAddList<B> result = new JastAddList<>();
+    B inner = new B();
+    inner.setName("AlsoCalculated-" + getName());
+    result.add(inner);
+    return result;
+  }
+}
diff --git a/testDumper/src/main/jastadd/testDumper.relast b/testDumper/src/main/jastadd/testDumper.relast
new file mode 100644
index 0000000000000000000000000000000000000000..c830eb955d70f390aa57f6fddb1982611a73d58c
--- /dev/null
+++ b/testDumper/src/main/jastadd/testDumper.relast
@@ -0,0 +1,12 @@
+Nameable ::= <Name> ;
+Root : Nameable ::= A B* [C] ;
+A : Nameable ::= B C ;
+B : Nameable ::= ;
+C : Nameable ::= <Unwanted:int> <RawReference:A> /Calculated:A/ /AlsoCalculated:B*/ ;
+
+rel B.oneA -> A ;
+rel B.maybeC? -> C ;
+rel B.manyA* -> A ;
+rel C.biA1 <-> A.biC1 ;
+rel C.biA2* <-> A.biC2 ;
+rel C.biA3? <-> A.biC3 ;
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
new file mode 100644
index 0000000000000000000000000000000000000000..de89eeb5a2a8be82fd354480163dadc5c7955cc8
--- /dev/null
+++ b/testDumper/src/test/java/de/tudresden/inf/st/jastadd/testDumper/TestSimple.java
@@ -0,0 +1,202 @@
+package de.tudresden.inf.st.jastadd.testDumper;
+
+import de.tudresden.inf.st.jastadd.dumpAst.ast.DumpNode;
+import org.jastadd.testDumper.ast.A;
+import org.jastadd.testDumper.ast.B;
+import org.jastadd.testDumper.ast.C;
+import org.jastadd.testDumper.ast.Root;
+import 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.assertThat;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class TestSimple {
+
+  @Test
+  public void testEmpty() {
+    Root root = new Root();
+    root.setName(ROOT_NAME);
+
+    List<DumpNode> nodes = TestUtils.dumpModel(root);
+    assertThat(nodes).flatExtracting(NAME_EXTRACTOR).containsExactly(ROOT_NAME);
+    DumpNode actualRoot = TestUtils.findByName(nodes, ROOT_NAME);
+    assertEquals(1, actualRoot.getNumDumpToken());
+    assertEquals(0, actualRoot.getNumDumpChildNode());
+    assertEquals(0, actualRoot.getNumDumpRelation());
+  }
+
+  @Test
+  public void testOneNormalChild() {
+    Root root = new Root();
+    root.setName(ROOT_NAME);
+    A a = new A();
+    a.setName(A_NAME);
+    root.setA(a);
+
+    List<DumpNode> nodes = TestUtils.dumpModel(root);
+    assertEquals(2, nodes.size());
+    assertThat(nodes).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(ROOT_NAME, A_NAME);
+    DumpNode actualRoot = TestUtils.findByName(nodes, ROOT_NAME);
+    assertEquals(1, actualRoot.getNumDumpToken());
+    assertEquals(1, actualRoot.getNumDumpChildNode());
+    assertEquals(0, actualRoot.getNumDumpRelation());
+    List<DumpNode> children = TestUtils.normalChildren(actualRoot);
+    assertThat(children).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(A_NAME);
+  }
+
+  @Test
+  public void testOneListChild() {
+    Root root = new Root();
+    root.setName(ROOT_NAME);
+    B b = new B();
+    b.setName(B_NAME);
+    root.addB(b);
+
+    List<DumpNode> nodes = TestUtils.dumpModel(root);
+    assertEquals(2, nodes.size());
+    assertThat(nodes).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(ROOT_NAME, B_NAME);
+    DumpNode actualRoot = TestUtils.findByName(nodes, ROOT_NAME);
+    assertEquals(1, actualRoot.getNumDumpToken());
+    assertEquals(1, actualRoot.getNumDumpChildNode());
+    assertEquals(0, actualRoot.getNumDumpRelation());
+    List<DumpNode> children = TestUtils.listChildren(actualRoot);
+    assertThat(children).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(B_NAME);
+  }
+
+  @Test
+  public void testOneOptChild() {
+    Root root = new Root();
+    root.setName(ROOT_NAME);
+    C c = new C();
+    c.setName(C_NAME);
+    root.setC(c);
+
+    List<DumpNode> nodes = TestUtils.dumpModel(root);
+    assertEquals(2, nodes.size());
+    assertThat(nodes).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(ROOT_NAME, C_NAME);
+    DumpNode actualRoot = TestUtils.findByName(nodes, ROOT_NAME);
+    assertEquals(1, actualRoot.getNumDumpToken());
+    assertEquals(1, actualRoot.getNumDumpChildNode());
+    assertEquals(0, actualRoot.getNumDumpRelation());
+    List<DumpNode> children = TestUtils.normalChildren(actualRoot);
+    assertThat(children).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(C_NAME);
+  }
+
+  @Test
+  public void testNormalUniRelation() {
+    Root root = relationSetup();
+    root.getB(0).setOneA(root.getA());
+
+    List<DumpNode> nodes = TestUtils.dumpModel(root);
+    assertEquals(4, nodes.size());
+    DumpNode actualB = TestUtils.findByName(nodes, B_NAME);
+    assertEquals(1, actualB.getNumDumpRelation());
+    assertEquals("OneA", actualB.getDumpRelation(0).label());
+    List<DumpNode> relationEnds = normalRelationChildren(actualB);
+    assertThat(relationEnds).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(A_NAME);
+  }
+
+  @Test
+  public void testListUniRelation() {
+    Root root = relationSetup();
+    root.getB(0).addManyA(root.getA());
+
+    List<DumpNode> nodes = TestUtils.dumpModel(root);
+    assertEquals(4, nodes.size());
+    DumpNode actualB = TestUtils.findByName(nodes, B_NAME);
+    assertEquals(1, actualB.getNumDumpRelation());
+    assertEquals("ManyA", actualB.getDumpRelation(0).label());
+    List<DumpNode> relationEnds = listRelationChildren(actualB);
+    assertThat(relationEnds).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(A_NAME);
+  }
+
+  @Test
+  public void testOptUniRelation() {
+    Root root = relationSetup();
+    root.getB(0).setMaybeC(root.getC());
+
+    List<DumpNode> nodes = TestUtils.dumpModel(root);
+    assertEquals(4, nodes.size());
+    DumpNode actualB = TestUtils.findByName(nodes, B_NAME);
+    assertEquals(1, actualB.getNumDumpRelation());
+    assertEquals("MaybeC", actualB.getDumpRelation(0).label());
+    List<DumpNode> relationEnds = normalRelationChildren(actualB);
+    assertThat(relationEnds).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(C_NAME);
+  }
+
+  @Test
+  public void testNormalBiRelation() {
+    Root root = relationSetup();
+    root.getC().setBiA1(root.getA());
+
+    List<DumpNode> nodes = TestUtils.dumpModel(root);
+    assertEquals(4, nodes.size());
+    // bidirectional relations are currently not found, instead there will be two unidirectional ones
+    DumpNode actualA = TestUtils.findByName(nodes, A_NAME);
+    assertEquals(1, actualA.getNumDumpRelation());
+    assertEquals("BiC1", actualA.getDumpRelation(0).label());
+    List<DumpNode> relationEndsOfA = normalRelationChildren(actualA);
+    assertThat(relationEndsOfA).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(C_NAME);
+    DumpNode actualC = TestUtils.findByName(nodes, C_NAME);
+    assertEquals(1, actualC.getNumDumpRelation());
+    assertEquals("BiA1", actualC.getDumpRelation(0).label());
+    List<DumpNode> relationEndsOfC = normalRelationChildren(actualC);
+    assertThat(relationEndsOfC).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(A_NAME);
+  }
+
+  @Test
+  public void testListBiRelation() {
+    Root root = relationSetup();
+    root.getC().addBiA2(root.getA());
+
+    List<DumpNode> nodes = TestUtils.dumpModel(root);
+    assertEquals(4, nodes.size());
+    // bidirectional relations are currently not found, instead there will be two unidirectional ones
+    DumpNode actualA = TestUtils.findByName(nodes, A_NAME);
+    assertEquals(1, actualA.getNumDumpRelation());
+    assertEquals("BiC2", actualA.getDumpRelation(0).label());
+    List<DumpNode> relationEndsOfA = normalRelationChildren(actualA);
+    assertThat(relationEndsOfA).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(C_NAME);
+    DumpNode actualC = TestUtils.findByName(nodes, C_NAME);
+    assertEquals(1, actualC.getNumDumpRelation());
+    assertEquals("BiA2", actualC.getDumpRelation(0).label());
+    List<DumpNode> relationEndsOfC = listRelationChildren(actualC);
+    assertThat(relationEndsOfC).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(A_NAME);
+  }
+
+  @Test
+  public void testOptBiRelation() {
+    Root root = relationSetup();
+    root.getC().setBiA3(root.getA());
+
+    List<DumpNode> nodes = TestUtils.dumpModel(root);
+    assertEquals(4, nodes.size());
+    // bidirectional relations are currently not found, instead there will be two unidirectional ones
+    DumpNode actualA = TestUtils.findByName(nodes, A_NAME);
+    assertEquals(1, actualA.getNumDumpRelation());
+    assertEquals("BiC3", actualA.getDumpRelation(0).label());
+    List<DumpNode> relationEndsOfA = normalRelationChildren(actualA);
+    assertThat(relationEndsOfA).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(C_NAME);
+    DumpNode actualC = TestUtils.findByName(nodes, C_NAME);
+    assertEquals(1, actualC.getNumDumpRelation());
+    assertEquals("BiA3", actualC.getDumpRelation(0).label());
+    List<DumpNode> relationEndsOfC = normalRelationChildren(actualC);
+    assertThat(relationEndsOfC).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(A_NAME);
+  }
+
+  @Test
+  public void testOneNormalReferenceToken() {
+    Root root = relationSetup();
+    root.getC().setRawReference(root.getA());
+
+    List<DumpNode> nodes = TestUtils.dumpModel(root);
+    assertEquals(4, nodes.size());
+    DumpNode actualC = TestUtils.findByName(nodes, C_NAME);
+    List<DumpNode> references = referenceTokens(actualC);
+    assertThat(references).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(A_NAME);
+  }
+
+}
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
new file mode 100644
index 0000000000000000000000000000000000000000..3bc908e0873254a7f76d27d7757d35e47bb4500a
--- /dev/null
+++ b/testDumper/src/test/java/de/tudresden/inf/st/jastadd/testDumper/TestUtils.java
@@ -0,0 +1,144 @@
+package de.tudresden.inf.st.jastadd.testDumper;
+
+import de.tudresden.inf.st.jastadd.dumpAst.ast.*;
+import org.assertj.core.api.Assertions;
+import org.jastadd.testDumper.ast.A;
+import org.jastadd.testDumper.ast.B;
+import org.jastadd.testDumper.ast.C;
+import org.jastadd.testDumper.ast.Root;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+public class TestUtils {
+  static class ExposingDumpBuilder extends DumpBuilder {
+
+    protected ExposingDumpBuilder(Object target) {
+      super(target);
+    }
+
+    @Override
+    public DumpAst build() {
+      return super.build();
+    }
+  }
+
+  public static final Random rand = new Random();
+  public static final String A_NAME = "A" + Integer.toHexString(rand.nextInt(0xFFFFFF));
+  public static final String B_NAME = "B" + Integer.toHexString(rand.nextInt(0xFFFFFF));
+  public static final String C_NAME = "C" + Integer.toHexString(rand.nextInt(0xFFFFFF));
+  public static final String ROOT_NAME = "root";
+
+  public static final Function<DumpNode, String> NAME_EXTRACTOR = dp -> {
+//    List<String> result = new ArrayList<>();
+    for (DumpToken dumpToken : dp.getDumpTokenList()) {
+      if (dumpToken.getName().equals("Name")) {
+        return dumpToken.asDumpValueToken().getValue().toString();
+      }
+    }
+//    return result;
+    return null;
+  };
+
+  public static List<DumpNode> dumpModel(Object target) {
+    return dumpModel(target, db -> {});
+  }
+
+  public static List<DumpNode> dumpModel(Object target, Consumer<DumpBuilder> options) {
+    ExposingDumpBuilder builder = new ExposingDumpBuilder(target);
+    options.accept(builder);
+    DumpAst dumpAst = builder.build();
+    List<DumpNode> result = new ArrayList<>();
+    for (DumpNode dumpNode : dumpAst.getDumpNodeList()) {
+      if (dumpNode.isAstNode()) {
+        result.add(dumpNode);
+      }
+    }
+    return result;
+  }
+
+  public static DumpNode findByName(List<DumpNode> nodes, String name) {
+    for (DumpNode node : nodes) {
+      if (name.equals(NAME_EXTRACTOR.apply(node))) {
+        return node;
+      }
+    }
+    Assertions.fail("No node found with name " + name);
+    // DummyCode to keep intellij silent
+    return new DumpNode();
+  }
+
+  public static List<DumpNode> normalChildren(DumpNode node) {
+    List<DumpNode> result = new ArrayList<>();
+    for (DumpChildNode dumpChildNode : node.getDumpChildNodeList()) {
+      if (!dumpChildNode.isList()) {
+        // then it is a DumpNormalChildNode
+        result.add(((DumpNormalChildNode) dumpChildNode).getDumpNode());
+      }
+    }
+    return result;
+  }
+
+  public static List<DumpNode> listChildren(DumpNode node) {
+    List<DumpNode> result = new ArrayList<>();
+    for (DumpChildNode dumpChildNode : node.getDumpChildNodeList()) {
+      if (dumpChildNode.isList()) {
+        // then it is a DumpListChildNode
+        ((DumpListChildNode) dumpChildNode).getInnerDumpNodeList().forEach(inner -> result.add(inner.getDumpNode()));
+      }
+    }
+    return result;
+  }
+
+  public static List<DumpNode> normalRelationChildren(DumpNode node) {
+    List<DumpNode> result = new ArrayList<>();
+    for (DumpRelation dumpRelation : node.getDumpRelationList()) {
+      if (!dumpRelation.isList()) {
+        // then it is a DumpNormalRelation
+        result.add(((DumpNormalRelation) dumpRelation).getDumpNode());
+      }
+    }
+    return result;
+  }
+
+  public static List<DumpNode> listRelationChildren(DumpNode node) {
+    List<DumpNode> result = new ArrayList<>();
+    for (DumpRelation dumpRelation : node.getDumpRelationList()) {
+      if (dumpRelation.isList()) {
+        // then it is a DumpListRelation
+        ((DumpListRelation) dumpRelation).getInnerDumpNodeList().forEach(inner -> result.add(inner.getDumpNode()));
+      }
+    }
+    return result;
+  }
+
+  public static List<DumpNode> referenceTokens(DumpNode node) {
+    List<DumpNode> result = new ArrayList<>();
+    for (DumpToken dumpToken : node.getDumpTokenList()) {
+      if (!dumpToken.isDumpValueToken()) {
+        // then it is a DumpReferenceToken
+        result.add(((DumpReferenceToken) dumpToken).getValue());
+      }
+    }
+    return result;
+  }
+
+  public static Root relationSetup() {
+    Root root = new Root();
+    root.setName(ROOT_NAME);
+    A a = new A();
+    a.setName(A_NAME);
+    root.setA(a);
+    B b = new B();
+    b.setName(B_NAME);
+    root.addB(b);
+    C c = new C();
+    c.setName(C_NAME);
+    root.setC(c);
+    return root;
+  }
+
+}