diff --git a/dumpAst2uml/build.gradle b/dumpAst2uml/build.gradle index 048b27dc3cb0d93cdfc2d580683197fa4e68bde3..f4badc1b73afbdf0e044ccd8faf7b71943c00022 100644 --- a/dumpAst2uml/build.gradle +++ b/dumpAst2uml/build.gradle @@ -32,7 +32,7 @@ dependencies { testImplementation group: 'org.assertj', name: 'assertj-core', version: '3.12.1' } -def versionFile = 'src/main/resources/dumpAst2umlVersion.properties' +def versionFile = 'src/main/resources/dumpAstVersion.properties' def oldProps = new Properties() try { diff --git a/dumpAst2uml/src/main/jastadd/DumpAst.relast b/dumpAst2uml/src/main/jastadd/DumpAst.relast index 91f4d16f74b37f33f4200aa9f91a79ac0f4a7947..42c18fddf417756538ebd9739877556667c6fa74 100644 --- a/dumpAst2uml/src/main/jastadd/DumpAst.relast +++ b/dumpAst2uml/src/main/jastadd/DumpAst.relast @@ -1,4 +1,7 @@ -DumpAst ::= DumpNode* <PackageName> <Scale:double> ; +DumpAst ::= DumpNode* <PackageName> BuildConfig PrintConfig ; +BuildConfig ::= <TypeIgnore> <TokenIgnore> <ChildIgnore> <AttributeIgnore> <RelationIgnore> <IgnoreEmptyString:boolean> <Debug:boolean> ; +PrintConfig ::= <Scale:double> <Version> Header* ; +Header ::= <Value> ; DumpNode ::= <Name> <Label> <Object:Object> DumpChildNode* DumpToken* DumpRelation* ; InnerDumpNode ; rel InnerDumpNode.DumpNode -> DumpNode ; diff --git a/dumpAst2uml/src/main/jastadd/Generation.jadd b/dumpAst2uml/src/main/jastadd/Generation.jadd index 68de1bf3c40db41a370deb9dfad5c74b23c48aef..8cf0aba63e98680cdcfe0ad7d5a8ea8be4d45d45 100644 --- a/dumpAst2uml/src/main/jastadd/Generation.jadd +++ b/dumpAst2uml/src/main/jastadd/Generation.jadd @@ -4,27 +4,96 @@ aspect Generation { return new DumpBuilder(obj); } } + public enum SkinParamBooleanSetting { + Monochrome, Shadowing, Handwritten + } + public enum SkinParamStringSetting { + backgroundColor + } public class DumpBuilder { private Object target; private String packageName; private DumpAst result; - private java.util.List<String> header = new java.util.ArrayList<>(); + private BuildConfig buildConfig = new BuildConfig(); + private PrintConfig printConfig = new PrintConfig(); protected DumpBuilder(Object target) { this.target = target; + printConfig.setScale(1); + printConfig.setVersion(readVersion()); + ignoreEmptyStrings(true); + } + + public DumpBuilder setDebug(boolean debug) { + buildConfig.setDebug(debug); + return this; + } + public DumpBuilder ignoreEmptyStrings(boolean ignoreThem) { + buildConfig.setIgnoreEmptyString(ignoreThem); + return this; } public DumpBuilder ignoreTypes(String... regexes) { - // TODO + updateIgnored(() -> buildConfig.getTypeIgnore(), s -> buildConfig.setTypeIgnore(s), regexes); + return this; + } + public DumpBuilder ignoreTokens(String... regexes) { + updateIgnored(() -> buildConfig.getTokenIgnore(), s -> buildConfig.setTokenIgnore(s), regexes); + return this; + } + public DumpBuilder ignoreAttributes(String... regexes) { + updateIgnored(() -> buildConfig.getAttributeIgnore(), s -> buildConfig.setAttributeIgnore(s), regexes); return this; } - public DumpBuilder skinParam(String option) { - // TODO + public DumpBuilder ignoreChildren(String... regexes) { + updateIgnored(() -> buildConfig.getChildIgnore(), s -> buildConfig.setChildIgnore(s), regexes); return this; } + public DumpBuilder ignoreRelations(String... regexes) { + updateIgnored(() -> buildConfig.getRelationIgnore(), s -> buildConfig.setRelationIgnore(s), regexes); + return this; + } + private void updateIgnored(java.util.function.Supplier<String> getter, java.util.function.Consumer<String> setter, String... values) { + for (String value : values) { + if (getter.get().isEmpty()) { + setter.accept(value); + } else { + setter.accept(getter.get() + "|" + value); + } + } + } + + public DumpBuilder customPreamble(String option) { + printConfig.addHeader(new Header(option)); + return this; + } + public DumpBuilder skinParam(SkinParamStringSetting setting, String value) { + customPreamble("skinparam " + setting.toString() + " " + value); + return this; + } + public DumpBuilder skinParam(SkinParamBooleanSetting setting, boolean value) { + customPreamble("skinparam " + setting.toString() + " " + value); + return this; + } + public DumpBuilder setScale(double value) { + printConfig.setScale(value); + return this; + } + + private String readVersion() { + try { + java.util.ResourceBundle resources = java.util.ResourceBundle.getBundle("dumpAstVersion"); + return resources.getString("version"); + } catch (java.util.MissingResourceException e) { + return "version ?"; + } + } + protected DumpAst build() { if (result == null) { result = new DumpAst(); result.setPackageName(this.packageName == null ? this.target.getClass().getPackage().getName() : this.packageName); + result.setBuildConfig(this.buildConfig); + result.setPrintConfig(this.printConfig); try { result.transform(new TransformationTransferInformation(), this.target); } catch (java.lang.reflect.InvocationTargetException e) { @@ -38,7 +107,6 @@ aspect Generation { return result; } public DumpBuilder dumpAsSource(java.nio.file.Path destination) throws java.io.IOException { - build().setScale(0.1); String content = build().toPlantUml(); try (java.io.Writer writer = java.nio.file.Files.newBufferedWriter(destination)) { writer.write(content); @@ -46,14 +114,12 @@ aspect Generation { return this; } public DumpBuilder dumpAsPNG(java.nio.file.Path destination) throws java.io.IOException { - build().setScale(0.1); String content = build().toPlantUml(); net.sourceforge.plantuml.SourceStringReader reader = new net.sourceforge.plantuml.SourceStringReader(content); reader.outputImage(java.nio.file.Files.newOutputStream(destination)); return this; } public DumpBuilder dumpAsSVG(java.nio.file.Path destination) throws java.io.IOException { - build().setScale(1); String content = build().toPlantUml(); net.sourceforge.plantuml.SourceStringReader reader = new net.sourceforge.plantuml.SourceStringReader(content); reader.outputImage(java.nio.file.Files.newOutputStream(destination), @@ -71,6 +137,10 @@ aspect Generation { if (tti.transformed.containsKey(obj)) { return tti.transformed.get(obj); } + if (matches(getBuildConfig().typeIgnorePattern(), obj.getClass().getSimpleName())) { + tti.transformed.put(obj, null); + return null; + } DumpNode node = new DumpNode(); node.setObject(obj); node.setLabel(obj.getClass().getSimpleName() + "@" + obj.hashCode()); @@ -90,10 +160,11 @@ aspect Generation { // -- singleChild -- for (java.lang.reflect.Method method : car.singleChildMethods) { Object target = method.invoke(obj); - if (target != null) { + DumpNode targetNode = transform(tti, target); + if (target != null && targetNode != null) { DumpNormalChildNode normalChild = new DumpNormalChildNode(); normalChild.setName(car.names.get(method)); - normalChild.setDumpNode(transform(tti, target)); + normalChild.setDumpNode(targetNode); node.addDumpChildNode(normalChild); } } @@ -103,8 +174,9 @@ aspect Generation { DumpListChildNode listChild = new DumpListChildNode(); listChild.setName(car.names.get(method)); for (Object target : targetList) { - if (target != null) { - listChild.addInnerDumpNode(new InnerDumpNode(transform(tti, target))); + DumpNode targetNode = transform(tti, target); + if (target != null && targetNode != null) { + listChild.addInnerDumpNode(new InnerDumpNode(targetNode)); } } node.addDumpChildNode(listChild); @@ -112,10 +184,11 @@ aspect Generation { // -- singleRelation -- for (java.lang.reflect.Method method : car.singleRelationMethods) { Object target = method.invoke(obj); - if (target != null) { + DumpNode targetNode = transform(tti, target); + if (target != null && targetNode != null) { DumpNormalRelation normalRelation = new DumpNormalRelation(); normalRelation.setName(car.names.get(method)); - normalRelation.setDumpNode(transform(tti, target)); + normalRelation.setDumpNode(targetNode); node.addDumpRelation(normalRelation); } } @@ -125,8 +198,9 @@ aspect Generation { DumpListRelation listRelation = new DumpListRelation(); listRelation.setName(car.names.get(method)); for (Object target : targetList) { - if (target != null) { - listRelation.addInnerDumpNode(new InnerDumpNode(transform(tti, target))); + DumpNode targetNode = transform(tti, target); + if (target != null && targetNode != null) { + listRelation.addInnerDumpNode(new InnerDumpNode(targetNode)); } } node.addDumpRelation(listRelation); @@ -136,17 +210,20 @@ aspect Generation { Object target = method.invoke(obj); if (target != null) { DumpNode targetNode = transform(tti, target); - DumpToken token; - if (targetNode.isAstNode()) { + DumpToken token = null; + if (targetNode != null && targetNode.isAstNode()) { token = new DumpReferenceToken().setValue(targetNode); } else { - // maybe ignore empty string values here - DumpValueToken valueToken = new DumpValueToken(); - valueToken.setValue(target); - token = valueToken; + if (target != null && (!getBuildConfig().getIgnoreEmptyString() || !target.toString().isEmpty())) { + DumpValueToken valueToken = new DumpValueToken(); + valueToken.setValue(target); + token = valueToken; + } + } + if (token != null) { + token.setName(car.names.get(method)); + node.addDumpToken(token); } - token.setName(car.names.get(method)); - node.addDumpToken(token); } } } @@ -165,10 +242,18 @@ aspect Generation { switch (simpleName) { case "Child": case "OptChild": - result.singleChildMethods.add(method); + String singleChildName = invokeName(annotation); + if (!matches(buildConfig().childIgnorePattern(), singleChildName)) { + result.singleChildMethods.add(method); + result.names.put(method, singleChildName); + } break; case "ListChild": - result.listChildMethods.add(method); + String listChildName = invokeName(annotation); + if (!matches(buildConfig().childIgnorePattern(), listChildName)) { + result.listChildMethods.add(method); + result.names.put(method, listChildName); + } break; case "Token": // heuristic for relations @@ -178,8 +263,10 @@ aspect Generation { try { java.lang.reflect.Method relationMethod = clazz.getMethod("get" + relationName); // normal get + token-name -> singleRelation - result.singleRelationMethods.add(relationMethod); - result.names.put(relationMethod, relationName); + if (!matches(buildConfig().relationIgnorePattern(), relationName)) { + result.singleRelationMethods.add(relationMethod); + result.names.put(relationMethod, relationName); + } continue; } catch (NoSuchMethodException e) { // ignore, but we know this is probably not a single relation @@ -188,25 +275,39 @@ aspect Generation { try { java.lang.reflect.Method relationMethod = clazz.getMethod("get" + relationName + "List"); // normal get + token-name + "List" -> listRelation - result.listRelationMethods.add(relationMethod); - result.names.put(relationMethod, relationName); + if (!matches(buildConfig().relationIgnorePattern(), relationName)) { + result.listRelationMethods.add(relationMethod); + result.names.put(relationMethod, relationName); + } continue; } catch (NoSuchMethodException e) { // ignore, but we know this is probably not a relation at all } } - result.tokenMethods.add(method); + if (!matches(buildConfig().tokenIgnorePattern(), tokenName)) { + result.tokenMethods.add(method); + result.names.put(method, tokenName); + } break; - default: - continue; } - result.names.put(method, invokeName(annotation)); } } } return result; } + syn java.util.regex.Pattern BuildConfig.typeIgnorePattern() = java.util.regex.Pattern.compile(getTypeIgnore()); + syn java.util.regex.Pattern BuildConfig.childIgnorePattern() = java.util.regex.Pattern.compile(getChildIgnore()); + syn java.util.regex.Pattern BuildConfig.tokenIgnorePattern() = java.util.regex.Pattern.compile(getTokenIgnore()); + syn java.util.regex.Pattern BuildConfig.attributeIgnorePattern() = java.util.regex.Pattern.compile(getAttributeIgnore()); + syn java.util.regex.Pattern BuildConfig.relationIgnorePattern() = java.util.regex.Pattern.compile(getRelationIgnore()); + static boolean ASTNode.matches(java.util.regex.Pattern p, String input) { + return p.matcher(input).matches(); + } + + // --- version --- (mustache has buildConfig as context) + syn String BuildConfig.version() = printConfig().getVersion(); + private static String DumpNode.invokeName(java.lang.annotation.Annotation annotation) throws java.lang.reflect.InvocationTargetException, IllegalAccessException, NoSuchMethodException { return (String) annotation.annotationType().getMethod("name").invoke(annotation); diff --git a/dumpAst2uml/src/main/jastadd/Navigation.jrag b/dumpAst2uml/src/main/jastadd/Navigation.jrag index 708d016d515fa4f95b46a7662adffb38c28067d6..eb85ca21852c8e7e31626b0ecacc2cb46e36de2f 100644 --- a/dumpAst2uml/src/main/jastadd/Navigation.jrag +++ b/dumpAst2uml/src/main/jastadd/Navigation.jrag @@ -8,4 +8,10 @@ aspect Navigation { // --- isDumpValueToken --- syn boolean DumpToken.isDumpValueToken() = false; eq DumpValueToken.isDumpValueToken() = true; -} \ No newline at end of file + + inh BuildConfig DumpNode.buildConfig(); + eq DumpAst.getChild().buildConfig() = getBuildConfig(); + + inh PrintConfig BuildConfig.printConfig(); + eq DumpAst.getChild().printConfig() = getPrintConfig(); +} diff --git a/dumpAst2uml/src/main/java/org/jastadd/dumpAst2uml/compiler/SimpleMain.java b/dumpAst2uml/src/main/java/org/jastadd/dumpAst2uml/compiler/SimpleMain.java index b0b6e02dffa8e7b4a9deb4e6f4ba6b1b25fa0eec..c85abf731723a81200b7e21f1843881570407818 100644 --- a/dumpAst2uml/src/main/java/org/jastadd/dumpAst2uml/compiler/SimpleMain.java +++ b/dumpAst2uml/src/main/java/org/jastadd/dumpAst2uml/compiler/SimpleMain.java @@ -17,6 +17,7 @@ import java.nio.file.Paths; import java.util.HashMap; import java.util.HashSet; import java.util.Set; +import java.util.regex.Pattern; import static org.jastadd.dumpAst2uml.compiler.SimpleMain.Kind.*; @@ -96,6 +97,7 @@ public class SimpleMain { } private static void printing() throws IOException { +// java.util.regex.Pattern p;p.pattern() System.setProperty("java.util.logging.manager", "org.apache.logging.log4j.jul.LogManager"); System.setProperty("mustache.debug", "true"); Grammar2Uml model = new Grammar2Uml(); @@ -113,6 +115,9 @@ public class SimpleMain { model.treeResolveAll(); // traverseInitial(model.toMustache()); Dumper.read(model.toMustache()) + .skinParam(SkinParamBooleanSetting.Monochrome, true) + .setDebug(true) + .ignoreTypes(".*Comment") .dumpAsSource(Paths.get("temp.plantuml")) .dumpAsSVG(Paths.get("temp.svg")); } @@ -135,8 +140,12 @@ public class SimpleMain { relation.setSource(new NormalRole(typeDeclA, "myB")); relation.setTarget(new UnnamedRole(typeDeclB)); grammar.addDeclaration(relation); + WhitespaceComment comment = new WhitespaceComment("// comment\\n"); + relation.addComment(comment); Dumper.read(program) + .ignoreTokens("Name") +// .ignoreTypes(".*Comment") .dumpAsSource(Paths.get("grammar.plantuml")) .dumpAsSVG(Paths.get("grammar.svg")); } diff --git a/dumpAst2uml/src/main/resources/dumpAst.mustache b/dumpAst2uml/src/main/resources/dumpAst.mustache index fba11038ec059e56a2ee633d789595a1b216064f..1a72eeec32b6c3f1867d50cd542de5ecec39e547 100644 --- a/dumpAst2uml/src/main/resources/dumpAst.mustache +++ b/dumpAst2uml/src/main/resources/dumpAst.mustache @@ -1,5 +1,11 @@ @startuml +{{#PrintConfig}} scale {{Scale}} +{{#Headers}} +{{Value}} +{{/Headers}} +{{/PrintConfig}} + {{#DumpNodes}} {{#isAstNode}} object "{{label}}" as {{name}} { @@ -39,4 +45,13 @@ object "{{label}}" as {{name}} { {{/isList}} {{/DumpRelations}} {{/DumpNodes}} +{{#BuildConfig}} +{{#Debug}} +legend right + %date() + dumpAst: {{version}} + plantuml: %version() +endlegend +{{/Debug}} +{{/BuildConfig}} @enduml diff --git a/dumpAst2uml/src/main/resources/dumpAst2umlVersion.properties b/dumpAst2uml/src/main/resources/dumpAst2umlVersion.properties deleted file mode 100644 index 9e9347a71a9cec4a1fb0a4118eae4835e757cb73..0000000000000000000000000000000000000000 --- a/dumpAst2uml/src/main/resources/dumpAst2umlVersion.properties +++ /dev/null @@ -1,2 +0,0 @@ -#Sat Sep 05 18:50:58 CEST 2020 -version=0.2 diff --git a/dumpAst2uml/src/main/resources/dumpAstVersion.properties b/dumpAst2uml/src/main/resources/dumpAstVersion.properties new file mode 100644 index 0000000000000000000000000000000000000000..cc8ed0e4a564b093ec353594cbc9df7795c5a5aa --- /dev/null +++ b/dumpAst2uml/src/main/resources/dumpAstVersion.properties @@ -0,0 +1,2 @@ +#Sat Sep 05 23:54:45 CEST 2020 +version=0.3