diff --git a/grammar2uml/src/main/java/de/tudresden/inf/st/jastadd/grammar2uml/compiler/Compiler.java b/grammar2uml/src/main/java/de/tudresden/inf/st/jastadd/grammar2uml/compiler/Compiler.java
index 5725e0db228d7e3b237172ecf7bd94936990eaf2..f318da191014d5df7410c0f5d0f4c5edf06029f6 100644
--- a/grammar2uml/src/main/java/de/tudresden/inf/st/jastadd/grammar2uml/compiler/Compiler.java
+++ b/grammar2uml/src/main/java/de/tudresden/inf/st/jastadd/grammar2uml/compiler/Compiler.java
@@ -47,8 +47,7 @@ public class Compiler extends AbstractCompiler {
    */
   private String readVersion() {
     try {
-      ResourceBundle resources = ResourceBundle.getBundle("grammar2umlVersion");
-      return resources.getString("version");
+      return ResourceBundle.getBundle("grammar2umlVersion").getString("version");
     } catch (MissingResourceException e) {
       return "version ?";
     }
diff --git a/testDumper/src/main/jastadd/testDumper.jrag b/testDumper/src/main/jastadd/testDumper.jrag
index 7c98b5787378aecfed4eae0e08664f704cb68c58..a0ef867b1fe9da0ef2b7f5c91cb9cfc9877800ea 100644
--- a/testDumper/src/main/jastadd/testDumper.jrag
+++ b/testDumper/src/main/jastadd/testDumper.jrag
@@ -1,4 +1,4 @@
-aspect Grammar {
+aspect GrammarGlobal {
   syn A C.getCalculated() {
     A result = new A();
     result.setName("Calculated-" + getName());
@@ -30,3 +30,12 @@ aspect Grammar {
   syn int Root.simpleAttr() = 42;
   syn A Root.referenceAttr() = getA();
 }
+
+aspect GrammarTypeLevel {
+  syn int AbstractT.simpleAttr() = 43;
+  syn nta A AbstractT.getCalculated() {
+    A result = new A();
+    result.setName("Calculated-" + getName());
+    return result;
+  }
+}
diff --git a/testDumper/src/main/jastadd/testDumper.relast b/testDumper/src/main/jastadd/testDumper.relast
index 487953442b55ff19aea891fef0030379afe80412..cd7321f17afb9ef78770946df842bf1ce12072d0 100644
--- a/testDumper/src/main/jastadd/testDumper.relast
+++ b/testDumper/src/main/jastadd/testDumper.relast
@@ -1,3 +1,4 @@
+// testcases with global inclusion/exclusion
 Nameable ::= <Name> ;
 Root : Nameable ::= A B* [C] ;
 A : Nameable ::= B MyC:C ;
@@ -11,3 +12,14 @@ rel B.manyA* -> A ;
 rel C.biA1 <-> A.biC1 ;
 rel C.biA2* <-> A.biC2 ;
 rel C.biA3? <-> A.biC3 ;
+
+// testcases with type-level inclusion/exclusion
+TRoot : Nameable ::= A T1 T2 T3 ;
+abstract AbstractT : Nameable ::= B Bee:B* <SomeValue> <Unwanted:int> ;
+T1 : AbstractT ;
+T2 : AbstractT ;
+T3 : AbstractT ;
+
+rel AbstractT.oneA -> A ;
+rel AbstractT.maybeA? -> A ;
+rel AbstractT.manyA* -> A ;
diff --git a/testDumper/src/test/java/de/tudresden/inf/st/jastadd/testDumper/TestIncluded.java b/testDumper/src/test/java/de/tudresden/inf/st/jastadd/testDumper/TestIncluded.java
index 7a6a360efd8bcb6e56b6438f9c07cd4c229d320d..cd6e5c83fc191d797b0b3cb8901749f28c840773 100644
--- a/testDumper/src/test/java/de/tudresden/inf/st/jastadd/testDumper/TestIncluded.java
+++ b/testDumper/src/test/java/de/tudresden/inf/st/jastadd/testDumper/TestIncluded.java
@@ -2,7 +2,6 @@ package de.tudresden.inf.st.jastadd.testDumper;
 
 import de.tudresden.inf.st.jastadd.dumpAst.ast.DumpNode;
 import org.jastadd.testDumper.ast.Root;
-import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
 
 import java.util.List;
diff --git a/testDumper/src/test/java/de/tudresden/inf/st/jastadd/testDumper/TestTypeLevel3.java b/testDumper/src/test/java/de/tudresden/inf/st/jastadd/testDumper/TestTypeLevel3.java
new file mode 100644
index 0000000000000000000000000000000000000000..023ffdb2977ff034acc712a8ef8d00c54dbfec99
--- /dev/null
+++ b/testDumper/src/test/java/de/tudresden/inf/st/jastadd/testDumper/TestTypeLevel3.java
@@ -0,0 +1,188 @@
+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.AbstractT;
+import org.jastadd.testDumper.ast.TRoot;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+import java.util.List;
+import java.util.function.Consumer;
+
+import static de.tudresden.inf.st.jastadd.testDumper.TestUtils.*;
+import static org.assertj.core.api.Assertions.*;
+
+/**
+ * Testing type-level exclusions.
+ * <p>
+ * Refer to {@link de.tudresden.inf.st.jastadd.dumpAst.ast.DumpBuilder DumpBuilder} for levels of inclusion/exclusion.
+ *
+ * @author rschoene - Initial contribution
+ */
+public class TestTypeLevel3 {
+
+  @Test
+  public void testTokenLevel2Excluded() {
+    Consumer<AbstractT> setUnwanted = t -> t.setUnwanted(5);
+    TRoot root = createTRoot(createA(A_NAME), createT1(setUnwanted), createT2(setUnwanted), createT3(setUnwanted));
+    Assertions.assertEquals(5, root.getT2().getUnwanted());
+
+    List<DumpNode> nodes = TestUtils.dumpModel(root, dp -> dp.excludeTokens("Unwanted"));
+    assertThat(nodes).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(ROOT_NAME, A_NAME, T1_NAME, T2_NAME, T3_NAME);
+    for (String name : Tx_NAMES) {
+      DumpNode actualTx = TestUtils.findByName(nodes, name);
+      assertThat(valueTokens(actualTx)).containsExactly(entry("Name", name));
+    }
+  }
+
+  @Test
+  public void testTokenLevel3SomeIncluded() {
+    Consumer<AbstractT> setUnwanted = t -> t.setUnwanted(5);
+    TRoot root = createTRoot(createA(A_NAME), createT1(setUnwanted), createT2(setUnwanted), createT3(setUnwanted));
+    Assertions.assertEquals(5, root.getT2().getUnwanted());
+
+    List<DumpNode> nodesT2 = TestUtils.dumpModel(root,
+        dp -> dp.excludeTokens("Unwanted")
+            .includeTokensFor("T2", "Unwanted"));
+    assertThat(nodesT2).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(ROOT_NAME, A_NAME, T1_NAME, T2_NAME, T3_NAME);
+    assertThat(valueTokens(TestUtils.findByName(nodesT2, T1_NAME))).containsExactly(
+        entry("Name", T1_NAME));
+    assertThat(valueTokens(TestUtils.findByName(nodesT2, T2_NAME))).containsExactly(
+        entry("Name", T2_NAME), entry("Unwanted", 5));
+    assertThat(valueTokens(TestUtils.findByName(nodesT2, T3_NAME))).containsExactly(
+        entry("Name", T3_NAME));
+
+    List<DumpNode> nodesT2T3 = TestUtils.dumpModel(root,
+        dp -> dp.excludeTokens("Unwanted")
+            .includeTokensFor("T2|T3", "Unwanted"));
+    assertThat(nodesT2T3).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(ROOT_NAME, A_NAME, T1_NAME, T2_NAME, T3_NAME);
+    assertThat(valueTokens(TestUtils.findByName(nodesT2T3, T1_NAME))).containsExactly(
+        entry("Name", T1_NAME));
+    assertThat(valueTokens(TestUtils.findByName(nodesT2T3, T2_NAME))).containsExactly(
+        entry("Name", T2_NAME), entry("Unwanted", 5));
+    assertThat(valueTokens(TestUtils.findByName(nodesT2T3, T3_NAME))).containsExactly(
+        entry("Name", T3_NAME), entry("Unwanted", 5));
+  }
+
+  @Test
+  public void testChildLevel2Excluded() {
+    Consumer<AbstractT> setB = t -> t.setB(createB(t.getName() + B_NAME));
+    TRoot root = createTRoot(createA(A_NAME), createT1(setB), createT2(setB), createT3(setB));
+    Assertions.assertEquals(T2_NAME + B_NAME, root.getT2().getB().getName());
+
+    List<DumpNode> nodes = TestUtils.dumpModel(root, dp -> dp.excludeChildren("B"));
+    assertThat(nodes).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(ROOT_NAME, A_NAME, T1_NAME, T2_NAME, T3_NAME);
+    for (String name : Tx_NAMES) {
+      DumpNode actualTx = TestUtils.findByName(nodes, name);
+      assertThat(listChildren(actualTx)).isEmpty();
+    }
+  }
+
+  @Test
+  public void testChildLevel3SomeIncluded() {
+    Consumer<AbstractT> setB = t -> t.setB(createB(t.getName() + B_NAME));
+    TRoot root = createTRoot(createA(A_NAME), createT1(setB), createT2(setB), createT3(setB));
+    Assertions.assertEquals(T2_NAME + B_NAME, root.getT2().getB().getName());
+
+    List<DumpNode> nodes = TestUtils.dumpModel(root,
+        dp -> dp.excludeChildren("B")
+            .includeChildrenFor("T2", "B"));
+    assertThat(nodes).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(ROOT_NAME, A_NAME, T1_NAME, T2_NAME, T3_NAME);
+    assertThat(listChildren(TestUtils.findByName(nodes, T1_NAME))).isEmpty();
+    assertThatMapOf(listChildren(TestUtils.findByName(nodes, T2_NAME)), "B").containsExactly(
+        T2_NAME + B_NAME);
+    assertThat(listChildren(TestUtils.findByName(nodes, T3_NAME))).isEmpty();
+  }
+
+  @Test
+  public void testRelationLevel2Excluded() {
+    final A a = createA(A_NAME);
+    Consumer<AbstractT> setOneA = t -> t.setOneA(a);
+    TRoot root = createTRoot(a, createT1(setOneA), createT2(setOneA), createT3(setOneA));
+    Assertions.assertEquals(a, root.getT2().getOneA());
+
+    List<DumpNode> nodes = TestUtils.dumpModel(root, dp -> dp.excludeRelations("OneA"));
+    assertThat(nodes).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(ROOT_NAME, A_NAME, T1_NAME, T2_NAME, T3_NAME);
+    for (String name : Tx_NAMES) {
+      DumpNode actualTx = TestUtils.findByName(nodes, name);
+      assertThat(normalRelationChildren(actualTx)).isEmpty();
+    }
+  }
+
+  @Test
+  public void testRelationLevel3SomeIncluded() {
+    final A a = createA(A_NAME);
+    Consumer<AbstractT> setOneA = t -> t.setOneA(a);
+    TRoot root = createTRoot(a, createT1(setOneA), createT2(setOneA), createT3(setOneA));
+    Assertions.assertEquals(a, root.getT2().getOneA());
+
+    List<DumpNode> nodes = TestUtils.dumpModel(root,
+        dp -> dp.excludeRelations("OneA")
+            .includeRelationsFor("T2", "OneA"));
+    assertThat(nodes).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(ROOT_NAME, A_NAME, T1_NAME, T2_NAME, T3_NAME);
+    assertThat(normalRelationChildren(TestUtils.findByName(nodes, T1_NAME))).isEmpty();
+    assertThatMapOf(normalRelationChildren(TestUtils.findByName(nodes, T2_NAME))).containsExactly(
+        tuple("OneA", A_NAME));
+    assertThat(normalRelationChildren(TestUtils.findByName(nodes, T3_NAME))).isEmpty();
+  }
+
+  @Test
+  public void testAttributeLevel2Included() {
+    TRoot root = createTRoot(createA(A_NAME), createT1(), createT2(), createT3());
+
+    List<DumpNode> nodes = dumpModel(root,
+        dp -> dp.includeAttributes("simpleAttr"));
+    assertThat(nodes).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(ROOT_NAME, A_NAME, T1_NAME, T2_NAME, T3_NAME);
+    for (String name : Tx_NAMES) {
+      DumpNode actualTx = findByName(nodes, name);
+      assertThat(valueTokens(actualTx)).containsExactly(entry("Name", name), entry("simpleAttr", 43));
+    }
+  }
+
+  @Test
+  public void testAttributeLevel3SomeExcluded() {
+    TRoot root = createTRoot(createA(A_NAME), createT1(), createT2(), createT3());
+
+    List<DumpNode> nodes = dumpModel(root,
+        dp -> dp.includeAttributes("simpleAttr")
+                .excludeAttributesFor("T2", "simpleAttr"));
+    assertThat(nodes).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(ROOT_NAME, A_NAME, T1_NAME, T2_NAME, T3_NAME);
+    assertThat(valueTokens(findByName(nodes, T1_NAME))).containsExactly(
+        entry("Name", T1_NAME), entry("simpleAttr", 43));
+    assertThat(valueTokens(findByName(nodes, T2_NAME))).containsExactly(
+        entry("Name", T2_NAME));
+    assertThat(valueTokens(findByName(nodes, T3_NAME))).containsExactly(
+        entry("Name", T3_NAME), entry("simpleAttr", 43));
+  }
+
+  @Test
+  public void testNTALevel2Included() {
+    TRoot root = createTRoot(createA(A_NAME), createT1(), createT2(), createT3());
+
+    List<DumpNode> nodes = dumpModel(root,
+        dp -> dp.includeNonterminalAttributes("Calculated"));
+    assertThat(nodes).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(ROOT_NAME, A_NAME, T1_NAME, T2_NAME, T3_NAME, "Calculated-" + T1_NAME, "Calculated-" + T2_NAME, "Calculated-" + T3_NAME);
+    for (String name : Tx_NAMES) {
+      assertThatMapOf(normalChildren(findByName(nodes, name))).containsOnly(
+          tuple("Calculated", "Calculated-" + T3_NAME));
+    }
+  }
+
+  @Test
+  public void testNTALevel3SomeExcluded() {
+    TRoot root = createTRoot(createA(A_NAME), createT1(), createT2(), createT3());
+
+    List<DumpNode> nodes = dumpModel(root,
+        dp -> dp.includeNonterminalAttributes("Calculated")
+            .excludeNonterminalAttributesFor("T2", "Calculated"));
+    assertThat(nodes).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(ROOT_NAME, A_NAME, T1_NAME, T2_NAME, T3_NAME, "Calculated-" + T1_NAME, "Calculated-" + T3_NAME);
+    assertThatMapOf(normalChildren(findByName(nodes, T1_NAME))).containsOnly(
+        tuple("Calculated", "Calculated-" + T1_NAME));
+    assertThat(normalChildren(findByName(nodes, T2_NAME))).isEmpty();
+    assertThatMapOf(normalChildren(findByName(nodes, T3_NAME))).containsOnly(
+        tuple("Calculated", "Calculated-" + T3_NAME));
+  }
+
+}
diff --git a/testDumper/src/test/java/de/tudresden/inf/st/jastadd/testDumper/TestTypeLevel4.java b/testDumper/src/test/java/de/tudresden/inf/st/jastadd/testDumper/TestTypeLevel4.java
new file mode 100644
index 0000000000000000000000000000000000000000..95679b86aa37af97c200bc0f4bb959de28d1e558
--- /dev/null
+++ b/testDumper/src/test/java/de/tudresden/inf/st/jastadd/testDumper/TestTypeLevel4.java
@@ -0,0 +1,209 @@
+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.AbstractT;
+import org.jastadd.testDumper.ast.TRoot;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.util.List;
+import java.util.function.Consumer;
+
+import static de.tudresden.inf.st.jastadd.testDumper.TestUtils.*;
+import static org.assertj.core.api.Assertions.*;
+
+/**
+ * Testing type-level inclusions.
+ * <p>
+ * Refer to {@link de.tudresden.inf.st.jastadd.dumpAst.ast.DumpBuilder DumpBuilder} for levels of inclusion/exclusion.
+ *
+ * @author rschoene - Initial contribution
+ */
+public class TestTypeLevel4 {
+
+  @Test
+  public void testTokenLevel3Included() {
+    Consumer<AbstractT> setUnwanted = t -> t.setUnwanted(5);
+    TRoot root = createTRoot(createA(A_NAME, createC(C_NAME, c -> c.setUnwanted(6))),
+        createT1(setUnwanted), createT2(setUnwanted), createT3(setUnwanted));
+    Assertions.assertEquals(5, root.getT2().getUnwanted());
+
+    List<DumpNode> nodes = dumpModel(root,
+        dp -> dp.excludeTokens(TOKEN_LABEL_UNWANTED)
+            .includeTokensFor("T.", TOKEN_LABEL_UNWANTED));
+    assertThat(nodes).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(
+        ROOT_NAME, A_NAME, C_NAME, T1_NAME, T2_NAME, T3_NAME);
+    for (String name : Tx_NAMES) {
+      DumpNode actualTx = findByName(nodes, name);
+      assertThat(valueTokens(actualTx)).containsOnly(entry("Name", name), entry(TOKEN_LABEL_UNWANTED, 5));
+    }
+    assertThat(valueTokens(findByName(nodes, C_NAME))).containsExactly(
+        entry("Name", C_NAME));
+  }
+
+  @Test
+  public void testTokenLevel4SomeExcluded() {
+    Consumer<AbstractT> setUnwanted = t -> t.setUnwanted(5);
+    TRoot root = createTRoot(createA(A_NAME, createC(C_NAME, c -> c.setUnwanted(6))),
+        createT1(setUnwanted), createT2(setUnwanted), createT3(setUnwanted));
+    Assertions.assertEquals(5, root.getT2().getUnwanted());
+
+    List<DumpNode> nodesT2 = dumpModel(root,
+        dp -> dp.excludeTokens(TOKEN_LABEL_UNWANTED)
+            .includeTokensFor("T.", TOKEN_LABEL_UNWANTED)
+            .excludeTokensFor("T3", TOKEN_LABEL_UNWANTED));
+    assertThat(nodesT2).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(
+        ROOT_NAME, A_NAME, C_NAME, T1_NAME, T2_NAME, T3_NAME);
+    assertThat(valueTokens(findByName(nodesT2, T1_NAME))).containsExactly(
+        entry("Name", T1_NAME), entry(TOKEN_LABEL_UNWANTED, 5));
+    assertThat(valueTokens(findByName(nodesT2, T2_NAME))).containsExactly(
+        entry("Name", T2_NAME), entry(TOKEN_LABEL_UNWANTED, 5));
+    assertThat(valueTokens(findByName(nodesT2, T3_NAME))).containsExactly(
+        entry("Name", T3_NAME));
+    assertThat(valueTokens(findByName(nodesT2, C_NAME))).containsExactly(
+        entry("Name", C_NAME));
+
+    List<DumpNode> nodesT2T3 = dumpModel(root,
+        dp -> dp.excludeTokens(TOKEN_LABEL_UNWANTED)
+            .includeTokensFor("T.", TOKEN_LABEL_UNWANTED)
+            .excludeTokensFor("T2|T3", TOKEN_LABEL_UNWANTED));
+    assertThat(nodesT2T3).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(
+        ROOT_NAME, A_NAME, C_NAME, T1_NAME, T2_NAME, T3_NAME);
+    assertThat(valueTokens(findByName(nodesT2T3, T1_NAME))).containsExactly(
+        entry("Name", T1_NAME), entry(TOKEN_LABEL_UNWANTED, 5));
+    assertThat(valueTokens(findByName(nodesT2T3, T2_NAME))).containsExactly(
+        entry("Name", T2_NAME));
+    assertThat(valueTokens(findByName(nodesT2T3, T3_NAME))).containsExactly(
+        entry("Name", T3_NAME));
+    assertThat(valueTokens(findByName(nodesT2T3, C_NAME))).containsExactly(
+        entry("Name", C_NAME));
+  }
+
+  @Test
+  public void testChildLevel3Included() {
+    Consumer<AbstractT> setB = t -> t.setB(createB(t.getName() + B_NAME));
+    TRoot root = createTRoot(createA(A_NAME), createT1(setB), createT2(setB), createT3(setB));
+    Assertions.assertEquals(T2_NAME + B_NAME, root.getT2().getB().getName());
+
+    List<DumpNode> nodes = dumpModel(root,
+        dp -> dp.excludeChildren("B")
+            .includeChildrenFor("T.", "B"));
+    assertThat(nodes).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(ROOT_NAME, A_NAME, T1_NAME, T2_NAME, T3_NAME, T1_NAME + B_NAME, T2_NAME + B_NAME, T3_NAME + B_NAME);
+    for (String name : Tx_NAMES) {
+      assertThatMapOf(listChildren(findByName(nodes, name)), "B").containsExactly(
+          name + B_NAME);
+    }
+  }
+
+  @Test
+  public void testChildLevel4SomeExcluded() {
+    Consumer<AbstractT> setB = t -> t.setB(createB(t.getName() + B_NAME));
+    TRoot root = createTRoot(createA(A_NAME), createT1(setB), createT2(setB), createT3(setB));
+    Assertions.assertEquals(T2_NAME + B_NAME, root.getT2().getB().getName());
+
+    List<DumpNode> nodes = dumpModel(root,
+        dp -> dp.excludeChildren("B")
+            .includeChildrenFor("T.", "B")
+            .excludeChildrenFor("T3", "B"));
+    assertThat(nodes).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(ROOT_NAME, A_NAME, T1_NAME, T2_NAME, T3_NAME, T1_NAME + B_NAME, T2_NAME + B_NAME);
+    assertThatMapOf(listChildren(findByName(nodes, T1_NAME)), "B").containsExactly(
+        T1_NAME + B_NAME);
+    assertThatMapOf(listChildren(findByName(nodes, T2_NAME)), "B").containsExactly(
+        T2_NAME + B_NAME);
+    assertThat(listChildren(findByName(nodes, T3_NAME))).isEmpty();
+  }
+
+  @Test
+  public void testRelationLevel3Included() {
+    final A a = createA(A_NAME);
+    Consumer<AbstractT> setOneA = t -> t.setOneA(a);
+    TRoot root = createTRoot(a, createT1(setOneA), createT2(setOneA), createT3(setOneA));
+    Assertions.assertEquals(a, root.getT2().getOneA());
+
+    List<DumpNode> nodes = dumpModel(root,
+        dp -> dp.excludeRelations("OneA")
+            .includeRelationsFor("T2", "OneA"));
+    assertThat(nodes).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(ROOT_NAME, A_NAME, T1_NAME, T2_NAME, T3_NAME);
+    assertThat(normalRelationChildren(findByName(nodes, T1_NAME))).isEmpty();
+    assertThatMapOf(normalRelationChildren(findByName(nodes, T2_NAME))).containsExactly(
+        tuple("OneA", A_NAME));
+    assertThat(normalRelationChildren(findByName(nodes, T3_NAME))).isEmpty();
+  }
+
+  @Test
+  public void testRelationLevel4SomeExcluded() {
+    final A a = createA(A_NAME);
+    Consumer<AbstractT> setOneA = t -> t.setOneA(a);
+    TRoot root = createTRoot(a, createT1(setOneA), createT2(setOneA), createT3(setOneA));
+    Assertions.assertEquals(a, root.getT2().getOneA());
+
+    List<DumpNode> nodes = dumpModel(root,
+        dp -> dp.excludeRelations("OneA")
+            .includeRelationsFor("T.", "OneA")
+            .excludeRelationsFor("T3", "OneA"));
+    assertThat(nodes).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(ROOT_NAME, A_NAME, T1_NAME, T2_NAME, T3_NAME);
+    assertThatMapOf(normalRelationChildren(findByName(nodes, T1_NAME))).containsExactly(
+        tuple("OneA", A_NAME));
+    assertThatMapOf(normalRelationChildren(findByName(nodes, T2_NAME))).containsExactly(
+        tuple("OneA", A_NAME));
+    assertThat(normalRelationChildren(findByName(nodes, T3_NAME))).isEmpty();
+  }
+
+  @Test
+  public void testAttributeLevel3Excluded() {
+    TRoot root = createTRoot(createA(A_NAME), createT1(), createT2(), createT3());
+
+    List<DumpNode> nodes = TestUtils.dumpModel(root,
+        dp -> dp.includeAttributes("simpleAttr")
+                .excludeAttributesFor("T.", "simpleAttr"));
+    assertThat(nodes).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(ROOT_NAME, A_NAME, T1_NAME, T2_NAME, T3_NAME);
+    for (String name : Tx_NAMES) {
+      DumpNode actualTx = TestUtils.findByName(nodes, name);
+      assertThat(valueTokens(actualTx)).containsExactly(entry("Name", name));
+    }
+  }
+
+  @Test
+  public void testAttributeLevel4SomeIncluded() {
+    TRoot root = createTRoot(createA(A_NAME), createT1(), createT2(), createT3());
+
+    List<DumpNode> nodes = TestUtils.dumpModel(root,
+        dp -> dp.includeAttributes("simpleAttr")
+                .excludeAttributesFor("T.", "simpleAttr")
+                .includeAttributesFor("T3", "simpleAttr"));
+    assertThat(nodes).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(ROOT_NAME, A_NAME, T1_NAME, T2_NAME, T3_NAME);
+    assertThat(valueTokens(TestUtils.findByName(nodes, T1_NAME))).containsExactly(
+        entry("Name", T1_NAME));
+    assertThat(valueTokens(TestUtils.findByName(nodes, T2_NAME))).containsExactly(
+        entry("Name", T2_NAME));
+    assertThat(valueTokens(TestUtils.findByName(nodes, T3_NAME))).containsExactly(
+        entry("Name", T3_NAME), entry("simpleAttr", 43));
+  }
+
+  @Test
+  public void testNTALevel3Excluded() {
+    TRoot root = createTRoot(createA(A_NAME), createT1(), createT2(), createT3());
+
+    List<DumpNode> nodes = TestUtils.dumpModel(root,
+        dp -> dp.includeNonterminalAttributes("Calculated")
+                .excludeNonterminalAttributesFor("T.", "Calculated"));
+    assertThat(nodes).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(ROOT_NAME, A_NAME, T1_NAME, T2_NAME, T3_NAME);
+    DumpNode actualT3 = TestUtils.findByName(nodes, T3_NAME);
+    assertThat(normalChildren(actualT3)).isEmpty();
+  }
+
+  @Test
+  public void testNTALevel4SomeIncluded() {
+    TRoot root = createTRoot(createA(A_NAME), createT1(), createT2(), createT3());
+
+    List<DumpNode> nodes = TestUtils.dumpModel(root,
+        dp -> dp.includeNonterminalAttributes("Calculated")
+            .excludeNonterminalAttributesFor("T.", "Calculated")
+            .includeNonterminalAttributesFor("T3", "Calculated"));
+    assertThat(nodes).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(ROOT_NAME, A_NAME, T1_NAME, T2_NAME, T3_NAME, "Calculated-" + T3_NAME);
+    DumpNode actualT3 = TestUtils.findByName(nodes, T3_NAME);
+    assertThatMapOf(normalChildren(actualT3)).containsOnly(tuple("Calculated", "Calculated-" + T3_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
index 89e7e7dccb5dbaf456c404bd6210979877500e73..dafc29d8b58b71679d806ed3e70db467b10a9de5 100644
--- a/testDumper/src/test/java/de/tudresden/inf/st/jastadd/testDumper/TestUtils.java
+++ b/testDumper/src/test/java/de/tudresden/inf/st/jastadd/testDumper/TestUtils.java
@@ -5,10 +5,7 @@ import org.assertj.core.api.AbstractListAssert;
 import org.assertj.core.api.Assertions;
 import org.assertj.core.api.ObjectAssert;
 import org.assertj.core.groups.Tuple;
-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.jastadd.testDumper.ast.*;
 
 import java.util.*;
 import java.util.function.Consumer;
@@ -30,6 +27,10 @@ public class TestUtils {
   public static final String B2_NAME = "B2" + Integer.toHexString(rand.nextInt(0xFFFFFF));
   public static final String B3_NAME = "B3" + Integer.toHexString(rand.nextInt(0xFFFFFF));
   public static final String C_NAME = "C" + Integer.toHexString(rand.nextInt(0xFFFFFF));
+  public static final String T1_NAME = "T1" + Integer.toHexString(rand.nextInt(0xFFFFFF));
+  public static final String T2_NAME = "T2" + Integer.toHexString(rand.nextInt(0xFFFFFF));
+  public static final String T3_NAME = "T3" + Integer.toHexString(rand.nextInt(0xFFFFFF));
+  public static final String[] Tx_NAMES = {T1_NAME, T2_NAME, T3_NAME};
 
   public static Root createRoot(A a, C c, B... bs) {
     Root result = new Root();
@@ -91,6 +92,47 @@ public class TestUtils {
     return result;
   }
 
+  public static TRoot createTRoot(A a, T1 t1, T2 t2, T3 t3) {
+    TRoot result = new TRoot();
+    result.setName(ROOT_NAME);
+    if (a != null) {
+      result.setA(a);
+    }
+    if (t1 != null) {
+      result.setT1(t1);
+    }
+    if (t2 != null) {
+      result.setT2(t2);
+    }
+    if (t3 != null) {
+      result.setT3(t3);
+    }
+    return result;
+  }
+
+  @SafeVarargs
+  public static T1 createT1(Consumer<AbstractT>... additionalSettings) {
+    return setupAbstractT(new T1(), T1_NAME, additionalSettings);
+  }
+
+  @SafeVarargs
+  public static T2 createT2(Consumer<AbstractT>... additionalSettings) {
+    return setupAbstractT(new T2(), T2_NAME, additionalSettings);
+  }
+
+  @SafeVarargs
+  public static T3 createT3(Consumer<AbstractT>... additionalSettings) {
+    return setupAbstractT(new T3(), T3_NAME, additionalSettings);
+  }
+
+  private static <T extends AbstractT> T setupAbstractT(T t, String name, Consumer<AbstractT>[] additionalSettings) {
+    t.setName(name);
+    for (Consumer<AbstractT> setting : additionalSettings) {
+      setting.accept(t);
+    }
+    return t;
+  }
+
   public static final Function<DumpNode, String> NAME_EXTRACTOR = node -> {
     for (DumpToken dumpToken : node.getDumpTokenList()) {
       if (dumpToken.getName().equals("Name")) {
@@ -106,11 +148,6 @@ public class TestUtils {
 
   public static AbstractListAssert<?, List<?>, Object, ObjectAssert<Object>> assertThatMapOf(Map<String, List<DumpNode>> map, String key) {
     return Assertions.assertThat(map.get(key)).flatExtracting(NAME_EXTRACTOR);
-//    return Assertions.assertThat(map).extractingFromEntries(Map.Entry::getKey, e -> {
-//      List<Object> result = new ArrayList<>();
-//      e.getValue().stream().map(NAME_EXTRACTOR).forEach(result::add);
-//      return tuple(result.toArray());
-//    });
   }
 
   public static List<DumpNode> dumpModel(Object target) {