diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index d07299bade9f50d37d4f4f7631238f18a08caeca..52200cdef6c313fa1c9122dccb9495960b49d7c0 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -45,6 +45,7 @@ publish_master: script: - "./gradlew publish" only: + - main - master ragdoc_build: @@ -55,7 +56,7 @@ ragdoc_build: needs: - build script: - - JAVA_FILES=$(find dumpAstWithPlantuml/src/ -name '*.java') + - JAVA_FILES=$(find dumpAst/src/ -name '*.java') - /ragdoc-builder/start-builder.sh -excludeGenerated -d data/ $JAVA_FILES artifacts: paths: @@ -77,6 +78,7 @@ ragdoc_view: only: - dev - main + - master artifacts: paths: - "pages/docs/ragdoc" @@ -96,3 +98,4 @@ pages: - public/ only: - main + - master diff --git a/README.md b/README.md new file mode 100644 index 0000000000000000000000000000000000000000..1b001cb8f7f67d2362aff1ea9f911948dbd1adde --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# DumpAst + +For documentation, please see https://jastadd.pages.st.inf.tu-dresden.de/relast2uml/ diff --git a/buildSrc/src/main/groovy/relast2uml.java-application-conventions.gradle b/buildSrc/src/main/groovy/relast2uml.java-application-conventions.gradle index a32686b88da9b321aa62cb18ddc319a12b9e483c..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 --- a/buildSrc/src/main/groovy/relast2uml.java-application-conventions.gradle +++ b/buildSrc/src/main/groovy/relast2uml.java-application-conventions.gradle @@ -1,4 +0,0 @@ -plugins { - id 'relast2uml.java-common-conventions' - id 'application' -} diff --git a/buildSrc/src/main/groovy/relast2uml.java-common-conventions.gradle b/buildSrc/src/main/groovy/relast2uml.java-common-conventions.gradle index 644c9bd4588c608573019e72d799a760d75a88dc..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 --- a/buildSrc/src/main/groovy/relast2uml.java-common-conventions.gradle +++ b/buildSrc/src/main/groovy/relast2uml.java-common-conventions.gradle @@ -1,19 +0,0 @@ -plugins { - id 'java' - id 'idea' - id 'com.github.ben-manes.versions' -} - -repositories { - mavenCentral() -} - -dependencies { - testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: "${jupiter_version}" - testImplementation group: 'org.assertj', name: 'assertj-core', version: '3.18.1' - testRuntimeOnly group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: "${jupiter_version}" -} - -tasks.named('test') { - useJUnitPlatform() -} diff --git a/buildSrc/src/main/groovy/relast2uml.java-jastadd-conventions.gradle b/buildSrc/src/main/groovy/relast2uml.java-jastadd-conventions.gradle index d773a87cb157572d2712c04e39b91ad4a4169c0b..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 --- a/buildSrc/src/main/groovy/relast2uml.java-jastadd-conventions.gradle +++ b/buildSrc/src/main/groovy/relast2uml.java-jastadd-conventions.gradle @@ -1,12 +0,0 @@ -plugins { - id 'relast2uml.java-common-conventions' - id 'java-library' -} - -dependencies { - api group: 'net.sf.beaver', name: 'beaver-rt', version: '0.9.11' -} - -File genSrc = file("src/gen/java") -sourceSets.main.java.srcDir genSrc -idea.module.generatedSourceDirs += genSrc diff --git a/buildSrc/src/main/groovy/relast2uml.java-publishing-conventions.gradle b/buildSrc/src/main/groovy/relast2uml.java-publishing-conventions.gradle index e3130c9008342d8dc2be8d80e2b29ba064cb31fa..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 --- a/buildSrc/src/main/groovy/relast2uml.java-publishing-conventions.gradle +++ b/buildSrc/src/main/groovy/relast2uml.java-publishing-conventions.gradle @@ -1,88 +0,0 @@ -plugins { - id 'java' - id 'idea' - id 'com.github.ben-manes.versions' - id 'maven-publish' -} - -repositories { - mavenCentral() -} - -dependencies { - testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: "${jupiter_version}" - testImplementation group: 'org.assertj', name: 'assertj-core', version: '3.18.1' - testRuntimeOnly group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: "${jupiter_version}" -} - -jar { - from { - configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } - } -} - -def versionFile = "src/main/resources/${project.getName()}Version.properties" -def oldProps = new Properties() - -try { - file(versionFile).withInputStream { stream -> oldProps.load(stream) } - version = oldProps['version'] -} catch (e) { - // this happens, if either the properties file is not present, or cannot be read from - throw new GradleException("File ${versionFile} not found or unreadable. Aborting.", e) -} - -task printVersion() { - doLast { - println(version) - } -} - -task newVersion() { - doFirst { - def props = new Properties() - props['version'] = value - props.store(file(versionFile).newWriter(), null) - } -} - -task setDevVersionForCI() { - doFirst { - def props = new Properties() - props['version'] = version + "-$System.env.CI_PIPELINE_IID" - props.store(file(versionFile).newWriter(), null) - } -} - -//679 -publishing { - publications { - maven(MavenPublication) { - groupId = 'de.tudresden.inf.st' -// from components.java - artifact("build/libs/${project.getName()}-${project.getVersion()}.jar") { - extension 'jar' - } - } - } - repositories { - maven { - url "https://git-st.inf.tu-dresden.de/api/v4/projects/679/packages/maven" - // Uncomment the following lines to publish manually (and comment out the other credentials section) -// credentials(HttpHeaderCredentials) { -// name = "Private-Token" -// value = gitLabPrivateToken // the variable resides in ~/.gradle/gradle.properties -// } - credentials(HttpHeaderCredentials) { - name = 'Job-Token' - value = System.getenv("CI_JOB_TOKEN") - } - authentication { - header(HttpHeaderAuthentication) - } - } - - } -} - -publish.dependsOn jar diff --git a/dumpAst/build.gradle b/dumpAst/build.gradle index b6793149b06ba134d59ad0886d55eda83be58b20..39e370c1df74809b6993201fd2082b35294e63b3 100644 --- a/dumpAst/build.gradle +++ b/dumpAst/build.gradle @@ -1,3 +1,4 @@ +// --- Buildscripts (must be at the top) --- buildscript { repositories.mavenLocal() repositories.mavenCentral() @@ -6,18 +7,35 @@ buildscript { } } +// --- Plugin definitions --- plugins { - id 'relast2uml.java-jastadd-conventions' - id 'relast2uml.java-publishing-conventions' + id 'java' + id 'idea' + id 'com.github.ben-manes.versions' + id 'java-library' + id 'maven-publish' } apply plugin: 'jastadd' +// --- Dependencies --- +repositories { + mavenCentral() +} + dependencies { - jastadd2 "org.jastadd:jastadd:2.3.4" - implementation group: 'com.github.spullara.mustache.java', name: 'compiler', version: "${mustache_java_version}" + jastadd2 "org.jastadd:jastadd:2.3.5" + implementation fileTree(include: ['plantuml.jar'], dir: '../libs') + api group: 'net.sf.beaver', name: 'beaver-rt', version: '0.9.11' + implementation group: 'com.github.spullara.mustache.java', name: 'compiler', version: "0.9.10" + implementation group: 'org.yaml', name: 'snakeyaml', version: '1.27' } +// --- Preprocessors --- +File genSrc = file("src/gen/java") +sourceSets.main.java.srcDir genSrc +idea.module.generatedSourceDirs += genSrc + File dumpAstGrammar = file('./src/main/jastadd/DumpAst.relast') File mustacheGrammar = file('./src/main/jastadd/mustache/Mustache.relast') @@ -54,6 +72,7 @@ task relast(type: JavaExec) { file('./src/gen/jastadd/DumpAstResolverStubs.jrag')) } +// --- JastAdd --- jastadd { configureModuleBuild() modules { @@ -96,4 +115,70 @@ jastadd { jastaddOptions = ["--lineColumnNumbers", "--List=JastAddList", "--safeLazy", "--visitCheck=true", "--rewrite=cnta", "--cache=all"] } +// --- Tests --- + +// --- Versioning and Publishing --- +group = 'de.tudresden.inf.st' + +jar { + from { + configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } + } +} + +def versionFile = "src/main/resources/${project.getName()}Version.properties" + +try { + def oldProps = new Properties() + file(versionFile).withInputStream { stream -> oldProps.load(stream) } + version = oldProps['version'] +} catch (e) { + // this happens, if either the properties file is not present, or cannot be read from + throw new GradleException("File ${versionFile} not found or unreadable. Aborting.", e) +} + +task printVersion() { + doLast { + println(version) + } +} + +task newVersion() { + doFirst { + def props = new Properties() + props['version'] = value + props.store(file(versionFile).newWriter(), null) + } +} + +task setDevVersionForCI() { + doFirst { + def props = new Properties() + props['version'] = version + "-$System.env.CI_PIPELINE_IID" + props.store(file(versionFile).newWriter(), null) + } +} + +publishing { + publications { + maven(MavenPublication) { + from components.java + } + } + repositories { + maven { + url "https://git-st.inf.tu-dresden.de/api/v4/projects/$System.env.CI_PROJECT_ID/packages/maven" + credentials(HttpHeaderCredentials) { + name = 'Job-Token' + value = System.getenv("CI_JOB_TOKEN") + } + authentication { + header(HttpHeaderAuthentication) + } + } + } +} + +// --- Task order --- generateAst.dependsOn relast +publish.dependsOn jar diff --git a/dumpAst/src/main/jastadd/DumpAst.relast b/dumpAst/src/main/jastadd/DumpAst.relast index 2fad59a121f72fb3629dda4bdbf7733a797ae1fa..3d76a4b0c5fb31fd428b970c8248da607f3126a8 100644 --- a/dumpAst/src/main/jastadd/DumpAst.relast +++ b/dumpAst/src/main/jastadd/DumpAst.relast @@ -1,18 +1,8 @@ DumpAst ::= DumpNode* <PackageName> BuildConfig PrintConfig ; -rel DumpAst.RootNode -> DumpNode ; - -BuildConfig ::= StyleInformation GlobalPatternCollection:PatternCollection - ExcludeTypePattern:TypePatternCollectionMapping* IncludeTypePattern:TypePatternCollectionMapping* - <TypeIgnorePattern> <IncludeEmptyString:boolean> <ExcludeNullNodes:boolean> <Debug:boolean>; -TypePatternCollectionMapping ::= <TypeRegex> PatternCollection ; -PatternCollection ::= <TokenPattern> <ChildPattern> <RelationPattern> <AttributePattern> <NonterminalAttributePattern> ; -StyleInformation ::= <NameMethod:StyleMethod> <BackgroundColorMethod:StyleMethod> <TextColorMethod:StyleMethod>; - -PrintConfig ::= <Scale:double> <Version> <OrderChildren:boolean> Header* ; -Header ::= <Value> ; +rel DumpAst.RootNode? -> DumpNode ; DumpNode ::= DumpChildNode* DumpToken* DumpRelation* - <Name> <Label> <BackgroundColor> <TextColor> <Object:Object> <Invisible:boolean> + <Name> <Label> <BackgroundColor> <TextColor> <Object:Object> <Invisible:boolean> <Computed:boolean> <ManualStereotypes> /InvisiblePath/ ; InnerDumpNode ; rel InnerDumpNode.DumpNode <-> DumpNode.ContainerOfInner ; @@ -35,7 +25,7 @@ DumpListRelation : DumpRelation ::= InnerDumpNode* ; // type of NTA InvisiblePath ::= InnerDumpNode* ; -ClassAnalysisResult ::= AnalysedMethod* ; +ClassAnalysisResult ::= ContainmentMethod:AnalysedMethod* OtherMethod:AnalysedMethod* ; abstract AnalysedMethod ::= <Method:java.lang.reflect.Method> <Name> ; abstract SingleChildMethod : AnalysedMethod ; @@ -52,3 +42,21 @@ ListRelationMethod : AnalysedMethod ; abstract TokenMethod : AnalysedMethod ; IntrinsicTokenMethod : TokenMethod ; AttributeMethod : TokenMethod ; + +BuildConfig ::= StyleInformation + GlobalPatternCollection:PatternCollection + ExcludeTypePattern:TypePatternCollectionMapping* + IncludeTypePattern:TypePatternCollectionMapping* + <TypeIgnorePattern> + <IncludeEmptyString:boolean> + <ExcludeNullNodes:boolean> + <Debug:boolean>; +TypePatternCollectionMapping ::= <TypeRegex> PatternCollection ; +PatternCollection ::= <TokenPattern> <ChildPattern> <RelationPattern> <AttributePattern> <NonterminalAttributePattern> ; +StyleInformation ::= <NameMethod:StyleMethod> <BackgroundColorMethod:StyleMethod> <TextColorMethod:StyleMethod> <StereotypeMethod:StyleMethod> <ComputedColor>; + +PrintConfig ::= Header* + <Scale:double> + <Version> + <OrderChildren:boolean> ; +Header ::= <Value> ; diff --git a/dumpAst/src/main/jastadd/GeneratedNavigation.jrag b/dumpAst/src/main/jastadd/GeneratedNavigation.jrag new file mode 100644 index 0000000000000000000000000000000000000000..be7166a650a2409f5b92c914c348ca1972a64a59 --- /dev/null +++ b/dumpAst/src/main/jastadd/GeneratedNavigation.jrag @@ -0,0 +1,224 @@ +aspect Navigation { + + /** Tests if TokenMethod is a IntrinsicTokenMethod. + * @return 'true' if this is a IntrinsicTokenMethod, otherwise 'false' + */ + syn boolean TokenMethod.isIntrinsicTokenMethod() = false; + eq IntrinsicTokenMethod.isIntrinsicTokenMethod() = true; + + /** Tests if TokenMethod is a AttributeMethod. + * @return 'true' if this is a AttributeMethod, otherwise 'false' + */ + syn boolean TokenMethod.isAttributeMethod() = false; + eq AttributeMethod.isAttributeMethod() = true; + + /** Tests if AnalysedMethod is a SingleChildMethod. + * @return 'true' if this is a SingleChildMethod, otherwise 'false' + */ + syn boolean AnalysedMethod.isSingleChildMethod() = false; + eq SingleChildMethod.isSingleChildMethod() = true; + + /** Tests if AnalysedMethod is a ListChildMethod. + * @return 'true' if this is a ListChildMethod, otherwise 'false' + */ + syn boolean AnalysedMethod.isListChildMethod() = false; + eq ListChildMethod.isListChildMethod() = true; + + /** Tests if AnalysedMethod is a SingleRelationMethod. + * @return 'true' if this is a SingleRelationMethod, otherwise 'false' + */ + syn boolean AnalysedMethod.isSingleRelationMethod() = false; + eq SingleRelationMethod.isSingleRelationMethod() = true; + + /** Tests if AnalysedMethod is a ListRelationMethod. + * @return 'true' if this is a ListRelationMethod, otherwise 'false' + */ + syn boolean AnalysedMethod.isListRelationMethod() = false; + eq ListRelationMethod.isListRelationMethod() = true; + + /** Tests if AnalysedMethod is a TokenMethod. + * @return 'true' if this is a TokenMethod, otherwise 'false' + */ + syn boolean AnalysedMethod.isTokenMethod() = false; + eq TokenMethod.isTokenMethod() = true; + + /** Tests if DumpRelation is a DumpNormalRelation. + * @return 'true' if this is a DumpNormalRelation, otherwise 'false' + */ + syn boolean DumpRelation.isDumpNormalRelation() = false; + eq DumpNormalRelation.isDumpNormalRelation() = true; + + /** Tests if DumpRelation is a DumpListRelation. + * @return 'true' if this is a DumpListRelation, otherwise 'false' + */ + syn boolean DumpRelation.isDumpListRelation() = false; + eq DumpListRelation.isDumpListRelation() = true; + + /** Tests if DumpChildNode is a DumpNormalChildNode. + * @return 'true' if this is a DumpNormalChildNode, otherwise 'false' + */ + syn boolean DumpChildNode.isDumpNormalChildNode() = false; + eq DumpNormalChildNode.isDumpNormalChildNode() = true; + + /** Tests if DumpChildNode is a DumpListChildNode. + * @return 'true' if this is a DumpListChildNode, otherwise 'false' + */ + syn boolean DumpChildNode.isDumpListChildNode() = false; + eq DumpListChildNode.isDumpListChildNode() = true; + + /** Tests if ListChildMethod is a NormalListChildMethod. + * @return 'true' if this is a NormalListChildMethod, otherwise 'false' + */ + syn boolean ListChildMethod.isNormalListChildMethod() = false; + eq NormalListChildMethod.isNormalListChildMethod() = true; + + /** Tests if ListChildMethod is a NTAListChildMethod. + * @return 'true' if this is a NTAListChildMethod, otherwise 'false' + */ + syn boolean ListChildMethod.isNTAListChildMethod() = false; + eq NTAListChildMethod.isNTAListChildMethod() = true; + + /** Tests if SingleChildMethod is a NormalSingleChildMethod. + * @return 'true' if this is a NormalSingleChildMethod, otherwise 'false' + */ + syn boolean SingleChildMethod.isNormalSingleChildMethod() = false; + eq NormalSingleChildMethod.isNormalSingleChildMethod() = true; + + /** Tests if SingleChildMethod is a NTASingleChildMethod. + * @return 'true' if this is a NTASingleChildMethod, otherwise 'false' + */ + syn boolean SingleChildMethod.isNTASingleChildMethod() = false; + eq NTASingleChildMethod.isNTASingleChildMethod() = true; + + /** Tests if DumpToken is a DumpReferenceToken. + * @return 'true' if this is a DumpReferenceToken, otherwise 'false' + */ + syn boolean DumpToken.isDumpReferenceToken() = false; + eq DumpReferenceToken.isDumpReferenceToken() = true; + + /** Tests if DumpToken is a DumpValueToken. + * @return 'true' if this is a DumpValueToken, otherwise 'false' + */ + syn boolean DumpToken.isDumpValueToken() = false; + eq DumpValueToken.isDumpValueToken() = true; + + /** casts a TokenMethod into a IntrinsicTokenMethod if possible. + * @return 'this' cast to a IntrinsicTokenMethod or 'null' + */ + syn IntrinsicTokenMethod TokenMethod.asIntrinsicTokenMethod(); + eq TokenMethod.asIntrinsicTokenMethod() = null; + eq IntrinsicTokenMethod.asIntrinsicTokenMethod() = this; + + /** casts a TokenMethod into a AttributeMethod if possible. + * @return 'this' cast to a AttributeMethod or 'null' + */ + syn AttributeMethod TokenMethod.asAttributeMethod(); + eq TokenMethod.asAttributeMethod() = null; + eq AttributeMethod.asAttributeMethod() = this; + + /** casts a AnalysedMethod into a SingleChildMethod if possible. + * @return 'this' cast to a SingleChildMethod or 'null' + */ + syn SingleChildMethod AnalysedMethod.asSingleChildMethod(); + eq AnalysedMethod.asSingleChildMethod() = null; + eq SingleChildMethod.asSingleChildMethod() = this; + + /** casts a AnalysedMethod into a ListChildMethod if possible. + * @return 'this' cast to a ListChildMethod or 'null' + */ + syn ListChildMethod AnalysedMethod.asListChildMethod(); + eq AnalysedMethod.asListChildMethod() = null; + eq ListChildMethod.asListChildMethod() = this; + + /** casts a AnalysedMethod into a SingleRelationMethod if possible. + * @return 'this' cast to a SingleRelationMethod or 'null' + */ + syn SingleRelationMethod AnalysedMethod.asSingleRelationMethod(); + eq AnalysedMethod.asSingleRelationMethod() = null; + eq SingleRelationMethod.asSingleRelationMethod() = this; + + /** casts a AnalysedMethod into a ListRelationMethod if possible. + * @return 'this' cast to a ListRelationMethod or 'null' + */ + syn ListRelationMethod AnalysedMethod.asListRelationMethod(); + eq AnalysedMethod.asListRelationMethod() = null; + eq ListRelationMethod.asListRelationMethod() = this; + + /** casts a AnalysedMethod into a TokenMethod if possible. + * @return 'this' cast to a TokenMethod or 'null' + */ + syn TokenMethod AnalysedMethod.asTokenMethod(); + eq AnalysedMethod.asTokenMethod() = null; + eq TokenMethod.asTokenMethod() = this; + + /** casts a DumpRelation into a DumpNormalRelation if possible. + * @return 'this' cast to a DumpNormalRelation or 'null' + */ + syn DumpNormalRelation DumpRelation.asDumpNormalRelation(); + eq DumpRelation.asDumpNormalRelation() = null; + eq DumpNormalRelation.asDumpNormalRelation() = this; + + /** casts a DumpRelation into a DumpListRelation if possible. + * @return 'this' cast to a DumpListRelation or 'null' + */ + syn DumpListRelation DumpRelation.asDumpListRelation(); + eq DumpRelation.asDumpListRelation() = null; + eq DumpListRelation.asDumpListRelation() = this; + + /** casts a DumpChildNode into a DumpNormalChildNode if possible. + * @return 'this' cast to a DumpNormalChildNode or 'null' + */ + syn DumpNormalChildNode DumpChildNode.asDumpNormalChildNode(); + eq DumpChildNode.asDumpNormalChildNode() = null; + eq DumpNormalChildNode.asDumpNormalChildNode() = this; + + /** casts a DumpChildNode into a DumpListChildNode if possible. + * @return 'this' cast to a DumpListChildNode or 'null' + */ + syn DumpListChildNode DumpChildNode.asDumpListChildNode(); + eq DumpChildNode.asDumpListChildNode() = null; + eq DumpListChildNode.asDumpListChildNode() = this; + + /** casts a ListChildMethod into a NormalListChildMethod if possible. + * @return 'this' cast to a NormalListChildMethod or 'null' + */ + syn NormalListChildMethod ListChildMethod.asNormalListChildMethod(); + eq ListChildMethod.asNormalListChildMethod() = null; + eq NormalListChildMethod.asNormalListChildMethod() = this; + + /** casts a ListChildMethod into a NTAListChildMethod if possible. + * @return 'this' cast to a NTAListChildMethod or 'null' + */ + syn NTAListChildMethod ListChildMethod.asNTAListChildMethod(); + eq ListChildMethod.asNTAListChildMethod() = null; + eq NTAListChildMethod.asNTAListChildMethod() = this; + + /** casts a SingleChildMethod into a NormalSingleChildMethod if possible. + * @return 'this' cast to a NormalSingleChildMethod or 'null' + */ + syn NormalSingleChildMethod SingleChildMethod.asNormalSingleChildMethod(); + eq SingleChildMethod.asNormalSingleChildMethod() = null; + eq NormalSingleChildMethod.asNormalSingleChildMethod() = this; + + /** casts a SingleChildMethod into a NTASingleChildMethod if possible. + * @return 'this' cast to a NTASingleChildMethod or 'null' + */ + syn NTASingleChildMethod SingleChildMethod.asNTASingleChildMethod(); + eq SingleChildMethod.asNTASingleChildMethod() = null; + eq NTASingleChildMethod.asNTASingleChildMethod() = this; + + /** casts a DumpToken into a DumpReferenceToken if possible. + * @return 'this' cast to a DumpReferenceToken or 'null' + */ + syn DumpReferenceToken DumpToken.asDumpReferenceToken(); + eq DumpToken.asDumpReferenceToken() = null; + eq DumpReferenceToken.asDumpReferenceToken() = this; + + /** casts a DumpToken into a DumpValueToken if possible. + * @return 'this' cast to a DumpValueToken or 'null' + */ + syn DumpValueToken DumpToken.asDumpValueToken(); + eq DumpToken.asDumpValueToken() = null; + eq DumpValueToken.asDumpValueToken() = this; + +} diff --git a/dumpAst/src/main/jastadd/GenerationBackend.jadd b/dumpAst/src/main/jastadd/GenerationBackend.jadd index beeb183de52a4adccbdb8413309b16c418b55511..be9aaff9334285645c3e4581838566d683879f5c 100644 --- a/dumpAst/src/main/jastadd/GenerationBackend.jadd +++ b/dumpAst/src/main/jastadd/GenerationBackend.jadd @@ -1,14 +1,62 @@ aspect GenerationBackend { class DumpAst { - private enum Source { - RELATION, INVISIBLE_PARENT, PARENT, ROOT + enum Source { + ROOT, NORMAL, RELATION + } + class TransformationOptions { + // todo should be read-only, i.e., copy-on-write + public Source source; + public boolean invisible; + public boolean allowNullObjects; + public boolean computed; + + public TransformationOptions asRelation() { + return fromSource(Source.RELATION, false); + } + + public TransformationOptions asRoot() { + return fromSource(Source.ROOT, false).allowNullObjectsOnce(); + } + + public TransformationOptions asNormal(boolean shouldBeInvisible) { + return fromSource(Source.NORMAL, shouldBeInvisible); + } + + public TransformationOptions computed(boolean computed) { + if (computed == this.computed) { + return this; + } + return new TransformationOptions(this.source, this.invisible, this.allowNullObjects, computed); + } + + public TransformationOptions allowNullObjectsOnce() { + return new TransformationOptions(this.source, this.invisible, !DumpAst.this.getBuildConfig().getExcludeNullNodes(), this.computed); + } + + private TransformationOptions fromSource(Source source, boolean shouldBeInvisible) { + return new TransformationOptions(source, this.invisible || shouldBeInvisible, this.computed); + } + + private TransformationOptions(Source source, boolean invisible, boolean computed) { + this(source, invisible, false, computed); + } + private TransformationOptions(Source source, boolean invisible, boolean allowNullObjects, boolean computed) { + this.source = source; + this.invisible = invisible; + this.allowNullObjects = allowNullObjects; + this.computed = computed; + } } } + private TransformationOptions DumpAst.options() { + return new TransformationOptions(Source.NORMAL, false, false); + } + // --- transform --- (need to be a method, because it alters the AST while traversing the object structure) protected DumpNode DumpAst.transform(TransformationTransferInformation tti, Object obj) throws java.lang.reflect.InvocationTargetException, IllegalAccessException, NoSuchMethodException { - DumpNode result = transform(tti, obj, Source.ROOT); + DumpNode result = transform(tti, obj, options().asRoot()); setRootNode(result); // post-process relationTargetsUnprocessed boolean someAreUnprocessed = true; @@ -17,21 +65,18 @@ aspect GenerationBackend { java.util.Map<DumpNode, Boolean> copy = new java.util.HashMap<>(tti.relationTargetsUnprocessed); for (java.util.Map.Entry<DumpNode, Boolean> entry : copy.entrySet()) { if (entry.getValue()) { - transform(tti, entry.getKey().getObject(), Source.ROOT); + transform(tti, entry.getKey().getObject(), options().asRoot()); someAreUnprocessed = true; } } } return result; } - protected DumpNode DumpAst.transform(TransformationTransferInformation tti, Object obj, Source source) - throws java.lang.reflect.InvocationTargetException, IllegalAccessException, NoSuchMethodException { - return transform(tti, obj, source, false); - } + protected DumpNode DumpAst.transform( - TransformationTransferInformation tti, Object obj, Source source, boolean allowNullObj) + TransformationTransferInformation tti, Object obj, TransformationOptions options) throws java.lang.reflect.InvocationTargetException, IllegalAccessException, NoSuchMethodException { - if (obj == null && !allowNullObj) { + if (obj == null && !options.allowNullObjects) { return null; } DumpNode node; @@ -44,7 +89,7 @@ aspect GenerationBackend { objClassName = obj.getClass().getSimpleName(); } if (node != null) { - if (source == Source.RELATION) { + if (options.source == Source.RELATION) { return node; } // either processing as parent, or later as root-node. so mark it as processed. @@ -52,7 +97,8 @@ aspect GenerationBackend { } else { node = new DumpNode(); node.setObject(obj); - node.setName("node" + tti.transformed.size()); + node.setName("node" + (tti.nodeCounter++)); + node.setComputed(options.computed); tti.transformed.put(obj, node); this.addDumpNode(node); } @@ -61,11 +107,11 @@ aspect GenerationBackend { } applyStyle(node); // do not process node further if coming from a relation - if (source == Source.RELATION) { + if (options.source == Source.RELATION) { tti.relationTargetsUnprocessed.put(node, true); return node; } - if (source == Source.INVISIBLE_PARENT || !isTypeEnabled(objClassName)) { + if (options.invisible || !isTypeEnabled(objClassName)) { node.setInvisible(true); } if (obj == null) { @@ -73,85 +119,121 @@ aspect GenerationBackend { return node; } final ClassAnalysisResult car = analyzeClass(obj.getClass()); - // -- singleChild -- - for (SingleChildMethod singleChildMethod : car.singleChildMethods()) { - Object target = singleChildMethod.getMethod().invoke(obj); - String childName = singleChildMethod.getName(); - DumpNode targetNode = transform(tti, target, nextSource(source, !isChildEnabled(objClassName, childName)), !getBuildConfig().getExcludeNullNodes()); - if (targetNode != null) { - DumpNormalChildNode normalChild = new DumpNormalChildNode(); - normalChild.setName(childName); - normalChild.setDumpNode(targetNode); - normalChild.setComputed(singleChildMethod.isNTASingleChildMethod()); - node.addDumpChildNode(normalChild); - } - } - // -- listChild -- - for (ListChildMethod listChildMethod : car.listChildMethods()) { - Iterable<?> targetList = (Iterable<?>) listChildMethod.getMethod().invoke(obj); - DumpListChildNode listChild = new DumpListChildNode(); - listChild.setComputed(listChildMethod.isNTAListChildMethod()); - String childName = listChildMethod.getName(); - boolean shouldBeInvisisble = !isChildEnabled(objClassName, childName); - listChild.setName(childName); - for (Object target : targetList) { - DumpNode targetNode = transform(tti, target, nextSource(source, shouldBeInvisisble)); - if (target != null && targetNode != null) { - listChild.addInnerDumpNode(new InnerDumpNode(targetNode)); + for (AnalysedMethod containmentMethod : car.getContainmentMethodList()) { + if (containmentMethod.isSingleChildMethod()) { + // -- singleChild -- + Object target = containmentMethod.getMethod().invoke(obj); + String childName = containmentMethod.getName(); + DumpNode targetNode = transform(tti, target, options.asNormal(!isChildEnabled(objClassName, childName)).allowNullObjectsOnce()); + if (targetNode != null) { + DumpNormalChildNode normalChild = new DumpNormalChildNode(); + normalChild.setName(childName); + normalChild.setDumpNode(targetNode); + normalChild.setComputed(false); + node.addDumpChildNode(normalChild); } - } - if (listChild.getNumInnerDumpNode() > 0) { - node.addDumpChildNode(listChild); - } - } - // -- singleRelation -- - for (SingleRelationMethod singleRelationMethod : car.singleRelationMethods()) { - Object target = singleRelationMethod.getMethod().invoke(obj); - DumpNode targetNode = transform(tti, target, Source.RELATION); - if (target != null && targetNode != null) { - DumpNormalRelation normalRelation = new DumpNormalRelation(); - normalRelation.setName(singleRelationMethod.getName()); - normalRelation.setDumpNode(targetNode); - node.addDumpRelation(normalRelation); + } else if (containmentMethod.isListChildMethod()) { + // -- listChild -- + Iterable<?> targetList = (Iterable<?>) containmentMethod.getMethod().invoke(obj); + DumpListChildNode listChild = new DumpListChildNode(); + listChild.setComputed(false); + String childName = containmentMethod.getName(); + boolean shouldBeInvisisble = !isChildEnabled(objClassName, childName); + listChild.setName(childName); + for (Object target : targetList) { + DumpNode targetNode = transform(tti, target, options.asNormal(shouldBeInvisisble)); + if (target != null && targetNode != null) { + listChild.addInnerDumpNode(new InnerDumpNode().setDumpNode(targetNode)); + } + } + if (listChild.getNumInnerDumpNode() > 0) { + node.addDumpChildNode(listChild); + } + } else { + throw new RuntimeException("Unknown containment method type " + containmentMethod); } } - // -- listRelation -- - for (ListRelationMethod listRelationMethod : car.listRelationMethods()) { - Iterable<?> targetList = (Iterable<?>) listRelationMethod.getMethod().invoke(obj); - DumpListRelation listRelation = new DumpListRelation(); - listRelation.setName(listRelationMethod.getName()); - for (Object target : targetList) { - DumpNode targetNode = transform(tti, target, Source.RELATION); + for (AnalysedMethod otherMethod : car.getOtherMethodList()) { + if (otherMethod.isSingleChildMethod()) { + // -- singleChild -- + Object target = otherMethod.getMethod().invoke(obj); + String childName = otherMethod.getName(); + boolean computed = otherMethod.asSingleChildMethod().isNTASingleChildMethod(); + DumpNode targetNode = transform(tti, target, options.asNormal(!isChildEnabled(objClassName, childName)).computed(computed).allowNullObjectsOnce()); + if (targetNode != null) { + DumpNormalChildNode normalChild = new DumpNormalChildNode(); + normalChild.setName(childName); + normalChild.setDumpNode(targetNode); + normalChild.setComputed(computed); + node.addDumpChildNode(normalChild); + } + } else if (otherMethod.isListChildMethod()) { + // -- listChild -- + Iterable<?> targetList = (Iterable<?>) otherMethod.getMethod().invoke(obj); + DumpListChildNode listChild = new DumpListChildNode(); + boolean computed = otherMethod.asListChildMethod().isNTAListChildMethod(); + listChild.setComputed(computed); + String childName = otherMethod.getName(); + boolean shouldBeInvisisble = !isChildEnabled(objClassName, childName); + listChild.setName(childName); + for (Object target : targetList) { + DumpNode targetNode = transform(tti, target, options.asNormal(shouldBeInvisisble).computed(computed)); + if (target != null && targetNode != null) { + listChild.addInnerDumpNode(new InnerDumpNode().setDumpNode(targetNode)); + } + } + if (listChild.getNumInnerDumpNode() > 0) { + node.addDumpChildNode(listChild); + } + } else if (otherMethod.isSingleRelationMethod()) { + // -- singleRelation -- + Object target = otherMethod.getMethod().invoke(obj); + DumpNode targetNode = transform(tti, target, options.asRelation()); if (target != null && targetNode != null) { - listRelation.addInnerDumpNode(new InnerDumpNode(targetNode)); + DumpNormalRelation normalRelation = new DumpNormalRelation(); + normalRelation.setName(otherMethod.getName()); + normalRelation.setDumpNode(targetNode); + node.addDumpRelation(normalRelation); } - } - if (listRelation.getNumInnerDumpNode() > 0) { - node.addDumpRelation(listRelation); - } - } - // -- token -- - for (TokenMethod tokenMethod : car.tokenMethods()) { - Object target = tokenMethod.getMethod().invoke(obj); - if (target != null) { - DumpNode targetNode = transform(tti, target, Source.RELATION); - DumpToken token = null; - // TODO check, if Iterable.isAssignableFrom(target.getClass()). - // if so, check isAstNode for first non-null to add DumpReferenceToken's, otherwise DumpValueToken's - if (targetNode != null && targetNode.isAstNode()) { - token = new DumpReferenceToken().setValue(targetNode); - } else { - if (target != null && (getBuildConfig().getIncludeEmptyString() || !target.toString().isEmpty())) { - DumpValueToken valueToken = new DumpValueToken(); - valueToken.setValue(target); - token = valueToken; + } else if (otherMethod.isListRelationMethod()) { + // -- listRelation -- + Iterable<?> targetList = (Iterable<?>) otherMethod.getMethod().invoke(obj); + DumpListRelation listRelation = new DumpListRelation(); + listRelation.setName(otherMethod.getName()); + for (Object target : targetList) { + DumpNode targetNode = transform(tti, target, options.asRelation()); + if (target != null && targetNode != null) { + listRelation.addInnerDumpNode(new InnerDumpNode(targetNode)); } } - if (token != null) { - token.setName(tokenMethod.getName()); - token.setComputed(tokenMethod.isAttributeMethod()); - node.addDumpToken(token); + if (listRelation.getNumInnerDumpNode() > 0) { + node.addDumpRelation(listRelation); + } + } else if (otherMethod.isTokenMethod()) { + // -- token -- + Object target = otherMethod.getMethod().invoke(obj); + if (target != null) { + DumpNode targetNode = transform(tti, target, options.asRelation()); + DumpToken token = null; + // TODO check, if Iterable.isAssignableFrom(target.getClass()). + // if so, check isAstNode for first non-null to add DumpReferenceToken's, otherwise DumpValueToken's + if (targetNode != null && targetNode.isAstNode()) { + token = new DumpReferenceToken().setValue(targetNode); + } else { + if (target != null && (getBuildConfig().getIncludeEmptyString() || !target.toString().isEmpty())) { + DumpValueToken valueToken = new DumpValueToken(); + valueToken.setValue(target); + token = valueToken; + } + } + if (token != null) { + token.setName(otherMethod.getName()); + token.setComputed(otherMethod.asTokenMethod().isAttributeMethod()); + node.addDumpToken(token); + } } + } else { + throw new RuntimeException("Unknown other method type " + otherMethod); } } return node; @@ -162,49 +244,49 @@ aspect GenerationBackend { node.setLabel(getBuildConfig().getStyleInformation().getNameMethod().get(obj)); node.setBackgroundColor(getBuildConfig().getStyleInformation().getBackgroundColorMethod().get(obj)); node.setTextColor(getBuildConfig().getStyleInformation().getTextColorMethod().get(obj)); - } - - private Source DumpAst.nextSource(Source currentSource, boolean shouldBeInvisible) { - return currentSource == Source.INVISIBLE_PARENT ? Source.INVISIBLE_PARENT : - (shouldBeInvisible ? Source.INVISIBLE_PARENT : Source.PARENT); + node.setManualStereotypes(getBuildConfig().getStyleInformation().getStereotypeMethod().get(obj)); } syn nta ClassAnalysisResult DumpAst.analyzeClass(java.lang.Class<?> clazz) { ClassAnalysisResult result = new ClassAnalysisResult(); String clazzName = clazz.getSimpleName(); + java.util.List<String> targetOrder = targetOrder(clazz); 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(); + String contextNameToAdd = null; + AnalysedMethod containmentMethodToAdd = null; switch (simpleName) { case "Child": - String singleChildName = invokeName(annotation); + contextNameToAdd = invokeName(annotation); NormalSingleChildMethod singleChildMethod = new NormalSingleChildMethod(); singleChildMethod.setMethod(method); - singleChildMethod.setName(singleChildName); - result.addAnalysedMethod(singleChildMethod); + singleChildMethod.setName(contextNameToAdd); + containmentMethodToAdd = singleChildMethod; break; case "OptChild": - String optChildName = invokeName(annotation); + contextNameToAdd = invokeName(annotation); try { // the annotated method is "get???Opt", but we want "get???" - java.lang.reflect.Method realGetter = clazz.getMethod("get" + optChildName); + java.lang.reflect.Method realGetter = clazz.getMethod("get" + contextNameToAdd); NormalSingleChildMethod normalSingleChildMethod = new NormalSingleChildMethod(); normalSingleChildMethod.setMethod(realGetter); - normalSingleChildMethod.setName(optChildName); - result.addAnalysedMethod(normalSingleChildMethod); + normalSingleChildMethod.setName(contextNameToAdd); + containmentMethodToAdd = normalSingleChildMethod; } catch (NoSuchMethodException e) { - System.err.println("Could not find getter for Opt-child " + optChildName + " in " + clazzName); + System.err.println("Could not find getter for Opt-child " + contextNameToAdd + " in " + clazzName); throw new RuntimeException(e); } break; case "ListChild": String listChildName = invokeName(annotation); + contextNameToAdd = listChildName; NormalListChildMethod normalListChildMethod = new NormalListChildMethod(); normalListChildMethod.setMethod(method); normalListChildMethod.setName(listChildName); - result.addAnalysedMethod(normalListChildMethod); + containmentMethodToAdd = normalListChildMethod; break; case "Token": // heuristic for relations @@ -218,7 +300,7 @@ aspect GenerationBackend { SingleRelationMethod singleRelationMethod = new SingleRelationMethod(); singleRelationMethod.setMethod(relationMethod); singleRelationMethod.setName(relationName); - result.addAnalysedMethod(singleRelationMethod); + result.addOtherMethod(singleRelationMethod); } continue; } catch (NoSuchMethodException e) { @@ -232,7 +314,7 @@ aspect GenerationBackend { ListRelationMethod listRelationMethod = new ListRelationMethod(); listRelationMethod.setMethod(relationMethod); listRelationMethod.setName(relationName); - result.addAnalysedMethod(listRelationMethod); + result.addOtherMethod(listRelationMethod); } continue; } catch (NoSuchMethodException e) { @@ -243,7 +325,7 @@ aspect GenerationBackend { IntrinsicTokenMethod tokenMethod = new IntrinsicTokenMethod(); tokenMethod.setMethod(method); tokenMethod.setName(tokenName); - result.addAnalysedMethod(tokenMethod); + result.addOtherMethod(tokenMethod); } break; case "Attribute": @@ -263,12 +345,12 @@ aspect GenerationBackend { NTAListChildMethod ntaListChildMethod = new NTAListChildMethod(); ntaListChildMethod.setMethod(method); ntaListChildMethod.setName(attributeName); - result.addAnalysedMethod(ntaListChildMethod); + result.addOtherMethod(ntaListChildMethod); } else { NTASingleChildMethod ntaSingleChildMethod = new NTASingleChildMethod(); ntaSingleChildMethod.setMethod(method); ntaSingleChildMethod.setName(attributeName); - result.addAnalysedMethod(ntaSingleChildMethod); + result.addOtherMethod(ntaSingleChildMethod); } } } else if (isAttributeEnabled(clazzName, attributeName)) { @@ -276,16 +358,53 @@ aspect GenerationBackend { AttributeMethod attributeMethod = new AttributeMethod(); attributeMethod.setMethod(method); attributeMethod.setName(attributeName); - result.addAnalysedMethod(attributeMethod); + result.addOtherMethod(attributeMethod); } break; } + if (containmentMethodToAdd != null) { + int indexOfContextInTarget = targetOrder.indexOf(contextNameToAdd); + if (indexOfContextInTarget == 0) { + result.getContainmentMethodList().insertChild(containmentMethodToAdd, 0); + continue; + } + if (indexOfContextInTarget == targetOrder.size() - 1) { + result.addContainmentMethod(containmentMethodToAdd); + continue; + } + + for (int i = 0, size = result.getNumContainmentMethod(); i < size; i++) { + String currentContextName = result.getContainmentMethod(i).getName(); + int indexOfCurrentInTarget = targetOrder.indexOf(currentContextName); + if (indexOfCurrentInTarget > indexOfContextInTarget) { + result.getContainmentMethodList().insertChild(containmentMethodToAdd, i); + break; + } + } + result.addContainmentMethod(containmentMethodToAdd); + } } } } return result; } + syn java.util.List<String> DumpAst.targetOrder(Class<?> clazz) { + for (java.lang.reflect.Constructor<?> method : clazz.getConstructors()) { + for (java.lang.annotation.Annotation annotation : method.getAnnotations()) { + String canonicalName = annotation.annotationType().getCanonicalName(); + if (canonicalName.startsWith(astNodeAnnotationPrefix())) { + String simpleName = annotation.annotationType().getSimpleName(); + if (simpleName.equals("Constructor")) { + return java.util.Arrays.asList((String[]) invokeMethod("name", annotation)); + } + } + } + } + // there is no constructor with an annotation, iff the nonterminal has no children + return null; + } + private String DumpAst.titleCase(String s) { if (s.isEmpty()) { return s; @@ -416,8 +535,8 @@ aspect GenerationBackend { return p.matcher(input).matches(); } - // --- version --- (mustache has buildConfig as context) - syn String BuildConfig.version() = printConfig().getVersion(); + // --- debug --- (mustache has printConfig as context) + syn boolean PrintConfig.debug() = buildConfig().getDebug(); private static String DumpAst.invokeName(java.lang.annotation.Annotation annotation) { return (String) invokeMethod("name", annotation); @@ -434,11 +553,15 @@ aspect GenerationBackend { } } + // --- isNull --- + syn boolean DumpNode.isNull() { + return getObject() == null; + } + // --- isAstNode --- syn boolean DumpNode.isAstNode() { if (getObject() == null) { - // this is only possible for normal child nodes, and they are ast nodes - return true; + return false; } Class<?> clazz = getObject().getClass(); for (java.lang.reflect.Method method : clazz.getMethods()) { @@ -468,7 +591,7 @@ aspect GenerationBackend { java.util.List<DumpNode> result = new java.util.ArrayList<>(); for (DumpChildNode childNode : getDumpChildNodeList()) { for (DumpNode inner : childNode.innerNodes(false)) { - if (inner.getInvisible()) { + if (inner != null && inner.getInvisible()) { result.addAll(inner.reachableThroughInvisible()); } else if (this.getInvisible()) { result.add(inner); @@ -487,6 +610,30 @@ aspect GenerationBackend { } } + // --- stereotypeList --- + syn String DumpNode.stereotypeList() { + StringBuilder sb = new StringBuilder(getManualStereotypes()); + if (!automaticStereotypes().isEmpty()) { + // add automatic stereotypes, if there are any + if (!getManualStereotypes().isEmpty()) { + // add a comma between manual and automatic, if both are present + sb.append(","); + } + sb.append(automaticStereotypes()); + } + String manualAndAutomaticStereotypes = sb.toString(); + if (manualAndAutomaticStereotypes.isEmpty()) { + return ""; + } + sb = new StringBuilder(); + for (String stereotype : manualAndAutomaticStereotypes.split(",")) { + sb.append(" <<").append(stereotype).append(">>"); + } + return sb.toString(); + } + // --- manualAndAutomaticStereotypes --- + syn String DumpNode.automaticStereotypes() = getComputed() ? "NTA" : ""; + // --- myChildren --- syn java.util.List<DumpNode> DumpNode.myChildren() { java.util.List<DumpNode> result = new java.util.ArrayList<>(); @@ -519,96 +666,22 @@ aspect GenerationBackend { class TransformationTransferInformation { java.util.Map<Object, DumpNode> transformed = new java.util.HashMap<>(); java.util.Map<DumpNode, Boolean> relationTargetsUnprocessed = new java.util.HashMap<>(); + int nodeCounter = 0; } - syn String DumpAst.toYaml(boolean prependCreationComment) { - Document doc = new Document(); - doc.setRootElement(getRootNode().toYaml()); - return doc.prettyPrint(prependCreationComment); - } - - syn MappingElement DumpNode.toYaml() { - MappingElement result = new MappingElement(); - // tokens are key-value-pairs - for (DumpToken token : getDumpTokenList()) { - if (token.isDumpValueToken()) { - result.put(token.getName(), makeValueElement(token.asDumpValueToken().getValue())); - } else { - result.put(token.getName(), token.asDumpReferenceToken().getValue().toYaml()); - } - } - // children - for (DumpChildNode child : getDumpChildNodeList()) { - ListElement list = new ListElement(); - for (DumpNode inner : child.innerNodes(true)) { - list.add(inner.toYaml()); - } - if (child.isList()) { - result.put(child.getName(), list); - } else if (list.getNumElement() == 1) { - result.put(child.getName(), list.getElement(0)); - } - } - // relations - for (DumpRelation relation : getDumpRelationList()) { - ListElement list = new ListElement(); - for (DumpNode inner : relation.innerNodes(true)) { - list.add(inner.toYaml()); - } - if (relation.isList()) { - result.put(relation.getName(), list); - } else if (list.getNumElement() == 1) { - result.put(relation.getName(), list.getElement(0)); - } - } - return result; - } - - private static ComplexElement ASTNode.HELPER_COMPLEX_ELEMENT = new MappingElement(); - protected static Element ASTNode.makeValueElement(Object obj) { - if (obj instanceof Integer) { - return ValueElement.of((int) obj); - } else if (obj instanceof Boolean) { - return ValueElement.of((boolean) obj); - } else { - return HELPER_COMPLEX_ELEMENT.makeStringElement(obj.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() { - } - } static StyleInformation StyleInformation.createDefault() { StyleInformation result = new StyleInformation(); result.setNameMethod(n -> n == null ? "null" : n.getClass().getSimpleName() + "@" + Integer.toHexString(n.hashCode())); result.setBackgroundColorMethod(n -> ""); result.setTextColorMethod(n -> ""); + result.setStereotypeMethod(n -> ""); + result.setComputedColor("blue"); return result; } + syn String DumpAst.computedColor() = getBuildConfig().getStyleInformation().getComputedColor(); + @FunctionalInterface public interface StyleMethod<ASTNODE> { String get(ASTNODE node); diff --git a/dumpAst/src/main/jastadd/GenerationFrontend.jadd b/dumpAst/src/main/jastadd/GenerationFrontend.jadd index 97b622a1ba1d333ad23e44dabb0a8571721cf6ab..3e6fcf0a93911c07bc48e6571a9764776da6c291 100644 --- a/dumpAst/src/main/jastadd/GenerationFrontend.jadd +++ b/dumpAst/src/main/jastadd/GenerationFrontend.jadd @@ -347,6 +347,16 @@ public class DumpBuilder { return this; } + public <ASTNODE> DumpBuilder setStereotypeMethod(StyleMethod<ASTNODE> stereotypeMethod) { + buildConfig.getStyleInformation().setStereotypeMethod(stereotypeMethod); + return this; + } + + public DumpBuilder setComputedColor(String color) { + buildConfig.getStyleInformation().setComputedColor(color); + return this; + } + public DumpBuilder setScale(double value) { printConfig.setScale(value); return this; @@ -369,7 +379,17 @@ public class DumpBuilder { protected DumpAst build() { if (result == null) { result = new DumpAst(); - result.setPackageName(this.packageName == null ? this.target.getClass().getPackage().getName() : this.packageName); + final String packageNameToUse; + if (this.packageName != null) { + packageNameToUse = this.packageName; + } else { + if (this.target == null) { + packageNameToUse = null; + } else { + packageNameToUse = this.target.getClass().getPackage().getName(); + } + } + result.setPackageName(packageNameToUse); result.setBuildConfig(this.buildConfig); result.setPrintConfig(this.printConfig); try { @@ -400,11 +420,38 @@ public class DumpBuilder { } public DumpBuilder dumpAsYaml(java.nio.file.Path destination, boolean prependCreationComment) throws java.io.IOException { - String content = build().toYaml(prependCreationComment); + String content = build().printYaml(prependCreationComment); try (java.io.Writer writer = java.nio.file.Files.newBufferedWriter(destination)) { writer.write(content); } return this; } + + /** + * Write out content as PNG image generated by plantuml. + * @param destination path of destination file + * @return this + * @throws java.io.IOException if an I/O error happend during opening or writing in that file + */ + 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; + } + + /** + * Write out content as SVG image generated by plantuml. + * @param destination path of destination file + * @return this + * @throws java.io.IOException if an I/O error happend during opening or writing in that file + */ + 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; + } } } diff --git a/dumpAst/src/main/jastadd/GenerationMustache.jrag b/dumpAst/src/main/jastadd/GenerationMustache.jrag index d69f4fba573580285101ce63946da0fda5ce52fd..cf9f53455aab8a2cef109e9e8d53a27e8fac4951 100644 --- a/dumpAst/src/main/jastadd/GenerationMustache.jrag +++ b/dumpAst/src/main/jastadd/GenerationMustache.jrag @@ -19,7 +19,35 @@ aspect GenerationMustache { 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); + String yaml = this.printYaml(false); + Object context = new org.yaml.snakeyaml.Yaml().load(new StringReader(yaml)); + m.execute(new java.io.PrintWriter(new AppendableWriter(sb)), context); 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/dumpAst/src/main/jastadd/GenerationToYaml.jrag b/dumpAst/src/main/jastadd/GenerationToYaml.jrag new file mode 100644 index 0000000000000000000000000000000000000000..30a093c9f2495e68f4b552cedf9ce16d77e7ed06 --- /dev/null +++ b/dumpAst/src/main/jastadd/GenerationToYaml.jrag @@ -0,0 +1,221 @@ +aspect GenerationToYaml { + syn String DumpAst.printYaml(boolean prependCreationComment) { + Document doc = new Document(); + doc.setRootElement(this.toYaml(false)); + return doc.prettyPrint(prependCreationComment); + } + + // todo: default impl should actually be abstract instead + syn MappingElement ASTNode.toYaml(boolean fromRelation) = new MappingElement(); + + static MappingElement ASTNode.safeToYaml(ASTNode node, boolean fromRelation) { + if (node == null) { + return null; + } + return node.toYaml(fromRelation); + } + + syn MappingElement DumpAst.toYaml(boolean fromRelation) { + MappingElement result = new MappingElement(); + // children + result.put("PrintConfig", safeToYaml(getPrintConfig(), fromRelation)); + addYamledList(result, "DumpNodes", getDumpNodeList(), fromRelation); + + // attributes + result.put("computedColor", computedColor()); + return result; + } + + static void ASTNode.addYamledList(MappingElement base, String key, Iterable<? extends ASTNode<?>> iterable, boolean fromRelation) { + ListElement innerList = new ListElement(); + for (ASTNode node : iterable) { + innerList.add(safeToYaml(node, fromRelation)); + } + base.put(key, innerList); + } + + syn MappingElement PrintConfig.toYaml(boolean fromRelation) { + MappingElement result = new MappingElement(); + // children + addYamledList(result, "Headers", getHeaderList(), fromRelation); + + // tokens + result.put("scale", getScale()); + result.put("version", getVersion()); + result.put("orderChildren", getOrderChildren()); + + // attributes + result.put("debug", debug()); + return result; + } + + syn MappingElement Header.toYaml(boolean fromRelation) { + MappingElement result = new MappingElement(); + // tokens + result.put("value", getValue()); + return result; + } + + syn MappingElement DumpNode.toYaml(boolean fromRelation) { + MappingElement result = new MappingElement(); + + // children + if (!fromRelation) { + addYamledList(result, "DumpChildNodes", getDumpChildNodeList(), fromRelation); + addYamledList(result, "DumpTokens", getDumpTokenList(), fromRelation); + addYamledList(result, "DumpRelations", getDumpRelationList(), fromRelation); + } + + // tokens + result.put("name", getName()); + if (!fromRelation) { + result.put("computed", getComputed()); + result.put("label", getLabel()); + result.put("backgroundColor", getBackgroundColor()); + result.put("textColor", getTextColor()); + result.put("invisible", getInvisible()); + } + + // attributes + if (!fromRelation) { + result.put("isNull", isNull()); + result.put("isAstNode", isAstNode()); + result.put("labelAndTextColor", labelAndTextColor()); + result.put("stereotypeList", stereotypeList()); + addYamledList(result, "myChildren", myChildren(), true); + } + result.put("hasSuccessor", hasSuccessor()); + result.put("successor", safeToYaml(successor(), true)); + + // NTAs + if (!fromRelation) { + result.put("InvisiblePath", safeToYaml(getInvisiblePath(), true)); + } + return result; + } + + syn MappingElement DumpChildNode.toYaml(boolean fromRelation) { + MappingElement result = new MappingElement(); + // tokens + result.put("name", getName()); + result.put("computed", getComputed()); + // attributes + result.put("label", label()); + result.put("isList", isList()); + result.put("outerNodeName", outerNodeName()); + + return result; + } + + syn MappingElement DumpNormalChildNode.toYaml(boolean fromRelation) { + MappingElement result = super.toYaml(fromRelation); + // attributes + result.put("bothVisible", bothVisible()); + result.put("innerNodeName", innerNodeName()); + return result; + } + + syn MappingElement DumpListChildNode.toYaml(boolean fromRelation) { + MappingElement result = super.toYaml(fromRelation); + // children + addYamledList(result, "InnerDumpNodes", getInnerDumpNodeList(), fromRelation); + return result; + } + + syn MappingElement DumpToken.toYaml(boolean fromRelation) { + MappingElement result = new MappingElement(); + // tokens + result.put("name", getName()); + result.put("computed", getComputed()); + // attributes + result.put("label", label()); + result.put("isDumpValueToken", isDumpValueToken()); + return result; + } + + syn MappingElement DumpValueToken.toYaml(boolean fromRelation) { + MappingElement result = super.toYaml(fromRelation); + // tokens + result.put("value", getValue().toString()); + return result; + } + + syn MappingElement DumpReferenceToken.toYaml(boolean fromRelation) { + MappingElement result = super.toYaml(fromRelation); + // tokens + result.put("innerNodeName", innerNodeName()); + result.put("outerNodeName", outerNodeName()); + return result; + } + + syn MappingElement DumpRelation.toYaml(boolean fromRelation) { + MappingElement result = new MappingElement(); + // tokens + result.put("name", getName()); + result.put("bidirectional", getBidirectional()); + // attributes + result.put("isList", isList()); + result.put("label", label()); + result.put("outerNodeName", outerNodeName()); + return result; + } + + syn MappingElement DumpNormalRelation.toYaml(boolean fromRelation) { + MappingElement result = super.toYaml(fromRelation); + // attributes + result.put("bothVisible", bothVisible()); + result.put("innerNodeName", innerNodeName()); + return result; + } + + syn MappingElement DumpListRelation.toYaml(boolean fromRelation) { + MappingElement result = super.toYaml(fromRelation); + // children + addYamledList(result, "InnerDumpNodes", getInnerDumpNodeList(), fromRelation); + return result; + } + + syn MappingElement InnerDumpNode.toYaml(boolean fromRelation) { + MappingElement result = new MappingElement(); + // attributes + result.put("bothVisible", bothVisible()); + result.put("innerNodeName", innerNodeName()); + result.put("outerNodeName", outerNodeName()); + result.put("label", label()); + return result; + } + + syn MappingElement InvisiblePath.toYaml(boolean fromRelation) { + MappingElement result = super.toYaml(fromRelation); + // children + addYamledList(result, "InnerDumpNodes", getInnerDumpNodeList(), fromRelation); + return result; + } + + // extension for mustache + public static ValueElement ValueElement.of(double value) { + return new ValueElement(false, String.valueOf(value)); + } + public MappingElement MappingElement.put(String key, double value) { + addKeyValuePair(key, ValueElement.of(value)); + return this; + } + + refine Helpers public void MappingElement.addKeyValuePair(String key, Element value) { + if (value == null) { + return; + } + refined(key, value); + } + + refine Helpers protected SimpleElement ComplexElement.makeStringElement(String value) { + if (value == null || value.equals("null")) { + return StringElement.of("null"); + } + if (value.isEmpty()) { + return StringElement.of(value); + } + return refined(value); + } + +} diff --git a/dumpAst/src/main/jastadd/Imports.jadd b/dumpAst/src/main/jastadd/Imports.jadd new file mode 100644 index 0000000000000000000000000000000000000000..41dcef642656cbdbe35aac8444a7d2c1dc2e7110 --- /dev/null +++ b/dumpAst/src/main/jastadd/Imports.jadd @@ -0,0 +1,4 @@ +import java.io.*; +import java.util.*; + +aspect Imports {} diff --git a/dumpAst/src/main/jastadd/Navigation.jrag b/dumpAst/src/main/jastadd/Navigation.jrag index 3f04c9c4392d6b02f4fa85b7329e9ae36fe140b9..c69141d3208bc5f99ee866d6417b043daaac9a03 100644 --- a/dumpAst/src/main/jastadd/Navigation.jrag +++ b/dumpAst/src/main/jastadd/Navigation.jrag @@ -5,20 +5,9 @@ aspect Navigation { 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; - - // --- asDumpReferenceToken --- - syn DumpReferenceToken DumpToken.asDumpReferenceToken() = null; - eq DumpReferenceToken.asDumpReferenceToken() = this; - // --- buildConfig --- inh BuildConfig DumpNode.buildConfig(); + inh BuildConfig PrintConfig.buildConfig(); eq DumpAst.getChild().buildConfig() = getBuildConfig(); // --- printConfig --- @@ -50,7 +39,7 @@ aspect Navigation { // --- innerNodes --- syn java.util.List<DumpNode> DumpChildNode.innerNodes(boolean onlyVisible); - eq DumpNormalChildNode.innerNodes(boolean onlyVisible) = onlyVisible && (containingDumpNode().getInvisible() || getDumpNode().getInvisible()) ? + eq DumpNormalChildNode.innerNodes(boolean onlyVisible) = getDumpNode() == null || onlyVisible && (containingDumpNode().getInvisible() || getDumpNode().getInvisible()) ? java.util.Collections.emptyList() : java.util.Collections.singletonList(getDumpNode()); eq DumpListChildNode.innerNodes(boolean onlyVisible) { @@ -59,9 +48,15 @@ aspect Navigation { } java.util.List<DumpNode> result = new java.util.ArrayList<>(); getInnerDumpNodeList().forEach(inner -> { - if (!onlyVisible || !inner.getDumpNode().getInvisible()) { - result.add(inner.getDumpNode()); + if (inner == null || inner.getDumpNode() == null) { + //noinspection UnnecessaryReturnStatement + return; } + if (onlyVisible && inner.getDumpNode().getInvisible()) { + //noinspection UnnecessaryReturnStatement + return; + } + result.add(inner.getDumpNode()); }); return result; } @@ -97,17 +92,4 @@ aspect Navigation { coll java.util.List<TokenMethod> ClassAnalysisResult.tokenMethods() [new java.util.ArrayList<>()] root ClassAnalysisResult; TokenMethod contributes this to ClassAnalysisResult.tokenMethods(); - - // --- isAttributeMethod --- - syn boolean TokenMethod.isAttributeMethod() = false; - eq AttributeMethod.isAttributeMethod() = true; - - // --- isNTASingleChildMethod --- - syn boolean SingleChildMethod.isNTASingleChildMethod() = false; - eq NTASingleChildMethod.isNTASingleChildMethod() = true; - - // --- isNTAListChildMethod --- - syn boolean ListChildMethod.isNTAListChildMethod() = false; - eq NTAListChildMethod.isNTAListChildMethod() = true; - } diff --git a/dumpAst/src/main/jastadd/Printing.jrag b/dumpAst/src/main/jastadd/Printing.jrag index 362451e3dfd3873f481ef6939793ba04c5dc7628..e4565af809152de0c654dd6bc613dd5aab2c3e02 100644 --- a/dumpAst/src/main/jastadd/Printing.jrag +++ b/dumpAst/src/main/jastadd/Printing.jrag @@ -7,9 +7,9 @@ aspect Printing { eq DumpNode.getChild().outerNodeName() = name(); // --- innerNodeName --- - syn String InnerDumpNode.innerNodeName() = getDumpNode().name(); - syn String DumpNormalChildNode.innerNodeName() = getDumpNode().name(); - syn String DumpNormalRelation.innerNodeName() = getDumpNode().name(); + syn String InnerDumpNode.innerNodeName() = getDumpNode() != null ? getDumpNode().name() : null; + syn String DumpNormalChildNode.innerNodeName() = getDumpNode() != null ? getDumpNode().name() : null; + syn String DumpNormalRelation.innerNodeName() = getDumpNode() != null ? getDumpNode().name() : null; syn String DumpReferenceToken.innerNodeName() = getValue().name(); // --- name --- @@ -20,9 +20,16 @@ aspect Printing { syn String DumpRelation.label() = getName(); syn String DumpToken.label() = getName() + (getComputed() ? "()" : ""); syn String DumpNode.label() = getLabel(); + inh String InnerDumpNode.label(); + eq DumpListChildNode.getInnerDumpNode(int index).label() = label() + "[" + index + "]"; + eq DumpListRelation.getInnerDumpNode(int index).label() = label() + "[" + index + "]"; + eq InvisiblePath.getInnerDumpNode(int index).label() = null; // --- bothVisible --- - syn boolean InnerDumpNode.bothVisible() = !containingDumpNode().getInvisible() && !getDumpNode().getInvisible(); - syn boolean DumpNormalChildNode.bothVisible() = !containingDumpNode().getInvisible() && !getDumpNode().getInvisible(); - syn boolean DumpNormalRelation.bothVisible() = !containingDumpNode().getInvisible() && !getDumpNode().getInvisible(); + boolean ASTNode.bothVisible(DumpNode one, DumpNode two) { + return one != null && two != null && !one.getInvisible() && !two.getInvisible(); + } + syn boolean InnerDumpNode.bothVisible() = bothVisible(containingDumpNode(), getDumpNode()); + syn boolean DumpNormalChildNode.bothVisible() = bothVisible(containingDumpNode(), getDumpNode()); + syn boolean DumpNormalRelation.bothVisible() = bothVisible(containingDumpNode(), getDumpNode()); } diff --git a/dumpAst/src/main/resources/dumpAst.mustache b/dumpAst/src/main/resources/dumpAst.mustache index 9dd0d02e100d308c175e494648008aebb663e5b0..a4c15afde2619529e104c4288b5d5fe80cf9244e 100644 --- a/dumpAst/src/main/resources/dumpAst.mustache +++ b/dumpAst/src/main/resources/dumpAst.mustache @@ -1,44 +1,58 @@ @startuml +skinparam object<<null>> { + BorderColor transparent + BackgroundColor transparent + shadowing false +} +hide <<null>> stereotype +skinparam object<<NTA>> { + BorderColor {{{computedColor}}} +} +hide <<NTA>> stereotype + {{#PrintConfig}} -scale {{Scale}} +scale {{{scale}}} {{#Headers}} -{{Value}} +{{{value}}} {{/Headers}} {{/PrintConfig}} {{#DumpNodes}} - {{#isAstNode}} - {{^Invisible}} -object "{{{labelAndTextColor}}}" as {{name}} {{#backgroundColor}}#{{{backgroundColor}}}{{/backgroundColor}} { + {{^invisible}} + {{#isNull}} +object "null" as {{{name}}}<<null>> + {{/isNull}} + {{#isAstNode}} +object "{{{labelAndTextColor}}}" as {{{name}}} {{{stereotypeList}}} {{#backgroundColor}}#{{{backgroundColor}}}{{/backgroundColor}} { {{#DumpTokens}} {{#isDumpValueToken}} - {{label}} = {{{Value}}} + {{{label}}} = {{{value}}} {{/isDumpValueToken}} {{/DumpTokens}} } - {{/Invisible}} - {{/isAstNode}} + {{/isAstNode}} + {{/invisible}} {{/DumpNodes}} {{#DumpNodes}} {{#DumpTokens}} - {{^Invisible}} + {{^invisible}} {{^isDumpValueToken}} -{{outerNodeName}} ..> {{innerNodeName}} : {{label}} +{{{outerNodeName}}} .{{#computed}}[#{{{computedColor}}}]{{/computed}}.> {{{innerNodeName}}} : {{{label}}} {{/isDumpValueToken}} - {{/Invisible}} + {{/invisible}} {{/DumpTokens}} {{#DumpChildNodes}} {{#isList}} {{#InnerDumpNodes}} {{#bothVisible}} -{{outerNodeName}} *-- {{innerNodeName}} : {{label}} +{{{outerNodeName}}} *-{{#computed}}[#{{{computedColor}}}]{{/computed}}- {{{innerNodeName}}} : {{{label}}} {{/bothVisible}} {{/InnerDumpNodes}} {{/isList}} {{^isList}} {{#bothVisible}} -{{outerNodeName}} *-- {{innerNodeName}} : {{label}} +{{{outerNodeName}}} *-{{#computed}}[#{{{computedColor}}}]{{/computed}}- {{{innerNodeName}}} : {{{label}}} {{/bothVisible}} {{/isList}} {{/DumpChildNodes}} @@ -46,38 +60,38 @@ object "{{{labelAndTextColor}}}" as {{name}} {{#backgroundColor}}#{{{backgroundC {{#isList}} {{#InnerDumpNodes}} {{#bothVisible}} -{{outerNodeName}} {{#Bidirectional}}<{{/Bidirectional}}--> {{innerNodeName}} : {{label}} +{{{outerNodeName}}} {{#bidirectional}}<{{/bidirectional}}--> {{{innerNodeName}}} : {{{label}}} {{/bothVisible}} {{/InnerDumpNodes}} {{/isList}} {{^isList}} {{#bothVisible}} -{{outerNodeName}} {{#Bidirectional}}<{{/Bidirectional}}--> {{innerNodeName}} : {{label}} +{{{outerNodeName}}} {{#bidirectional}}<{{/bidirectional}}--> {{{innerNodeName}}} : {{{label}}} {{/bothVisible}} {{/isList}} {{/DumpRelations}} - {{^Invisible}} + {{^invisible}} {{#InvisiblePath}} {{#InnerDumpNodes}} -{{outerNodeName}} o.. {{innerNodeName}} +{{{outerNodeName}}} o.. {{{innerNodeName}}} {{/InnerDumpNodes}} {{/InvisiblePath}} - {{/Invisible}} + {{/invisible}} {{#PrintConfig}}{{#orderChildren}} {{#myChildren}} {{#hasSuccessor}} -{{name}} -[hidden]right-> {{#successor}}{{name}}{{/successor}} +{{{name}}} -[hidden]right-> {{#successor}}{{{name}}}{{/successor}} {{/hasSuccessor}} {{/myChildren}} {{/orderChildren}}{{/PrintConfig}} {{/DumpNodes}} -{{#BuildConfig}} - {{#Debug}} +{{#PrintConfig}} + {{#debug}} legend right %date() - dumpAst: {{version}} + dumpAst: {{{version}}} plantuml: %version() endlegend - {{/Debug}} -{{/BuildConfig}} + {{/debug}} +{{/PrintConfig}} @enduml diff --git a/dumpAst/src/main/resources/dumpAstVersion.properties b/dumpAst/src/main/resources/dumpAstVersion.properties index e0a9ca936f23641719ffee3bfed385ca97d946b8..4f787e6a9ba7ce9674d053068c4f55768e8c5294 100644 --- a/dumpAst/src/main/resources/dumpAstVersion.properties +++ b/dumpAst/src/main/resources/dumpAstVersion.properties @@ -1,2 +1,2 @@ -#Thu Feb 24 09:45:15 CET 2022 -version=0.3.6 +#Fri Mar 11 12:41:31 CET 2022 +version=1.0.0 diff --git a/dumpAstWithPlantuml/build.gradle b/dumpAstWithPlantuml/build.gradle deleted file mode 100644 index 03e3324a59b067b1c8f45dc1f7b44975194b779e..0000000000000000000000000000000000000000 --- a/dumpAstWithPlantuml/build.gradle +++ /dev/null @@ -1,99 +0,0 @@ -buildscript { - repositories.mavenLocal() - repositories.mavenCentral() - dependencies { - classpath group: 'org.jastadd', name: 'jastaddgradle', version: '1.13.3' - } -} - -plugins { - id 'relast2uml.java-jastadd-conventions' - id 'relast2uml.java-publishing-conventions' -} - -apply plugin: 'jastadd' - -dependencies { - implementation fileTree(include: ['plantuml.jar'], dir: '../libs') - - implementation group: 'com.github.spullara.mustache.java', name: 'compiler', version: "${mustache_java_version}" -} - -File dumpAstGrammar = file('../dumpAst/src/main/jastadd/DumpAst.relast') - -task relast(type: JavaExec) { - group = 'Build' - main = "-jar" - - doFirst { - delete "src/gen/jastadd/*.ast" - delete "src/gen/jastadd/DumpAst.jadd" - delete "src/gen/jastadd/DumpAstRefResolver.jadd" - delete "src/gen/jastadd/DumpAstResolverStubs.jrag" - mkdir "src/gen/jastadd/" - } - - args = [ - "../libs/relast.jar", - dumpAstGrammar, -// "./src/main/jastadd/MustacheNodes.relast", - "--listClass=java.util.ArrayList", - "--jastAddList=JastAddList", - "--useJastAddNames", - "--file", - "--resolverHelper", - "--grammarName=./src/gen/jastadd/DumpAst" - ] - - inputs.files(file("../libs/relast.jar"), - dumpAstGrammar) - 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')) -} - -jastadd { - configureModuleBuild() - modules { - //noinspection GroovyAssignabilityCheck - module("DumpAstWithPlantuml") { - - java { - basedir "." - include "src/main/**/*.java" - include "src/gen/**/*.java" - } - - jastadd { - basedir ".." - include "dumpAst/src/main/jastadd/**/*.ast" - include "dumpAst/src/main/jastadd/**/*.jadd" - include "dumpAst/src/main/jastadd/**/*.jrag" - include "dumpAstWithPlantuml/src/main/jastadd/**/*.jadd" - include "dumpAst/src/gen/jastadd/**/*.ast" - include "dumpAst/src/gen/jastadd/**/*.jadd" - include "dumpAst/src/gen/jastadd/**/*.jrag" - } - } - } - - cleanGen.doFirst { - delete "src/gen/java/de" - delete "src/gen-res/BuildInfo.properties" - } - - preprocessParser.doFirst { - - args += ["--no-beaver-symbol"] - - } - - module = "DumpAstWithPlantuml" - 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"] -} - -generateAst.dependsOn relast diff --git a/dumpAstWithPlantuml/src/main/jastadd/Generation.jadd b/dumpAstWithPlantuml/src/main/jastadd/Generation.jadd deleted file mode 100644 index 35905af0dc789adfc17971ed23ede287db032d52..0000000000000000000000000000000000000000 --- a/dumpAstWithPlantuml/src/main/jastadd/Generation.jadd +++ /dev/null @@ -1,29 +0,0 @@ -aspect Generation { - - /** - * Write out content as PNG image generated by plantuml. - * @param destination path of destination file - * @return this - * @throws java.io.IOException if an I/O error happend during opening or writing in that file - */ - public DumpBuilder 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; - } - - /** - * Write out content as SVG image generated by plantuml. - * @param destination path of destination file - * @return this - * @throws java.io.IOException if an I/O error happend during opening or writing in that file - */ - public DumpBuilder 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; - } -} \ No newline at end of file diff --git a/dumpAstWithPlantuml/src/main/resources/dumpAst.mustache b/dumpAstWithPlantuml/src/main/resources/dumpAst.mustache deleted file mode 120000 index e549bf371db8fa4554a0b276e52a3c5b978af0a3..0000000000000000000000000000000000000000 --- a/dumpAstWithPlantuml/src/main/resources/dumpAst.mustache +++ /dev/null @@ -1 +0,0 @@ -../../../../dumpAst/src/main/resources/dumpAst.mustache \ No newline at end of file diff --git a/dumpAstWithPlantuml/src/main/resources/dumpAstWithPlantumlVersion.properties b/dumpAstWithPlantuml/src/main/resources/dumpAstWithPlantumlVersion.properties deleted file mode 120000 index 19186b38f76bfc2fb8f855e0c5bafe020db4bae4..0000000000000000000000000000000000000000 --- a/dumpAstWithPlantuml/src/main/resources/dumpAstWithPlantumlVersion.properties +++ /dev/null @@ -1 +0,0 @@ -../../../../dumpAst/src/main/resources/dumpAstVersion.properties \ No newline at end of file diff --git a/dumpAstWithPlantuml/.gitignore b/featureTest/.gitignore similarity index 54% rename from dumpAstWithPlantuml/.gitignore rename to featureTest/.gitignore index 87b4cdd3d7c6a41502ca98703abeeb69a1d536fb..7609b2344767a4c3fd8857201dac2c4d1dff200c 100644 --- a/dumpAstWithPlantuml/.gitignore +++ b/featureTest/.gitignore @@ -3,3 +3,5 @@ src/gen-res/ src/gen/ out/ *.class +/featureTest.png +/featureTest.yml diff --git a/featureTest/build.gradle b/featureTest/build.gradle new file mode 100644 index 0000000000000000000000000000000000000000..ee343385c7fc3719b4420d063d34db968fb346bb --- /dev/null +++ b/featureTest/build.gradle @@ -0,0 +1,111 @@ +// --- Buildscripts (must be at the top) --- +buildscript { + repositories.mavenCentral() + dependencies { + classpath group: 'org.jastadd', name: 'jastaddgradle', version: '1.13.3' + } +} + +// --- Plugin definitions --- +plugins { + id 'java' + id 'java-library' + id 'idea' + id 'application' + id 'com.github.ben-manes.versions' +} + +apply plugin: 'jastadd' + +// --- Dependencies --- +repositories { + mavenCentral() +} + +dependencies { + implementation project(":dumpAst") + + api group: 'net.sf.beaver', name: 'beaver-rt', version: '0.9.11' + + testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: "${jupiter_version}" + testImplementation group: 'org.assertj', name: 'assertj-core', version: '3.18.1' + testRuntimeOnly group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: "${jupiter_version}" +} + +// --- Preprocessors --- +File genSrc = file("src/gen/java") +sourceSets.main.java.srcDir genSrc +idea.module.generatedSourceDirs += genSrc + +File testingGrammar = file('./src/main/jastadd/featureTest.relast') + +task relast(type: JavaExec) { + group = 'Build' + main = "-jar" + + doFirst { + delete "src/gen/jastadd/*.ast" + delete "src/gen/jastadd/featureTest.jadd" + delete "src/gen/jastadd/featureTestRefResolver.jadd" + delete "src/gen/jastadd/featureTestResolverStubs.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/featureTest" + ] + + inputs.files(file("../libs/relast.jar"), + testingGrammar) + outputs.files(file("./src/gen/jastadd/featureTest.ast"), + file("./src/gen/jastadd/featureTest.jadd"), + file("./src/gen/jastadd/featureTestRefResolver.jadd"), + file('./src/gen/jastadd/featureTestResolverStubs.jrag')) +} + +// --- JastAdd --- +jastadd { + configureModuleBuild() + modules { + //noinspection GroovyAssignabilityCheck + module("featureTest") { + jastadd { + basedir ".." + include "featureTest/src/main/jastadd/**/*.ast" + include "featureTest/src/main/jastadd/**/*.jadd" + include "featureTest/src/main/jastadd/**/*.jrag" + include "featureTest/src/gen/jastadd/**/*.ast" + include "featureTest/src/gen/jastadd/**/*.jadd" + include "featureTest/src/gen/jastadd/**/*.jrag" + } + } + } + + cleanGen.doFirst { + delete "src/gen/java/org" + delete "src/gen-res/BuildInfo.properties" + } + + module = "featureTest" + astPackage = 'org.jastadd.featureTest.ast' + genDir = 'src/gen/java' + buildInfoDir = 'src/gen-res' + jastaddOptions = ["--lineColumnNumbers", "--List=JastAddList", "--safeLazy", "--visitCheck=true", "--rewrite=cnta", "--cache=all"] +} + +// --- Tests --- +test.useJUnitPlatform() + +// --- Versioning and Publishing --- +mainClassName = 'de.tudresden.inf.st.jastadd.featureTest.FeatureTestMain' + +// --- Task order --- +generateAst.dependsOn relast diff --git a/featureTest/src/main/jastadd/featureTest.jrag b/featureTest/src/main/jastadd/featureTest.jrag new file mode 100644 index 0000000000000000000000000000000000000000..4001126135dcb55faee01ca8edfc8c8f959f019f --- /dev/null +++ b/featureTest/src/main/jastadd/featureTest.jrag @@ -0,0 +1,46 @@ +aspect GrammarGlobal { + syn A C.getCalculated() { + A result = new A(); + result.setName("Calculated-" + getName()); + D innerD = new D(); + result.setD(innerD); + 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; + } + + syn int Root.simpleAttr() = 42; + syn A Root.referenceAttr() = getA(); + + syn boolean ASTNode.isA() = false; + eq A.isA() = true; +} + +aspect GrammarTypeLevel { + syn int AbstractT.simpleAttr() = 43; + syn nta A AbstractT.getCalculated() { + A result = new A(); + result.setName("Calculated-" + getName()); + return result; + } +} diff --git a/featureTest/src/main/jastadd/featureTest.relast b/featureTest/src/main/jastadd/featureTest.relast new file mode 100644 index 0000000000000000000000000000000000000000..7b66cc96e3e0af88dd7c124928fca2ae86fa5e53 --- /dev/null +++ b/featureTest/src/main/jastadd/featureTest.relast @@ -0,0 +1,26 @@ +// testcases with global inclusion/exclusion +Nameable ::= <Name> ; +Root : Nameable ::= A B* [C]; +A : Nameable ::= B MyC:C D; +B : Nameable ::= <OtherValue> ; +C : Nameable ::= [A] <Unwanted:int> <RawReference:A> /Calculated:A/ /AlsoCalculated:B*/ ; +SubC : C ::= <RawReference:A> <Unwanted:int> [A] /Calculated:A/ /AlsoCalculated:B*/ ; +D; + +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 ; + +// testcases with type-level inclusion/exclusion +TRoot : Nameable ::= A T1 T2 T3 ; +abstract AbstractT : Nameable ::= B Bee:B* <SomeValue> <Unwanted:int> ; +T1 : AbstractT ; +T2 : AbstractT ; +T3 : AbstractT ; + +rel AbstractT.oneA -> A ; +rel AbstractT.maybeA? -> A ; +rel AbstractT.manyA* -> A ; diff --git a/featureTest/src/main/java/de/tudresden/inf/st/jastadd/featureTest/FeatureTestMain.java b/featureTest/src/main/java/de/tudresden/inf/st/jastadd/featureTest/FeatureTestMain.java new file mode 100644 index 0000000000000000000000000000000000000000..298298c64c1ce5ff4f1a646c9aaacc2c465cf70f --- /dev/null +++ b/featureTest/src/main/java/de/tudresden/inf/st/jastadd/featureTest/FeatureTestMain.java @@ -0,0 +1,59 @@ +package de.tudresden.inf.st.jastadd.featureTest; + +import de.tudresden.inf.st.jastadd.dumpAst.ast.Dumper; +import de.tudresden.inf.st.jastadd.dumpAst.ast.SkinParamBooleanSetting; +import de.tudresden.inf.st.jastadd.dumpAst.ast.SkinParamStringSetting; +import org.jastadd.featureTest.ast.*; + +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; + +/** + * Main class of feature test. + * + * @author rschoene - Initial contribution + */ +public class FeatureTestMain { + + public static void main(String[] args) throws IOException { + Root root = new Root(); + root.setName("Root1"); + A a = new A(); + a.setName("A2"); + B b1 = new B(); + b1.setName("B3"); + C c = new C(); + c.setName("C4"); + c.setRawReference(a); + b1.setOneA(a); + B b2 = new B(); + b2.setName("B5"); + C myC = new C(); + myC.setName("C6"); + a.setMyC(myC); + root.setA(a); + root.addB(b1); + root.addB(b2); + root.setC(c); + + Path pathToYaml = Paths.get("featureTest.yml"); + Path pathToPng = Paths.get("featureTest.png"); +// Dumper.read(null).dumpAsPNG(pathToPng); + Dumper +// .read(null) + .read(root) +// .customPreamble("hide empty members") + .skinParam(SkinParamBooleanSetting.Shadowing, false) + .includeAttributes("referenceAttr") + .includeNonterminalAttributes("Calculated") + .includeNonterminalAttributes("AlsoCalculatedListNewSyntax") + .dumpAsYaml(pathToYaml, true) + .dumpAsPNG(pathToPng); + } + + private void m() { + //noinspection UnnecessaryReturnStatement + return; + } +} diff --git a/pages/docs/adding.md b/pages/docs/adding.md index 2822997b946d501554f3644f016452e95a1ee0a7..8bfc64a25902b465da7c07f6da1a73e7979258b2 100644 --- a/pages/docs/adding.md +++ b/pages/docs/adding.md @@ -1,12 +1,12 @@ -# Add relast2uml to your project +# Add DumpAst to your project -If you want to use `Relast2Uml`, either use the latest [pre-build version](#use-packaged-version) or clone the repository and [build it yourself](#build-from-source). +If you want to use `DumpAst`, either use the latest [pre-build version](#use-packaged-version) or clone the repository and [build it yourself](#build-from-source). ## Use packaged version Check the [package overview page](https://git-st.inf.tu-dresden.de/jastadd/relast2uml/-/packages) to find the latest versions of the individual packages. -First add this GitLab as a repository in your `build.gradle`: +Add this GitLab as a repository, and `dumpAst` as a dependency in your `build.gradle`: ``` repositories { @@ -15,68 +15,32 @@ repositories { url "https://git-st.inf.tu-dresden.de/api/v4/groups/jastadd/-/packages/maven" } } -``` - -### grammar2uml - -To use `Grammar2uml`, add it as a dependency: - -``` -configurations { - grammar2umlClasspath -} - -dependencies { - grammar2umlClasspath group: 'de.tudresden.inf.st', name: 'grammar2uml', version: '0.1.1' -} -``` -Finally, add a task to create your visualization: - -``` -task grammar2uml(type: JavaExec) { - main = 'de.tudresden.inf.st.jastadd.grammar2uml.compiler.Compiler' - classpath = configurations.grammar2umlClasspath - - args([ - '--verbose', - 'src/main/jastadd/GoalModel.relast' - ]) -} -``` - -### dumpAst - -To use `DumpAst` or `DumpAstWithPlantuml`, add it as a dependency: - -``` dependencies { - implementation group: 'de.tudresden.inf.st', name: 'dumpAst', version: '0.3.4' - // or - implementation group: 'de.tudresden.inf.st', name: 'dumpAstWithPlantuml', version: '0.3.4' + implementation group: 'de.tudresden.inf.st', name: 'dumpAst', version: '0.3.7' } ``` - ## Build from source -If you want to build the tools of `Relast2Uml` from source, first build the jar from the [Relast2Uml repository](https://git-st.inf.tu-dresden.de/jastadd/relast2uml): +If you want to build the tools of `DumpAst` from source, first build the jar from the [repository](https://git-st.inf.tu-dresden.de/jastadd/relast2uml): ```bash git clone https://git-st.inf.tu-dresden.de/jastadd/relast2uml.git cd relast2uml ./gradlew jar -ls dumpAst/build/libs/ dumpAstWithPlantuml/build/libs/ +ls dumpAst/build/libs/ ``` -Those JARs can then be copied to your project. +This JAR can then be copied to your project. ```bash cp dumpAst/build/libs/dumpAst-<version>.jar ../your-project/libs/dumpAst.jar cd ../your-project/ ``` -Finally, this JAR has to be integrated into your build process. In case [Gradle](https://gradle.org/) is used, the JAR file needs to be added as dependency using: +Finally, this JAR has to be integrated into your build process. +In case [Gradle](https://gradle.org/) is used, the JAR file needs to be added as dependency using: ```groovy dependencies { diff --git a/pages/docs/img/dumpAst.png b/pages/docs/img/dumpAst.png index db1fecd4eeef97f01d961f003ca2e2cd2e55d525..919d8b1df6856c215f512fb2fe0101b9b8510b90 100644 Binary files a/pages/docs/img/dumpAst.png and b/pages/docs/img/dumpAst.png differ diff --git a/pages/docs/index.md b/pages/docs/index.md index 43ab1c3a63dc9349e98eba10d6f0e7bc6a18c17a..80e8008f7e47b4ebc8676959db78292d306c4e76 100644 --- a/pages/docs/index.md +++ b/pages/docs/index.md @@ -23,9 +23,9 @@ Using the return value, the following methods are supported and can be chained t |---------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | customPreamble | Add the given string as preamble to the visualization. | | disableTypes | Disable all objects with types matching at least one of the given regex strings. Disabled objects won't be included in any output. However, their children are still processed. | -| dumpAsPNG | Write out content as PNG image generated by plantuml. (**Only available in dumpAstWithPlantuml**) | +| dumpAsPNG | Write out content as PNG image generated by plantuml. | | dumpAsSource | Write out content as plantuml source code | -| dumpAsSVG | Write out content as SVG image generated by plantuml. (**Only available in dumpAstWithPlantuml**) | +| dumpAsSVG | Write out content as SVG image generated by plantuml. | | dumpAsYaml | Write out content as YAML representation of the ASTNode | | enableDebug | Add debug information in dumped content, mainly version numbers. | | excludeAttributesFor | *experimental, documentation missing* | diff --git a/settings.gradle b/settings.gradle index 631fcc974ebb903bd09ad6b13cd6dd5fbd875f3d..3eb76f1e946e81f5037864327765fdf452bf8c5c 100644 --- a/settings.gradle +++ b/settings.gradle @@ -2,5 +2,6 @@ rootProject.name = 'relast2uml' include 'relast.preprocessor' include 'dumpAst' -include 'dumpAstWithPlantuml' include 'testDumper' +include 'featureTest' + diff --git a/testDumper/build.gradle b/testDumper/build.gradle index 1c0603dc55c06e22ddea0a727ea178102ece369a..34cd313f4905be14e54079c763fab87a07460f95 100644 --- a/testDumper/build.gradle +++ b/testDumper/build.gradle @@ -1,21 +1,42 @@ +// --- Buildscripts (must be at the top) --- buildscript { - repositories.mavenLocal() repositories.mavenCentral() dependencies { classpath group: 'org.jastadd', name: 'jastaddgradle', version: '1.13.3' } } +// --- Plugin definitions --- plugins { - id 'relast2uml.java-jastadd-conventions' + id 'java' + id 'java-library' + id 'idea' + id 'com.github.ben-manes.versions' } apply plugin: 'jastadd' +// --- Dependencies --- +repositories { + mavenCentral() +} + dependencies { - testImplementation project(':dumpAst') + implementation project(':dumpAst') + + api group: 'net.sf.beaver', name: 'beaver-rt', version: '0.9.11' + + testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: "${jupiter_version}" + testImplementation group: 'org.assertj', name: 'assertj-core', version: '3.18.1' + testRuntimeOnly group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: "${jupiter_version}" } + +// --- Preprocessors --- +File genSrc = file("src/gen/java") +sourceSets.main.java.srcDir genSrc +idea.module.generatedSourceDirs += genSrc + File testingGrammar = file('./src/main/jastadd/testDumper.relast') task relast(type: JavaExec) { @@ -50,6 +71,7 @@ task relast(type: JavaExec) { file('./src/gen/jastadd/testDumperResolverStubs.jrag')) } +// --- JastAdd --- jastadd { configureModuleBuild() modules { @@ -92,4 +114,10 @@ jastadd { jastaddOptions = ["--lineColumnNumbers", "--List=JastAddList", "--safeLazy", "--visitCheck=true", "--rewrite=cnta", "--cache=all"] } +// --- Tests --- +test.useJUnitPlatform() + +// --- Versioning and Publishing --- + +// --- Task order --- generateAst.dependsOn relast diff --git a/testDumper/src/test/java/de/tudresden/inf/st/jastadd/testDumper/TestDumperMain.java b/testDumper/src/test/java/de/tudresden/inf/st/jastadd/testDumper/TestDumperMain.java deleted file mode 100644 index d7b14047c0b144339dead510f89f8ea510e60464..0000000000000000000000000000000000000000 --- a/testDumper/src/test/java/de/tudresden/inf/st/jastadd/testDumper/TestDumperMain.java +++ /dev/null @@ -1,52 +0,0 @@ -package de.tudresden.inf.st.jastadd.testDumper; - -import de.tudresden.inf.st.jastadd.dumpAst.ast.DumpAst; -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 java.util.stream.Collectors; - -public class TestDumperMain { - public static void main(String[] args) { - Root root = new Root(); - root.setName("Root1"); - A a = new A(); - a.setName("A2"); - B b1 = new B(); - b1.setName("B3"); - C c = new C(); - c.setName("C4"); - c.setRawReference(a); - b1.setOneA(a); - B b2 = new B(); - b2.setName("B5"); - C myC = new C(); - myC.setName("C6"); - a.setMyC(myC); - root.setA(a); - root.addB(b1); - root.addB(b2); - root.setC(c); - - TestUtils.ExposingDumpBuilder builder = new TestUtils.ExposingDumpBuilder(root); - builder.includeAttributes("simpleAttr") - .orderChildren() - .includeNonterminalAttributes("getCalculated") - .setNameMethod(n -> n == null ? "null" : n.getClass().getSimpleName()); - - System.out.println(">> PlantUml"); - DumpAst dumpAst = builder.build(); - System.out.println(dumpAst.toPlantUml()); - - DumpNode node = dumpAst.getDumpNode(0); - System.out.println(node.getName()); - System.out.println(node.myChildren().stream().map(DumpNode::getName).collect(Collectors.joining(", "))); - -// System.out.println(">> YAML begin"); -// System.out.println(builder.build().toYaml(true)); -// System.out.println(">> YAML end"); - } -} 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 index 1ccba6cc518d0ec487b937f0efd81068013ba2af..1b9be71cc6a27b381a94127d8cab991f06a87479 100644 --- 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 @@ -6,13 +6,16 @@ import de.tudresden.inf.st.jastadd.dumpAst.ast.DumpNode; import org.jastadd.testDumper.ast.*; import org.junit.jupiter.api.Test; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.List; import java.util.Optional; import static de.tudresden.inf.st.jastadd.testDumper.TestUtils.*; import static org.assertj.core.api.Assertions.*; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.*; public class TestSimple { @@ -28,6 +31,22 @@ public class TestSimple { assertEquals(0, actualRoot.getNumDumpRelation()); } + @Test + public void testCustomPreamble() throws IOException { + Root root = createRoot(null, null); + String preamble = "{<\n>\n}"; + ExposingDumpBuilder builder = new ExposingDumpBuilder(root); + builder.excludeNullNodes().customPreamble(preamble); + + Path path = Paths.get("src/gen/resources/"); + Files.createDirectories(path); + builder.dumpAsYaml(path.resolve("customPreamble.yaml"), true); + + DumpAst dumpAst = builder.build(); + String puml = dumpAst.toPlantUml(); + assertThat(puml).contains(preamble); + } + @Test void testChildlessNonterminal() { Root root = createRoot(createA(A_NAME, a -> a.setD(new D())), null); List<DumpNode> nodes = TestUtils.dumpModel(root); @@ -80,10 +99,11 @@ public class TestSimple { @Test public void testOrderedListChildren() { - Root root = createRoot(null, null, createB(B1_NAME), createB(B2_NAME), createB(B3_NAME)); + Root root = createRoot(createA(A_NAME), createC(C_NAME), createB(B1_NAME), createB(B2_NAME), createB(B3_NAME)); List<DumpNode> nodes = TestUtils.dumpModel(root, DumpBuilder::orderChildren); - assertThat(nodes).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(ROOT_NAME, B1_NAME, B2_NAME, B3_NAME); + assertThat(nodes).flatExtracting(NAME_EXTRACTOR).containsExactlyInAnyOrder( + ROOT_NAME, A_NAME, B1_NAME, B2_NAME, B3_NAME, C_NAME); DumpNode actualRoot = TestUtils.findByName(nodes, ROOT_NAME); // in grammar: DumpAst ::= [...] DumpNode* [...]; @@ -91,7 +111,30 @@ public class TestSimple { assertTrue(((DumpAst) actualRoot.getParent().getParent()).getPrintConfig().getOrderChildren()); List<DumpNode> children = actualRoot.myChildren(); - assertThat(children).extracting(NAME_EXTRACTOR).containsExactlyInAnyOrder(B1_NAME, B2_NAME, B3_NAME); + assertThat(children).extracting(NAME_EXTRACTOR).containsExactly( + A_NAME, B1_NAME, B2_NAME, B3_NAME, C_NAME); + + DumpNode actualA = TestUtils.findByName(nodes, A_NAME); + DumpNode actualB1 = TestUtils.findByName(nodes, B1_NAME); + DumpNode actualB2 = TestUtils.findByName(nodes, B2_NAME); + DumpNode actualB3 = TestUtils.findByName(nodes, B3_NAME); + DumpNode actualC = TestUtils.findByName(nodes, C_NAME); + + for (DumpNode d : children) { + System.out.println(d.getName() + "/" + d.getLabel() + " = " + d); + } + + assertEquals(actualB1, actualA.successor(), actualA.successor().getName()); + assertEquals(actualB2, actualB1.successor(), actualB1.successor().getName()); + assertEquals(actualB3, actualB2.successor(), actualB2.successor().getName()); + assertEquals(actualC, actualB3.successor(), actualB3.successor().getName()); + assertNull(actualC.successor()); + + assertTrue(actualA.hasSuccessor()); + assertTrue(actualB1.hasSuccessor()); + assertTrue(actualB2.hasSuccessor()); + assertTrue(actualB3.hasSuccessor()); + assertFalse(actualC.hasSuccessor()); } @Test diff --git a/testDumper/src/test/java/de/tudresden/inf/st/jastadd/testDumper/TestUtils.java b/testDumper/src/test/java/de/tudresden/inf/st/jastadd/testDumper/TestUtils.java index 10a0859b6dec99ff2b7797c0ea382c774722bdfd..b3e8fa92d7ddfcefc0331facc0d27e228f6363b8 100644 --- a/testDumper/src/test/java/de/tudresden/inf/st/jastadd/testDumper/TestUtils.java +++ b/testDumper/src/test/java/de/tudresden/inf/st/jastadd/testDumper/TestUtils.java @@ -163,7 +163,7 @@ public class TestUtils { dumpAst.toPlantUml(); List<DumpNode> result = new ArrayList<>(); for (DumpNode dumpNode : dumpAst.getDumpNodeList()) { - if (dumpNode.isAstNode() && !dumpNode.getInvisible()) { + if ((dumpNode.isAstNode() || dumpNode.isNull()) && !dumpNode.getInvisible()) { result.add(dumpNode); } } @@ -187,7 +187,7 @@ public class TestUtils { if (!dumpChildNode.isList()) { // then it is a DumpNormalChildNode DumpNode target = ((DumpNormalChildNode) dumpChildNode).getDumpNode(); - if (!target.getInvisible()) { + if (target != null && !target.getInvisible()) { result.put(dumpChildNode.getName(), target); } } @@ -201,7 +201,7 @@ public class TestUtils { if (dumpChildNode.isList()) { // then it is a DumpListChildNode ((DumpListChildNode) dumpChildNode).getInnerDumpNodeList().forEach(inner -> { - if (!inner.getDumpNode().getInvisible()) { + if (inner.getDumpNode() != null && !inner.getDumpNode().getInvisible()) { result.computeIfAbsent(dumpChildNode.getName(), key -> new ArrayList<>()).add(inner.getDumpNode()); } });