diff --git a/dumpAst2uml/.gitignore b/dumpAst/.gitignore similarity index 100% rename from dumpAst2uml/.gitignore rename to dumpAst/.gitignore diff --git a/dumpAst2uml/build.gradle b/dumpAst/build.gradle similarity index 78% rename from dumpAst2uml/build.gradle rename to dumpAst/build.gradle index f4badc1b73afbdf0e044ccd8faf7b71943c00022..0c1159eba4f504d5795d437d98f5e81853c7b4ea 100644 --- a/dumpAst2uml/build.gradle +++ b/dumpAst/build.gradle @@ -4,7 +4,7 @@ apply plugin: 'idea' sourceCompatibility = 1.8 -mainClassName = 'org.jastadd.dumpAst2uml.compiler.Compiler' +mainClassName = 'de.tudresden.inf.st.jastadd.dumpAst.compiler.Compiler' repositories { jcenter() @@ -82,9 +82,9 @@ task relast(type: JavaExec) { doFirst { delete "src/gen/jastadd/*.ast" - delete "src/gen/jastadd/DumpAst2.jadd" - delete "src/gen/jastadd/DumpAstlRefResolver.jadd" - delete "src/gen/jastadd/DumpAstlResolverStubs.jrag" + delete "src/gen/jastadd/DumpAst.jadd" + delete "src/gen/jastadd/DumpAstRefResolver.jadd" + delete "src/gen/jastadd/DumpAstResolverStubs.jrag" mkdir "src/gen/jastadd/" } @@ -97,12 +97,12 @@ task relast(type: JavaExec) { "--useJastAddNames", "--file", "--resolverHelper", - "--grammarName=./src/gen/jastadd/DumpAst2" + "--grammarName=./src/gen/jastadd/DumpAst" ] inputs.files(file("../libs/relast.jar"), dumpAstGrammar) - outputs.files(file("./src/gen/jastadd/DumpAst2.ast"), + outputs.files(file("./src/gen/jastadd/DumpAst.ast"), file("./src/gen/jastadd/DumpAst.jadd"), file("./src/gen/jastadd/DumpAstRefResolver.jadd"), file('./src/gen/jastadd/DumpAstResolverStubs.jrag')) @@ -112,28 +112,28 @@ jastadd { configureModuleBuild() modules { //noinspection GroovyAssignabilityCheck - module("DumpAst2Uml") { + module("DumpAst") { java { basedir ".." - include "dumpAst2uml/src/main/**/*.java" - include "dumpAst2uml/src/gen/**/*.java" + include "dumpAst/src/main/**/*.java" + include "dumpAst/src/gen/**/*.java" } jastadd { basedir ".." - include "dumpAst2uml/src/main/jastadd/**/*.ast" - include "dumpAst2uml/src/main/jastadd/**/*.jadd" - include "dumpAst2uml/src/main/jastadd/**/*.jrag" - include "dumpAst2uml/src/gen/jastadd/**/*.ast" - include "dumpAst2uml/src/gen/jastadd/**/*.jadd" - include "dumpAst2uml/src/gen/jastadd/**/*.jrag" + include "dumpAst/src/main/jastadd/**/*.ast" + include "dumpAst/src/main/jastadd/**/*.jadd" + include "dumpAst/src/main/jastadd/**/*.jrag" + include "dumpAst/src/gen/jastadd/**/*.ast" + include "dumpAst/src/gen/jastadd/**/*.jadd" + include "dumpAst/src/gen/jastadd/**/*.jrag" } } } cleanGen.doFirst { - delete "src/gen/java/org" + delete "src/gen/java/de" delete "src/gen-res/BuildInfo.properties" } @@ -143,8 +143,8 @@ jastadd { } - module = "DumpAst2Uml" - astPackage = 'org.jastadd.dumpAst2uml.ast' + module = "DumpAst" + astPackage = 'de.tudresden.inf.st.jastadd.dumpAst.ast' genDir = 'src/gen/java' buildInfoDir = 'src/gen-res' jastaddOptions = ["--lineColumnNumbers", "--List=JastAddList", "--safeLazy", "--visitCheck=true", "--rewrite=cnta", "--cache=all"] diff --git a/dumpAst2uml/src/main/jastadd/DumpAst.relast b/dumpAst/src/main/jastadd/DumpAst.relast similarity index 83% rename from dumpAst2uml/src/main/jastadd/DumpAst.relast rename to dumpAst/src/main/jastadd/DumpAst.relast index 42c18fddf417756538ebd9739877556667c6fa74..e7acd5daa36734376fa0858fbd1cd026b857f152 100644 --- a/dumpAst2uml/src/main/jastadd/DumpAst.relast +++ b/dumpAst/src/main/jastadd/DumpAst.relast @@ -1,26 +1,22 @@ -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 ; - -abstract DumpChildNode ::= <Name> ; -DumpNormalChildNode : DumpChildNode ; -rel DumpNormalChildNode.DumpNode -> DumpNode ; -DumpListChildNode : DumpChildNode ::= InnerDumpNode* ; -//DumpOptChildNode : DumpChildNode ; -//rel DumpOptChildNode.DumpNode? -> DumpNode ; - -abstract DumpToken ::= <Name> ; -DumpReferenceToken : DumpToken ; -rel DumpReferenceToken.Value -> DumpNode ; -DumpValueToken : DumpToken ::= <Value:Object> ; - -abstract DumpRelation ::= <Name> <Bidirectional:boolean> ; -DumpNormalRelation : DumpRelation ; -rel DumpNormalRelation.DumpNode -> DumpNode ; -DumpListRelation : DumpRelation ::= InnerDumpNode* ; -//DumpOptRelation : DumpRelation ; -//rel DumpOptRelation.DumpNode? -> DumpNode ; +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 ; + +abstract DumpChildNode ::= <Name> ; +DumpNormalChildNode : DumpChildNode ; +rel DumpNormalChildNode.DumpNode -> DumpNode ; +DumpListChildNode : DumpChildNode ::= InnerDumpNode* ; + +abstract DumpToken ::= <Name> ; +DumpReferenceToken : DumpToken ; +rel DumpReferenceToken.Value -> DumpNode ; +DumpValueToken : DumpToken ::= <Value:Object> ; + +abstract DumpRelation ::= <Name> <Bidirectional:boolean> ; +DumpNormalRelation : DumpRelation ; +rel DumpNormalRelation.DumpNode -> DumpNode ; +DumpListRelation : DumpRelation ::= InnerDumpNode* ; diff --git a/dumpAst2uml/src/main/jastadd/Generation.jadd b/dumpAst/src/main/jastadd/Generation.jadd similarity index 91% rename from dumpAst2uml/src/main/jastadd/Generation.jadd rename to dumpAst/src/main/jastadd/Generation.jadd index 8cf0aba63e98680cdcfe0ad7d5a8ea8be4d45d45..692220c3af5bec01d169da9279466d8961f85438 100644 --- a/dumpAst2uml/src/main/jastadd/Generation.jadd +++ b/dumpAst/src/main/jastadd/Generation.jadd @@ -1,393 +1,424 @@ -aspect Generation { - public class Dumper { - public static DumpBuilder read(Object obj) { - 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 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) { - 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 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) { - throw new RuntimeException("Could not transform :(", e); - } catch (IllegalAccessException e) { - throw new RuntimeException("Could not transform :(", e); - } catch (NoSuchMethodException e) { - throw new RuntimeException("Could not transform :(", e); - } - } - return result; - } - public DumpBuilder dumpAsSource(java.nio.file.Path destination) throws java.io.IOException { - String content = build().toPlantUml(); - try (java.io.Writer writer = java.nio.file.Files.newBufferedWriter(destination)) { - writer.write(content); - } - return this; - } - public DumpBuilder dumpAsPNG(java.nio.file.Path destination) throws java.io.IOException { - 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 { - String content = build().toPlantUml(); - net.sourceforge.plantuml.SourceStringReader reader = new net.sourceforge.plantuml.SourceStringReader(content); - reader.outputImage(java.nio.file.Files.newOutputStream(destination), - new net.sourceforge.plantuml.FileFormatOption(net.sourceforge.plantuml.FileFormat.SVG)); - return this; - } - } - // --- transform --- (need to be a method, because it alters the AST while traversing the object structure) - // maybe return type is unncessary - protected DumpNode DumpAst.transform(TransformationTransferInformation tti, Object obj) - throws java.lang.reflect.InvocationTargetException, IllegalAccessException, NoSuchMethodException { - if (obj == null) { - return null; - } - 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()); - node.setName("node" + tti.transformed.size()); - tti.transformed.put(obj, node); - this.addDumpNode(node); - if (node.isAstNode()) { - // only caching node.analyseClass does not help, since we want to do this only once per class of a node - final ClassAnalysisResult car; - Class<?> clazz = obj.getClass(); - if (tti.classAnalysisResults.containsKey(clazz)) { - car = tti.classAnalysisResults.get(clazz); - } else { - car = node.analyzeClass(); - tti.classAnalysisResults.put(clazz, car); - } - // -- singleChild -- - for (java.lang.reflect.Method method : car.singleChildMethods) { - Object target = method.invoke(obj); - DumpNode targetNode = transform(tti, target); - if (target != null && targetNode != null) { - DumpNormalChildNode normalChild = new DumpNormalChildNode(); - normalChild.setName(car.names.get(method)); - normalChild.setDumpNode(targetNode); - node.addDumpChildNode(normalChild); - } - } - // -- listChild -- - for (java.lang.reflect.Method method : car.listChildMethods) { - Iterable<?> targetList = (Iterable<?>) method.invoke(obj); - DumpListChildNode listChild = new DumpListChildNode(); - listChild.setName(car.names.get(method)); - for (Object target : targetList) { - DumpNode targetNode = transform(tti, target); - if (target != null && targetNode != null) { - listChild.addInnerDumpNode(new InnerDumpNode(targetNode)); - } - } - node.addDumpChildNode(listChild); - } - // -- singleRelation -- - for (java.lang.reflect.Method method : car.singleRelationMethods) { - Object target = method.invoke(obj); - DumpNode targetNode = transform(tti, target); - if (target != null && targetNode != null) { - DumpNormalRelation normalRelation = new DumpNormalRelation(); - normalRelation.setName(car.names.get(method)); - normalRelation.setDumpNode(targetNode); - node.addDumpRelation(normalRelation); - } - } - // -- listRelation -- - for (java.lang.reflect.Method method : car.listRelationMethods) { - Iterable<?> targetList = (Iterable<?>) method.invoke(obj); - DumpListRelation listRelation = new DumpListRelation(); - listRelation.setName(car.names.get(method)); - for (Object target : targetList) { - DumpNode targetNode = transform(tti, target); - if (target != null && targetNode != null) { - listRelation.addInnerDumpNode(new InnerDumpNode(targetNode)); - } - } - node.addDumpRelation(listRelation); - } - // -- token -- - for (java.lang.reflect.Method method : car.tokenMethods) { - Object target = method.invoke(obj); - if (target != null) { - DumpNode targetNode = transform(tti, target); - DumpToken token = null; - if (targetNode != null && targetNode.isAstNode()) { - token = new DumpReferenceToken().setValue(targetNode); - } else { - 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); - } - } - } - } - return node; - } - - ClassAnalysisResult DumpNode.analyzeClass() - throws java.lang.reflect.InvocationTargetException, IllegalAccessException, NoSuchMethodException { - ClassAnalysisResult result = new ClassAnalysisResult(); - Class<?> clazz = getObject().getClass(); - for (java.lang.reflect.Method method : clazz.getMethods()) { - for (java.lang.annotation.Annotation annotation : method.getAnnotations()) { - String canonicalName = annotation.annotationType().getCanonicalName(); - if (canonicalName.startsWith(astNodeAnnotationPrefix())) { - String simpleName = annotation.annotationType().getSimpleName(); - switch (simpleName) { - case "Child": - case "OptChild": - String singleChildName = invokeName(annotation); - if (!matches(buildConfig().childIgnorePattern(), singleChildName)) { - result.singleChildMethods.add(method); - result.names.put(method, singleChildName); - } - break; - case "ListChild": - String listChildName = invokeName(annotation); - if (!matches(buildConfig().childIgnorePattern(), listChildName)) { - result.listChildMethods.add(method); - result.names.put(method, listChildName); - } - break; - case "Token": - // heuristic for relations - String tokenName = invokeName(annotation); - if (tokenName.startsWith("_impl_")) { - String relationName = tokenName.substring(6); - try { - java.lang.reflect.Method relationMethod = clazz.getMethod("get" + relationName); - // normal get + token-name -> singleRelation - 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 - } - // try list-relation next - try { - java.lang.reflect.Method relationMethod = clazz.getMethod("get" + relationName + "List"); - // normal get + token-name + "List" -> listRelation - 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 - } - } - if (!matches(buildConfig().tokenIgnorePattern(), tokenName)) { - result.tokenMethods.add(method); - result.names.put(method, tokenName); - } - break; - } - } - } - } - 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); - } - - syn boolean DumpNode.isAstNode() { - Class<?> clazz = getObject().getClass(); - for (java.lang.reflect.Constructor<?> constructor : clazz.getConstructors()) { - for (java.lang.annotation.Annotation annotation : constructor.getAnnotations()) { - if (annotation.annotationType().getCanonicalName().startsWith(astNodeAnnotationPrefix()) && - annotation.annotationType().getSimpleName().equals("Constructor")) { - return true; - } - } - } - return false; - } - - inh String DumpNode.astNodeAnnotationPrefix(); - eq DumpAst.getDumpNode().astNodeAnnotationPrefix() = getPackageName() + ".ASTNodeAnnotation"; - - class TransformationTransferInformation { - java.util.Map<Object, DumpNode> transformed = new java.util.HashMap<>(); - java.util.Map<Class<?>, ClassAnalysisResult> classAnalysisResults = new java.util.HashMap<>(); - } - - class ClassAnalysisResult { - java.util.List<java.lang.reflect.Method> singleChildMethods = new java.util.ArrayList<>(); - java.util.List<java.lang.reflect.Method> listChildMethods = new java.util.ArrayList<>(); - java.util.List<java.lang.reflect.Method> singleRelationMethods = new java.util.ArrayList<>(); - java.util.List<java.lang.reflect.Method> listRelationMethods = new java.util.ArrayList<>(); - java.util.List<java.lang.reflect.Method> tokenMethods = new java.util.ArrayList<>(); - java.util.Map<java.lang.reflect.Method, String> names = new java.util.HashMap<>(); - } - - syn String DumpAst.toPlantUml() { - StringBuilder sb = new StringBuilder(); - com.github.mustachejava.reflect.ReflectionObjectHandler roh = new com.github.mustachejava.reflect.ReflectionObjectHandler() { - @Override - public com.github.mustachejava.Binding createBinding(String name, final com.github.mustachejava.TemplateContext tc, com.github.mustachejava.Code code) { - return new com.github.mustachejava.reflect.GuardedBinding(this, name, tc, code) { - @Override - protected synchronized com.github.mustachejava.util.Wrapper getWrapper(String name, java.util.List<Object> scopes) { - com.github.mustachejava.util.Wrapper wrapper = super.getWrapper(name, scopes); - if (wrapper instanceof com.github.mustachejava.reflect.MissingWrapper) { - throw new com.github.mustachejava.MustacheException(name + " not found in " + tc); - } - return wrapper; - } - }; - } - }; - com.github.mustachejava.DefaultMustacheFactory mf = new com.github.mustachejava.DefaultMustacheFactory(); - mf.setObjectHandler(roh); - com.github.mustachejava.Mustache m = mf.compile("dumpAst.mustache"); - m.execute(new java.io.PrintWriter(new AppendableWriter(sb)), this); - return sb.toString(); - } - - public class AppendableWriter extends java.io.Writer { - private final StringBuilder sb; - - public AppendableWriter(StringBuilder sb) { - this.sb = sb; - } - - @Override - public void write(char[] chars, int off, int len) { - sb.append(chars, off, len); - } - - @Override - public void write(String str) { - sb.append(str); - } - - @Override - public void flush() {} - - @Override - public void close() {} - } -} \ No newline at end of file +aspect GenerationFrontend { + public class Dumper { + public static DumpBuilder read(Object obj) { + 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 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) { + 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 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) { + throw new RuntimeException("Could not transform :(", e); + } catch (IllegalAccessException e) { + throw new RuntimeException("Could not transform :(", e); + } catch (NoSuchMethodException e) { + throw new RuntimeException("Could not transform :(", e); + } + } + return result; + } + public DumpBuilder dumpAsSource(java.nio.file.Path destination) throws java.io.IOException { + String content = build().toPlantUml(); + try (java.io.Writer writer = java.nio.file.Files.newBufferedWriter(destination)) { + writer.write(content); + } + return this; + } + public DumpBuilder dumpAsPNG(java.nio.file.Path destination) throws java.io.IOException { + 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 { + String content = build().toPlantUml(); + net.sourceforge.plantuml.SourceStringReader reader = new net.sourceforge.plantuml.SourceStringReader(content); + reader.outputImage(java.nio.file.Files.newOutputStream(destination), + new net.sourceforge.plantuml.FileFormatOption(net.sourceforge.plantuml.FileFormat.SVG)); + return this; + } + } +} + +aspect GenerationBackend { + // --- transform --- (need to be a method, because it alters the AST while traversing the object structure) + // maybe return type is unncessary + protected DumpNode DumpAst.transform(TransformationTransferInformation tti, Object obj) + throws java.lang.reflect.InvocationTargetException, IllegalAccessException, NoSuchMethodException { + if (obj == null) { + return null; + } + 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()); + node.setName("node" + tti.transformed.size()); + tti.transformed.put(obj, node); + this.addDumpNode(node); + if (node.isAstNode()) { + // only caching node.analyseClass does not help, since we want to do this only once per class of a node + final ClassAnalysisResult car; + Class<?> clazz = obj.getClass(); + if (tti.classAnalysisResults.containsKey(clazz)) { + car = tti.classAnalysisResults.get(clazz); + } else { + car = node.analyzeClass(); + tti.classAnalysisResults.put(clazz, car); + } + // -- singleChild -- + for (java.lang.reflect.Method method : car.singleChildMethods) { + Object target = method.invoke(obj); + DumpNode targetNode = transform(tti, target); + if (target != null && targetNode != null) { + DumpNormalChildNode normalChild = new DumpNormalChildNode(); + normalChild.setName(car.names.get(method)); + normalChild.setDumpNode(targetNode); + node.addDumpChildNode(normalChild); + } + } + // -- listChild -- + for (java.lang.reflect.Method method : car.listChildMethods) { + Iterable<?> targetList = (Iterable<?>) method.invoke(obj); + DumpListChildNode listChild = new DumpListChildNode(); + listChild.setName(car.names.get(method)); + for (Object target : targetList) { + DumpNode targetNode = transform(tti, target); + if (target != null && targetNode != null) { + listChild.addInnerDumpNode(new InnerDumpNode(targetNode)); + } + } + if (listChild.getNumInnerDumpNode() > 0) { + node.addDumpChildNode(listChild); + } + } + // -- singleRelation -- + for (java.lang.reflect.Method method : car.singleRelationMethods) { + Object target = method.invoke(obj); + DumpNode targetNode = transform(tti, target); + if (target != null && targetNode != null) { + DumpNormalRelation normalRelation = new DumpNormalRelation(); + normalRelation.setName(car.names.get(method)); + normalRelation.setDumpNode(targetNode); + node.addDumpRelation(normalRelation); + } + } + // -- listRelation -- + for (java.lang.reflect.Method method : car.listRelationMethods) { + Iterable<?> targetList = (Iterable<?>) method.invoke(obj); + DumpListRelation listRelation = new DumpListRelation(); + listRelation.setName(car.names.get(method)); + for (Object target : targetList) { + DumpNode targetNode = transform(tti, target); + if (target != null && targetNode != null) { + listRelation.addInnerDumpNode(new InnerDumpNode(targetNode)); + } + } + if (listRelation.getNumInnerDumpNode() > 0) { + node.addDumpRelation(listRelation); + } + } + // -- token -- + for (java.lang.reflect.Method method : car.tokenMethods) { + Object target = method.invoke(obj); + if (target != null) { + DumpNode targetNode = transform(tti, target); + DumpToken token = null; + if (targetNode != null && targetNode.isAstNode()) { + token = new DumpReferenceToken().setValue(targetNode); + } else { + 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); + } + } + } + } + return node; + } + + ClassAnalysisResult DumpNode.analyzeClass() + throws java.lang.reflect.InvocationTargetException, IllegalAccessException, NoSuchMethodException { + ClassAnalysisResult result = new ClassAnalysisResult(); + Class<?> clazz = getObject().getClass(); + for (java.lang.reflect.Method method : clazz.getMethods()) { + for (java.lang.annotation.Annotation annotation : method.getAnnotations()) { + String canonicalName = annotation.annotationType().getCanonicalName(); + if (canonicalName.startsWith(astNodeAnnotationPrefix())) { + String simpleName = annotation.annotationType().getSimpleName(); + switch (simpleName) { + case "Child": + String singleChildName = invokeName(annotation); + if (!matches(buildConfig().childIgnorePattern(), singleChildName)) { + result.singleChildMethods.add(method); + result.names.put(method, singleChildName); + } + break; + case "OptChild": + String optChildName = invokeName(annotation); + if (!matches(buildConfig().childIgnorePattern(), optChildName)) { + try { + // the annotated method is "get???Opt", but we want "get???" + java.lang.reflect.Method realGetter = clazz.getMethod("get" + optChildName); + result.singleChildMethods.add(realGetter); + result.names.put(realGetter, optChildName); + } catch (NoSuchMethodException e) { + System.err.println("Could not find getter for Opt-child " + optChildName + " in " + clazz.getName()); + throw new RuntimeException(e); + } + } + break; + case "ListChild": + String listChildName = invokeName(annotation); + if (!matches(buildConfig().childIgnorePattern(), listChildName)) { + result.listChildMethods.add(method); + result.names.put(method, listChildName); + } + break; + case "Token": + // heuristic for relations + String tokenName = invokeName(annotation); + if (tokenName.startsWith("_impl_")) { + String relationName = titleCase(tokenName.substring(6)); + try { + java.lang.reflect.Method relationMethod = clazz.getMethod("get" + relationName); + // normal get + token-name -> singleRelation + 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 + } + // try list-relation next + try { + java.lang.reflect.Method relationMethod = clazz.getMethod("get" + relationName + "List"); + // normal get + token-name + "List" -> listRelation + 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 + } + } + if (!matches(buildConfig().tokenIgnorePattern(), tokenName)) { + result.tokenMethods.add(method); + result.names.put(method, tokenName); + } + break; + case "Attribute": + // TODO. check for isNTA=true. then check for whether target is instance of iterable. if so, add to listChildren, otherwise to singelChild + // TODO. if isNTA=false, then handle attribute + break + } + } + } + } + return result; + } + + private String DumpNode.titleCase(String s) { + if (s.isEmpty()) { + return s; + } + return Character.toUpperCase(s.charAt(0)) + s.substring(1); + } + + 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); + } + + syn boolean DumpNode.isAstNode() { + Class<?> clazz = getObject().getClass(); + for (java.lang.reflect.Constructor<?> constructor : clazz.getConstructors()) { + for (java.lang.annotation.Annotation annotation : constructor.getAnnotations()) { + if (annotation.annotationType().getCanonicalName().startsWith(astNodeAnnotationPrefix()) && + annotation.annotationType().getSimpleName().equals("Constructor")) { + return true; + } + } + } + return false; + } + + inh String DumpNode.astNodeAnnotationPrefix(); + eq DumpAst.getDumpNode().astNodeAnnotationPrefix() = getPackageName() + ".ASTNodeAnnotation"; + + class TransformationTransferInformation { + java.util.Map<Object, DumpNode> transformed = new java.util.HashMap<>(); + java.util.Map<Class<?>, ClassAnalysisResult> classAnalysisResults = new java.util.HashMap<>(); + } + + class ClassAnalysisResult { + java.util.List<java.lang.reflect.Method> singleChildMethods = new java.util.ArrayList<>(); + java.util.List<java.lang.reflect.Method> listChildMethods = new java.util.ArrayList<>(); + java.util.List<java.lang.reflect.Method> singleRelationMethods = new java.util.ArrayList<>(); + java.util.List<java.lang.reflect.Method> listRelationMethods = new java.util.ArrayList<>(); + java.util.List<java.lang.reflect.Method> tokenMethods = new java.util.ArrayList<>(); + java.util.Map<java.lang.reflect.Method, String> names = new java.util.HashMap<>(); + } + + syn String DumpAst.toPlantUml() { + StringBuilder sb = new StringBuilder(); + com.github.mustachejava.reflect.ReflectionObjectHandler roh = new com.github.mustachejava.reflect.ReflectionObjectHandler() { + @Override + public com.github.mustachejava.Binding createBinding(String name, final com.github.mustachejava.TemplateContext tc, com.github.mustachejava.Code code) { + return new com.github.mustachejava.reflect.GuardedBinding(this, name, tc, code) { + @Override + protected synchronized com.github.mustachejava.util.Wrapper getWrapper(String name, java.util.List<Object> scopes) { + com.github.mustachejava.util.Wrapper wrapper = super.getWrapper(name, scopes); + if (wrapper instanceof com.github.mustachejava.reflect.MissingWrapper) { + throw new com.github.mustachejava.MustacheException(name + " not found in " + tc); + } + return wrapper; + } + }; + } + }; + com.github.mustachejava.DefaultMustacheFactory mf = new com.github.mustachejava.DefaultMustacheFactory(); + mf.setObjectHandler(roh); + com.github.mustachejava.Mustache m = mf.compile("dumpAst.mustache"); + m.execute(new java.io.PrintWriter(new AppendableWriter(sb)), this); + return sb.toString(); + } + + public class AppendableWriter extends java.io.Writer { + private final StringBuilder sb; + + public AppendableWriter(StringBuilder sb) { + this.sb = sb; + } + + @Override + public void write(char[] chars, int off, int len) { + sb.append(chars, off, len); + } + + @Override + public void write(String str) { + sb.append(str); + } + + @Override + public void flush() {} + + @Override + public void close() {} + } +} diff --git a/dumpAst2uml/src/main/jastadd/Navigation.jrag b/dumpAst/src/main/jastadd/Navigation.jrag similarity index 74% rename from dumpAst2uml/src/main/jastadd/Navigation.jrag rename to dumpAst/src/main/jastadd/Navigation.jrag index eb85ca21852c8e7e31626b0ecacc2cb46e36de2f..a8adbd2246cb45d06fb74cf1f0ed8e260afc8f42 100644 --- a/dumpAst2uml/src/main/jastadd/Navigation.jrag +++ b/dumpAst/src/main/jastadd/Navigation.jrag @@ -1,17 +1,23 @@ -aspect Navigation { - // --- isList --- - syn boolean DumpChildNode.isList() = false; - eq DumpListChildNode.isList() = true; - syn boolean DumpRelation.isList() = false; - eq DumpListRelation.isList() = true; - - // --- isDumpValueToken --- - syn boolean DumpToken.isDumpValueToken() = false; - eq DumpValueToken.isDumpValueToken() = true; - - inh BuildConfig DumpNode.buildConfig(); - eq DumpAst.getChild().buildConfig() = getBuildConfig(); - - inh PrintConfig BuildConfig.printConfig(); - eq DumpAst.getChild().printConfig() = getPrintConfig(); -} +aspect Navigation { + // --- isList --- + syn boolean DumpChildNode.isList() = false; + eq DumpListChildNode.isList() = true; + syn boolean DumpRelation.isList() = false; + eq DumpListRelation.isList() = true; + + // --- isDumpValueToken --- + syn boolean DumpToken.isDumpValueToken() = false; + eq DumpValueToken.isDumpValueToken() = true; + + // --- asDumpValueToken --- + syn DumpValueToken DumpToken.asDumpValueToken() = null; + eq DumpValueToken.asDumpValueToken() = this; + + // --- buildConfig --- + inh BuildConfig DumpNode.buildConfig(); + eq DumpAst.getChild().buildConfig() = getBuildConfig(); + + // --- printConfig --- + inh PrintConfig BuildConfig.printConfig(); + eq DumpAst.getChild().printConfig() = getPrintConfig(); +} diff --git a/dumpAst2uml/src/main/jastadd/Printing.jrag b/dumpAst/src/main/jastadd/Printing.jrag similarity index 100% rename from dumpAst2uml/src/main/jastadd/Printing.jrag rename to dumpAst/src/main/jastadd/Printing.jrag diff --git a/dumpAst2uml/src/main/java/org/jastadd/dumpAst2uml/compiler/SimpleMain.java b/dumpAst/src/main/java/de/tudresden/inf/st/jastadd/dumpAst/SimpleMain.java similarity index 96% rename from dumpAst2uml/src/main/java/org/jastadd/dumpAst2uml/compiler/SimpleMain.java rename to dumpAst/src/main/java/de/tudresden/inf/st/jastadd/dumpAst/SimpleMain.java index c85abf731723a81200b7e21f1843881570407818..812d1a1881aa84804f3fe28fc4852de64ab48056 100644 --- a/dumpAst2uml/src/main/java/org/jastadd/dumpAst2uml/compiler/SimpleMain.java +++ b/dumpAst/src/main/java/de/tudresden/inf/st/jastadd/dumpAst/SimpleMain.java @@ -1,12 +1,13 @@ -package org.jastadd.dumpAst2uml.compiler; +package de.tudresden.inf.st.jastadd.dumpAst; import beaver.Parser; -import org.jastadd.dumpAst2uml.ast.*; -import org.jastadd.grammar2uml.ast.*; -import org.jastadd.grammar2uml.parser.Grammar2UmlParser; -import org.jastadd.grammar2uml.scanner.Grammar2UmlScanner; +import de.tudresden.inf.st.jastadd.dumpAst.ast.*; +import de.tudresden.inf.st.jastadd.grammar2uml.ast.*; +import de.tudresden.inf.st.jastadd.grammar2uml.parser.Grammar2UmlParser; +import de.tudresden.inf.st.jastadd.grammar2uml.scanner.Grammar2UmlScanner; -import java.io.*; +import java.io.BufferedReader; +import java.io.IOException; import java.lang.annotation.Annotation; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; @@ -14,12 +15,10 @@ import java.lang.reflect.Method; import java.nio.file.Files; import java.nio.file.Path; 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.*; +import static de.tudresden.inf.st.jastadd.dumpAst.SimpleMain.Kind.*; /** * Testing Relast2Uml without parser. diff --git a/dumpAst2uml/src/main/resources/dumpAst.mustache b/dumpAst/src/main/resources/dumpAst.mustache similarity index 95% rename from dumpAst2uml/src/main/resources/dumpAst.mustache rename to dumpAst/src/main/resources/dumpAst.mustache index 1a72eeec32b6c3f1867d50cd542de5ecec39e547..6b375b3706e4223af9143e095664265950d7ac2e 100644 --- a/dumpAst2uml/src/main/resources/dumpAst.mustache +++ b/dumpAst/src/main/resources/dumpAst.mustache @@ -1,57 +1,57 @@ -@startuml -{{#PrintConfig}} -scale {{Scale}} -{{#Headers}} -{{Value}} -{{/Headers}} -{{/PrintConfig}} - -{{#DumpNodes}} -{{#isAstNode}} -object "{{label}}" as {{name}} { -{{#DumpTokens}} -{{#isDumpValueToken}} - {{label}} = {{{Value}}} -{{/isDumpValueToken}} -{{/DumpTokens}} -} -{{/isAstNode}} -{{/DumpNodes}} - -{{#DumpNodes}} -{{#DumpTokens}} -{{^isDumpValueToken}} -{{outerNodeName}} ..> {{innerNodeName}} : {{label}} -{{/isDumpValueToken}} -{{/DumpTokens}} -{{#DumpChildNodes}} -{{#isList}} -{{#InnerDumpNodes}} -{{outerNodeName}} *-- {{innerNodeName}} : {{label}} -{{/InnerDumpNodes}} -{{/isList}} -{{^isList}} -{{outerNodeName}} *-- {{innerNodeName}} : {{label}} -{{/isList}} -{{/DumpChildNodes}} -{{#DumpRelations}} -{{#isList}} -{{#InnerDumpNodes}} -{{outerNodeName}} {{#Bidirectional}}<{{/Bidirectional}}--> {{innerNodeName}} : {{label}} -{{/InnerDumpNodes}} -{{/isList}} -{{^isList}} -{{outerNodeName}} {{#Bidirectional}}<{{/Bidirectional}}--> {{innerNodeName}} : {{label}} -{{/isList}} -{{/DumpRelations}} -{{/DumpNodes}} -{{#BuildConfig}} -{{#Debug}} -legend right - %date() - dumpAst: {{version}} - plantuml: %version() -endlegend -{{/Debug}} -{{/BuildConfig}} -@enduml +@startuml +{{#PrintConfig}} +scale {{Scale}} +{{#Headers}} +{{Value}} +{{/Headers}} +{{/PrintConfig}} + +{{#DumpNodes}} +{{#isAstNode}} +object "{{label}}" as {{name}} { +{{#DumpTokens}} +{{#isDumpValueToken}} + {{label}} = {{{Value}}} +{{/isDumpValueToken}} +{{/DumpTokens}} +} +{{/isAstNode}} +{{/DumpNodes}} + +{{#DumpNodes}} +{{#DumpTokens}} +{{^isDumpValueToken}} +{{outerNodeName}} ..> {{innerNodeName}} : {{label}} +{{/isDumpValueToken}} +{{/DumpTokens}} +{{#DumpChildNodes}} +{{#isList}} +{{#InnerDumpNodes}} +{{outerNodeName}} *-- {{innerNodeName}} : {{label}} +{{/InnerDumpNodes}} +{{/isList}} +{{^isList}} +{{outerNodeName}} *-- {{innerNodeName}} : {{label}} +{{/isList}} +{{/DumpChildNodes}} +{{#DumpRelations}} +{{#isList}} +{{#InnerDumpNodes}} +{{outerNodeName}} {{#Bidirectional}}<{{/Bidirectional}}--> {{innerNodeName}} : {{label}} +{{/InnerDumpNodes}} +{{/isList}} +{{^isList}} +{{outerNodeName}} {{#Bidirectional}}<{{/Bidirectional}}--> {{innerNodeName}} : {{label}} +{{/isList}} +{{/DumpRelations}} +{{/DumpNodes}} +{{#BuildConfig}} +{{#Debug}} +legend right + %date() + dumpAst: {{version}} + plantuml: %version() +endlegend +{{/Debug}} +{{/BuildConfig}} +@enduml diff --git a/dumpAst/src/main/resources/dumpAstVersion.properties b/dumpAst/src/main/resources/dumpAstVersion.properties new file mode 100644 index 0000000000000000000000000000000000000000..ea4c6af67e252847ff32363384187c7dccdd61f4 --- /dev/null +++ b/dumpAst/src/main/resources/dumpAstVersion.properties @@ -0,0 +1,2 @@ +#Fri Sep 11 23:37:05 CEST 2020 +version=0.3.1 diff --git a/dumpAst2uml/src/main/resources/dumpAstVersion.properties b/dumpAst2uml/src/main/resources/dumpAstVersion.properties deleted file mode 100644 index cc8ed0e4a564b093ec353594cbc9df7795c5a5aa..0000000000000000000000000000000000000000 --- a/dumpAst2uml/src/main/resources/dumpAstVersion.properties +++ /dev/null @@ -1,2 +0,0 @@ -#Sat Sep 05 23:54:45 CEST 2020 -version=0.3 diff --git a/grammar2uml/build.gradle b/grammar2uml/build.gradle index 578aaa1664d74c94a9d2adecb49cd5a78ae24370..d4e998bd3c5ee2c284c38da757f8e7f61d45383c 100644 --- a/grammar2uml/build.gradle +++ b/grammar2uml/build.gradle @@ -4,7 +4,7 @@ apply plugin: 'idea' sourceCompatibility = 1.8 -mainClassName = 'org.jastadd.grammar2uml.compiler.Compiler' +mainClassName = 'de.tudresden.inf.st.jastadd.grammar2uml.compiler.Compiler' repositories { jcenter() @@ -162,7 +162,7 @@ jastadd { } cleanGen.doFirst { - delete "src/gen/java/org" + delete "src/gen/java/de" delete "src/gen-res/BuildInfo.properties" } @@ -174,7 +174,7 @@ jastadd { module = "Grammar2Uml" - astPackage = 'org.jastadd.grammar2uml.ast' + astPackage = 'de.tudresden.inf.st.jastadd.grammar2uml.ast' parser.name = 'Grammar2UmlParser' @@ -182,8 +182,8 @@ jastadd { buildInfoDir = 'src/gen-res' - scanner.genDir = "src/gen/java/org/jastadd/grammar2uml/scanner" - parser.genDir = "src/gen/java/org/jastadd/grammar2uml/parser" + scanner.genDir = "src/gen/java/de/tudresden/inf/st/jastadd/grammar2uml/scanner" + parser.genDir = "src/gen/java/de/tudresden/inf/st/jastadd/grammar2uml/parser" jastaddOptions = ["--lineColumnNumbers", "--List=JastAddList", "--safeLazy", "--visitCheck=true", "--rewrite=cnta", "--cache=all"] } diff --git a/grammar2uml/src/main/jastadd/backend/Generation.jadd b/grammar2uml/src/main/jastadd/backend/Generation.jadd index 7001c2f290bd9d8dd5a132f3c1d248c941055747..45276bd4aa3744de020a75512bc9ef62e7e130fb 100644 --- a/grammar2uml/src/main/jastadd/backend/Generation.jadd +++ b/grammar2uml/src/main/jastadd/backend/Generation.jadd @@ -186,7 +186,30 @@ aspect AspectGeneration { com.github.mustachejava.DefaultMustacheFactory mf = new com.github.mustachejava.DefaultMustacheFactory(); // mf.setObjectHandler(roh); com.github.mustachejava.Mustache m = mf.compile("grammar2uml.mustache"); - m.execute(new java.io.PrintWriter(new org.jastadd.grammar2uml.compiler.AppendableWriter(sb)), this); + m.execute(new java.io.PrintWriter(new AppendableWriter(sb)), this); return sb.toString(); } + public class AppendableWriter extends java.io.Writer { + private final StringBuilder sb; + + public AppendableWriter(StringBuilder sb) { + this.sb = sb; + } + + @Override + public void write(char[] chars, int off, int len) { + sb.append(chars, off, len); + } + + @Override + public void write(String str) { + sb.append(str); + } + + @Override + public void flush() {} + + @Override + public void close() {} + } } diff --git a/grammar2uml/src/main/jastadd/parser/Preamble.parser b/grammar2uml/src/main/jastadd/parser/Preamble.parser index 6d8af56847d11654ce2e75845dd5a27c317d273d..b65eedbcac607a2495d8d514d48ba2eb7d415ed2 100644 --- a/grammar2uml/src/main/jastadd/parser/Preamble.parser +++ b/grammar2uml/src/main/jastadd/parser/Preamble.parser @@ -1,6 +1,6 @@ %header {: -package org.jastadd.grammar2uml.parser; -import org.jastadd.grammar2uml.ast.*; +package de.tudresden.inf.st.jastadd.grammar2uml.parser; +import de.tudresden.inf.st.jastadd.grammar2uml.ast.*; :}; %goal goal; diff --git a/grammar2uml/src/main/jastadd/scanner/Header.flex b/grammar2uml/src/main/jastadd/scanner/Header.flex index 5050ac3e25313c07411bc53ed2b49a3c66bc3c86..1fa63001776ed524713f182197ccb097127394a8 100644 --- a/grammar2uml/src/main/jastadd/scanner/Header.flex +++ b/grammar2uml/src/main/jastadd/scanner/Header.flex @@ -1,6 +1,6 @@ -package org.jastadd.grammar2uml.scanner; +package de.tudresden.inf.st.jastadd.grammar2uml.scanner; -import org.jastadd.grammar2uml.parser.Grammar2UmlParser.Terminals; +import de.tudresden.inf.st.jastadd.grammar2uml.parser.Grammar2UmlParser.Terminals; %% %public diff --git a/grammar2uml/src/main/java/org/jastadd/grammar2uml/compiler/Compiler.java b/grammar2uml/src/main/java/de/tudresden/inf/st/jastadd/grammar2uml/compiler/Compiler.java similarity index 93% rename from grammar2uml/src/main/java/org/jastadd/grammar2uml/compiler/Compiler.java rename to grammar2uml/src/main/java/de/tudresden/inf/st/jastadd/grammar2uml/compiler/Compiler.java index 7b96d64fcd8c3504071992af6062ab24ef312fd5..fcb3b16d4378d64052ea88df14d63d755c988b70 100644 --- a/grammar2uml/src/main/java/org/jastadd/grammar2uml/compiler/Compiler.java +++ b/grammar2uml/src/main/java/de/tudresden/inf/st/jastadd/grammar2uml/compiler/Compiler.java @@ -1,16 +1,16 @@ -package org.jastadd.grammar2uml.compiler; +package de.tudresden.inf.st.jastadd.grammar2uml.compiler; import beaver.Parser; import org.jastadd.option.BooleanOption; import org.jastadd.option.ValueOption; import org.jastadd.relast.compiler.AbstractCompiler; import org.jastadd.relast.compiler.CompilerException; -import org.jastadd.grammar2uml.ast.ErrorMessage; -import org.jastadd.grammar2uml.ast.GrammarFile; -import org.jastadd.grammar2uml.ast.Program; -import org.jastadd.grammar2uml.ast.Grammar2Uml; -import org.jastadd.grammar2uml.parser.Grammar2UmlParser; -import org.jastadd.grammar2uml.scanner.Grammar2UmlScanner; +import de.tudresden.inf.st.jastadd.grammar2uml.ast.ErrorMessage; +import de.tudresden.inf.st.jastadd.grammar2uml.ast.GrammarFile; +import de.tudresden.inf.st.jastadd.grammar2uml.ast.Program; +import de.tudresden.inf.st.jastadd.grammar2uml.ast.Grammar2Uml; +import de.tudresden.inf.st.jastadd.grammar2uml.parser.Grammar2UmlParser; +import de.tudresden.inf.st.jastadd.grammar2uml.scanner.Grammar2UmlScanner; import java.io.BufferedReader; import java.io.IOException; diff --git a/grammar2uml/src/main/java/org/jastadd/grammar2uml/compiler/SimpleMain.java b/grammar2uml/src/main/java/de/tudresden/inf/st/jastadd/grammar2uml/compiler/SimpleMain.java similarity index 89% rename from grammar2uml/src/main/java/org/jastadd/grammar2uml/compiler/SimpleMain.java rename to grammar2uml/src/main/java/de/tudresden/inf/st/jastadd/grammar2uml/compiler/SimpleMain.java index ff422991c640c7308c89a990c9baa71e031cd9e6..b8313b8bf15297cd1e2ab1844c02277ea5b8c1f3 100644 --- a/grammar2uml/src/main/java/org/jastadd/grammar2uml/compiler/SimpleMain.java +++ b/grammar2uml/src/main/java/de/tudresden/inf/st/jastadd/grammar2uml/compiler/SimpleMain.java @@ -1,9 +1,9 @@ -package org.jastadd.grammar2uml.compiler; +package de.tudresden.inf.st.jastadd.grammar2uml.compiler; import beaver.Parser; -import org.jastadd.grammar2uml.ast.*; -import org.jastadd.grammar2uml.parser.Grammar2UmlParser; -import org.jastadd.grammar2uml.scanner.Grammar2UmlScanner; +import de.tudresden.inf.st.jastadd.grammar2uml.ast.*; +import de.tudresden.inf.st.jastadd.grammar2uml.parser.Grammar2UmlParser; +import de.tudresden.inf.st.jastadd.grammar2uml.scanner.Grammar2UmlScanner; import java.io.BufferedReader; import java.io.IOException; diff --git a/grammar2uml/src/main/java/org/jastadd/grammar2uml/compiler/Utils.java b/grammar2uml/src/main/java/de/tudresden/inf/st/jastadd/grammar2uml/compiler/Utils.java similarity index 87% rename from grammar2uml/src/main/java/org/jastadd/grammar2uml/compiler/Utils.java rename to grammar2uml/src/main/java/de/tudresden/inf/st/jastadd/grammar2uml/compiler/Utils.java index e10374b3155f7a394cf44a32a1dc06315d455382..496daa3bbe6f1745e2b69c6b0b412e0b1b62d29b 100644 --- a/grammar2uml/src/main/java/org/jastadd/grammar2uml/compiler/Utils.java +++ b/grammar2uml/src/main/java/de/tudresden/inf/st/jastadd/grammar2uml/compiler/Utils.java @@ -1,4 +1,4 @@ -package org.jastadd.grammar2uml.compiler; +package de.tudresden.inf.st.jastadd.grammar2uml.compiler; import java.util.*; import java.util.function.Predicate; diff --git a/grammar2uml/src/main/java/org/jastadd/grammar2uml/compiler/AppendableWriter.java b/grammar2uml/src/main/java/org/jastadd/grammar2uml/compiler/AppendableWriter.java deleted file mode 100644 index 318f67fa93eccbdf25a30ced491e59115235f9da..0000000000000000000000000000000000000000 --- a/grammar2uml/src/main/java/org/jastadd/grammar2uml/compiler/AppendableWriter.java +++ /dev/null @@ -1,37 +0,0 @@ -package org.jastadd.grammar2uml.compiler; - -import java.io.IOException; -import java.io.Writer; - -/** - * Writer appending to a StringBuilder. - * - * @author rschoene - Initial contribution - */ -public class AppendableWriter extends Writer { - private final StringBuilder sb; - - public AppendableWriter(StringBuilder sb) { - this.sb = sb; - } - - @Override - public void write(char[] chars, int off, int len) throws IOException { - sb.append(chars, off, len); - } - - @Override - public void write(String str) throws IOException { - sb.append(str); - } - - @Override - public void flush() { - - } - - @Override - public void close() { - - } -} diff --git a/grammar2uml/src/test/java/org/jastadd/grammar2uml/test/CompilerTest.java b/grammar2uml/src/test/java/de/tudresden/inf/st/jastadd/grammar2uml/test/CompilerTest.java similarity index 92% rename from grammar2uml/src/test/java/org/jastadd/grammar2uml/test/CompilerTest.java rename to grammar2uml/src/test/java/de/tudresden/inf/st/jastadd/grammar2uml/test/CompilerTest.java index bf5af06acffe0e527a2f13e7890a6a8eee6bc683..a622034ad0cead21b95749243a0b18047ab55374 100644 --- a/grammar2uml/src/test/java/org/jastadd/grammar2uml/test/CompilerTest.java +++ b/grammar2uml/src/test/java/de/tudresden/inf/st/jastadd/grammar2uml/test/CompilerTest.java @@ -1,7 +1,7 @@ -package org.jastadd.grammar2uml.test; +package de.tudresden.inf.st.jastadd.grammar2uml.test; import org.jastadd.relast.compiler.CompilerException; -import org.jastadd.grammar2uml.compiler.Compiler; +import de.tudresden.inf.st.jastadd.grammar2uml.compiler.Compiler; import org.junit.jupiter.api.Test; import java.io.File; diff --git a/settings.gradle b/settings.gradle index 3278510c64b72f36f4d307f08eea5f134a2d99d9..5f2b84d4f102e477be734afb63cc5dd074c20dfe 100644 --- a/settings.gradle +++ b/settings.gradle @@ -2,4 +2,5 @@ rootProject.name = 'relast2uml' include 'relast.preprocessor' include 'grammar2uml' -include 'dumpAst2uml' +include 'dumpAst' +include 'testDumper' diff --git a/testDumper/.gitignore b/testDumper/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..87b4cdd3d7c6a41502ca98703abeeb69a1d536fb --- /dev/null +++ b/testDumper/.gitignore @@ -0,0 +1,5 @@ +build +src/gen-res/ +src/gen/ +out/ +*.class diff --git a/testDumper/build.gradle b/testDumper/build.gradle new file mode 100644 index 0000000000000000000000000000000000000000..9980fef2d08a4422fc26a598b495b1dc16d18ce1 --- /dev/null +++ b/testDumper/build.gradle @@ -0,0 +1,110 @@ +apply plugin: 'jastadd' +apply plugin: 'idea' + +repositories { + mavenCentral() +} + +buildscript { + repositories.jcenter() + dependencies { + classpath 'org.jastadd:jastaddgradle:1.13.3' + } +} + +dependencies { + runtime group: 'org.jastadd', name: 'jastadd', version: '2.3.4' + + testImplementation project(':dumpAst') + testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.4.0' + testRuntimeOnly group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.4.0' + testImplementation group: 'org.assertj', name: 'assertj-core', version: '3.12.1' +} + +File genSrc = file("src/gen/java") +sourceSets.main.java.srcDir genSrc +idea.module.generatedSourceDirs += genSrc + +test { + useJUnitPlatform() + + maxHeapSize = '1G' +} + +File testingGrammar = file('./src/main/jastadd/testDumper.relast') + +task relast(type: JavaExec) { + group = 'Build' + main = "-jar" + + doFirst { + delete "src/gen/jastadd/*.ast" + delete "src/gen/jastadd/testDumper.jadd" + delete "src/gen/jastadd/testDumperRefResolver.jadd" + delete "src/gen/jastadd/testDumperResolverStubs.jrag" + mkdir "src/gen/jastadd/" + } + + args = [ + "../libs/relast.jar", + testingGrammar, +// "./src/main/jastadd/MustacheNodes.relast", + "--listClass=java.util.ArrayList", + "--jastAddList=JastAddList", + "--useJastAddNames", + "--file", + "--resolverHelper", + "--grammarName=./src/gen/jastadd/testDumper" + ] + + inputs.files(file("../libs/relast.jar"), + testingGrammar) + outputs.files(file("./src/gen/jastadd/testDumper.ast"), + file("./src/gen/jastadd/testDumper.jadd"), + file("./src/gen/jastadd/testDumperRefResolver.jadd"), + file('./src/gen/jastadd/testDumperResolverStubs.jrag')) +} + +jastadd { + configureModuleBuild() + modules { + //noinspection GroovyAssignabilityCheck + module("testDumper") { + + java { + basedir ".." + include "testDumper/src/main/**/*.java" + include "testDumper/src/gen/**/*.java" + } + + jastadd { + basedir ".." + include "testDumper/src/main/jastadd/**/*.ast" + include "testDumper/src/main/jastadd/**/*.jadd" + include "testDumper/src/main/jastadd/**/*.jrag" + include "testDumper/src/gen/jastadd/**/*.ast" + include "testDumper/src/gen/jastadd/**/*.jadd" + include "testDumper/src/gen/jastadd/**/*.jrag" + } + } + } + + cleanGen.doFirst { + delete "src/gen/java/org" + delete "src/gen-res/BuildInfo.properties" + } + + preprocessParser.doFirst { + + args += ["--no-beaver-symbol"] + + } + + module = "testDumper" + astPackage = 'org.jastadd.testDumper.ast' + genDir = 'src/gen/java' + buildInfoDir = 'src/gen-res' + jastaddOptions = ["--lineColumnNumbers", "--List=JastAddList", "--safeLazy", "--visitCheck=true", "--rewrite=cnta", "--cache=all"] +} + +generateAst.dependsOn relast diff --git a/testDumper/src/main/jastadd/testDumper.jrag b/testDumper/src/main/jastadd/testDumper.jrag new file mode 100644 index 0000000000000000000000000000000000000000..c27464bea4ff4586b04c35efdb77a20412b3e36e --- /dev/null +++ b/testDumper/src/main/jastadd/testDumper.jrag @@ -0,0 +1,29 @@ +aspect Grammar { + syn A C.getCalculated() { + A result = new A(); + result.setName("Calculated-" + getName()); + return result; + } + + syn JastAddList<B> C.getAlsoCalculatedList() { + JastAddList<B> result = new JastAddList<>(); + B inner = new B(); + inner.setName("AlsoCalculated-" + getName()); + result.add(inner); + return result; + } + + syn nta A C.getCalculatedNewSyntax() { + A result = new A(); + result.setName("Calculated-" + getName()); + return result; + } + + syn nta JastAddList<B> C.getAlsoCalculatedListNewSyntax() { + JastAddList<B> result = new JastAddList<>(); + B inner = new B(); + inner.setName("AlsoCalculated-" + getName()); + result.add(inner); + return result; + } +} diff --git a/testDumper/src/main/jastadd/testDumper.relast b/testDumper/src/main/jastadd/testDumper.relast new file mode 100644 index 0000000000000000000000000000000000000000..c830eb955d70f390aa57f6fddb1982611a73d58c --- /dev/null +++ b/testDumper/src/main/jastadd/testDumper.relast @@ -0,0 +1,12 @@ +Nameable ::= <Name> ; +Root : Nameable ::= A B* [C] ; +A : Nameable ::= B C ; +B : Nameable ::= ; +C : Nameable ::= <Unwanted:int> <RawReference:A> /Calculated:A/ /AlsoCalculated:B*/ ; + +rel B.oneA -> A ; +rel B.maybeC? -> C ; +rel B.manyA* -> A ; +rel C.biA1 <-> A.biC1 ; +rel C.biA2* <-> A.biC2 ; +rel C.biA3? <-> A.biC3 ; diff --git a/testDumper/src/test/java/de/tudresden/inf/st/jastadd/testDumper/TestSimple.java b/testDumper/src/test/java/de/tudresden/inf/st/jastadd/testDumper/TestSimple.java new file mode 100644 index 0000000000000000000000000000000000000000..de89eeb5a2a8be82fd354480163dadc5c7955cc8 --- /dev/null +++ b/testDumper/src/test/java/de/tudresden/inf/st/jastadd/testDumper/TestSimple.java @@ -0,0 +1,202 @@ +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.B; +import org.jastadd.testDumper.ast.C; +import org.jastadd.testDumper.ast.Root; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static de.tudresden.inf.st.jastadd.testDumper.TestUtils.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class TestSimple { + + @Test + public void testEmpty() { + Root root = new Root(); + root.setName(ROOT_NAME); + + List<DumpNode> nodes = TestUtils.dumpModel(root); + assertThat(nodes).flatExtracting(NAME_EXTRACTOR).containsExactly(ROOT_NAME); + DumpNode actualRoot = TestUtils.findByName(nodes, ROOT_NAME); + assertEquals(1, actualRoot.getNumDumpToken()); + assertEquals(0, actualRoot.getNumDumpChildNode()); + assertEquals(0, actualRoot.getNumDumpRelation()); + } + + @Test + public void testOneNormalChild() { + Root root = new Root(); + root.setName(ROOT_NAME); + A a = new A(); + a.setName(A_NAME); + root.setA(a); + + List<DumpNode> nodes = TestUtils.dumpModel(root); + assertEquals(2, nodes.size()); + assertThat(nodes).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(ROOT_NAME, A_NAME); + DumpNode actualRoot = TestUtils.findByName(nodes, ROOT_NAME); + assertEquals(1, actualRoot.getNumDumpToken()); + assertEquals(1, actualRoot.getNumDumpChildNode()); + assertEquals(0, actualRoot.getNumDumpRelation()); + List<DumpNode> children = TestUtils.normalChildren(actualRoot); + assertThat(children).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(A_NAME); + } + + @Test + public void testOneListChild() { + Root root = new Root(); + root.setName(ROOT_NAME); + B b = new B(); + b.setName(B_NAME); + root.addB(b); + + List<DumpNode> nodes = TestUtils.dumpModel(root); + assertEquals(2, nodes.size()); + assertThat(nodes).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(ROOT_NAME, B_NAME); + DumpNode actualRoot = TestUtils.findByName(nodes, ROOT_NAME); + assertEquals(1, actualRoot.getNumDumpToken()); + assertEquals(1, actualRoot.getNumDumpChildNode()); + assertEquals(0, actualRoot.getNumDumpRelation()); + List<DumpNode> children = TestUtils.listChildren(actualRoot); + assertThat(children).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(B_NAME); + } + + @Test + public void testOneOptChild() { + Root root = new Root(); + root.setName(ROOT_NAME); + C c = new C(); + c.setName(C_NAME); + root.setC(c); + + List<DumpNode> nodes = TestUtils.dumpModel(root); + assertEquals(2, nodes.size()); + assertThat(nodes).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(ROOT_NAME, C_NAME); + DumpNode actualRoot = TestUtils.findByName(nodes, ROOT_NAME); + assertEquals(1, actualRoot.getNumDumpToken()); + assertEquals(1, actualRoot.getNumDumpChildNode()); + assertEquals(0, actualRoot.getNumDumpRelation()); + List<DumpNode> children = TestUtils.normalChildren(actualRoot); + assertThat(children).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(C_NAME); + } + + @Test + public void testNormalUniRelation() { + Root root = relationSetup(); + root.getB(0).setOneA(root.getA()); + + List<DumpNode> nodes = TestUtils.dumpModel(root); + assertEquals(4, nodes.size()); + DumpNode actualB = TestUtils.findByName(nodes, B_NAME); + assertEquals(1, actualB.getNumDumpRelation()); + assertEquals("OneA", actualB.getDumpRelation(0).label()); + List<DumpNode> relationEnds = normalRelationChildren(actualB); + assertThat(relationEnds).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(A_NAME); + } + + @Test + public void testListUniRelation() { + Root root = relationSetup(); + root.getB(0).addManyA(root.getA()); + + List<DumpNode> nodes = TestUtils.dumpModel(root); + assertEquals(4, nodes.size()); + DumpNode actualB = TestUtils.findByName(nodes, B_NAME); + assertEquals(1, actualB.getNumDumpRelation()); + assertEquals("ManyA", actualB.getDumpRelation(0).label()); + List<DumpNode> relationEnds = listRelationChildren(actualB); + assertThat(relationEnds).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(A_NAME); + } + + @Test + public void testOptUniRelation() { + Root root = relationSetup(); + root.getB(0).setMaybeC(root.getC()); + + List<DumpNode> nodes = TestUtils.dumpModel(root); + assertEquals(4, nodes.size()); + DumpNode actualB = TestUtils.findByName(nodes, B_NAME); + assertEquals(1, actualB.getNumDumpRelation()); + assertEquals("MaybeC", actualB.getDumpRelation(0).label()); + List<DumpNode> relationEnds = normalRelationChildren(actualB); + assertThat(relationEnds).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(C_NAME); + } + + @Test + public void testNormalBiRelation() { + Root root = relationSetup(); + root.getC().setBiA1(root.getA()); + + List<DumpNode> nodes = TestUtils.dumpModel(root); + assertEquals(4, nodes.size()); + // bidirectional relations are currently not found, instead there will be two unidirectional ones + DumpNode actualA = TestUtils.findByName(nodes, A_NAME); + assertEquals(1, actualA.getNumDumpRelation()); + assertEquals("BiC1", actualA.getDumpRelation(0).label()); + List<DumpNode> relationEndsOfA = normalRelationChildren(actualA); + assertThat(relationEndsOfA).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(C_NAME); + DumpNode actualC = TestUtils.findByName(nodes, C_NAME); + assertEquals(1, actualC.getNumDumpRelation()); + assertEquals("BiA1", actualC.getDumpRelation(0).label()); + List<DumpNode> relationEndsOfC = normalRelationChildren(actualC); + assertThat(relationEndsOfC).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(A_NAME); + } + + @Test + public void testListBiRelation() { + Root root = relationSetup(); + root.getC().addBiA2(root.getA()); + + List<DumpNode> nodes = TestUtils.dumpModel(root); + assertEquals(4, nodes.size()); + // bidirectional relations are currently not found, instead there will be two unidirectional ones + DumpNode actualA = TestUtils.findByName(nodes, A_NAME); + assertEquals(1, actualA.getNumDumpRelation()); + assertEquals("BiC2", actualA.getDumpRelation(0).label()); + List<DumpNode> relationEndsOfA = normalRelationChildren(actualA); + assertThat(relationEndsOfA).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(C_NAME); + DumpNode actualC = TestUtils.findByName(nodes, C_NAME); + assertEquals(1, actualC.getNumDumpRelation()); + assertEquals("BiA2", actualC.getDumpRelation(0).label()); + List<DumpNode> relationEndsOfC = listRelationChildren(actualC); + assertThat(relationEndsOfC).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(A_NAME); + } + + @Test + public void testOptBiRelation() { + Root root = relationSetup(); + root.getC().setBiA3(root.getA()); + + List<DumpNode> nodes = TestUtils.dumpModel(root); + assertEquals(4, nodes.size()); + // bidirectional relations are currently not found, instead there will be two unidirectional ones + DumpNode actualA = TestUtils.findByName(nodes, A_NAME); + assertEquals(1, actualA.getNumDumpRelation()); + assertEquals("BiC3", actualA.getDumpRelation(0).label()); + List<DumpNode> relationEndsOfA = normalRelationChildren(actualA); + assertThat(relationEndsOfA).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(C_NAME); + DumpNode actualC = TestUtils.findByName(nodes, C_NAME); + assertEquals(1, actualC.getNumDumpRelation()); + assertEquals("BiA3", actualC.getDumpRelation(0).label()); + List<DumpNode> relationEndsOfC = normalRelationChildren(actualC); + assertThat(relationEndsOfC).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(A_NAME); + } + + @Test + public void testOneNormalReferenceToken() { + Root root = relationSetup(); + root.getC().setRawReference(root.getA()); + + List<DumpNode> nodes = TestUtils.dumpModel(root); + assertEquals(4, nodes.size()); + DumpNode actualC = TestUtils.findByName(nodes, C_NAME); + List<DumpNode> references = referenceTokens(actualC); + assertThat(references).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(A_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 new file mode 100644 index 0000000000000000000000000000000000000000..3bc908e0873254a7f76d27d7757d35e47bb4500a --- /dev/null +++ b/testDumper/src/test/java/de/tudresden/inf/st/jastadd/testDumper/TestUtils.java @@ -0,0 +1,144 @@ +package de.tudresden.inf.st.jastadd.testDumper; + +import de.tudresden.inf.st.jastadd.dumpAst.ast.*; +import org.assertj.core.api.Assertions; +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 java.util.ArrayList; +import java.util.List; +import java.util.Random; +import java.util.function.Consumer; +import java.util.function.Function; + +public class TestUtils { + static class ExposingDumpBuilder extends DumpBuilder { + + protected ExposingDumpBuilder(Object target) { + super(target); + } + + @Override + public DumpAst build() { + return super.build(); + } + } + + public static final Random rand = new Random(); + public static final String A_NAME = "A" + Integer.toHexString(rand.nextInt(0xFFFFFF)); + public static final String B_NAME = "B" + Integer.toHexString(rand.nextInt(0xFFFFFF)); + public static final String C_NAME = "C" + Integer.toHexString(rand.nextInt(0xFFFFFF)); + public static final String ROOT_NAME = "root"; + + public static final Function<DumpNode, String> NAME_EXTRACTOR = dp -> { +// List<String> result = new ArrayList<>(); + for (DumpToken dumpToken : dp.getDumpTokenList()) { + if (dumpToken.getName().equals("Name")) { + return dumpToken.asDumpValueToken().getValue().toString(); + } + } +// return result; + return null; + }; + + public static List<DumpNode> dumpModel(Object target) { + return dumpModel(target, db -> {}); + } + + public static List<DumpNode> dumpModel(Object target, Consumer<DumpBuilder> options) { + ExposingDumpBuilder builder = new ExposingDumpBuilder(target); + options.accept(builder); + DumpAst dumpAst = builder.build(); + List<DumpNode> result = new ArrayList<>(); + for (DumpNode dumpNode : dumpAst.getDumpNodeList()) { + if (dumpNode.isAstNode()) { + result.add(dumpNode); + } + } + return result; + } + + public static DumpNode findByName(List<DumpNode> nodes, String name) { + for (DumpNode node : nodes) { + if (name.equals(NAME_EXTRACTOR.apply(node))) { + return node; + } + } + Assertions.fail("No node found with name " + name); + // DummyCode to keep intellij silent + return new DumpNode(); + } + + public static List<DumpNode> normalChildren(DumpNode node) { + List<DumpNode> result = new ArrayList<>(); + for (DumpChildNode dumpChildNode : node.getDumpChildNodeList()) { + if (!dumpChildNode.isList()) { + // then it is a DumpNormalChildNode + result.add(((DumpNormalChildNode) dumpChildNode).getDumpNode()); + } + } + return result; + } + + public static List<DumpNode> listChildren(DumpNode node) { + List<DumpNode> result = new ArrayList<>(); + for (DumpChildNode dumpChildNode : node.getDumpChildNodeList()) { + if (dumpChildNode.isList()) { + // then it is a DumpListChildNode + ((DumpListChildNode) dumpChildNode).getInnerDumpNodeList().forEach(inner -> result.add(inner.getDumpNode())); + } + } + return result; + } + + public static List<DumpNode> normalRelationChildren(DumpNode node) { + List<DumpNode> result = new ArrayList<>(); + for (DumpRelation dumpRelation : node.getDumpRelationList()) { + if (!dumpRelation.isList()) { + // then it is a DumpNormalRelation + result.add(((DumpNormalRelation) dumpRelation).getDumpNode()); + } + } + return result; + } + + public static List<DumpNode> listRelationChildren(DumpNode node) { + List<DumpNode> result = new ArrayList<>(); + for (DumpRelation dumpRelation : node.getDumpRelationList()) { + if (dumpRelation.isList()) { + // then it is a DumpListRelation + ((DumpListRelation) dumpRelation).getInnerDumpNodeList().forEach(inner -> result.add(inner.getDumpNode())); + } + } + return result; + } + + public static List<DumpNode> referenceTokens(DumpNode node) { + List<DumpNode> result = new ArrayList<>(); + for (DumpToken dumpToken : node.getDumpTokenList()) { + if (!dumpToken.isDumpValueToken()) { + // then it is a DumpReferenceToken + result.add(((DumpReferenceToken) dumpToken).getValue()); + } + } + return result; + } + + public static Root relationSetup() { + Root root = new Root(); + root.setName(ROOT_NAME); + A a = new A(); + a.setName(A_NAME); + root.setA(a); + B b = new B(); + b.setName(B_NAME); + root.addB(b); + C c = new C(); + c.setName(C_NAME); + root.setC(c); + return root; + } + +}