diff --git a/dumpAst.base/src/main/jastadd/ClassAnalysis.jrag b/dumpAst.base/src/main/jastadd/ClassAnalysis.jrag index fdd6052df4fc03bb14e50d12eb11c36ec37d5f43..6558bc1eeb13ad3f63c0413ff0e212ea0d8bd635 100644 --- a/dumpAst.base/src/main/jastadd/ClassAnalysis.jrag +++ b/dumpAst.base/src/main/jastadd/ClassAnalysis.jrag @@ -48,29 +48,28 @@ aspect ClassAnalysis { String tokenName = invokeName(annotation); if (tokenName.startsWith("_impl_")) { String relationName = titleCase(tokenName.substring(6)); - try { - java.lang.reflect.Method relationMethod = clazz.getMethod("get" + relationName); + if (present(getMethod(clazz, "get" + relationName), relationMethod -> { // normal get + token-name -> singleRelation - SingleRelationMethod singleRelationMethod = new SingleRelationMethod(); + SingleRelationMethod singleRelationMethod = getMethod(clazz, "has" + relationName).isPresent() ? + new OptRelationMethod() : 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 } + // we know here this is probably not a single or opt relation // try list-relation next - try { - java.lang.reflect.Method relationMethod = clazz.getMethod("get" + relationName + "List"); + if (present(getMethod(clazz, "get" + relationName + "List"), relationMethod -> { // normal get + token-name + "List" -> listRelation 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 } + // we know here this is probably not a relation at all } IntrinsicTokenMethod tokenMethod = new IntrinsicTokenMethod(); tokenMethod.setMethod(method); @@ -201,6 +200,20 @@ aspect ClassAnalysis { } } + private static java.util.Optional<java.lang.reflect.Method> DumpAst.getMethod(Class<?> clazz, String methodName) { + try { + java.lang.reflect.Method method = clazz.getMethod(methodName); + return java.util.Optional.of(method); + } catch (NoSuchMethodException e) { + return java.util.Optional.empty(); + } + } + + private static <T> boolean DumpAst.present(java.util.Optional<T> optional, java.util.function.Consumer<T> callback) { + optional.ifPresent(callback); + return optional.isPresent(); + } + // --- astNodeAnnotationPrefix --- syn String DumpAst.astNodeAnnotationPrefix() = getPackageName() + ".ASTNodeAnnotation"; inh String DumpNode.astNodeAnnotationPrefix(); diff --git a/dumpAst.base/src/main/jastadd/DumpAst.relast b/dumpAst.base/src/main/jastadd/DumpAst.relast index bca6fd7a7f153906a5d551f25f9b5e696b4991e1..da8cb5a2a2d943bc8cbdaa3102051a0573673749 100644 --- a/dumpAst.base/src/main/jastadd/DumpAst.relast +++ b/dumpAst.base/src/main/jastadd/DumpAst.relast @@ -6,13 +6,13 @@ DumpNode ::= DumpChildNode* DumpToken* DumpRelation* /InvisiblePath/ ; InnerDumpNode ::= <OriginalIndex:int>; -rel InnerDumpNode.DumpNode <-> DumpNode.ContainerOfInner ; +rel InnerDumpNode.DumpNode <-> DumpNode.ContainerOfInner? ; InnerRelationDumpNode ::= <OriginalIndex:int>; -rel InnerRelationDumpNode.DumpNode -> DumpNode ; // .ContainerOfInner* +rel InnerRelationDumpNode.DumpNode <-> DumpNode.ContainerOfRelationInner* ; abstract DumpChildNode ::= <Name> <Computed:boolean> ; DumpNormalChildNode : DumpChildNode ; -rel DumpNormalChildNode.DumpNode <-> DumpNode.ContainerOfNormalChild ; +rel DumpNormalChildNode.DumpNode <-> DumpNode.ContainerOfNormalChild? ; DumpListChildNode : DumpChildNode ::= InnerDumpNode* ; abstract DumpToken ::= <Name> <Computed:boolean> ; @@ -45,6 +45,7 @@ NormalListChildMethod : ListChildMethod ; NTAListChildMethod : ListChildMethod ; SingleRelationMethod : AnalysedMethod ; +OptRelationMethod : SingleRelationMethod ; ListRelationMethod : AnalysedMethod ; // TODO can the refine bug also happen for refined attributes? diff --git a/dumpAst.base/src/main/jastadd/GeneratedNavigation.jrag b/dumpAst.base/src/main/jastadd/GeneratedNavigation.jrag index 4ef4b469c69254a667aa3449d9735b6a4a254bc6..723bfbacbb38a3682abb0b5fc6a01a20cf2bb3b2 100644 --- a/dumpAst.base/src/main/jastadd/GeneratedNavigation.jrag +++ b/dumpAst.base/src/main/jastadd/GeneratedNavigation.jrag @@ -36,6 +36,12 @@ aspect Navigation { syn boolean AnalysedMethod.isSingleRelationMethod() = false; eq SingleRelationMethod.isSingleRelationMethod() = true; + /** Tests if SingleRelationMethod is a OptRelationMethod. + * @return 'true' if this is a OptRelationMethod, otherwise 'false' + */ + syn boolean SingleRelationMethod.isOptRelationMethod() = false; + eq OptRelationMethod.isOptRelationMethod() = true; + /** Tests if AnalysedMethod is a ListRelationMethod. * @return 'true' if this is a ListRelationMethod, otherwise 'false' */ diff --git a/dumpAst.base/src/main/jastadd/Navigation.jrag b/dumpAst.base/src/main/jastadd/Navigation.jrag index dc4ce9a3e857777d7305d07f48a436fb18c0bfc7..fcec323db3a60fecb4eaa93690d73cbdd4b118a0 100644 --- a/dumpAst.base/src/main/jastadd/Navigation.jrag +++ b/dumpAst.base/src/main/jastadd/Navigation.jrag @@ -85,10 +85,10 @@ aspect Navigation { return result; } - // --- innerNotNull --- - syn boolean DumpNormalRelation.innerNotNull() = !printConfig().getRelationWithRank() && getDumpNode() != null && getDumpNode().getObject() != null; - syn boolean DumpReferenceToken.innerNotNull() = !printConfig().getRelationWithRank() && getValue() != null && getValue().getObject() != null; - syn boolean InnerRelationDumpNode.innerNotNull() = !printConfig().getRelationWithRank() && getDumpNode() != null && getDumpNode().getObject() != null; + // --- innerNotNullOrEmpty --- + syn boolean DumpNormalRelation.innerNotNullOrEmpty() = !printConfig().getRelationWithRank() && getDumpNode() != null && getDumpNode().getObject() != null && getDumpNode().getObject() != DumpAst.EMPTY; + syn boolean DumpReferenceToken.innerNotNullOrEmpty() = !printConfig().getRelationWithRank() && getValue() != null && getValue().getObject() != null && getValue().getObject() != DumpAst.EMPTY; + syn boolean InnerRelationDumpNode.innerNotNullOrEmpty() = !printConfig().getRelationWithRank() && getDumpNode() != null && getDumpNode().getObject() != null && getDumpNode().getObject() != DumpAst.EMPTY; // === Method naviagtion === coll java.util.List<SingleChildMethod> ClassAnalysisResult.singleChildMethods() [new java.util.ArrayList<>()] root ClassAnalysisResult; diff --git a/dumpAst.base/src/main/jastadd/Printing.jrag b/dumpAst.base/src/main/jastadd/Printing.jrag index 2d64f5f5604f6cb16c78ab0d317aed2033349df3..74937113a800de6850174dfbf13995c38b6ea3af 100644 --- a/dumpAst.base/src/main/jastadd/Printing.jrag +++ b/dumpAst.base/src/main/jastadd/Printing.jrag @@ -25,10 +25,14 @@ aspect Printing { syn String DumpNode.label() = getLabel(); inh String InnerDumpNode.label(); inh String InnerRelationDumpNode.label(); - eq DumpListChildNode.getInnerDumpNode(int index).label() = label() + "[" + - chooseIndex(getInnerDumpNode(index).getOriginalIndex(), index) + "]"; - eq DumpListRelation.getInnerRelationDumpNode(int index).label() = label() + "[" + - chooseIndex(getInnerRelationDumpNode(index).getOriginalIndex(), index) + "]"; + eq DumpListChildNode.getInnerDumpNode(int index).label() = label() + + (getInnerDumpNode(index).getDumpNode().isEmpty() ? + "" : + "[" + chooseIndex(getInnerDumpNode(index).getOriginalIndex(), index) + "]"); + eq DumpListRelation.getInnerRelationDumpNode(int index).label() = label() + + (getInnerRelationDumpNode(index).getDumpNode().isEmpty() ? + "" : + "[" + chooseIndex(getInnerRelationDumpNode(index).getOriginalIndex(), index) + "]"); eq DumpReferenceListToken.getInnerRelationDumpNode(int index).label() = label() + "[" + index + "]"; eq InvisiblePath.getInnerRelationDumpNode(int index).label() = null; protected int ASTNode.chooseIndex(int originalIndex, int inheritedIndex) { diff --git a/dumpAst.base/src/main/jastadd/TemplateContext.jrag b/dumpAst.base/src/main/jastadd/TemplateContext.jrag index a1d09c4ecac06f97159af0265dd46ced245f4843..a559311c3c6fb8ae58a93cf19ea72a1b81ba5936 100644 --- a/dumpAst.base/src/main/jastadd/TemplateContext.jrag +++ b/dumpAst.base/src/main/jastadd/TemplateContext.jrag @@ -8,6 +8,11 @@ aspect TemplateContext { return getObject() == null; } + // --- isEmpty --- + syn boolean DumpNode.isEmpty() { + return getObject() == DumpAst.EMPTY; + } + // --- isAstNode --- syn boolean DumpNode.isAstNode() { if (getObject() == null) { diff --git a/dumpAst.base/src/main/jastadd/ToYaml.jrag b/dumpAst.base/src/main/jastadd/ToYaml.jrag index 0df35a8701bd1a8b4d5b93ab04d47a327ce21436..ba9c7e8b72bde8a6112ee63a2c374176ef664fa6 100644 --- a/dumpAst.base/src/main/jastadd/ToYaml.jrag +++ b/dumpAst.base/src/main/jastadd/ToYaml.jrag @@ -71,6 +71,7 @@ aspect ToYaml { // attributes if (!fromRelation) { result.put("isNull", isNull()); + result.put("isEmpty", isEmpty()); result.put("isAstNode", isAstNode()); result.put("labelAndTextColor", labelAndTextColor()); result.put("stereotypeList", stereotypeList()); @@ -137,7 +138,7 @@ aspect ToYaml { MappingElement result = super.toYaml(fromRelation); // attributes result.put("innerNodeName", innerNodeName()); - result.put("innerNotNull", innerNotNull()); + result.put("innerNotNullOrEmpty", innerNotNullOrEmpty()); result.put("outerNodeName", outerNodeName()); return result; } @@ -170,7 +171,7 @@ aspect ToYaml { // attributes result.put("bothVisible", bothVisible()); result.put("innerNodeName", innerNodeName()); - result.put("innerNotNull", innerNotNull()); + result.put("innerNotNullOrEmpty", innerNotNullOrEmpty()); return result; } @@ -196,7 +197,7 @@ aspect ToYaml { // attributes result.put("bothVisible", bothVisible()); result.put("innerNodeName", innerNodeName()); - result.put("innerNotNull", innerNotNull()); + result.put("innerNotNullOrEmpty", innerNotNullOrEmpty()); result.put("outerNodeName", outerNodeName()); result.put("label", label()); return result; diff --git a/dumpAst.base/src/main/jastadd/Transform.jadd b/dumpAst.base/src/main/jastadd/Transform.jadd index bc8cfbaa552963df4422d901369a58438b0a8ac1..d1a71ae0cddd2b5ca54b66d2e5afc47d6e5c7023 100644 --- a/dumpAst.base/src/main/jastadd/Transform.jadd +++ b/dumpAst.base/src/main/jastadd/Transform.jadd @@ -53,6 +53,8 @@ aspect Transform { return new TransformationOptions(Source.NORMAL, false, false); } + public static final Object DumpAst.EMPTY = new Object(); + // --- 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 { @@ -81,7 +83,7 @@ aspect Transform { } DumpNode node; String objClassName; - if (obj == null) { + if (obj == null || obj == EMPTY) { node = null; objClassName = "null"; } else { @@ -114,8 +116,8 @@ aspect Transform { if (options.invisible || !isTypeEnabled(objClassName)) { node.setInvisible(true); } - if (obj == null) { - // for a null object, we do not need any further analysis + if (obj == null || obj == EMPTY) { + // for a null or empty object, we do not need any further analysis return node; } final ClassAnalysisResult car = analyzeClass(obj.getClass()); @@ -143,7 +145,7 @@ aspect Transform { DumpNode targetNode = transform(tti, target, options.asNormal(false).allowNullObjectsOnce()); if (targetNode != null) { DumpNormalChildNode normalChild = new DumpNormalChildNode(); - normalChild.setName(childName); + normalChild.setName(childName + (containmentMethod.isOptChildMethod() ? "?" : "")); normalChild.setDumpNode(targetNode); normalChild.setComputed(false); node.addDumpChildNode(normalChild); @@ -166,9 +168,11 @@ aspect Transform { listChild.addInnerDumpNode(new InnerDumpNode().setDumpNode(targetNode).setOriginalIndex(index)); } } - if (listChild.getNumInnerDumpNode() > 0) { - node.addDumpChildNode(listChild); + if (listChild.getNumInnerDumpNode() == 0) { + listChild.addInnerDumpNode(new InnerDumpNode().setDumpNode(transform(tti, EMPTY, + options.asNormal(false).allowNullObjectsOnce()))); } + node.addDumpChildNode(listChild); } else { throw new RuntimeException("Unknown containment method type " + containmentMethod); } @@ -222,7 +226,8 @@ aspect Transform { DumpNode targetNode = transform(tti, target, options.asRelation()); if (targetNode != null) { DumpNormalRelation normalRelation = new DumpNormalRelation(); - normalRelation.setName(otherMethod.getName()); + normalRelation.setName(otherMethod.getName() + + (otherMethod.asSingleRelationMethod().isOptRelationMethod() ? "?" : "")); normalRelation.setDumpNode(targetNode); node.addDumpRelation(normalRelation); } @@ -243,9 +248,11 @@ aspect Transform { .setOriginalIndex(index)); } } - if (listRelation.getNumInnerRelationDumpNode() > 0) { - node.addDumpRelation(listRelation); + if (listRelation.getNumInnerRelationDumpNode() == 0) { + listRelation.addInnerRelationDumpNode(new InnerRelationDumpNode().setDumpNode(transform(tti, EMPTY, + options.asNormal(false).allowNullObjectsOnce()))); } + node.addDumpRelation(listRelation); } else if (otherMethod.isTokenMethod()) { // -- token -- TokenMethod tokenMethod = otherMethod.asTokenMethod(); diff --git a/dumpAst.base/src/main/resources/dumpAst.mustache b/dumpAst.base/src/main/resources/dumpAst.mustache index da993c1d4d995ea2f8b15f4129207be3bc14d08a..80010c0882ada38aafc77e43b4f2a2f79dd091ce 100644 --- a/dumpAst.base/src/main/resources/dumpAst.mustache +++ b/dumpAst.base/src/main/resources/dumpAst.mustache @@ -22,6 +22,9 @@ scale {{{scale}}} {{#isNull}} object "null" as {{{name}}}<<null>> {{/isNull}} + {{#isEmpty}} +object "[]" as {{{name}}}<<null>> + {{/isEmpty}} {{#isAstNode}} object "{{{labelAndTextColor}}}" as {{{name}}} {{{stereotypeList}}} {{#backgroundColor}}#{{{backgroundColor}}}{{/backgroundColor}} { {{#DumpTokens}} @@ -40,13 +43,13 @@ object "{{{labelAndTextColor}}}" as {{{name}}} {{{stereotypeList}}} {{#backgroun {{#isList}} {{#InnerRelationDumpNode}} {{#bothVisible}} -{{{outerNodeName}}} .[#black{{#computed}},#{{{computedColor}}}{{/computed}}{{#innerNotNull}},norank{{/innerNotNull}}].> {{{innerNodeName}}} : {{{label}}} +{{{outerNodeName}}} .[#black{{#computed}},#{{{computedColor}}}{{/computed}}{{#innerNotNullOrEmpty}},norank{{/innerNotNullOrEmpty}}].> {{{innerNodeName}}} : {{{label}}} {{/bothVisible}} {{/InnerRelationDumpNode}} {{/isList}} {{^isList}} {{^isDumpValueToken}} -{{{outerNodeName}}} .[#black{{#computed}},#{{{computedColor}}}{{/computed}}{{#innerNotNull}},norank{{/innerNotNull}}].> {{{innerNodeName}}} : {{{label}}} +{{{outerNodeName}}} .[#black{{#computed}},#{{{computedColor}}}{{/computed}}{{#innerNotNullOrEmpty}},norank{{/innerNotNullOrEmpty}}].> {{{innerNodeName}}} : {{{label}}} {{/isDumpValueToken}} {{/isList}} {{/invisible}} @@ -69,13 +72,13 @@ object "{{{labelAndTextColor}}}" as {{{name}}} {{{stereotypeList}}} {{#backgroun {{#isList}} {{#InnerRelationDumpNode}} {{#bothVisible}} -{{{outerNodeName}}} {{#bidirectional}}<{{/bidirectional}}-{{#innerNotNull}}[norank]{{/innerNotNull}}-> {{{innerNodeName}}} : {{{label}}} +{{{outerNodeName}}} {{#bidirectional}}<{{/bidirectional}}-{{#innerNotNullOrEmpty}}[norank]{{/innerNotNullOrEmpty}}-> {{{innerNodeName}}} : {{{label}}} {{/bothVisible}} {{/InnerRelationDumpNode}} {{/isList}} {{^isList}} {{#bothVisible}} -{{{outerNodeName}}} {{#bidirectional}}<{{/bidirectional}}-{{#innerNotNull}}[norank]{{/innerNotNull}}-> {{{innerNodeName}}} : {{{label}}} +{{{outerNodeName}}} {{#bidirectional}}<{{/bidirectional}}-{{#innerNotNullOrEmpty}}[norank]{{/innerNotNullOrEmpty}}-> {{{innerNodeName}}} : {{{label}}} {{/bothVisible}} {{/isList}} {{/DumpRelations}} diff --git a/dumpAst.base/src/main/resources/dumpAstVersion.properties b/dumpAst.base/src/main/resources/dumpAstVersion.properties index a399bec009f8841666c286fea1666bc282fe6fa5..9cbc2fe8b6856a6b73f2780b3e8765a417f2394e 100644 --- a/dumpAst.base/src/main/resources/dumpAstVersion.properties +++ b/dumpAst.base/src/main/resources/dumpAstVersion.properties @@ -1,2 +1,2 @@ -#Thu Oct 06 10:00:14 CEST 2022 -version=2.0.2 +#Fri Oct 07 16:26:50 CEST 2022 +version=2.0.3