From 99fb204c8c89582706085f7003d4ec2623013f28 Mon Sep 17 00:00:00 2001
From: rschoene <rene.schoene@tu-dresden.de>
Date: Mon, 10 Oct 2022 16:27:15 +0200
Subject: [PATCH] 3.0.0

- prepare new release
- coherent styling of nodes and relation labels
---
 dumpAst.base/src/main/jastadd/DumpAst.relast  |  17 +-
 dumpAst.base/src/main/jastadd/Frontend.jrag   |  22 +-
 dumpAst.base/src/main/jastadd/Transform.jadd  | 113 ++++++--
 .../st/jastadd/dumpAst/ast/DumpBuilder.java   | 258 ++++++++----------
 4 files changed, 236 insertions(+), 174 deletions(-)

diff --git a/dumpAst.base/src/main/jastadd/DumpAst.relast b/dumpAst.base/src/main/jastadd/DumpAst.relast
index da8cb5a..0a8c621 100644
--- a/dumpAst.base/src/main/jastadd/DumpAst.relast
+++ b/dumpAst.base/src/main/jastadd/DumpAst.relast
@@ -5,13 +5,13 @@ DumpNode ::=  DumpChildNode* DumpToken* DumpRelation*
  <Name> <Label> <BackgroundColor> <TextColor> <Object:Object> <Invisible:boolean> <Computed:boolean> <ManualStereotypes>
  /InvisiblePath/ ;
 
-InnerDumpNode ::= <OriginalIndex:int>;
+InnerDumpNode ::= <OriginalIndex:int> <Label> <TextColor> ;
 rel InnerDumpNode.DumpNode <-> DumpNode.ContainerOfInner? ;
-InnerRelationDumpNode ::= <OriginalIndex:int>;
+InnerRelationDumpNode ::= <OriginalIndex:int> <Label> <TextColor> ;
 rel InnerRelationDumpNode.DumpNode <-> DumpNode.ContainerOfRelationInner* ;
 
 abstract DumpChildNode ::= <Name> <Computed:boolean> ;
-DumpNormalChildNode : DumpChildNode ;
+DumpNormalChildNode : DumpChildNode ::= <TextColor> ;
 rel DumpNormalChildNode.DumpNode <-> DumpNode.ContainerOfNormalChild? ;
 DumpListChildNode : DumpChildNode ::= InnerDumpNode* ;
 
@@ -22,7 +22,7 @@ DumpReferenceListToken : DumpToken ::= InnerRelationDumpNode* ;
 DumpValueToken : DumpToken ::= <Value:Object> ;
 
 abstract DumpRelation ::= <Name> <Bidirectional:boolean> ;
-DumpNormalRelation : DumpRelation ;
+DumpNormalRelation : DumpRelation ::= <TextColor> ;
 rel DumpNormalRelation.DumpNode -> DumpNode ;
 DumpListRelation : DumpRelation ::= InnerRelationDumpNode* ;
 
@@ -67,11 +67,16 @@ BuildConfig ::= StyleInformation
  <Debug:boolean>;
 TypePatternCollectionMapping ::= <TypeRegex> PatternCollection ;
 PatternCollection ::= <TokenPattern> <ChildPattern> <RelationPattern> <AttributePattern> <NonterminalAttributePattern> ;
-StyleInformation ::= <NameMethod:StyleMethod> <BackgroundColorMethod:StyleMethod> <TextColorMethod:StyleMethod> <StereotypeMethod:StyleMethod> <ComputedColor>;
+StyleInformation ::= <ComputedColor>;
 
 PrintConfig ::= Header*
  <Scale:double>
  <Version>
  <RelationWithRank:boolean>
- <OrderChildren:boolean> ;
+ <OrderChildren:boolean>
+ <NodeStyleDefinition:NodeStyleDefinition>
+ <RelationStyleDefinition:RelationStyleDefinition>;
 Header ::= <Value> ;
+
+NodeStyle ::= <Label> <BackgroundColor> <TextColor> <StereoTypes> ;
+RelationStyle ::= <Label> <TextColor> ;
diff --git a/dumpAst.base/src/main/jastadd/Frontend.jrag b/dumpAst.base/src/main/jastadd/Frontend.jrag
index f5bf7c2..a13403e 100644
--- a/dumpAst.base/src/main/jastadd/Frontend.jrag
+++ b/dumpAst.base/src/main/jastadd/Frontend.jrag
@@ -20,17 +20,27 @@ aspect Frontend {
 
   static StyleInformation StyleInformation.createDefault() {
     StyleInformation result = new StyleInformation();
-    result.setNameMethod(n -> n == null ? "null" : n.getClass().getSimpleName() + "@" + Integer.toHexString(n.hashCode()));
-    result.setBackgroundColorMethod(n -> "");
-    result.setTextColorMethod(n -> "");
-    result.setStereotypeMethod(n -> "");
     result.setComputedColor("blue");
     return result;
   }
 
   @FunctionalInterface
-  public interface StyleMethod<ASTNODE> {
-    String get(ASTNODE node);
+  public interface NodeStyleDefinition<ASTNODE> {
+    void style(ASTNODE node, NodeStyle style);
+  }
+
+  @FunctionalInterface
+  public interface RelationStyleDefinition<ASTNODE> {
+    void style(ASTNODE source, ASTNODE target, String context, RelationStyle style);
+  }
+
+  public void NodeStyle.useSimpleLabel() {
+    setLabel(node.getObject() == null ? "null" : node.getObject().getClass().getSimpleName());
+  }
+
+  public void NodeStyle.useSimpleLabelWithHashCode() {
+    setLabel(node.getObject() == null ? "null" :
+        node.getObject().getClass().getSimpleName() + "@" + Integer.toHexString(node.getObject().hashCode()));
   }
 
   @FunctionalInterface
diff --git a/dumpAst.base/src/main/jastadd/Transform.jadd b/dumpAst.base/src/main/jastadd/Transform.jadd
index bafbe3b..a942231 100644
--- a/dumpAst.base/src/main/jastadd/Transform.jadd
+++ b/dumpAst.base/src/main/jastadd/Transform.jadd
@@ -176,6 +176,7 @@ aspect Transform {
       normalChild.setDumpNode(targetNode);
       normalChild.setComputed(false);
       node.addDumpChildNode(normalChild);
+      applyStyle(normalChild);
     }
     return true;
   }
@@ -189,6 +190,8 @@ aspect Transform {
     listChild.setComputed(false);
     String childName = containmentMethod.getName();
     listChild.setName(childName);
+    node.addDumpChildNode(listChild);
+
     int index = -1;
     for (Object target : targetList) {
       index++;
@@ -197,14 +200,17 @@ aspect Transform {
       }
       DumpNode targetNode = transform(tti, target, options.asNormal(false));
       if (target != null && targetNode != null) {
-        listChild.addInnerDumpNode(new InnerDumpNode().setDumpNode(targetNode).setOriginalIndex(index));
+        InnerDumpNode inner = new InnerDumpNode().setDumpNode(targetNode).setOriginalIndex(index);
+        listChild.addInnerDumpNode(inner);
+        applyStyle(inner);
       }
     }
     if (listChild.getNumInnerDumpNode() == 0) {
-      listChild.addInnerDumpNode(new InnerDumpNode().setDumpNode(transform(tti, EMPTY,
-          options.asNormal(false).allowNullObjectsOnce())));
+      InnerDumpNode inner = new InnerDumpNode().setDumpNode(transform(tti, EMPTY,
+          options.asNormal(false).allowNullObjectsOnce()));
+      listChild.addInnerDumpNode(inner);
+      applyStyle(inner);
     }
-    node.addDumpChildNode(listChild);
     return true;
   }
 
@@ -227,6 +233,7 @@ aspect Transform {
       normalChild.setDumpNode(targetNode);
       normalChild.setComputed(computed);
       node.addDumpChildNode(normalChild);
+      applyStyle(normalChild);
     }
     return true;
   }
@@ -245,15 +252,20 @@ aspect Transform {
     boolean computed = otherMethod.asListChildMethod().isNTAListChildMethod();
     listChild.setComputed(computed);
     listChild.setName(childName);
+    boolean notAddedYet = true;
     for (Object target : targetList) {
       DumpNode targetNode = transform(tti, target, options.asNormal(false).computed(computed));
       if (target != null && targetNode != null) {
-        listChild.addInnerDumpNode(new InnerDumpNode().setDumpNode(targetNode));
+        InnerDumpNode inner = new InnerDumpNode().setDumpNode(targetNode);
+        listChild.addInnerDumpNode(inner);
+        if (notAddedYet) {
+          // list child has to be added before application of style, thus inline adding with condition
+          node.addDumpChildNode(listChild);
+          notAddedYet = false;
+        }
+        applyStyle(inner);
       }
     }
-    if (listChild.getNumInnerDumpNode() > 0) {
-      node.addDumpChildNode(listChild);
-    }
     return true;
   }
 
@@ -272,6 +284,7 @@ aspect Transform {
           (otherMethod.asSingleRelationMethod().isOptRelationMethod() ? "?" : ""));
       normalRelation.setDumpNode(targetNode);
       node.addDumpRelation(normalRelation);
+      applyStyle(normalRelation);
     }
     return true;
   }
@@ -283,6 +296,8 @@ aspect Transform {
     Iterable<?> targetList = (Iterable<?>) otherMethod.getMethod().invoke(obj);
     DumpListRelation listRelation = new DumpListRelation();
     listRelation.setName(otherMethod.getName());
+    node.addDumpRelation(listRelation);
+
     int index = -1;
     for (Object target : targetList) {
       index++;
@@ -291,15 +306,17 @@ aspect Transform {
       }
       DumpNode targetNode = transform(tti, target, options.asRelation());
       if (target != null && targetNode != null) {
-        listRelation.addInnerRelationDumpNode(new InnerRelationDumpNode().setDumpNode(targetNode)
-            .setOriginalIndex(index));
+        InnerRelationDumpNode inner = new InnerRelationDumpNode().setDumpNode(targetNode).setOriginalIndex(index);
+        listRelation.addInnerRelationDumpNode(inner);
+        applyStyle(inner);
       }
     }
     if (listRelation.getNumInnerRelationDumpNode() == 0) {
-      listRelation.addInnerRelationDumpNode(new InnerRelationDumpNode().setDumpNode(transform(tti, EMPTY,
-          options.asNormal(false).allowNullObjectsOnce())));
+      InnerRelationDumpNode inner = new InnerRelationDumpNode().setDumpNode(transform(tti, EMPTY,
+          options.asNormal(false).allowNullObjectsOnce()));
+      listRelation.addInnerRelationDumpNode(inner);
+      applyStyle(inner);
     }
-    node.addDumpRelation(listRelation);
     return true;
   }
 
@@ -368,12 +385,72 @@ aspect Transform {
     }
   }
 
+  // === NodeStyle ===
+  DumpNode NodeStyle.node;
+
+  NodeStyle DumpNode.createDefaultStyle() {
+    NodeStyle result = new NodeStyle();
+    result.node = this;
+    result.useSimpleLabelWithHashCode();
+    return result;
+  }
+
   private void DumpAst.applyStyle(DumpNode node) {
-    Object obj = node.getObject();
-    node.setLabel(getBuildConfig().getStyleInformation().getNameMethod().get(obj));
-    node.setBackgroundColor(getBuildConfig().getStyleInformation().getBackgroundColorMethod().get(obj));
-    node.setTextColor(getBuildConfig().getStyleInformation().getTextColorMethod().get(obj));
-    node.setManualStereotypes(getBuildConfig().getStyleInformation().getStereotypeMethod().get(obj));
+    NodeStyle style = node.createDefaultStyle();
+    getPrintConfig().getNodeStyleDefinition().style(node.getObject(), style);
+    node.setLabel(style.getLabel());
+    node.setBackgroundColor(style.getBackgroundColor());
+    node.setTextColor(style.getTextColor());
+    node.setManualStereotypes(style.getStereoTypes());
+  }
+
+  // === RelationStyle ===
+  RelationStyle DumpNormalChildNode.createDefaultStyle() {
+    return new RelationStyle().setLabel(getName());
+  }
+
+  RelationStyle InnerDumpNode.createDefaultStyle() {
+    return new RelationStyle().setLabel(containingDumpListChildNode().getName());
+  }
+
+  RelationStyle DumpNormalRelation.createDefaultStyle() {
+    return new RelationStyle().setLabel(getName());
+  }
+
+  RelationStyle InnerRelationDumpNode.createDefaultStyle() {
+    return new RelationStyle().setLabel(containingDumpListRelation().getName());
+  }
+
+  private void DumpAst.applyStyle(DumpNormalChildNode node) {
+    RelationStyle style = node.createDefaultStyle();
+    getPrintConfig().getRelationStyleDefinition().style(node.containingDumpNode().getObject(),
+        node.getDumpNode().getObject(), node.getName(), style);
+    node.setName(style.getLabel());
+    node.setTextColor(style.getTextColor());
+  }
+
+  private void DumpAst.applyStyle(InnerDumpNode node) {
+    RelationStyle style = node.createDefaultStyle();
+    getPrintConfig().getRelationStyleDefinition().style(node.containingDumpNode().getObject(),
+        node.getDumpNode().getObject(), node.containingDumpListChildNode().getName(), style);
+    node.setLabel(style.getLabel());
+    node.setTextColor(style.getTextColor());
+  }
+
+  private void DumpAst.applyStyle(DumpNormalRelation node) {
+    RelationStyle style = node.createDefaultStyle();
+    getPrintConfig().getRelationStyleDefinition().style(node.containingDumpNode().getObject(),
+        node.getDumpNode().getObject(), node.getName(), style);
+    node.setName(style.getLabel());
+    node.setTextColor(style.getTextColor());
+  }
+
+  private void DumpAst.applyStyle(InnerRelationDumpNode node) {
+    RelationStyle style = node.createDefaultStyle();
+    getPrintConfig().getRelationStyleDefinition().style(node.containingDumpNode().getObject(),
+        node.getDumpNode().getObject(), node.containingDumpListRelation().getName(), style);
+    node.setLabel(style.getLabel());
+    node.setTextColor(style.getTextColor());
   }
 
   // TODO: add new attributes for: {token,child,relation,attribute,nta}Enabled(String parentType, String name). 1) just move implementation into this attribute. 2) add include/exclude on type-level to it.
diff --git a/dumpAst.base/src/main/java/de/tudresden/inf/st/jastadd/dumpAst/ast/DumpBuilder.java b/dumpAst.base/src/main/java/de/tudresden/inf/st/jastadd/dumpAst/ast/DumpBuilder.java
index 1e71098..0f94480 100644
--- a/dumpAst.base/src/main/java/de/tudresden/inf/st/jastadd/dumpAst/ast/DumpBuilder.java
+++ b/dumpAst.base/src/main/java/de/tudresden/inf/st/jastadd/dumpAst/ast/DumpBuilder.java
@@ -22,79 +22,86 @@ import static de.tudresden.inf.st.jastadd.dumpAst.ast.ASTNode.matches;
 public class DumpBuilder {
   private final Object target;
   private String packageName;
-  private DumpAst result;
-  private final BuildConfig buildConfig;
-  private final PrintConfig printConfig;
+  private DumpAst dumpAst;
+
+  private boolean built;
 
   protected DumpBuilder(Object target) {
     this.target = target;
-    buildConfig = new BuildConfig();
-    buildConfig.setIncludeChildMethod((parentNode, childNode, contextName) -> {
+    this.built = false;
+    this.dumpAst = new DumpAst();
+    dumpAst.setBuildConfig(new BuildConfig());
+    dumpAst.getBuildConfig().setIncludeChildMethod((parentNode, childNode, contextName) -> {
       // level 4: excluded for type? -> return no
-      PatternCollection excludeOnType = buildConfig.matchExcludePatternCollection(parentNode.getClass().getSimpleName());
+      PatternCollection excludeOnType = dumpAst.getBuildConfig().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());
+      PatternCollection includeOnType = dumpAst.getBuildConfig().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);
+      return !matches(dumpAst.getBuildConfig().getGlobalPatternCollection().childPattern(), contextName);
     });
-    buildConfig.setIncludeRelationMethod((sourceNode, targetNode, roleName) -> {
+    dumpAst.getBuildConfig().setIncludeRelationMethod((sourceNode, targetNode, roleName) -> {
       // level 4: excluded for type? -> return no
-      PatternCollection excludeOnType = buildConfig.matchExcludePatternCollection(sourceNode.getClass().getSimpleName());
+      PatternCollection excludeOnType = dumpAst.getBuildConfig().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());
+      PatternCollection includeOnType = dumpAst.getBuildConfig().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);
+      return !matches(dumpAst.getBuildConfig().getGlobalPatternCollection().relationPattern(), roleName);
     });
-    buildConfig.setIncludeTokenMethod((node, tokenName, value) -> {
+    dumpAst.getBuildConfig().setIncludeTokenMethod((node, tokenName, value) -> {
       // level 4: excluded for type? -> return no
-      PatternCollection excludeOnType = buildConfig.matchExcludePatternCollection(node.getClass().getSimpleName());
+      PatternCollection excludeOnType = dumpAst.getBuildConfig().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());
+      PatternCollection includeOnType = dumpAst.getBuildConfig().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);
+      return !matches(dumpAst.getBuildConfig().getGlobalPatternCollection().tokenPattern(), tokenName);
     });
-    buildConfig.setIncludeAttributeMethod((node, attributeName, isNTA, supplier) -> {
+    dumpAst.getBuildConfig().setIncludeAttributeMethod((node, attributeName, isNTA, supplier) -> {
       // level 4: included for type? -> return yes
-      PatternCollection includeOnType = buildConfig.matchIncludePatternCollection(node.getClass().getSimpleName());
+      PatternCollection includeOnType = dumpAst.getBuildConfig().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());
+      PatternCollection excludeOnType = dumpAst.getBuildConfig().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();
+      PatternCollection global = dumpAst.getBuildConfig().getGlobalPatternCollection();
       return matches(isNTA ? global.ntaPattern() : global.attributePattern(), attributeName);
     });
-    buildConfig.setGlobalPatternCollection(new PatternCollection());
-    buildConfig.setStyleInformation(StyleInformation.createDefault());
-    printConfig = new PrintConfig();
-    printConfig.setScale(1);
-    printConfig.setVersion(readVersion());
+    dumpAst.getBuildConfig().setGlobalPatternCollection(new PatternCollection());
+    dumpAst.getBuildConfig().setStyleInformation(StyleInformation.createDefault());
+    dumpAst.setPrintConfig(new PrintConfig());
+    dumpAst.getPrintConfig().setScale(1);
+    dumpAst.getPrintConfig().setVersion(readVersion());
+  }
+
+  private DumpBuilder thisWithResetBuilt() {
+    this.built = false;
+    return this;
   }
 
   /**
@@ -104,8 +111,8 @@ public class DumpBuilder {
    * @return this
    */
   public DumpBuilder enableDebug() {
-    buildConfig.setDebug(true);
-    return this;
+    dumpAst.getBuildConfig().setDebug(true);
+    return thisWithResetBuilt();
   }
 
   /**
@@ -115,7 +122,7 @@ public class DumpBuilder {
    */
   public DumpBuilder setPackageName(String packageName) {
     this.packageName = packageName;
-    return this;
+    return thisWithResetBuilt();
   }
 
   /**
@@ -124,8 +131,8 @@ public class DumpBuilder {
    * @return this
    */
   public DumpBuilder includeEmptyStringsOnTokens() {
-    buildConfig.setIncludeEmptyString(true);
-    return this;
+    dumpAst.getBuildConfig().setIncludeEmptyString(true);
+    return thisWithResetBuilt();
   }
 
   /**
@@ -133,8 +140,8 @@ public class DumpBuilder {
    * @return this
    */
   public DumpBuilder enableRelationWithRank() {
-    printConfig.setRelationWithRank(true);
-    return this;
+    dumpAst.getPrintConfig().setRelationWithRank(true);
+    return thisWithResetBuilt();
   }
 
   // --- Types ---
@@ -151,20 +158,20 @@ public class DumpBuilder {
    * @see java.util.regex.Pattern#compile(String)
    */
   public DumpBuilder disableTypes(String regex, String... moreRegexes) {
-    updateRegexes(buildConfig::getTypeIgnorePattern,
-        buildConfig::setTypeIgnorePattern,
+    updateRegexes(dumpAst.getBuildConfig()::getTypeIgnorePattern,
+        dumpAst.getBuildConfig()::setTypeIgnorePattern,
         regex, moreRegexes);
-    return this;
+    return thisWithResetBuilt();
   }
 
   // --- Tokens ---
 
   public <ASTNODE> DumpBuilder includeTokensWhen(IncludeTokenMethod<ASTNODE> spec) {
-    buildConfig.setIncludeTokenMethod(spec);
-    if (!buildConfig.getGlobalPatternCollection().getTokenPattern().isEmpty()) {
+    dumpAst.getBuildConfig().setIncludeTokenMethod(spec);
+    if (!dumpAst.getBuildConfig().getGlobalPatternCollection().getTokenPattern().isEmpty()) {
       System.err.println("Overriding previous filters for tokens");
     }
-    return this;
+    return thisWithResetBuilt();
   }
 
   /**
@@ -179,10 +186,10 @@ public class DumpBuilder {
    */
   @Deprecated(since = "2.0.1")
   public DumpBuilder excludeTokens(String regex, String... moreRegexes) {
-    updateRegexes(() -> buildConfig.getGlobalPatternCollection().getTokenPattern(),
-        s -> buildConfig.getGlobalPatternCollection().setTokenPattern(s),
+    updateRegexes(() -> dumpAst.getBuildConfig().getGlobalPatternCollection().getTokenPattern(),
+        s -> dumpAst.getBuildConfig().getGlobalPatternCollection().setTokenPattern(s),
         regex, moreRegexes);
-    return this;
+    return thisWithResetBuilt();
   }
 
   /**
@@ -202,7 +209,7 @@ public class DumpBuilder {
     updateRegexes(collection::getTokenPattern,
         collection::setTokenPattern,
         regex, moreRegexes);
-    return this;
+    return thisWithResetBuilt();
   }
 
   /**
@@ -222,17 +229,17 @@ public class DumpBuilder {
     updateRegexes(collection::getTokenPattern,
         collection::setTokenPattern,
         regex, moreRegexes);
-    return this;
+    return thisWithResetBuilt();
   }
 
   // --- Children ---
 
   public <ASTNODE> DumpBuilder includeChildWhen(IncludeChildMethod<ASTNODE> spec) {
-    buildConfig.setIncludeChildMethod(spec);
-    if (!buildConfig.getGlobalPatternCollection().getChildPattern().isEmpty()) {
+    dumpAst.getBuildConfig().setIncludeChildMethod(spec);
+    if (!dumpAst.getBuildConfig().getGlobalPatternCollection().getChildPattern().isEmpty()) {
       System.err.println("Overriding previous filters for children");
     }
-    return this;
+    return thisWithResetBuilt();
   }
 
   /**
@@ -248,10 +255,10 @@ public class DumpBuilder {
    */
   @Deprecated(since = "2.0.1")
   public DumpBuilder excludeChildren(String regex, String... moreRegexes) {
-    updateRegexes(() -> buildConfig.getGlobalPatternCollection().getChildPattern(),
-        s -> buildConfig.getGlobalPatternCollection().setChildPattern(s),
+    updateRegexes(() -> dumpAst.getBuildConfig().getGlobalPatternCollection().getChildPattern(),
+        s -> dumpAst.getBuildConfig().getGlobalPatternCollection().setChildPattern(s),
         regex, moreRegexes);
-    return this;
+    return thisWithResetBuilt();
   }
 
   /**
@@ -272,7 +279,7 @@ public class DumpBuilder {
     updateRegexes(collection::getChildPattern,
         collection::setChildPattern,
         regex, moreRegexes);
-    return this;
+    return thisWithResetBuilt();
   }
 
   /**
@@ -293,17 +300,17 @@ public class DumpBuilder {
     updateRegexes(collection::getChildPattern,
         collection::setChildPattern,
         regex, moreRegexes);
-    return this;
+    return thisWithResetBuilt();
   }
 
   // --- Attributes ---
 
   public <ASTNODE> DumpBuilder includeAttributeWhen(IncludeAttributeMethod<ASTNODE> spec) {
-    buildConfig.setIncludeAttributeMethod(spec);
-    if (!buildConfig.getGlobalPatternCollection().getAttributePattern().isEmpty()) {
+    dumpAst.getBuildConfig().setIncludeAttributeMethod(spec);
+    if (!dumpAst.getBuildConfig().getGlobalPatternCollection().getAttributePattern().isEmpty()) {
       System.err.println("Overriding previous filters for attributes");
     }
-    return this;
+    return thisWithResetBuilt();
   }
 
   /**
@@ -318,10 +325,10 @@ public class DumpBuilder {
    */
   @Deprecated(since = "2.0.1")
   public DumpBuilder includeAttributes(String regex, String... moreRegexes) {
-    updateRegexes(() -> buildConfig.getGlobalPatternCollection().getAttributePattern(),
-        s -> buildConfig.getGlobalPatternCollection().setAttributePattern(s),
+    updateRegexes(() -> dumpAst.getBuildConfig().getGlobalPatternCollection().getAttributePattern(),
+        s -> dumpAst.getBuildConfig().getGlobalPatternCollection().setAttributePattern(s),
         regex, moreRegexes);
-    return this;
+    return thisWithResetBuilt();
   }
 
   /**
@@ -341,7 +348,7 @@ public class DumpBuilder {
     updateRegexes(collection::getAttributePattern,
         collection::setAttributePattern,
         regex, moreRegexes);
-    return this;
+    return thisWithResetBuilt();
   }
 
   /**
@@ -361,7 +368,7 @@ public class DumpBuilder {
     updateRegexes(collection::getAttributePattern,
         collection::setAttributePattern,
         regex, moreRegexes);
-    return this;
+    return thisWithResetBuilt();
   }
 
   // --- Nonterminal-Attributes ---
@@ -382,10 +389,10 @@ public class DumpBuilder {
    */
   @Deprecated(since = "2.0.1")
   public DumpBuilder includeNonterminalAttributes(String regex, String... moreRegexes) {
-    updateRegexes(() -> buildConfig.getGlobalPatternCollection().getNonterminalAttributePattern(),
-        s -> buildConfig.getGlobalPatternCollection().setNonterminalAttributePattern(s),
+    updateRegexes(() -> dumpAst.getBuildConfig().getGlobalPatternCollection().getNonterminalAttributePattern(),
+        s -> dumpAst.getBuildConfig().getGlobalPatternCollection().setNonterminalAttributePattern(s),
         regex, moreRegexes);
-    return this;
+    return thisWithResetBuilt();
   }
 
   /**
@@ -409,7 +416,7 @@ public class DumpBuilder {
     updateRegexes(collection::getNonterminalAttributePattern,
         collection::setNonterminalAttributePattern,
         regex, moreRegexes);
-    return this;
+    return thisWithResetBuilt();
   }
 
   /**
@@ -433,17 +440,17 @@ public class DumpBuilder {
     updateRegexes(collection::getNonterminalAttributePattern,
         collection::setNonterminalAttributePattern,
         regex, moreRegexes);
-    return this;
+    return thisWithResetBuilt();
   }
 
   // --- Relations ---
 
   public <ASTNODE> DumpBuilder includeRelationsWhen(IncludeRelationMethod<ASTNODE> spec) {
-    buildConfig.setIncludeRelationMethod(spec);
-    if (!buildConfig.getGlobalPatternCollection().getRelationPattern().isEmpty()) {
+    dumpAst.getBuildConfig().setIncludeRelationMethod(spec);
+    if (!dumpAst.getBuildConfig().getGlobalPatternCollection().getRelationPattern().isEmpty()) {
       System.err.println("Overriding previous filters for relations");
     }
-    return this;
+    return thisWithResetBuilt();
   }
 
   /**
@@ -460,10 +467,10 @@ public class DumpBuilder {
    */
   @Deprecated(since = "2.0.1")
   public DumpBuilder excludeRelations(String regex, String... moreRegexes) {
-    updateRegexes(() -> buildConfig.getGlobalPatternCollection().getRelationPattern(),
-        s -> buildConfig.getGlobalPatternCollection().setRelationPattern(s),
+    updateRegexes(() -> dumpAst.getBuildConfig().getGlobalPatternCollection().getRelationPattern(),
+        s -> dumpAst.getBuildConfig().getGlobalPatternCollection().setRelationPattern(s),
         regex, moreRegexes);
-    return this;
+    return thisWithResetBuilt();
   }
 
   /**
@@ -485,7 +492,7 @@ public class DumpBuilder {
     updateRegexes(collection::getRelationPattern,
         collection::setRelationPattern,
         regex, moreRegexes);
-    return this;
+    return thisWithResetBuilt();
   }
 
   /**
@@ -507,7 +514,7 @@ public class DumpBuilder {
     updateRegexes(collection::getRelationPattern,
         collection::setRelationPattern,
         regex, moreRegexes);
-    return this;
+    return thisWithResetBuilt();
   }
 
   // --- Settings ---
@@ -519,8 +526,8 @@ public class DumpBuilder {
    * @return this
    */
   public DumpBuilder excludeNullNodes() {
-    buildConfig.setExcludeNullNodes(true);
-    return this;
+    dumpAst.getBuildConfig().setExcludeNullNodes(true);
+    return thisWithResetBuilt();
   }
 
   /**
@@ -530,8 +537,8 @@ public class DumpBuilder {
    * @return this
    */
   public DumpBuilder includeNullNodes() {
-    buildConfig.setExcludeNullNodes(false);
-    return this;
+    dumpAst.getBuildConfig().setExcludeNullNodes(false);
+    return thisWithResetBuilt();
   }
 
   /**
@@ -540,8 +547,8 @@ public class DumpBuilder {
    * @return this
    */
   public DumpBuilder customPreamble(String option) {
-    printConfig.addHeader(new Header(option));
-    return this;
+    dumpAst.getPrintConfig().addHeader(new Header(option));
+    return thisWithResetBuilt();
   }
 
   /**
@@ -552,7 +559,7 @@ public class DumpBuilder {
    */
   public DumpBuilder skinParam(SkinParamStringSetting setting, String value) {
     customPreamble("skinparam " + setting.toString() + " " + value);
-    return this;
+    return thisWithResetBuilt();
   }
 
   /**
@@ -563,63 +570,29 @@ public class DumpBuilder {
    */
   public DumpBuilder skinParam(SkinParamBooleanSetting setting, boolean value) {
     customPreamble("skinparam " + setting.toString() + " " + value);
-    return this;
-  }
-
-  /**
-   * Set the method defining, what name a node has (default: {@code n -> n == null ? "null" : n.getClass().getSimpleName() + "@" + Integer.toHexString(n.hashCode())}).
-   *
-   * <p>Example:<br>
-   * {@code builder.<ASTNode<?>>setNameMethod(n -> n.isA() ? "A" : "Not A")}
-   * @param nameMethod a style method
-   * @param <ASTNODE> the type of ASTNode
-   * @return this
-   */
-  public <ASTNODE> DumpBuilder setNameMethod(StyleMethod<ASTNODE> nameMethod) {
-    buildConfig.getStyleInformation().setNameMethod(nameMethod);
-    return this;
-  }
-
-  /**
-   * Set the method defining, what background color a node has (default: {@code n -> ""}).
-   *
-   * <p>Example:<br>
-   * {@code builder.<ASTNode<?>>setBackgroundColorMethod(n -> n.isA() ? "red" : "blue")}
-   * @param colorMethod a style method
-   * @param <ASTNODE> the type of ASTNode
-   * @return this
-   */
-  public <ASTNODE> DumpBuilder setBackgroundColorMethod(StyleMethod<ASTNODE> colorMethod) {
-    buildConfig.getStyleInformation().setBackgroundColorMethod(colorMethod);
-    return this;
+    return thisWithResetBuilt();
   }
 
   /**
-   * Set the method defining, what text color a node has (default: {@code n -> ""}).
-   *
-   * <p>Example:<br>
-   * {@code builder.<ASTNode<?>>setTextColorMethod(n -> n.isA() ? "black" : "white")}
-   * @param colorMethod a style method
-   * @param <ASTNODE> the type of ASTNode
+   * Set the styling definition for all nodes to change appearance, e.g., of background color.
+   * @param styleDefinition the new style definition
    * @return this
+   * @param <ASTNODE> type of AstNode
    */
-  public <ASTNODE> DumpBuilder setTextColorMethod(StyleMethod<ASTNODE> colorMethod) {
-    buildConfig.getStyleInformation().setTextColorMethod(colorMethod);
-    return this;
+  public <ASTNODE> DumpBuilder nodeStyle(NodeStyleDefinition<ASTNODE> styleDefinition) {
+    dumpAst.getPrintConfig().setNodeStyleDefinition(styleDefinition);
+    return thisWithResetBuilt();
   }
 
   /**
-   * Set the method defining, what stereotype a node has (default: {@code n -> ""}).
-   *
-   * <p>Example:<br>
-   * {@code builder.<ASTNode<?>>setStereotypeMethod(n -> n.isA() ? "MyStereoType" : "")}
-   * @param stereotypeMethod a style method
-   * @param <ASTNODE> the type of ASTNode
+   * Set the styling definition for all relations to change appearance, e.g., of its label.
+   * @param styleDefinition the new style definition
    * @return this
+   * @param <ASTNODE> type of AstNode
    */
-  public <ASTNODE> DumpBuilder setStereotypeMethod(StyleMethod<ASTNODE> stereotypeMethod) {
-    buildConfig.getStyleInformation().setStereotypeMethod(stereotypeMethod);
-    return this;
+  public <ASTNODE> DumpBuilder relationStyle(RelationStyleDefinition<ASTNODE> styleDefinition) {
+    dumpAst.getPrintConfig().setRelationStyleDefinition(styleDefinition);
+    return thisWithResetBuilt();
   }
 
   /**
@@ -629,8 +602,8 @@ public class DumpBuilder {
    * @see <a href="https://plantuml.com/en/color">https://plantuml.com/en/color</a>
    */
   public DumpBuilder setComputedColor(String color) {
-    buildConfig.getStyleInformation().setComputedColor(color);
-    return this;
+    dumpAst.getBuildConfig().getStyleInformation().setComputedColor(color);
+    return thisWithResetBuilt();
   }
 
   /**
@@ -639,8 +612,8 @@ public class DumpBuilder {
    * @return this
    */
   public DumpBuilder setScale(double value) {
-    printConfig.setScale(value);
-    return this;
+    dumpAst.getPrintConfig().setScale(value);
+    return thisWithResetBuilt();
   }
 
   /**
@@ -648,8 +621,8 @@ public class DumpBuilder {
    * @return this
    */
   public DumpBuilder orderChildren() {
-    printConfig.setOrderChildren(true);
-    return this;
+    dumpAst.getPrintConfig().setOrderChildren(true);
+    return thisWithResetBuilt();
   }
 
   // --- Dump methods ---
@@ -738,8 +711,7 @@ public class DumpBuilder {
   // --- Helper methods ---
 
   protected DumpAst build() throws TransformationException {
-    if (result == null) {
-      result = new DumpAst();
+    if (!built) {
       final String packageNameToUse;
       if (this.packageName != null) {
         packageNameToUse = this.packageName;
@@ -750,39 +722,37 @@ public class DumpBuilder {
           packageNameToUse = this.target.getClass().getPackage().getName();
         }
       }
-      result.setPackageName(packageNameToUse);
-      result.setBuildConfig(this.buildConfig);
-      result.setPrintConfig(this.printConfig);
+      dumpAst.setPackageName(packageNameToUse);
       try {
-        result.transform(new TransformationTransferInformation(), this.target);
+        dumpAst.transform(new TransformationTransferInformation(), this.target);
       } catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) {
-        result = null;
+        dumpAst = null;
         throw new TransformationException(e);
       }
     }
-    return result;
+    return dumpAst;
   }
 
   private PatternCollection findOrCreateIncludePatternCollection(String typeRegex) {
-    PatternCollection result = buildConfig.findIncludePatternCollection(typeRegex);
+    PatternCollection result = dumpAst.getBuildConfig().findIncludePatternCollection(typeRegex);
     if (result == null) {
       TypePatternCollectionMapping mapping = new TypePatternCollectionMapping();
       mapping.setTypeRegex(typeRegex);
       result = new PatternCollection();
       mapping.setPatternCollection(result);
-      buildConfig.addIncludeTypePattern(mapping);
+      dumpAst.getBuildConfig().addIncludeTypePattern(mapping);
     }
     return result;
   }
 
   private PatternCollection findOrCreateExcludePatternCollection(String typeRegex) {
-    PatternCollection result = buildConfig.findExcludePatternCollection(typeRegex);
+    PatternCollection result = dumpAst.getBuildConfig().findExcludePatternCollection(typeRegex);
     if (result == null) {
       TypePatternCollectionMapping mapping = new TypePatternCollectionMapping();
       mapping.setTypeRegex(typeRegex);
       result = new PatternCollection();
       mapping.setPatternCollection(result);
-      buildConfig.addExcludeTypePattern(mapping);
+      dumpAst.getBuildConfig().addExcludeTypePattern(mapping);
     }
     return result;
   }
-- 
GitLab