From 618592766353ac52ce6be0f870b3f18b5f2e7e5e Mon Sep 17 00:00:00 2001
From: rschoene <rene.schoene@tu-dresden.de>
Date: Thu, 23 Jun 2022 16:14:20 +0200
Subject: [PATCH] 1.1.0

- include parts using lambda functions
- children of excluded children are now surveyed as well (previously excluded)
---
 dumpAst/build.gradle                          |   5 +
 dumpAst/src/main/jastadd/DumpAst.relast       |   4 +
 .../src/main/jastadd/GenerationBackend.jadd   | 214 +++++++-----------
 .../st/jastadd/dumpAst/ast/DumpBuilder.java   |  96 ++++++++
 .../main/resources/dumpAstVersion.properties  |   4 +-
 .../jastadd/featureTest/FeatureTestMain.java  |  39 ++--
 .../st/jastadd/testDumper/TestExcluded.java   |   3 +-
 7 files changed, 221 insertions(+), 144 deletions(-)

diff --git a/dumpAst/build.gradle b/dumpAst/build.gradle
index c3060c8..11c2716 100644
--- a/dumpAst/build.gradle
+++ b/dumpAst/build.gradle
@@ -159,6 +159,11 @@ task setDevVersionForCI() {
     }
 }
 
+java {
+    withJavadocJar()
+    withSourcesJar()
+}
+
 publishing {
     publications {
         maven(MavenPublication) {
diff --git a/dumpAst/src/main/jastadd/DumpAst.relast b/dumpAst/src/main/jastadd/DumpAst.relast
index 035dfc4..337c14c 100644
--- a/dumpAst/src/main/jastadd/DumpAst.relast
+++ b/dumpAst/src/main/jastadd/DumpAst.relast
@@ -51,6 +51,10 @@ BuildConfig ::= StyleInformation
  GlobalPatternCollection:PatternCollection
  ExcludeTypePattern:TypePatternCollectionMapping*
  IncludeTypePattern:TypePatternCollectionMapping*
+ <IncludeRelationMethod:IncludeRelationMethod>
+ <IncludeChildMethod:IncludeChildMethod>
+ <IncludeAttributeMethod:IncludeAttributeMethod>
+ <IncludeTokenMethod:IncludeTokenMethod>
  <TypeIgnorePattern>
  <IncludeEmptyString:boolean>
  <ExcludeNullNodes:boolean>
diff --git a/dumpAst/src/main/jastadd/GenerationBackend.jadd b/dumpAst/src/main/jastadd/GenerationBackend.jadd
index dace530..1c2a06f 100644
--- a/dumpAst/src/main/jastadd/GenerationBackend.jadd
+++ b/dumpAst/src/main/jastadd/GenerationBackend.jadd
@@ -127,7 +127,10 @@ aspect GenerationBackend {
         // -- singleChild --
         Object target = containmentMethod.getMethod().invoke(obj);
         String childName = containmentMethod.getName();
-        DumpNode targetNode = transform(tti, target, options.asNormal(!isChildEnabled(objClassName, childName)).allowNullObjectsOnce());
+        if (!getBuildConfig().getIncludeChildMethod().shouldInclude(obj, target, childName)) {
+          continue;
+        }
+        DumpNode targetNode = transform(tti, target, options.asNormal(false).allowNullObjectsOnce());
         if (targetNode != null) {
           DumpNormalChildNode normalChild = new DumpNormalChildNode();
           normalChild.setName(childName);
@@ -141,10 +144,12 @@ aspect GenerationBackend {
         DumpListChildNode listChild = new DumpListChildNode();
         listChild.setComputed(false);
         String childName = containmentMethod.getName();
-        boolean shouldBeInvisible = !isChildEnabled(objClassName, childName);
         listChild.setName(childName);
         for (Object target : targetList) {
-          DumpNode targetNode = transform(tti, target, options.asNormal(shouldBeInvisible));
+          if (!getBuildConfig().getIncludeChildMethod().shouldInclude(obj, target, childName)) {
+            continue;
+          }
+          DumpNode targetNode = transform(tti, target, options.asNormal(false));
           if (target != null && targetNode != null) {
             listChild.addInnerDumpNode(new InnerDumpNode().setDumpNode(targetNode));
           }
@@ -162,7 +167,13 @@ aspect GenerationBackend {
         Object target = otherMethod.getMethod().invoke(obj);
         String childName = otherMethod.getName();
         boolean computed = otherMethod.asSingleChildMethod().isNTASingleChildMethod();
-        DumpNode targetNode = transform(tti, target, options.asNormal(!isChildEnabled(objClassName, childName)).computed(computed).allowNullObjectsOnce());
+        boolean shouldInclude = computed ?
+            getBuildConfig().getIncludeAttributeMethod().shouldInclude(obj, childName, true, () -> catchedInvoke(otherMethod.getMethod(), obj)) :
+            getBuildConfig().getIncludeChildMethod().shouldInclude(obj, target, childName);
+        if (!shouldInclude) {
+          continue;
+        }
+        DumpNode targetNode = transform(tti, target, options.asNormal(false).computed(computed).allowNullObjectsOnce());
         if (targetNode != null) {
           DumpNormalChildNode normalChild = new DumpNormalChildNode();
           normalChild.setName(childName);
@@ -177,10 +188,15 @@ aspect GenerationBackend {
         boolean computed = otherMethod.asListChildMethod().isNTAListChildMethod();
         listChild.setComputed(computed);
         String childName = otherMethod.getName();
-        boolean shouldBeInvisible = !isChildEnabled(objClassName, childName);
         listChild.setName(childName);
         for (Object target : targetList) {
-          DumpNode targetNode = transform(tti, target, options.asNormal(shouldBeInvisible).computed(computed));
+          boolean shouldInclude = computed ?
+              getBuildConfig().getIncludeAttributeMethod().shouldInclude(obj, childName, true, () -> catchedInvoke(otherMethod.getMethod(), obj)) :
+              getBuildConfig().getIncludeChildMethod().shouldInclude(obj, target, childName);
+          if (!shouldInclude) {
+            continue;
+          }
+          DumpNode targetNode = transform(tti, target, options.asNormal(false).computed(computed));
           if (target != null && targetNode != null) {
             listChild.addInnerDumpNode(new InnerDumpNode().setDumpNode(targetNode));
           }
@@ -191,6 +207,9 @@ aspect GenerationBackend {
       } else if (otherMethod.isSingleRelationMethod()) {
         // -- singleRelation --
         Object target = otherMethod.getMethod().invoke(obj);
+        if (!getBuildConfig().getIncludeRelationMethod().shouldInclude(obj, target, otherMethod.getName())) {
+          continue;
+        }
         DumpNode targetNode = transform(tti, target, options.asRelation());
         if (targetNode != null) {
           DumpNormalRelation normalRelation = new DumpNormalRelation();
@@ -204,6 +223,9 @@ aspect GenerationBackend {
         DumpListRelation listRelation = new DumpListRelation();
         listRelation.setName(otherMethod.getName());
         for (Object target : targetList) {
+          if (!getBuildConfig().getIncludeRelationMethod().shouldInclude(obj, target, otherMethod.getName())) {
+            continue;
+          }
           DumpNode targetNode = transform(tti, target, options.asRelation());
           if (target != null && targetNode != null) {
             listRelation.addInnerRelationDumpNode(new InnerRelationDumpNode(targetNode));
@@ -214,12 +236,14 @@ aspect GenerationBackend {
         }
       } else if (otherMethod.isTokenMethod()) {
         // -- token --
+        TokenMethod tokenMethod = otherMethod.asTokenMethod();
+        boolean shouldInclude = tokenMethod.isAttributeMethod() ?
+            getBuildConfig().getIncludeAttributeMethod().shouldInclude(obj, otherMethod.getName(), false, () -> catchedInvoke(otherMethod.getMethod(), obj)) :
+            getBuildConfig().getIncludeTokenMethod().shouldInclude(obj, otherMethod.getName(), otherMethod.getMethod().invoke(obj));
+        if (!shouldInclude) {
+          continue;
+        }
         Object target = otherMethod.getMethod().invoke(obj);
-//        java.util.function.Consumer<DumpToken> handleToken = token -> {
-//            token.setName(otherMethod.getName());
-//            token.setComputed(otherMethod.asTokenMethod().isAttributeMethod());
-//            node.addDumpToken(token);
-//        };
         if (target != null) {
           DumpToken token = null;
           boolean atLeastOneASTNode = false;
@@ -267,6 +291,16 @@ aspect GenerationBackend {
     return node;
   }
 
+  private Object DumpAst.catchedInvoke(java.lang.reflect.Method method, Object obj) {
+    try {
+      return method.invoke(obj);
+    }
+    catch (Exception e) {
+      e.printStackTrace();
+      return null;
+    }
+  }
+
   private void DumpAst.applyStyle(DumpNode node) {
     Object obj = node.getObject();
     node.setLabel(getBuildConfig().getStyleInformation().getNameMethod().get(obj));
@@ -324,12 +358,10 @@ aspect GenerationBackend {
                 try {
                   java.lang.reflect.Method relationMethod = clazz.getMethod("get" + relationName);
                   // normal get + token-name -> singleRelation
-                  if (isRelationEnabled(clazzName, relationName)) {
-                    SingleRelationMethod singleRelationMethod = new SingleRelationMethod();
-                    singleRelationMethod.setMethod(relationMethod);
-                    singleRelationMethod.setName(relationName);
-                    result.addOtherMethod(singleRelationMethod);
-                  }
+                  SingleRelationMethod singleRelationMethod = new SingleRelationMethod();
+                  singleRelationMethod.setMethod(relationMethod);
+                  singleRelationMethod.setName(relationName);
+                  result.addOtherMethod(singleRelationMethod);
                   continue;
                 } catch (NoSuchMethodException e) {
                   // ignore, but we know this is probably not a single relation
@@ -338,23 +370,19 @@ aspect GenerationBackend {
                 try {
                   java.lang.reflect.Method relationMethod = clazz.getMethod("get" + relationName + "List");
                   // normal get + token-name + "List" -> listRelation
-                  if (isRelationEnabled(clazzName, relationName)) {
-                    ListRelationMethod listRelationMethod = new ListRelationMethod();
-                    listRelationMethod.setMethod(relationMethod);
-                    listRelationMethod.setName(relationName);
-                    result.addOtherMethod(listRelationMethod);
-                  }
+                  ListRelationMethod listRelationMethod = new ListRelationMethod();
+                  listRelationMethod.setMethod(relationMethod);
+                  listRelationMethod.setName(relationName);
+                  result.addOtherMethod(listRelationMethod);
                   continue;
                 } catch (NoSuchMethodException e) {
                   // ignore, but we know this is probably not a relation at all
                 }
               }
-              if (isTokenEnabled(clazzName, tokenName)) {
-                IntrinsicTokenMethod tokenMethod = new IntrinsicTokenMethod();
-                tokenMethod.setMethod(method);
-                tokenMethod.setName(tokenName);
-                result.addOtherMethod(tokenMethod);
-              }
+              IntrinsicTokenMethod tokenMethod = new IntrinsicTokenMethod();
+              tokenMethod.setMethod(method);
+              tokenMethod.setName(tokenName);
+              result.addOtherMethod(tokenMethod);
               break;
             case "Attribute":
               String attributeName = method.getName();
@@ -368,20 +396,18 @@ aspect GenerationBackend {
                 if (attributeName.endsWith("List")) {
                   attributeName = attributeName.substring(0, attributeName.length() - 4);
                 }
-                if (isNonterminalAttributeEnabled(clazzName, attributeName)) {
-                  if (Iterable.class.isAssignableFrom(method.getReturnType())) {
-                    NTAListChildMethod ntaListChildMethod = new NTAListChildMethod();
-                    ntaListChildMethod.setMethod(method);
-                    ntaListChildMethod.setName(attributeName);
-                    result.addOtherMethod(ntaListChildMethod);
-                  } else {
-                    NTASingleChildMethod ntaSingleChildMethod = new NTASingleChildMethod();
-                    ntaSingleChildMethod.setMethod(method);
-                    ntaSingleChildMethod.setName(attributeName);
-                    result.addOtherMethod(ntaSingleChildMethod);
-                  }
+                if (Iterable.class.isAssignableFrom(method.getReturnType())) {
+                  NTAListChildMethod ntaListChildMethod = new NTAListChildMethod();
+                  ntaListChildMethod.setMethod(method);
+                  ntaListChildMethod.setName(attributeName);
+                  result.addOtherMethod(ntaListChildMethod);
+                } else {
+                  NTASingleChildMethod ntaSingleChildMethod = new NTASingleChildMethod();
+                  ntaSingleChildMethod.setMethod(method);
+                  ntaSingleChildMethod.setName(attributeName);
+                  result.addOtherMethod(ntaSingleChildMethod);
                 }
-              } else if (isAttributeEnabled(clazzName, attributeName)) {
+              } else {
                 // normal attribute
                 AttributeMethod attributeMethod = new AttributeMethod();
                 attributeMethod.setMethod(method);
@@ -446,90 +472,6 @@ aspect GenerationBackend {
   syn boolean DumpAst.isTypeEnabled(String typeName) {
     return !matches(getBuildConfig().typeIgnorePattern(), typeName);
   }
-  // --- isTokenEnabled ---
-  syn boolean DumpAst.isTokenEnabled(String parentType, String tokenName) {
-    // level 4: excluded for type? -> return no
-    PatternCollection excludeOnType = getBuildConfig().matchExcludePatternCollection(parentType);
-    if (excludeOnType != null && matches(excludeOnType.tokenPattern(), tokenName)) {
-      return false;
-    }
-    // level 3: included for type? -> return yes
-    PatternCollection includeOnType = getBuildConfig().matchIncludePatternCollection(parentType);
-    if (includeOnType != null && matches(includeOnType.tokenPattern(), tokenName)) {
-      return true;
-    }
-    // level 2: globally excluded? -> return no
-    // level 1: otherwise return yes
-    return !matches(getBuildConfig().getGlobalPatternCollection().tokenPattern(), tokenName);
-  }
-
-  // --- isChildEnabled ---
-  syn boolean DumpAst.isChildEnabled(String parentType, String childName) {
-    // level 4: excluded for type? -> return no
-    PatternCollection excludeOnType = getBuildConfig().matchExcludePatternCollection(parentType);
-    if (excludeOnType != null && matches(excludeOnType.childPattern(), childName)) {
-      return false;
-    }
-    // level 3: included for type? -> return yes
-    PatternCollection includeOnType = getBuildConfig().matchIncludePatternCollection(parentType);
-    if (includeOnType != null && matches(includeOnType.childPattern(), childName)) {
-      return true;
-    }
-    // level 2: globally excluded? -> return no
-    // level 1: otherwise return yes
-    return !matches(getBuildConfig().getGlobalPatternCollection().childPattern(), childName);
-  }
-
-  // --- isRelationEnabled ---
-  syn boolean DumpAst.isRelationEnabled(String parentType, String relationName) {
-    // level 4: excluded for type? -> return no
-    PatternCollection excludeOnType = getBuildConfig().matchExcludePatternCollection(parentType);
-    if (excludeOnType != null && matches(excludeOnType.relationPattern(), relationName)) {
-      return false;
-    }
-    // level 3: included for type? -> return yes
-    PatternCollection includeOnType = getBuildConfig().matchIncludePatternCollection(parentType);
-    if (includeOnType != null && matches(includeOnType.relationPattern(), relationName)) {
-      return true;
-    }
-    // level 2: globally excluded? -> return no
-    // level 1: otherwise return yes
-    return !matches(getBuildConfig().getGlobalPatternCollection().relationPattern(), relationName);
-  }
-
-  // --- isAttributeEnabled ---
-  syn boolean DumpAst.isAttributeEnabled(String parentType, String attributeName) {
-    // level 4: included for type? -> return yes
-    PatternCollection includeOnType = getBuildConfig().matchIncludePatternCollection(parentType);
-    if (includeOnType != null && matches(includeOnType.attributePattern(), attributeName)) {
-      return true;
-    }
-    // level 3: excluded for type? -> return no
-    PatternCollection excludeOnType = getBuildConfig().matchExcludePatternCollection(parentType);
-    if (excludeOnType != null && matches(excludeOnType.attributePattern(), attributeName)) {
-      return false;
-    }
-    // level 2: globally included? -> return yes
-    // level 1: otherwise return no
-    return matches(getBuildConfig().getGlobalPatternCollection().attributePattern(), attributeName);
-  }
-
-  // --- isNonterminalAttributeEnabled ---
-  syn boolean DumpAst.isNonterminalAttributeEnabled(String parentType, String ntaName) {
-    // level 4: included for type? -> return yes
-    PatternCollection includeOnType = getBuildConfig().matchIncludePatternCollection(parentType);
-    if (includeOnType != null && matches(includeOnType.ntaPattern(), ntaName)) {
-      return true;
-    }
-    // level 3: excluded for type? -> return no
-    PatternCollection excludeOnType = getBuildConfig().matchExcludePatternCollection(parentType);
-    if (excludeOnType != null && matches(excludeOnType.ntaPattern(), ntaName)) {
-      return false;
-    }
-    // level 2: globally included? -> return yes
-    // level 1: otherwise return no
-    return matches(getBuildConfig().getGlobalPatternCollection().ntaPattern(), ntaName);
-  }
 
   // --- match{In,Ex}cludePatternCollection ---
   syn PatternCollection BuildConfig.matchIncludePatternCollection(String typeName) {
@@ -714,4 +656,24 @@ aspect GenerationBackend {
   public interface StyleMethod<ASTNODE> {
     String get(ASTNODE node);
   }
+
+  @FunctionalInterface
+  public interface IncludeRelationMethod<ASTNODE> {
+    boolean shouldInclude(ASTNODE sourceNode, ASTNODE targetNode, String roleName);
+  }
+
+  @FunctionalInterface
+  public interface IncludeChildMethod<ASTNODE> {
+    boolean shouldInclude(ASTNODE parentNode, ASTNODE childNode, String contextName);
+  }
+
+  @FunctionalInterface
+  public interface IncludeAttributeMethod<ASTNODE> {
+    boolean shouldInclude(ASTNODE node, String attributeName, boolean isNTA, java.util.function.Supplier<Object> value);
+  }
+
+  @FunctionalInterface
+  public interface IncludeTokenMethod<ASTNODE> {
+    boolean shouldInclude(ASTNODE node, String tokenName, Object value);
+  }
 }
diff --git a/dumpAst/src/main/java/de/tudresden/inf/st/jastadd/dumpAst/ast/DumpBuilder.java b/dumpAst/src/main/java/de/tudresden/inf/st/jastadd/dumpAst/ast/DumpBuilder.java
index 424b05a..9fdc7b0 100644
--- a/dumpAst/src/main/java/de/tudresden/inf/st/jastadd/dumpAst/ast/DumpBuilder.java
+++ b/dumpAst/src/main/java/de/tudresden/inf/st/jastadd/dumpAst/ast/DumpBuilder.java
@@ -2,6 +2,8 @@ package de.tudresden.inf.st.jastadd.dumpAst.ast;
 
 import java.lang.reflect.InvocationTargetException;
 
+import static de.tudresden.inf.st.jastadd.dumpAst.ast.ASTNode.matches;
+
 /**
  * Building a dump.
  * <p>
@@ -42,6 +44,68 @@ public class DumpBuilder {
   protected DumpBuilder(Object target) {
     this.target = target;
     buildConfig = new BuildConfig();
+    buildConfig.setIncludeChildMethod((parentNode, childNode, contextName) -> {
+      System.out.printf("child: %s, %s, %s%n", parentNode, childNode, contextName);
+      // level 4: excluded for type? -> return no
+      PatternCollection excludeOnType = buildConfig.matchExcludePatternCollection(parentNode.getClass().getSimpleName());
+      if (excludeOnType != null && matches(excludeOnType.childPattern(), contextName)) {
+        return false;
+      }
+      // level 3: included for type? -> return yes
+      PatternCollection includeOnType = buildConfig.matchIncludePatternCollection(parentNode.getClass().getSimpleName());
+      if (includeOnType != null && matches(includeOnType.childPattern(), contextName)) {
+        return true;
+      }
+      // level 2: globally excluded? -> return no
+      // level 1: otherwise return yes
+      return !matches(buildConfig.getGlobalPatternCollection().childPattern(), contextName);
+    });
+    buildConfig.setIncludeRelationMethod((sourceNode, targetNode, roleName) -> {
+      // level 4: excluded for type? -> return no
+      PatternCollection excludeOnType = buildConfig.matchExcludePatternCollection(sourceNode.getClass().getSimpleName());
+      if (excludeOnType != null && matches(excludeOnType.relationPattern(), roleName)) {
+        return false;
+      }
+      // level 3: included for type? -> return yes
+      PatternCollection includeOnType = buildConfig.matchIncludePatternCollection(sourceNode.getClass().getSimpleName());
+      if (includeOnType != null && matches(includeOnType.relationPattern(), roleName)) {
+        return true;
+      }
+      // level 2: globally excluded? -> return no
+      // level 1: otherwise return yes
+      return !matches(buildConfig.getGlobalPatternCollection().relationPattern(), roleName);
+    });
+    buildConfig.setIncludeTokenMethod((node, tokenName, value) -> {
+      // level 4: excluded for type? -> return no
+      PatternCollection excludeOnType = buildConfig.matchExcludePatternCollection(node.getClass().getSimpleName());
+      if (excludeOnType != null && matches(excludeOnType.tokenPattern(), tokenName)) {
+        return false;
+      }
+      // level 3: included for type? -> return yes
+      PatternCollection includeOnType = buildConfig.matchIncludePatternCollection(node.getClass().getSimpleName());
+      if (includeOnType != null && matches(includeOnType.tokenPattern(), tokenName)) {
+        return true;
+      }
+      // level 2: globally excluded? -> return no
+      // level 1: otherwise return yes
+      return !matches(buildConfig.getGlobalPatternCollection().tokenPattern(), tokenName);
+    });
+    buildConfig.setIncludeAttributeMethod((node, attributeName, isNTA, supplier) -> {
+      // level 4: included for type? -> return yes
+      PatternCollection includeOnType = buildConfig.matchIncludePatternCollection(node.getClass().getSimpleName());
+      if (includeOnType != null && matches(isNTA ? includeOnType.ntaPattern() : includeOnType.attributePattern(), attributeName)) {
+        return true;
+      }
+      // level 3: excluded for type? -> return no
+      PatternCollection excludeOnType = buildConfig.matchExcludePatternCollection(node.getClass().getSimpleName());
+      if (excludeOnType != null && matches(isNTA ? excludeOnType.ntaPattern() : excludeOnType.attributePattern(), attributeName)) {
+        return false;
+      }
+      // level 2: globally included? -> return yes
+      // level 1: otherwise return no
+      PatternCollection global = buildConfig.getGlobalPatternCollection();
+      return matches(isNTA ? global.ntaPattern() : global.attributePattern(), attributeName);
+    });
     buildConfig.setGlobalPatternCollection(new PatternCollection());
     buildConfig.setStyleInformation(StyleInformation.createDefault());
     printConfig = new PrintConfig();
@@ -111,6 +175,14 @@ public class DumpBuilder {
 
   // --- Tokens ---
 
+  public <ASTNODE> DumpBuilder includeTokensWhen(IncludeTokenMethod<ASTNODE> spec) {
+    buildConfig.setIncludeTokenMethod(spec);
+    if (!buildConfig.getGlobalPatternCollection().getTokenPattern().isEmpty()) {
+      System.err.println("Overriding previous filters for tokens");
+    }
+    return this;
+  }
+
   /**
    * Exclude tokens and their value if the token name matches at least one of the given regex strings.
    * <p>
@@ -168,6 +240,14 @@ public class DumpBuilder {
 
   // --- Children ---
 
+  public <ASTNODE> DumpBuilder includeChildWhen(IncludeChildMethod<ASTNODE> spec) {
+    buildConfig.setIncludeChildMethod(spec);
+    if (!buildConfig.getGlobalPatternCollection().getChildPattern().isEmpty()) {
+      System.err.println("Overriding previous filters for children");
+    }
+    return this;
+  }
+
   /**
    * Exclude every child whose name (i.e., context) matches at least on of the given regex strings.
    * This means, that the complete object and its (transitive) children will never be included in any output.
@@ -228,6 +308,14 @@ public class DumpBuilder {
 
   // --- Attributes ---
 
+  public <ASTNODE> DumpBuilder includeAttributeWhen(IncludeAttributeMethod<ASTNODE> spec) {
+    buildConfig.setIncludeAttributeMethod(spec);
+    if (!buildConfig.getGlobalPatternCollection().getAttributePattern().isEmpty()) {
+      System.err.println("Overriding previous filters for attributes");
+    }
+    return this;
+  }
+
   /**
    * Include attributes (as tokens) and their value if the attribute name matches at least on of the given regex strings.
    * <p>
@@ -354,6 +442,14 @@ public class DumpBuilder {
 
   // --- Relations ---
 
+  public <ASTNODE> DumpBuilder includeRelationsWhen(IncludeRelationMethod<ASTNODE> spec) {
+    buildConfig.setIncludeRelationMethod(spec);
+    if (!buildConfig.getGlobalPatternCollection().getRelationPattern().isEmpty()) {
+      System.err.println("Overriding previous filters for relations");
+    }
+    return this;
+  }
+
   /**
    * Exclude every relation whose role-name matches at least on of the given regex strings.
    * This means two things: a) the relation to any potential target object(s) is never shown, and b) the target
diff --git a/dumpAst/src/main/resources/dumpAstVersion.properties b/dumpAst/src/main/resources/dumpAstVersion.properties
index eb7274f..086530a 100644
--- a/dumpAst/src/main/resources/dumpAstVersion.properties
+++ b/dumpAst/src/main/resources/dumpAstVersion.properties
@@ -1,2 +1,2 @@
-#Tue Jun 21 14:31:38 CEST 2022
-version=1.0.4
+#Thu Jun 23 16:13:20 CEST 2022
+version=1.1.0
diff --git a/featureTest/src/main/java/de/tudresden/inf/st/jastadd/featureTest/FeatureTestMain.java b/featureTest/src/main/java/de/tudresden/inf/st/jastadd/featureTest/FeatureTestMain.java
index eb1cb8c..99cd85d 100644
--- a/featureTest/src/main/java/de/tudresden/inf/st/jastadd/featureTest/FeatureTestMain.java
+++ b/featureTest/src/main/java/de/tudresden/inf/st/jastadd/featureTest/FeatureTestMain.java
@@ -2,14 +2,14 @@ package de.tudresden.inf.st.jastadd.featureTest;
 
 import de.tudresden.inf.st.jastadd.dumpAst.ast.Dumper;
 import de.tudresden.inf.st.jastadd.dumpAst.ast.SkinParamBooleanSetting;
-import de.tudresden.inf.st.jastadd.dumpAst.ast.SkinParamStringSetting;
-import org.jastadd.featureTest.ast.*;
+import org.jastadd.featureTest.ast.A;
+import org.jastadd.featureTest.ast.B;
+import org.jastadd.featureTest.ast.C;
+import org.jastadd.featureTest.ast.Root;
 
 import java.io.IOException;
 import java.nio.file.Path;
 import java.nio.file.Paths;
-import java.util.function.Consumer;
-import java.util.function.Function;
 
 /**
  * Main class of feature test.
@@ -40,26 +40,35 @@ public class FeatureTestMain {
 
     Path pathToYaml = Paths.get("featureTest.yml");
     Path pathToPng = Paths.get("featureTest.png");
-//    Dumper.read(null).dumpAsPNG(pathToPng);
     Dumper
 //        .read(null)
         .read(root)
 //        .customPreamble("hide empty members")
+        .customPreamble("title My fancy title")
+        .includeChildWhen((parentNode, childNode, contextName) -> {
+          if (parentNode instanceof A && ((A) parentNode).getName().equals("A2")) {
+            return false;
+          }
+          return !contextName.equals("MyC");
+        })
+        .includeRelationsWhen((sourceNode, targetNode, roleName) ->
+            !(sourceNode instanceof B) || !((B) sourceNode).getName().equals("B6.1.1"))
+        .includeAttributeWhen((node, attributeName, isNTA, supplier) -> {
+          switch (attributeName) {
+            case "referenceAttr":
+            case "collectBs":
+            case "Calculated":
+            case "AlsoCalculatedListNewSyntax":
+              return true;
+            default:
+              return false;
+          }
+        })
         .skinParam(SkinParamBooleanSetting.Shadowing, false)
 //        .enableRelationWithRank()
-        .includeAttributes("referenceAttr"
-            , "collectBs"
-        )
-        .includeNonterminalAttributes("Calculated")
-        .includeNonterminalAttributes("AlsoCalculatedListNewSyntax")
         .dumpAsYaml(pathToYaml, true)
         .dumpAsPNG(pathToPng)
         .dumpAsSource(Paths.get("featureTest.puml"))
     ;
   }
-
-  private void m() {
-    //noinspection UnnecessaryReturnStatement
-    return;
-  }
 }
diff --git a/testDumper/src/test/java/de/tudresden/inf/st/jastadd/testDumper/TestExcluded.java b/testDumper/src/test/java/de/tudresden/inf/st/jastadd/testDumper/TestExcluded.java
index 8758864..a122a05 100644
--- a/testDumper/src/test/java/de/tudresden/inf/st/jastadd/testDumper/TestExcluded.java
+++ b/testDumper/src/test/java/de/tudresden/inf/st/jastadd/testDumper/TestExcluded.java
@@ -312,7 +312,8 @@ public class TestExcluded {
     root.getA().getB().setOneA(root.getA().getMyC().getA());
 
     List<DumpNode> nodes = TestUtils.dumpModel(root, db -> db.excludeChildren("MyC"));
-    assertThat(nodes).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(ROOT_NAME, A_NAME, B_NAME);
+    assertThat(nodes).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(
+        ROOT_NAME, A_NAME, B_NAME, A2_Name, B2_NAME);
     DumpNode actualA = findByName(nodes, A_NAME);
     assertThatMapOf(normalChildren(actualA)).containsExactlyInAnyOrder(tuple("B", B_NAME));
   }
-- 
GitLab