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); } }