diff --git a/dumpAst2uml/build.gradle b/dumpAst2uml/build.gradle
index 048b27dc3cb0d93cdfc2d580683197fa4e68bde3..f4badc1b73afbdf0e044ccd8faf7b71943c00022 100644
--- a/dumpAst2uml/build.gradle
+++ b/dumpAst2uml/build.gradle
@@ -32,7 +32,7 @@ dependencies {
     testImplementation group: 'org.assertj', name: 'assertj-core', version: '3.12.1'
 }
 
-def versionFile = 'src/main/resources/dumpAst2umlVersion.properties'
+def versionFile = 'src/main/resources/dumpAstVersion.properties'
 def oldProps = new Properties()
 
 try {
diff --git a/dumpAst2uml/src/main/jastadd/DumpAst.relast b/dumpAst2uml/src/main/jastadd/DumpAst.relast
index 91f4d16f74b37f33f4200aa9f91a79ac0f4a7947..42c18fddf417756538ebd9739877556667c6fa74 100644
--- a/dumpAst2uml/src/main/jastadd/DumpAst.relast
+++ b/dumpAst2uml/src/main/jastadd/DumpAst.relast
@@ -1,4 +1,7 @@
-DumpAst ::= DumpNode* <PackageName> <Scale:double> ;
+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 ;
diff --git a/dumpAst2uml/src/main/jastadd/Generation.jadd b/dumpAst2uml/src/main/jastadd/Generation.jadd
index 68de1bf3c40db41a370deb9dfad5c74b23c48aef..8cf0aba63e98680cdcfe0ad7d5a8ea8be4d45d45 100644
--- a/dumpAst2uml/src/main/jastadd/Generation.jadd
+++ b/dumpAst2uml/src/main/jastadd/Generation.jadd
@@ -4,27 +4,96 @@ aspect Generation {
       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 java.util.List<String> header = new java.util.ArrayList<>();
+    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) {
-      // TODO
+      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 skinParam(String option) {
-      // TODO
+    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) {
@@ -38,7 +107,6 @@ aspect Generation {
       return result;
     }
     public DumpBuilder dumpAsSource(java.nio.file.Path destination) throws java.io.IOException {
-      build().setScale(0.1);
       String content = build().toPlantUml();
       try (java.io.Writer writer = java.nio.file.Files.newBufferedWriter(destination)) {
         writer.write(content);
@@ -46,14 +114,12 @@ aspect Generation {
       return this;
     }
     public DumpBuilder dumpAsPNG(java.nio.file.Path destination) throws java.io.IOException {
-      build().setScale(0.1);
       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 {
-      build().setScale(1);
       String content = build().toPlantUml();
       net.sourceforge.plantuml.SourceStringReader reader = new net.sourceforge.plantuml.SourceStringReader(content);
       reader.outputImage(java.nio.file.Files.newOutputStream(destination),
@@ -71,6 +137,10 @@ aspect Generation {
     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());
@@ -90,10 +160,11 @@ aspect Generation {
       // -- singleChild --
       for (java.lang.reflect.Method method : car.singleChildMethods) {
         Object target = method.invoke(obj);
-        if (target != null) {
+        DumpNode targetNode = transform(tti, target);
+        if (target != null && targetNode != null) {
           DumpNormalChildNode normalChild = new DumpNormalChildNode();
           normalChild.setName(car.names.get(method));
-          normalChild.setDumpNode(transform(tti, target));
+          normalChild.setDumpNode(targetNode);
           node.addDumpChildNode(normalChild);
         }
       }
@@ -103,8 +174,9 @@ aspect Generation {
         DumpListChildNode listChild = new DumpListChildNode();
         listChild.setName(car.names.get(method));
         for (Object target : targetList) {
-          if (target != null) {
-            listChild.addInnerDumpNode(new InnerDumpNode(transform(tti, target)));
+          DumpNode targetNode = transform(tti, target);
+          if (target != null && targetNode != null) {
+            listChild.addInnerDumpNode(new InnerDumpNode(targetNode));
           }
         }
         node.addDumpChildNode(listChild);
@@ -112,10 +184,11 @@ aspect Generation {
       // -- singleRelation --
       for (java.lang.reflect.Method method : car.singleRelationMethods) {
         Object target = method.invoke(obj);
-        if (target != null) {
+        DumpNode targetNode = transform(tti, target);
+        if (target != null && targetNode != null) {
           DumpNormalRelation normalRelation = new DumpNormalRelation();
           normalRelation.setName(car.names.get(method));
-          normalRelation.setDumpNode(transform(tti, target));
+          normalRelation.setDumpNode(targetNode);
           node.addDumpRelation(normalRelation);
         }
       }
@@ -125,8 +198,9 @@ aspect Generation {
         DumpListRelation listRelation = new DumpListRelation();
         listRelation.setName(car.names.get(method));
         for (Object target : targetList) {
-          if (target != null) {
-            listRelation.addInnerDumpNode(new InnerDumpNode(transform(tti, target)));
+          DumpNode targetNode = transform(tti, target);
+          if (target != null && targetNode != null) {
+            listRelation.addInnerDumpNode(new InnerDumpNode(targetNode));
           }
         }
         node.addDumpRelation(listRelation);
@@ -136,17 +210,20 @@ aspect Generation {
         Object target = method.invoke(obj);
         if (target != null) {
           DumpNode targetNode = transform(tti, target);
-          DumpToken token;
-          if (targetNode.isAstNode()) {
+          DumpToken token = null;
+          if (targetNode != null && targetNode.isAstNode()) {
             token = new DumpReferenceToken().setValue(targetNode);
           } else {
-            // maybe ignore empty string values here
-            DumpValueToken valueToken = new DumpValueToken();
-            valueToken.setValue(target);
-            token = valueToken;
+            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);
           }
-          token.setName(car.names.get(method));
-          node.addDumpToken(token);
         }
       }
     }
@@ -165,10 +242,18 @@ aspect Generation {
           switch (simpleName) {
             case "Child":
             case "OptChild":
-              result.singleChildMethods.add(method);
+              String singleChildName = invokeName(annotation);
+              if (!matches(buildConfig().childIgnorePattern(), singleChildName)) {
+                result.singleChildMethods.add(method);
+                result.names.put(method, singleChildName);
+              }
               break;
             case "ListChild":
-              result.listChildMethods.add(method);
+              String listChildName = invokeName(annotation);
+              if (!matches(buildConfig().childIgnorePattern(), listChildName)) {
+                result.listChildMethods.add(method);
+                result.names.put(method, listChildName);
+              }
               break;
             case "Token":
               // heuristic for relations
@@ -178,8 +263,10 @@ aspect Generation {
                 try {
                   java.lang.reflect.Method relationMethod = clazz.getMethod("get" + relationName);
                   // normal get + token-name -> singleRelation
-                  result.singleRelationMethods.add(relationMethod);
-                  result.names.put(relationMethod, relationName);
+                  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
@@ -188,25 +275,39 @@ aspect Generation {
                 try {
                   java.lang.reflect.Method relationMethod = clazz.getMethod("get" + relationName + "List");
                   // normal get + token-name + "List" -> listRelation
-                  result.listRelationMethods.add(relationMethod);
-                  result.names.put(relationMethod, relationName);
+                  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
                 }
               }
-              result.tokenMethods.add(method);
+              if (!matches(buildConfig().tokenIgnorePattern(), tokenName)) {
+                result.tokenMethods.add(method);
+                result.names.put(method, tokenName);
+              }
               break;
-            default:
-              continue;
           }
-          result.names.put(method, invokeName(annotation));
         }
       }
     }
     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);
diff --git a/dumpAst2uml/src/main/jastadd/Navigation.jrag b/dumpAst2uml/src/main/jastadd/Navigation.jrag
index 708d016d515fa4f95b46a7662adffb38c28067d6..eb85ca21852c8e7e31626b0ecacc2cb46e36de2f 100644
--- a/dumpAst2uml/src/main/jastadd/Navigation.jrag
+++ b/dumpAst2uml/src/main/jastadd/Navigation.jrag
@@ -8,4 +8,10 @@ aspect Navigation {
   // --- isDumpValueToken ---
   syn boolean DumpToken.isDumpValueToken() = false;
   eq DumpValueToken.isDumpValueToken() = true;
-}
\ No newline at end of file
+
+  inh BuildConfig DumpNode.buildConfig();
+  eq DumpAst.getChild().buildConfig() = getBuildConfig();
+
+  inh PrintConfig BuildConfig.printConfig();
+  eq DumpAst.getChild().printConfig() = getPrintConfig();
+}
diff --git a/dumpAst2uml/src/main/java/org/jastadd/dumpAst2uml/compiler/SimpleMain.java b/dumpAst2uml/src/main/java/org/jastadd/dumpAst2uml/compiler/SimpleMain.java
index b0b6e02dffa8e7b4a9deb4e6f4ba6b1b25fa0eec..c85abf731723a81200b7e21f1843881570407818 100644
--- a/dumpAst2uml/src/main/java/org/jastadd/dumpAst2uml/compiler/SimpleMain.java
+++ b/dumpAst2uml/src/main/java/org/jastadd/dumpAst2uml/compiler/SimpleMain.java
@@ -17,6 +17,7 @@ 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.*;
 
@@ -96,6 +97,7 @@ public class SimpleMain {
   }
 
   private static void printing() throws IOException {
+//    java.util.regex.Pattern p;p.pattern()
     System.setProperty("java.util.logging.manager", "org.apache.logging.log4j.jul.LogManager");
     System.setProperty("mustache.debug", "true");
     Grammar2Uml model = new Grammar2Uml();
@@ -113,6 +115,9 @@ public class SimpleMain {
     model.treeResolveAll();
 //    traverseInitial(model.toMustache());
     Dumper.read(model.toMustache())
+        .skinParam(SkinParamBooleanSetting.Monochrome, true)
+        .setDebug(true)
+        .ignoreTypes(".*Comment")
         .dumpAsSource(Paths.get("temp.plantuml"))
         .dumpAsSVG(Paths.get("temp.svg"));
   }
@@ -135,8 +140,12 @@ public class SimpleMain {
     relation.setSource(new NormalRole(typeDeclA, "myB"));
     relation.setTarget(new UnnamedRole(typeDeclB));
     grammar.addDeclaration(relation);
+    WhitespaceComment comment = new WhitespaceComment("// comment\\n");
+    relation.addComment(comment);
 
     Dumper.read(program)
+        .ignoreTokens("Name")
+//        .ignoreTypes(".*Comment")
         .dumpAsSource(Paths.get("grammar.plantuml"))
         .dumpAsSVG(Paths.get("grammar.svg"));
   }
diff --git a/dumpAst2uml/src/main/resources/dumpAst.mustache b/dumpAst2uml/src/main/resources/dumpAst.mustache
index fba11038ec059e56a2ee633d789595a1b216064f..1a72eeec32b6c3f1867d50cd542de5ecec39e547 100644
--- a/dumpAst2uml/src/main/resources/dumpAst.mustache
+++ b/dumpAst2uml/src/main/resources/dumpAst.mustache
@@ -1,5 +1,11 @@
 @startuml
+{{#PrintConfig}}
 scale {{Scale}}
+{{#Headers}}
+{{Value}}
+{{/Headers}}
+{{/PrintConfig}}
+
 {{#DumpNodes}}
 {{#isAstNode}}
 object "{{label}}" as {{name}} {
@@ -39,4 +45,13 @@ object "{{label}}" as {{name}} {
 {{/isList}}
 {{/DumpRelations}}
 {{/DumpNodes}}
+{{#BuildConfig}}
+{{#Debug}}
+legend right
+  %date()
+  dumpAst: {{version}}
+  plantuml: %version()
+endlegend
+{{/Debug}}
+{{/BuildConfig}}
 @enduml
diff --git a/dumpAst2uml/src/main/resources/dumpAst2umlVersion.properties b/dumpAst2uml/src/main/resources/dumpAst2umlVersion.properties
deleted file mode 100644
index 9e9347a71a9cec4a1fb0a4118eae4835e757cb73..0000000000000000000000000000000000000000
--- a/dumpAst2uml/src/main/resources/dumpAst2umlVersion.properties
+++ /dev/null
@@ -1,2 +0,0 @@
-#Sat Sep 05 18:50:58 CEST 2020
-version=0.2
diff --git a/dumpAst2uml/src/main/resources/dumpAstVersion.properties b/dumpAst2uml/src/main/resources/dumpAstVersion.properties
new file mode 100644
index 0000000000000000000000000000000000000000..cc8ed0e4a564b093ec353594cbc9df7795c5a5aa
--- /dev/null
+++ b/dumpAst2uml/src/main/resources/dumpAstVersion.properties
@@ -0,0 +1,2 @@
+#Sat Sep 05 23:54:45 CEST 2020
+version=0.3