diff --git a/dumpAst/src/main/jastadd/GenerationBackend.jadd b/dumpAst/src/main/jastadd/GenerationBackend.jadd
index ca04a6046c498bd4fb84102976bddcb2aa81f51b..45364244597f02931eb40747ea2f7883180279a4 100644
--- a/dumpAst/src/main/jastadd/GenerationBackend.jadd
+++ b/dumpAst/src/main/jastadd/GenerationBackend.jadd
@@ -1,61 +1,62 @@
 aspect GenerationBackend {
   class DumpAst {
-    private enum Source {
-      RELATION, INVISIBLE_PARENT, PARENT, ROOT
+    enum Source {
+      ROOT, NORMAL, RELATION
+    }
+    class TransformationOptions {
+      // todo should be read-only, i.e., copy-on-write
+      public Source source;
+      public boolean invisible;
+      public boolean allowNullObjects;
+      public boolean computed;
+
+      public TransformationOptions asRelation() {
+        return fromSource(Source.RELATION, false);
+      }
+
+      public TransformationOptions asRoot() {
+        return fromSource(Source.ROOT, false);
+      }
+
+      public TransformationOptions asNormal(boolean shouldBeInvisible) {
+        return fromSource(Source.NORMAL, shouldBeInvisible);
+      }
+
+      public TransformationOptions computed(boolean computed) {
+        if (computed == this.computed) {
+          return this;
+        }
+        return new TransformationOptions(this.source, this.invisible, this.allowNullObjects, computed);
+      }
+
+      public TransformationOptions allowNullObjectsOnce() {
+        return new TransformationOptions(this.source, this.invisible, !DumpAst.this.getBuildConfig().getExcludeNullNodes(), this.computed);
+      }
+
+      private TransformationOptions fromSource(Source source, boolean shouldBeInvisible) {
+        return new TransformationOptions(source, this.invisible || shouldBeInvisible, this.computed);
+      }
+
+      private TransformationOptions(Source source, boolean invisible, boolean computed) {
+        this(source, invisible, false, computed);
+      }
+      private TransformationOptions(Source source, boolean invisible, boolean allowNullObjects, boolean computed) {
+        this.source = source;
+        this.invisible = invisible;
+        this.allowNullObjects = allowNullObjects;
+        this.computed = computed;
+      }
     }
   }
 
-  class TransformationOptions {
-    // todo should be read-only, i.e., copy-on-write
-    public boolean relation = false;
-    public boolean invisible = false;
-    public boolean root = false;
-    public boolean allowNullObject = false;
-    public boolean computed = false;
-
-    public static TransformationOptions with() {
-      return new TransformationOptions();
-    }
-
-    public TransformationOptions relationTrue() {
-      TransformationOptions copy = new TransformationOptions(this);
-      copy.relation = true;
-      return copy;
-    }
-    public TransformationOptions invisibleTrue() {
-      TransformationOptions copy = new TransformationOptions(this);
-      copy.invisible = true;
-      return copy;
-    }
-    public TransformationOptions rootTrue() {
-      TransformationOptions copy = new TransformationOptions(this);
-      copy.root = true;
-      return copy;
-    }
-    public TransformationOptions allowNullObjectTrue() {
-      TransformationOptions copy = new TransformationOptions(this);
-      copy.allowNullObject = true;
-      return copy;
-    }
-    public TransformationOptions computedTrue() {
-      TransformationOptions copy = new TransformationOptions(this);
-      copy.computed = true;
-      return copy;
-    }
-    protected TransformationOptions() {}
-    protected TransformationOptions(TransformationOptions prototype) {
-      this.relation = prototype.relation;
-      this.invisible = prototype.invisible;
-      this.root = prototype.root;
-      this.allowNullObject = prototype.allowNullObject;
-      this.computed = prototype.computed;
-    }
+  private TransformationOptions DumpAst.options() {
+    return new TransformationOptions(Source.NORMAL, false, false);
   }
 
   // --- transform --- (need to be a method, because it alters the AST while traversing the object structure)
   protected DumpNode DumpAst.transform(TransformationTransferInformation tti, Object obj)
       throws java.lang.reflect.InvocationTargetException, IllegalAccessException, NoSuchMethodException {
-    DumpNode result = transform(tti, obj, Source.ROOT);
+    DumpNode result = transform(tti, obj, options().asRoot());
     setRootNode(result);
     // post-process relationTargetsUnprocessed
     boolean someAreUnprocessed = true;
@@ -64,21 +65,18 @@ aspect GenerationBackend {
       java.util.Map<DumpNode, Boolean> copy = new java.util.HashMap<>(tti.relationTargetsUnprocessed);
       for (java.util.Map.Entry<DumpNode, Boolean> entry : copy.entrySet()) {
         if (entry.getValue()) {
-          transform(tti, entry.getKey().getObject(), Source.ROOT);
+          transform(tti, entry.getKey().getObject(), options().asRoot());
           someAreUnprocessed = true;
         }
       }
     }
     return result;
   }
-  protected DumpNode DumpAst.transform(TransformationTransferInformation tti, Object obj, Source source)
-      throws java.lang.reflect.InvocationTargetException, IllegalAccessException, NoSuchMethodException {
-    return transform(tti, obj, source, false);
-  }
+
   protected DumpNode DumpAst.transform(
-      TransformationTransferInformation tti, Object obj, Source source, boolean allowNullObj)
+      TransformationTransferInformation tti, Object obj, TransformationOptions options)
       throws java.lang.reflect.InvocationTargetException, IllegalAccessException, NoSuchMethodException {
-    if (obj == null && !allowNullObj) {
+    if (obj == null && !options.allowNullObjects) {
       return null;
     }
     DumpNode node;
@@ -91,7 +89,7 @@ aspect GenerationBackend {
       objClassName = obj.getClass().getSimpleName();
     }
     if (node != null) {
-      if (source == Source.RELATION) {
+      if (options.source == Source.RELATION) {
         return node;
       }
       // either processing as parent, or later as root-node. so mark it as processed.
@@ -100,6 +98,7 @@ aspect GenerationBackend {
       node = new DumpNode();
       node.setObject(obj);
       node.setName("node" + (tti.nodeCounter++));
+      node.setComputed(options.computed);
       tti.transformed.put(obj, node);
       this.addDumpNode(node);
     }
@@ -108,11 +107,11 @@ aspect GenerationBackend {
     }
     applyStyle(node);
     // do not process node further if coming from a relation
-    if (source == Source.RELATION) {
+    if (options.source == Source.RELATION) {
       tti.relationTargetsUnprocessed.put(node, true);
       return node;
     }
-    if (source == Source.INVISIBLE_PARENT || !isTypeEnabled(objClassName)) {
+    if (options.invisible || !isTypeEnabled(objClassName)) {
       node.setInvisible(true);
     }
     if (obj == null) {
@@ -125,7 +124,7 @@ aspect GenerationBackend {
         // -- singleChild --
         Object target = containmentMethod.getMethod().invoke(obj);
         String childName = containmentMethod.getName();
-        DumpNode targetNode = transform(tti, target, nextSource(source, !isChildEnabled(objClassName, childName)), !getBuildConfig().getExcludeNullNodes());
+        DumpNode targetNode = transform(tti, target, options.asNormal(!isChildEnabled(objClassName, childName)).allowNullObjectsOnce());
         if (targetNode != null) {
           DumpNormalChildNode normalChild = new DumpNormalChildNode();
           normalChild.setName(childName);
@@ -142,7 +141,7 @@ aspect GenerationBackend {
         boolean shouldBeInvisisble = !isChildEnabled(objClassName, childName);
         listChild.setName(childName);
         for (Object target : targetList) {
-          DumpNode targetNode = transform(tti, target, nextSource(source, shouldBeInvisisble));
+          DumpNode targetNode = transform(tti, target, options.asNormal(shouldBeInvisisble));
           if (target != null && targetNode != null) {
             listChild.addInnerDumpNode(new InnerDumpNode().setDumpNode(targetNode));
           }
@@ -159,24 +158,26 @@ aspect GenerationBackend {
         // -- singleChild --
         Object target = otherMethod.getMethod().invoke(obj);
         String childName = otherMethod.getName();
-        DumpNode targetNode = transform(tti, target, nextSource(source, !isChildEnabled(objClassName, childName)), !getBuildConfig().getExcludeNullNodes());
+        boolean computed = otherMethod.asSingleChildMethod().isNTASingleChildMethod();
+        DumpNode targetNode = transform(tti, target, options.asNormal(!isChildEnabled(objClassName, childName)).computed(computed).allowNullObjectsOnce());
         if (targetNode != null) {
           DumpNormalChildNode normalChild = new DumpNormalChildNode();
           normalChild.setName(childName);
           normalChild.setDumpNode(targetNode);
-          normalChild.setComputed(otherMethod.asSingleChildMethod().isNTASingleChildMethod());
+          normalChild.setComputed(computed);
           node.addDumpChildNode(normalChild);
         }
       } else if (otherMethod.isListChildMethod()) {
         // -- listChild --
         Iterable<?> targetList = (Iterable<?>) otherMethod.getMethod().invoke(obj);
         DumpListChildNode listChild = new DumpListChildNode();
-        listChild.setComputed(otherMethod.asListChildMethod().isNTAListChildMethod());
+        boolean computed = otherMethod.asListChildMethod().isNTAListChildMethod();
+        listChild.setComputed(computed);
         String childName = otherMethod.getName();
         boolean shouldBeInvisisble = !isChildEnabled(objClassName, childName);
         listChild.setName(childName);
         for (Object target : targetList) {
-          DumpNode targetNode = transform(tti, target, nextSource(source, shouldBeInvisisble));
+          DumpNode targetNode = transform(tti, target, options.asNormal(shouldBeInvisisble).computed(computed));
           if (target != null && targetNode != null) {
             listChild.addInnerDumpNode(new InnerDumpNode().setDumpNode(targetNode));
           }
@@ -187,7 +188,7 @@ aspect GenerationBackend {
       } else if (otherMethod.isSingleRelationMethod()) {
         // -- singleRelation --
         Object target = otherMethod.getMethod().invoke(obj);
-        DumpNode targetNode = transform(tti, target, Source.RELATION);
+        DumpNode targetNode = transform(tti, target, options.asRelation());
         if (target != null && targetNode != null) {
           DumpNormalRelation normalRelation = new DumpNormalRelation();
           normalRelation.setName(otherMethod.getName());
@@ -200,7 +201,7 @@ aspect GenerationBackend {
         DumpListRelation listRelation = new DumpListRelation();
         listRelation.setName(otherMethod.getName());
         for (Object target : targetList) {
-          DumpNode targetNode = transform(tti, target, Source.RELATION);
+          DumpNode targetNode = transform(tti, target, options.asRelation());
           if (target != null && targetNode != null) {
             listRelation.addInnerDumpNode(new InnerDumpNode(targetNode));
           }
@@ -212,7 +213,7 @@ aspect GenerationBackend {
         // -- token --
         Object target = otherMethod.getMethod().invoke(obj);
         if (target != null) {
-          DumpNode targetNode = transform(tti, target, Source.RELATION);
+          DumpNode targetNode = transform(tti, target, options.asRelation());
           DumpToken token = null;
           // TODO check, if Iterable.isAssignableFrom(target.getClass()).
           //      if so, check isAstNode for first non-null to add DumpReferenceToken's, otherwise DumpValueToken's
@@ -246,11 +247,6 @@ aspect GenerationBackend {
     node.setManualStereotypes(getBuildConfig().getStyleInformation().getStereotypeMethod().get(obj));
   }
 
-  private Source DumpAst.nextSource(Source currentSource, boolean shouldBeInvisible) {
-    return currentSource == Source.INVISIBLE_PARENT ? Source.INVISIBLE_PARENT :
-        (shouldBeInvisible ? Source.INVISIBLE_PARENT : Source.PARENT);
-  }
-
   syn nta ClassAnalysisResult DumpAst.analyzeClass(java.lang.Class<?> clazz) {
     ClassAnalysisResult result = new ClassAnalysisResult();
     String clazzName = clazz.getSimpleName();
@@ -565,7 +561,6 @@ aspect GenerationBackend {
   // --- isAstNode ---
   syn boolean DumpNode.isAstNode() {
     if (getObject() == null) {
-      // todo: check if false works
       return false;
     }
     Class<?> clazz = getObject().getClass();
diff --git a/dumpAst/src/main/resources/dumpAst.mustache b/dumpAst/src/main/resources/dumpAst.mustache
index 9f8fb93ebe582514a1244429d449b21d8ae2e46b..a4c15afde2619529e104c4288b5d5fe80cf9244e 100644
--- a/dumpAst/src/main/resources/dumpAst.mustache
+++ b/dumpAst/src/main/resources/dumpAst.mustache
@@ -5,6 +5,10 @@ skinparam object<<null>> {
    shadowing false
 }
 hide <<null>> stereotype
+skinparam object<<NTA>> {
+    BorderColor {{{computedColor}}}
+}
+hide <<NTA>> stereotype
 
 {{#PrintConfig}}
 scale {{{scale}}}
diff --git a/featureTest/src/main/jastadd/featureTest.jrag b/featureTest/src/main/jastadd/featureTest.jrag
index 4cedce8cfa75749d2fb90adcb0ef5766e36e0ca1..4001126135dcb55faee01ca8edfc8c8f959f019f 100644
--- a/featureTest/src/main/jastadd/featureTest.jrag
+++ b/featureTest/src/main/jastadd/featureTest.jrag
@@ -2,6 +2,8 @@ aspect GrammarGlobal {
   syn A C.getCalculated() {
     A result = new A();
     result.setName("Calculated-" + getName());
+    D innerD = new D();
+    result.setD(innerD);
     return result;
   }
 
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 3a8dbc3c512d76a32d9cc0ca18ea95f35ac55ea0..b3e8fa92d7ddfcefc0331facc0d27e228f6363b8 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
@@ -163,7 +163,7 @@ public class TestUtils {
     dumpAst.toPlantUml();
     List<DumpNode> result = new ArrayList<>();
     for (DumpNode dumpNode : dumpAst.getDumpNodeList()) {
-      if (dumpNode.isAstNode() && !dumpNode.getInvisible()) {
+      if ((dumpNode.isAstNode() || dumpNode.isNull()) && !dumpNode.getInvisible()) {
         result.add(dumpNode);
       }
     }