Skip to content
Snippets Groups Projects
Commit f1d9cf2c authored by René Schöne's avatar René Schöne
Browse files

Merge branch 'dev' into 'main'

1.1.0

See merge request jastadd/relast2uml!11
parents 469f5036 f9d17e5f
No related branches found
No related tags found
1 merge request!111.1.0
Pipeline #13991 failed
Showing
with 1148 additions and 667 deletions
variables: variables:
GIT_SUBMODULE_STRATEGY: recursive GIT_SUBMODULE_STRATEGY: recursive
before_script:
- export GRADLE_USER_HOME=`pwd`/.gradle
cache:
paths:
- .gradle/wrapper
- .gradle/caches
stages: stages:
- build - build
- test - test
......
[submodule "relast.preprocessor"]
path = relast.preprocessor
url = ../relast-preprocessor.git
[submodule "dumpAst/src/main/jastadd/mustache"] [submodule "dumpAst/src/main/jastadd/mustache"]
path = dumpAst/src/main/jastadd/mustache path = dumpAst/src/main/jastadd/mustache
url = ../mustache url = ../mustache
plugins { plugins {
id 'com.github.ben-manes.versions' version '0.36.0' id 'com.github.ben-manes.versions' version '0.36.0'
id 'java'
}
java.toolchain.languageVersion = JavaLanguageVersion.of(11)
if (JavaVersion.current().isJava8Compatible()) {
allprojects {
tasks.withType(Javadoc) {
options.addStringOption('Xdoclint:none', '-quiet')
}
}
} }
// --- Buildscripts (must be at the top) --- // --- Buildscripts (must be at the top) ---
buildscript { buildscript {
repositories.mavenLocal()
repositories.mavenCentral() repositories.mavenCentral()
dependencies { dependencies {
classpath group: 'org.jastadd', name: 'jastaddgradle', version: '1.13.3' classpath group: 'org.jastadd', name: 'jastaddgradle', version: '1.13.3'
...@@ -9,23 +8,30 @@ buildscript { ...@@ -9,23 +8,30 @@ buildscript {
// --- Plugin definitions --- // --- Plugin definitions ---
plugins { plugins {
id 'com.github.ben-manes.versions'
id 'java' id 'java'
id 'idea' id 'idea'
id 'com.github.ben-manes.versions' id 'org.jastadd'
id 'java-library' id 'java-library'
id 'maven-publish' id 'maven-publish'
} }
apply plugin: 'jastadd'
// --- Dependencies --- // --- Dependencies ---
repositories { repositories {
mavenCentral() mavenCentral()
maven {
name 'gitlab-maven'
url 'https://git-st.inf.tu-dresden.de/api/v4/groups/jastadd/-/packages/maven'
}
}
configurations {
relast
} }
dependencies { dependencies {
jastadd2 "org.jastadd:jastadd:2.3.5" jastadd2 "org.jastadd:jastadd:2.3.5"
// https://mvnrepository.com/artifact/net.sourceforge.plantuml/plantuml relast group: 'org.jastadd', name: 'relast', version: "${relast_version}"
implementation group: 'net.sourceforge.plantuml', name: 'plantuml', version: '1.2022.2' implementation group: 'net.sourceforge.plantuml', name: 'plantuml', version: '1.2022.2'
api group: 'net.sf.beaver', name: 'beaver-rt', version: '0.9.11' 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: 'com.github.spullara.mustache.java', name: 'compiler', version: "0.9.10"
...@@ -42,18 +48,15 @@ File mustacheGrammar = file('./src/main/jastadd/mustache/Mustache.relast') ...@@ -42,18 +48,15 @@ File mustacheGrammar = file('./src/main/jastadd/mustache/Mustache.relast')
task relast(type: JavaExec) { task relast(type: JavaExec) {
group = 'Build' group = 'Build'
main = "-jar" classpath = configurations.relast
mainClass = 'org.jastadd.relast.compiler.Compiler'
doFirst { doFirst {
delete "src/gen/jastadd/*.ast" delete "src/gen/jastadd/*"
delete "src/gen/jastadd/DumpAst.jadd"
delete "src/gen/jastadd/DumpAstRefResolver.jadd"
delete "src/gen/jastadd/DumpAstResolverStubs.jrag"
mkdir "src/gen/jastadd/" mkdir "src/gen/jastadd/"
} }
args = [ args = [
"../libs/relast.jar",
dumpAstGrammar, dumpAstGrammar,
mustacheGrammar, mustacheGrammar,
"--listClass=java.util.ArrayList", "--listClass=java.util.ArrayList",
...@@ -63,14 +66,6 @@ task relast(type: JavaExec) { ...@@ -63,14 +66,6 @@ task relast(type: JavaExec) {
"--resolverHelper", "--resolverHelper",
"--grammarName=./src/gen/jastadd/DumpAst" "--grammarName=./src/gen/jastadd/DumpAst"
] ]
inputs.files(file("../libs/relast.jar"),
dumpAstGrammar,
mustacheGrammar)
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 --- // --- JastAdd ---
...@@ -121,6 +116,16 @@ jastadd { ...@@ -121,6 +116,16 @@ jastadd {
// --- Versioning and Publishing --- // --- Versioning and Publishing ---
group = 'de.tudresden.inf.st' group = 'de.tudresden.inf.st'
task fatJar(type: Jar) {
dependsOn jar
group = "build"
archiveAppendix = "fatjar"
from sourceSets.main.output
from {
configurations.runtimeClasspath.collect {it.isDirectory() ? it : zipTree(it) }
}
}
def versionFile = "src/main/resources/${project.getName()}Version.properties" def versionFile = "src/main/resources/${project.getName()}Version.properties"
try { try {
...@@ -154,6 +159,11 @@ task setDevVersionForCI() { ...@@ -154,6 +159,11 @@ task setDevVersionForCI() {
} }
} }
java {
withJavadocJar()
withSourcesJar()
}
publishing { publishing {
publications { publications {
maven(MavenPublication) { maven(MavenPublication) {
......
...@@ -4,8 +4,11 @@ rel DumpAst.RootNode? -> DumpNode ; ...@@ -4,8 +4,11 @@ rel DumpAst.RootNode? -> DumpNode ;
DumpNode ::= DumpChildNode* DumpToken* DumpRelation* DumpNode ::= DumpChildNode* DumpToken* DumpRelation*
<Name> <Label> <BackgroundColor> <TextColor> <Object:Object> <Invisible:boolean> <Computed:boolean> <ManualStereotypes> <Name> <Label> <BackgroundColor> <TextColor> <Object:Object> <Invisible:boolean> <Computed:boolean> <ManualStereotypes>
/InvisiblePath/ ; /InvisiblePath/ ;
InnerDumpNode ; InnerDumpNode ;
rel InnerDumpNode.DumpNode <-> DumpNode.ContainerOfInner ; rel InnerDumpNode.DumpNode <-> DumpNode.ContainerOfInner ;
InnerRelationDumpNode;
rel InnerRelationDumpNode.DumpNode -> DumpNode ; // .ContainerOfInner*
abstract DumpChildNode ::= <Name> <Computed:boolean> ; abstract DumpChildNode ::= <Name> <Computed:boolean> ;
DumpNormalChildNode : DumpChildNode ; DumpNormalChildNode : DumpChildNode ;
...@@ -15,15 +18,16 @@ DumpListChildNode : DumpChildNode ::= InnerDumpNode* ; ...@@ -15,15 +18,16 @@ DumpListChildNode : DumpChildNode ::= InnerDumpNode* ;
abstract DumpToken ::= <Name> <Computed:boolean> ; abstract DumpToken ::= <Name> <Computed:boolean> ;
DumpReferenceToken : DumpToken ; DumpReferenceToken : DumpToken ;
rel DumpReferenceToken.Value -> DumpNode ; rel DumpReferenceToken.Value -> DumpNode ;
DumpReferenceListToken : DumpToken ::= InnerRelationDumpNode* ;
DumpValueToken : DumpToken ::= <Value:Object> ; DumpValueToken : DumpToken ::= <Value:Object> ;
abstract DumpRelation ::= <Name> <Bidirectional:boolean> ; abstract DumpRelation ::= <Name> <Bidirectional:boolean> ;
DumpNormalRelation : DumpRelation ; DumpNormalRelation : DumpRelation ;
rel DumpNormalRelation.DumpNode -> DumpNode ; rel DumpNormalRelation.DumpNode -> DumpNode ;
DumpListRelation : DumpRelation ::= InnerDumpNode* ; DumpListRelation : DumpRelation ::= InnerRelationDumpNode* ;
// type of NTA // type of NTA
InvisiblePath ::= InnerDumpNode* ; InvisiblePath ::= InnerRelationDumpNode* ;
ClassAnalysisResult ::= ContainmentMethod:AnalysedMethod* OtherMethod:AnalysedMethod* ; ClassAnalysisResult ::= ContainmentMethod:AnalysedMethod* OtherMethod:AnalysedMethod* ;
abstract AnalysedMethod ::= <Method:java.lang.reflect.Method> <Name> ; abstract AnalysedMethod ::= <Method:java.lang.reflect.Method> <Name> ;
...@@ -47,6 +51,10 @@ BuildConfig ::= StyleInformation ...@@ -47,6 +51,10 @@ BuildConfig ::= StyleInformation
GlobalPatternCollection:PatternCollection GlobalPatternCollection:PatternCollection
ExcludeTypePattern:TypePatternCollectionMapping* ExcludeTypePattern:TypePatternCollectionMapping*
IncludeTypePattern:TypePatternCollectionMapping* IncludeTypePattern:TypePatternCollectionMapping*
<IncludeRelationMethod:IncludeRelationMethod>
<IncludeChildMethod:IncludeChildMethod>
<IncludeAttributeMethod:IncludeAttributeMethod>
<IncludeTokenMethod:IncludeTokenMethod>
<TypeIgnorePattern> <TypeIgnorePattern>
<IncludeEmptyString:boolean> <IncludeEmptyString:boolean>
<ExcludeNullNodes:boolean> <ExcludeNullNodes:boolean>
...@@ -58,5 +66,6 @@ StyleInformation ::= <NameMethod:StyleMethod> <BackgroundColorMethod:StyleMethod ...@@ -58,5 +66,6 @@ StyleInformation ::= <NameMethod:StyleMethod> <BackgroundColorMethod:StyleMethod
PrintConfig ::= Header* PrintConfig ::= Header*
<Scale:double> <Scale:double>
<Version> <Version>
<RelationWithRank:boolean>
<OrderChildren:boolean> ; <OrderChildren:boolean> ;
Header ::= <Value> ; Header ::= <Value> ;
...@@ -11,7 +11,7 @@ aspect GenerationBackend { ...@@ -11,7 +11,7 @@ aspect GenerationBackend {
public boolean computed; public boolean computed;
public TransformationOptions asRelation() { public TransformationOptions asRelation() {
return fromSource(Source.RELATION, false); return fromSource(Source.RELATION, false).allowNullObjectsOnce();
} }
public TransformationOptions asRoot() { public TransformationOptions asRoot() {
...@@ -119,12 +119,18 @@ aspect GenerationBackend { ...@@ -119,12 +119,18 @@ aspect GenerationBackend {
return node; return node;
} }
final ClassAnalysisResult car = analyzeClass(obj.getClass()); final ClassAnalysisResult car = analyzeClass(obj.getClass());
if (getBuildConfig().getDebug()) {
System.out.println("for node " + obj + ", analysis was:\n" + car.prettyPrint());
}
for (AnalysedMethod containmentMethod : car.getContainmentMethodList()) { for (AnalysedMethod containmentMethod : car.getContainmentMethodList()) {
if (containmentMethod.isSingleChildMethod()) { if (containmentMethod.isSingleChildMethod()) {
// -- singleChild -- // -- singleChild --
Object target = containmentMethod.getMethod().invoke(obj); Object target = containmentMethod.getMethod().invoke(obj);
String childName = containmentMethod.getName(); String childName = containmentMethod.getName();
DumpNode targetNode = transform(tti, target, options.asNormal(!isChildEnabled(objClassName, childName)).allowNullObjectsOnce()); if (!getBuildConfig().getIncludeChildMethod().shouldInclude(obj, target, childName)) {
continue;
}
DumpNode targetNode = transform(tti, target, options.asNormal(false).allowNullObjectsOnce());
if (targetNode != null) { if (targetNode != null) {
DumpNormalChildNode normalChild = new DumpNormalChildNode(); DumpNormalChildNode normalChild = new DumpNormalChildNode();
normalChild.setName(childName); normalChild.setName(childName);
...@@ -138,10 +144,12 @@ aspect GenerationBackend { ...@@ -138,10 +144,12 @@ aspect GenerationBackend {
DumpListChildNode listChild = new DumpListChildNode(); DumpListChildNode listChild = new DumpListChildNode();
listChild.setComputed(false); listChild.setComputed(false);
String childName = containmentMethod.getName(); String childName = containmentMethod.getName();
boolean shouldBeInvisisble = !isChildEnabled(objClassName, childName);
listChild.setName(childName); listChild.setName(childName);
for (Object target : targetList) { for (Object target : targetList) {
DumpNode targetNode = transform(tti, target, options.asNormal(shouldBeInvisisble)); if (!getBuildConfig().getIncludeChildMethod().shouldInclude(obj, target, childName)) {
continue;
}
DumpNode targetNode = transform(tti, target, options.asNormal(false));
if (target != null && targetNode != null) { if (target != null && targetNode != null) {
listChild.addInnerDumpNode(new InnerDumpNode().setDumpNode(targetNode)); listChild.addInnerDumpNode(new InnerDumpNode().setDumpNode(targetNode));
} }
...@@ -159,7 +167,13 @@ aspect GenerationBackend { ...@@ -159,7 +167,13 @@ aspect GenerationBackend {
Object target = otherMethod.getMethod().invoke(obj); Object target = otherMethod.getMethod().invoke(obj);
String childName = otherMethod.getName(); String childName = otherMethod.getName();
boolean computed = otherMethod.asSingleChildMethod().isNTASingleChildMethod(); boolean computed = otherMethod.asSingleChildMethod().isNTASingleChildMethod();
DumpNode targetNode = transform(tti, target, options.asNormal(!isChildEnabled(objClassName, childName)).computed(computed).allowNullObjectsOnce()); boolean shouldInclude = computed ?
getBuildConfig().getIncludeAttributeMethod().shouldInclude(obj, childName, true, () -> catchedInvoke(otherMethod.getMethod(), obj)) :
getBuildConfig().getIncludeChildMethod().shouldInclude(obj, target, childName);
if (!shouldInclude) {
continue;
}
DumpNode targetNode = transform(tti, target, options.asNormal(false).computed(computed).allowNullObjectsOnce());
if (targetNode != null) { if (targetNode != null) {
DumpNormalChildNode normalChild = new DumpNormalChildNode(); DumpNormalChildNode normalChild = new DumpNormalChildNode();
normalChild.setName(childName); normalChild.setName(childName);
...@@ -174,10 +188,15 @@ aspect GenerationBackend { ...@@ -174,10 +188,15 @@ aspect GenerationBackend {
boolean computed = otherMethod.asListChildMethod().isNTAListChildMethod(); boolean computed = otherMethod.asListChildMethod().isNTAListChildMethod();
listChild.setComputed(computed); listChild.setComputed(computed);
String childName = otherMethod.getName(); String childName = otherMethod.getName();
boolean shouldBeInvisisble = !isChildEnabled(objClassName, childName);
listChild.setName(childName); listChild.setName(childName);
for (Object target : targetList) { for (Object target : targetList) {
DumpNode targetNode = transform(tti, target, options.asNormal(shouldBeInvisisble).computed(computed)); boolean shouldInclude = computed ?
getBuildConfig().getIncludeAttributeMethod().shouldInclude(obj, childName, true, () -> catchedInvoke(otherMethod.getMethod(), obj)) :
getBuildConfig().getIncludeChildMethod().shouldInclude(obj, target, childName);
if (!shouldInclude) {
continue;
}
DumpNode targetNode = transform(tti, target, options.asNormal(false).computed(computed));
if (target != null && targetNode != null) { if (target != null && targetNode != null) {
listChild.addInnerDumpNode(new InnerDumpNode().setDumpNode(targetNode)); listChild.addInnerDumpNode(new InnerDumpNode().setDumpNode(targetNode));
} }
...@@ -188,8 +207,11 @@ aspect GenerationBackend { ...@@ -188,8 +207,11 @@ aspect GenerationBackend {
} else if (otherMethod.isSingleRelationMethod()) { } else if (otherMethod.isSingleRelationMethod()) {
// -- singleRelation -- // -- singleRelation --
Object target = otherMethod.getMethod().invoke(obj); Object target = otherMethod.getMethod().invoke(obj);
if (!getBuildConfig().getIncludeRelationMethod().shouldInclude(obj, target, otherMethod.getName())) {
continue;
}
DumpNode targetNode = transform(tti, target, options.asRelation()); DumpNode targetNode = transform(tti, target, options.asRelation());
if (target != null && targetNode != null) { if (targetNode != null) {
DumpNormalRelation normalRelation = new DumpNormalRelation(); DumpNormalRelation normalRelation = new DumpNormalRelation();
normalRelation.setName(otherMethod.getName()); normalRelation.setName(otherMethod.getName());
normalRelation.setDumpNode(targetNode); normalRelation.setDumpNode(targetNode);
...@@ -201,31 +223,61 @@ aspect GenerationBackend { ...@@ -201,31 +223,61 @@ aspect GenerationBackend {
DumpListRelation listRelation = new DumpListRelation(); DumpListRelation listRelation = new DumpListRelation();
listRelation.setName(otherMethod.getName()); listRelation.setName(otherMethod.getName());
for (Object target : targetList) { for (Object target : targetList) {
if (!getBuildConfig().getIncludeRelationMethod().shouldInclude(obj, target, otherMethod.getName())) {
continue;
}
DumpNode targetNode = transform(tti, target, options.asRelation()); DumpNode targetNode = transform(tti, target, options.asRelation());
if (target != null && targetNode != null) { if (target != null && targetNode != null) {
listRelation.addInnerDumpNode(new InnerDumpNode(targetNode)); listRelation.addInnerRelationDumpNode(new InnerRelationDumpNode(targetNode));
} }
} }
if (listRelation.getNumInnerDumpNode() > 0) { if (listRelation.getNumInnerRelationDumpNode() > 0) {
node.addDumpRelation(listRelation); node.addDumpRelation(listRelation);
} }
} else if (otherMethod.isTokenMethod()) { } else if (otherMethod.isTokenMethod()) {
// -- token -- // -- token --
TokenMethod tokenMethod = otherMethod.asTokenMethod();
boolean shouldInclude = tokenMethod.isAttributeMethod() ?
getBuildConfig().getIncludeAttributeMethod().shouldInclude(obj, otherMethod.getName(), false, () -> catchedInvoke(otherMethod.getMethod(), obj)) :
getBuildConfig().getIncludeTokenMethod().shouldInclude(obj, otherMethod.getName(), otherMethod.getMethod().invoke(obj));
if (!shouldInclude) {
continue;
}
Object target = otherMethod.getMethod().invoke(obj); Object target = otherMethod.getMethod().invoke(obj);
if (target != null) { if (target != null) {
DumpNode targetNode = transform(tti, target, options.asRelation());
DumpToken token = null; DumpToken token = null;
// TODO check, if Iterable.isAssignableFrom(target.getClass()). boolean atLeastOneASTNode = false;
// if so, check isAstNode for first non-null to add DumpReferenceToken's, otherwise DumpValueToken's if (Iterable.class.isAssignableFrom(target.getClass())) {
java.util.List<DumpNode> nodes = new java.util.ArrayList<>();
Iterable iterable = (Iterable) target;
for (Object element : iterable) {
// TODO check if isAstNode for first non-null. if yes, use DumpReferenceListToken, other DumpValueToken
DumpNode nodeForElement = transform(tti, element, options.asRelation());
nodes.add(nodeForElement);
if (nodeForElement != null && nodeForElement.isAstNode()) {
atLeastOneASTNode = true;
}
}
if (atLeastOneASTNode) {
DumpReferenceListToken listToken = new DumpReferenceListToken();
nodes.forEach(element -> {
listToken.addInnerRelationDumpNode(new InnerRelationDumpNode().setDumpNode(element));
});
token = listToken;
}
}
if (!atLeastOneASTNode) {
DumpNode targetNode = transform(tti, target, options.asRelation());
if (targetNode != null && targetNode.isAstNode()) { if (targetNode != null && targetNode.isAstNode()) {
token = new DumpReferenceToken().setValue(targetNode); token = new DumpReferenceToken().setValue(targetNode);
} else { } else {
if (target != null && (getBuildConfig().getIncludeEmptyString() || !target.toString().isEmpty())) { if (getBuildConfig().getIncludeEmptyString() || !target.toString().isEmpty()) {
DumpValueToken valueToken = new DumpValueToken(); DumpValueToken valueToken = new DumpValueToken();
valueToken.setValue(target); valueToken.setValue(target);
token = valueToken; token = valueToken;
} }
} }
}
if (token != null) { if (token != null) {
token.setName(otherMethod.getName()); token.setName(otherMethod.getName());
token.setComputed(otherMethod.asTokenMethod().isAttributeMethod()); token.setComputed(otherMethod.asTokenMethod().isAttributeMethod());
...@@ -239,6 +291,16 @@ aspect GenerationBackend { ...@@ -239,6 +291,16 @@ aspect GenerationBackend {
return node; return node;
} }
private Object DumpAst.catchedInvoke(java.lang.reflect.Method method, Object obj) {
try {
return method.invoke(obj);
}
catch (Exception e) {
e.printStackTrace();
return null;
}
}
private void DumpAst.applyStyle(DumpNode node) { private void DumpAst.applyStyle(DumpNode node) {
Object obj = node.getObject(); Object obj = node.getObject();
node.setLabel(getBuildConfig().getStyleInformation().getNameMethod().get(obj)); node.setLabel(getBuildConfig().getStyleInformation().getNameMethod().get(obj));
...@@ -251,7 +313,7 @@ aspect GenerationBackend { ...@@ -251,7 +313,7 @@ aspect GenerationBackend {
ClassAnalysisResult result = new ClassAnalysisResult(); ClassAnalysisResult result = new ClassAnalysisResult();
String clazzName = clazz.getSimpleName(); String clazzName = clazz.getSimpleName();
java.util.List<String> targetOrder = targetOrder(clazz); java.util.List<String> targetOrder = targetOrder(clazz);
for (java.lang.reflect.Method method : clazz.getMethods()) { methodLoop: for (java.lang.reflect.Method method : clazz.getMethods()) {
for (java.lang.annotation.Annotation annotation : method.getAnnotations()) { for (java.lang.annotation.Annotation annotation : method.getAnnotations()) {
String canonicalName = annotation.annotationType().getCanonicalName(); String canonicalName = annotation.annotationType().getCanonicalName();
if (canonicalName.startsWith(astNodeAnnotationPrefix())) { if (canonicalName.startsWith(astNodeAnnotationPrefix())) {
...@@ -296,12 +358,10 @@ aspect GenerationBackend { ...@@ -296,12 +358,10 @@ aspect GenerationBackend {
try { try {
java.lang.reflect.Method relationMethod = clazz.getMethod("get" + relationName); java.lang.reflect.Method relationMethod = clazz.getMethod("get" + relationName);
// normal get + token-name -> singleRelation // normal get + token-name -> singleRelation
if (isRelationEnabled(clazzName, relationName)) {
SingleRelationMethod singleRelationMethod = new SingleRelationMethod(); SingleRelationMethod singleRelationMethod = new SingleRelationMethod();
singleRelationMethod.setMethod(relationMethod); singleRelationMethod.setMethod(relationMethod);
singleRelationMethod.setName(relationName); singleRelationMethod.setName(relationName);
result.addOtherMethod(singleRelationMethod); result.addOtherMethod(singleRelationMethod);
}
continue; continue;
} catch (NoSuchMethodException e) { } catch (NoSuchMethodException e) {
// ignore, but we know this is probably not a single relation // ignore, but we know this is probably not a single relation
...@@ -310,23 +370,19 @@ aspect GenerationBackend { ...@@ -310,23 +370,19 @@ aspect GenerationBackend {
try { try {
java.lang.reflect.Method relationMethod = clazz.getMethod("get" + relationName + "List"); java.lang.reflect.Method relationMethod = clazz.getMethod("get" + relationName + "List");
// normal get + token-name + "List" -> listRelation // normal get + token-name + "List" -> listRelation
if (isRelationEnabled(clazzName, relationName)) {
ListRelationMethod listRelationMethod = new ListRelationMethod(); ListRelationMethod listRelationMethod = new ListRelationMethod();
listRelationMethod.setMethod(relationMethod); listRelationMethod.setMethod(relationMethod);
listRelationMethod.setName(relationName); listRelationMethod.setName(relationName);
result.addOtherMethod(listRelationMethod); result.addOtherMethod(listRelationMethod);
}
continue; continue;
} catch (NoSuchMethodException e) { } catch (NoSuchMethodException e) {
// ignore, but we know this is probably not a relation at all // ignore, but we know this is probably not a relation at all
} }
} }
if (isTokenEnabled(clazzName, tokenName)) {
IntrinsicTokenMethod tokenMethod = new IntrinsicTokenMethod(); IntrinsicTokenMethod tokenMethod = new IntrinsicTokenMethod();
tokenMethod.setMethod(method); tokenMethod.setMethod(method);
tokenMethod.setName(tokenName); tokenMethod.setName(tokenName);
result.addOtherMethod(tokenMethod); result.addOtherMethod(tokenMethod);
}
break; break;
case "Attribute": case "Attribute":
String attributeName = method.getName(); String attributeName = method.getName();
...@@ -340,7 +396,6 @@ aspect GenerationBackend { ...@@ -340,7 +396,6 @@ aspect GenerationBackend {
if (attributeName.endsWith("List")) { if (attributeName.endsWith("List")) {
attributeName = attributeName.substring(0, attributeName.length() - 4); attributeName = attributeName.substring(0, attributeName.length() - 4);
} }
if (isNonterminalAttributeEnabled(clazzName, attributeName)) {
if (Iterable.class.isAssignableFrom(method.getReturnType())) { if (Iterable.class.isAssignableFrom(method.getReturnType())) {
NTAListChildMethod ntaListChildMethod = new NTAListChildMethod(); NTAListChildMethod ntaListChildMethod = new NTAListChildMethod();
ntaListChildMethod.setMethod(method); ntaListChildMethod.setMethod(method);
...@@ -352,8 +407,7 @@ aspect GenerationBackend { ...@@ -352,8 +407,7 @@ aspect GenerationBackend {
ntaSingleChildMethod.setName(attributeName); ntaSingleChildMethod.setName(attributeName);
result.addOtherMethod(ntaSingleChildMethod); result.addOtherMethod(ntaSingleChildMethod);
} }
} } else {
} else if (isAttributeEnabled(clazzName, attributeName)) {
// normal attribute // normal attribute
AttributeMethod attributeMethod = new AttributeMethod(); AttributeMethod attributeMethod = new AttributeMethod();
attributeMethod.setMethod(method); attributeMethod.setMethod(method);
...@@ -366,11 +420,11 @@ aspect GenerationBackend { ...@@ -366,11 +420,11 @@ aspect GenerationBackend {
int indexOfContextInTarget = targetOrder.indexOf(contextNameToAdd); int indexOfContextInTarget = targetOrder.indexOf(contextNameToAdd);
if (indexOfContextInTarget == 0) { if (indexOfContextInTarget == 0) {
result.getContainmentMethodList().insertChild(containmentMethodToAdd, 0); result.getContainmentMethodList().insertChild(containmentMethodToAdd, 0);
continue; continue methodLoop;
} }
if (indexOfContextInTarget == targetOrder.size() - 1) { if (indexOfContextInTarget == targetOrder.size() - 1) {
result.addContainmentMethod(containmentMethodToAdd); result.addContainmentMethod(containmentMethodToAdd);
continue; continue methodLoop;
} }
for (int i = 0, size = result.getNumContainmentMethod(); i < size; i++) { for (int i = 0, size = result.getNumContainmentMethod(); i < size; i++) {
...@@ -378,7 +432,7 @@ aspect GenerationBackend { ...@@ -378,7 +432,7 @@ aspect GenerationBackend {
int indexOfCurrentInTarget = targetOrder.indexOf(currentContextName); int indexOfCurrentInTarget = targetOrder.indexOf(currentContextName);
if (indexOfCurrentInTarget > indexOfContextInTarget) { if (indexOfCurrentInTarget > indexOfContextInTarget) {
result.getContainmentMethodList().insertChild(containmentMethodToAdd, i); result.getContainmentMethodList().insertChild(containmentMethodToAdd, i);
break; continue methodLoop;
} }
} }
result.addContainmentMethod(containmentMethodToAdd); result.addContainmentMethod(containmentMethodToAdd);
...@@ -418,90 +472,6 @@ aspect GenerationBackend { ...@@ -418,90 +472,6 @@ aspect GenerationBackend {
syn boolean DumpAst.isTypeEnabled(String typeName) { syn boolean DumpAst.isTypeEnabled(String typeName) {
return !matches(getBuildConfig().typeIgnorePattern(), typeName); return !matches(getBuildConfig().typeIgnorePattern(), typeName);
} }
// --- isTokenEnabled ---
syn boolean DumpAst.isTokenEnabled(String parentType, String tokenName) {
// level 4: excluded for type? -> return no
PatternCollection excludeOnType = getBuildConfig().matchExcludePatternCollection(parentType);
if (excludeOnType != null && matches(excludeOnType.tokenPattern(), tokenName)) {
return false;
}
// level 3: included for type? -> return yes
PatternCollection includeOnType = getBuildConfig().matchIncludePatternCollection(parentType);
if (includeOnType != null && matches(includeOnType.tokenPattern(), tokenName)) {
return true;
}
// level 2: globally excluded? -> return no
// level 1: otherwise return yes
return !matches(getBuildConfig().getGlobalPatternCollection().tokenPattern(), tokenName);
}
// --- isChildEnabled ---
syn boolean DumpAst.isChildEnabled(String parentType, String childName) {
// level 4: excluded for type? -> return no
PatternCollection excludeOnType = getBuildConfig().matchExcludePatternCollection(parentType);
if (excludeOnType != null && matches(excludeOnType.childPattern(), childName)) {
return false;
}
// level 3: included for type? -> return yes
PatternCollection includeOnType = getBuildConfig().matchIncludePatternCollection(parentType);
if (includeOnType != null && matches(includeOnType.childPattern(), childName)) {
return true;
}
// level 2: globally excluded? -> return no
// level 1: otherwise return yes
return !matches(getBuildConfig().getGlobalPatternCollection().childPattern(), childName);
}
// --- isRelationEnabled ---
syn boolean DumpAst.isRelationEnabled(String parentType, String relationName) {
// level 4: excluded for type? -> return no
PatternCollection excludeOnType = getBuildConfig().matchExcludePatternCollection(parentType);
if (excludeOnType != null && matches(excludeOnType.relationPattern(), relationName)) {
return false;
}
// level 3: included for type? -> return yes
PatternCollection includeOnType = getBuildConfig().matchIncludePatternCollection(parentType);
if (includeOnType != null && matches(includeOnType.relationPattern(), relationName)) {
return true;
}
// level 2: globally excluded? -> return no
// level 1: otherwise return yes
return !matches(getBuildConfig().getGlobalPatternCollection().relationPattern(), relationName);
}
// --- isAttributeEnabled ---
syn boolean DumpAst.isAttributeEnabled(String parentType, String attributeName) {
// level 4: included for type? -> return yes
PatternCollection includeOnType = getBuildConfig().matchIncludePatternCollection(parentType);
if (includeOnType != null && matches(includeOnType.attributePattern(), attributeName)) {
return true;
}
// level 3: excluded for type? -> return no
PatternCollection excludeOnType = getBuildConfig().matchExcludePatternCollection(parentType);
if (excludeOnType != null && matches(excludeOnType.attributePattern(), attributeName)) {
return false;
}
// level 2: globally included? -> return yes
// level 1: otherwise return no
return matches(getBuildConfig().getGlobalPatternCollection().attributePattern(), attributeName);
}
// --- isNonterminalAttributeEnabled ---
syn boolean DumpAst.isNonterminalAttributeEnabled(String parentType, String ntaName) {
// level 4: included for type? -> return yes
PatternCollection includeOnType = getBuildConfig().matchIncludePatternCollection(parentType);
if (includeOnType != null && matches(includeOnType.ntaPattern(), ntaName)) {
return true;
}
// level 3: excluded for type? -> return no
PatternCollection excludeOnType = getBuildConfig().matchExcludePatternCollection(parentType);
if (excludeOnType != null && matches(excludeOnType.ntaPattern(), ntaName)) {
return false;
}
// level 2: globally included? -> return yes
// level 1: otherwise return no
return matches(getBuildConfig().getGlobalPatternCollection().ntaPattern(), ntaName);
}
// --- match{In,Ex}cludePatternCollection --- // --- match{In,Ex}cludePatternCollection ---
syn PatternCollection BuildConfig.matchIncludePatternCollection(String typeName) { syn PatternCollection BuildConfig.matchIncludePatternCollection(String typeName) {
...@@ -581,7 +551,7 @@ aspect GenerationBackend { ...@@ -581,7 +551,7 @@ aspect GenerationBackend {
syn InvisiblePath DumpNode.getInvisiblePath() { syn InvisiblePath DumpNode.getInvisiblePath() {
InvisiblePath result = new InvisiblePath(); InvisiblePath result = new InvisiblePath();
for (DumpNode successor : reachableThroughInvisible()) { for (DumpNode successor : reachableThroughInvisible()) {
result.addInnerDumpNode(new InnerDumpNode(successor)); result.addInnerRelationDumpNode(new InnerRelationDumpNode(successor));
} }
return result; return result;
} }
...@@ -686,4 +656,24 @@ aspect GenerationBackend { ...@@ -686,4 +656,24 @@ aspect GenerationBackend {
public interface StyleMethod<ASTNODE> { public interface StyleMethod<ASTNODE> {
String get(ASTNODE node); String get(ASTNODE node);
} }
@FunctionalInterface
public interface IncludeRelationMethod<ASTNODE> {
boolean shouldInclude(ASTNODE sourceNode, ASTNODE targetNode, String roleName);
}
@FunctionalInterface
public interface IncludeChildMethod<ASTNODE> {
boolean shouldInclude(ASTNODE parentNode, ASTNODE childNode, String contextName);
}
@FunctionalInterface
public interface IncludeAttributeMethod<ASTNODE> {
boolean shouldInclude(ASTNODE node, String attributeName, boolean isNTA, java.util.function.Supplier<Object> value);
}
@FunctionalInterface
public interface IncludeTokenMethod<ASTNODE> {
boolean shouldInclude(ASTNODE node, String tokenName, Object value);
}
} }
aspect GenerationFrontend {
public class Dumper {
/**
* Prepare to read in the given object. Use the <code>dump*</code> methods to actually dump its content.
* @param obj the object to dump
* @return a builder to adjust dump options
*/
public static DumpBuilder read(Object obj) {
return new DumpBuilder(obj);
}
}
public enum SkinParamBooleanSetting {
/** Print in grayscale? */
Monochrome,
/** Use shadows? */
Shadowing,
/** Use handwritten style? */
Handwritten
}
public enum SkinParamStringSetting {
/** Set color of background */
backgroundColor
}
/**
* Building a dump.
* <p>
*
* <h3>Inclusion and Exclusion of Types</h3>
* Types can be only be disabled, see {@link #disableTypes(String[])}.
*
* <h3>Inclusion and Exclusion of Childrens, tokens and relations</h3>
* Childrens, tokens and relations are included by default.
* This can be changed using exclusions and inclusion, both in general and per-type.
* They are applied in the following order making later conditions take precedence over the first ones.
* <ol>
* <li>Include everything as default.
* <li>Exclude general.
* <li>Include per type.
* <li>Exclude per type.
* </ol>
*
* <h3>Inclusion and Exclusion of Attributes</h3>
* Attributes are excluded by default, i.e., not shown.
* This can be changed using inclusions and exclusions, both in general and per-type.
* They are applied in the following order making later conditions take precedence over the first ones.
* <ol>
* <li> Exclude everything as default.
* <li> Include general.
* <li> Exclude per type.
* <li> Include per type
* </ol>
*/
public class DumpBuilder {
private final Object target;
private String packageName;
private DumpAst result;
private final BuildConfig buildConfig;
private final PrintConfig printConfig;
protected DumpBuilder(Object target) {
this.target = target;
buildConfig = new BuildConfig();
buildConfig.setGlobalPatternCollection(new PatternCollection());
buildConfig.setStyleInformation(StyleInformation.createDefault());
printConfig = new PrintConfig();
printConfig.setScale(1);
printConfig.setVersion(readVersion());
}
/**
* Add debug information in dumped content, mainly version numbers.
* @return this
*/
public DumpBuilder enableDebug() {
buildConfig.setDebug(true);
return this;
}
/**
* Include empty strings for all tokens
* @return this
*/
public DumpBuilder includeEmptyStringsOnTokens() {
buildConfig.setIncludeEmptyString(true);
return this;
}
/**
* 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.
* <p>
* See {@link DumpBuilder} for details on inclusion and exclusion precedence.
* @param regexes patterns to match type names
* @return this
* @see java.util.regex.Pattern#compile(java.lang.String)
*/
public DumpBuilder disableTypes(String regex, String... moreRegexes) {
updateRegexes(() -> buildConfig.getTypeIgnorePattern(),
s -> buildConfig.setTypeIgnorePattern(s),
regex, moreRegexes);
return this;
}
/**
* Exclude tokens and their value if the token name matches at least one of the given regex strings.
* <p>
* See {@link DumpBuilder} for details on inclusion and exclusion precedence.
* @param regexes regex patterns to match token names
* @return this
* @see java.util.regex.Pattern#compile(java.lang.String)
*/
public DumpBuilder excludeTokens(String regex, String... moreRegexes) {
updateRegexes(() -> buildConfig.getGlobalPatternCollection().getTokenPattern(),
s -> buildConfig.getGlobalPatternCollection().setTokenPattern(s),
regex, moreRegexes);
return this;
}
/** TODO: document, implement, sort */
public DumpBuilder excludeTokensFor(String typeRegex, String regex, String... moreRegexes) {
PatternCollection collection = findOrCreateExcludePatternCollection(typeRegex);
updateRegexes(() -> collection.getTokenPattern(),
s -> collection.setTokenPattern(s),
regex, moreRegexes);
return this;
}
/** TODO: document, implement, sort */
public DumpBuilder excludeChildrenFor(String typeRegex, String regex, String... moreRegexes) {
PatternCollection collection = findOrCreateExcludePatternCollection(typeRegex);
updateRegexes(() -> collection.getChildPattern(),
s -> collection.setChildPattern(s),
regex, moreRegexes);
return this;
}
/** TODO: document, implement, sort */
public DumpBuilder excludeRelationsFor(String typeRegex, String regex, String... moreRegexes) {
PatternCollection collection = findOrCreateExcludePatternCollection(typeRegex);
updateRegexes(() -> collection.getRelationPattern(),
s -> collection.setRelationPattern(s),
regex, moreRegexes);
return this;
}
/** TODO: document, implement, sort */
public DumpBuilder excludeAttributesFor(String typeRegex, String regex, String... moreRegexes) {
PatternCollection collection = findOrCreateExcludePatternCollection(typeRegex);
updateRegexes(() -> collection.getAttributePattern(),
s -> collection.setAttributePattern(s),
regex, moreRegexes);
return this;
}
/** TODO: document, implement, sort */
public DumpBuilder excludeNonterminalAttributesFor(String typeRegex, String regex, String... moreRegexes) {
PatternCollection collection = findOrCreateExcludePatternCollection(typeRegex);
updateRegexes(() -> collection.getNonterminalAttributePattern(),
s -> collection.setNonterminalAttributePattern(s),
regex, moreRegexes);
return this;
}
/** TODO: document, implement, sort */
public DumpBuilder includeTokensFor(String typeRegex, String regex, String... moreRegexes) {
PatternCollection collection = findOrCreateIncludePatternCollection(typeRegex);
updateRegexes(() -> collection.getTokenPattern(),
s -> collection.setTokenPattern(s),
regex, moreRegexes);
return this;
}
/** TODO: document, implement, sort */
public DumpBuilder includeChildrenFor(String typeRegex, String regex, String... moreRegexes) {
PatternCollection collection = findOrCreateIncludePatternCollection(typeRegex);
updateRegexes(() -> collection.getChildPattern(),
s -> collection.setChildPattern(s),
regex, moreRegexes);
return this;
}
/** TODO: document, implement, sort */
public DumpBuilder includeRelationsFor(String typeRegex, String regex, String... moreRegexes) {
PatternCollection collection = findOrCreateIncludePatternCollection(typeRegex);
updateRegexes(() -> collection.getRelationPattern(),
s -> collection.setRelationPattern(s),
regex, moreRegexes);
return this;
}
/** TODO: document, implement, sort */
public DumpBuilder includeAttributesFor(String typeRegex, String regex, String... moreRegexes) {
PatternCollection collection = findOrCreateIncludePatternCollection(typeRegex);
updateRegexes(() -> collection.getAttributePattern(),
s -> collection.setAttributePattern(s),
regex, moreRegexes);
return this;
}
/** TODO: document, implement, sort */
public DumpBuilder includeNonterminalAttributesFor(String typeRegex, String regex, String... moreRegexes) {
PatternCollection collection = findOrCreateIncludePatternCollection(typeRegex);
updateRegexes(() -> collection.getNonterminalAttributePattern(),
s -> collection.setNonterminalAttributePattern(s),
regex, moreRegexes);
return this;
}
/**
* Include attributes (as tokens) and their value if the attribute name matches at least on of the given regex strings.
* <p>
* See {@link DumpBuilder} for details on inclusion and exclusion precedence.
* @param regexes regex patterns to match token names
* @return this
* @see java.util.regex.Pattern#compile(java.lang.String)
*/
public DumpBuilder includeAttributes(String regex, String... moreRegexes) {
updateRegexes(() -> buildConfig.getGlobalPatternCollection().getAttributePattern(),
s -> buildConfig.getGlobalPatternCollection().setAttributePattern(s),
regex, moreRegexes);
return this;
}
/**
* Includes nonterminal-attributes (as children) and their values if
* their attribute name matches at least on of the given regex strings.
* <br>
* <b>Note</b>: A leading "get" and a trailing "List" in the name will be removed prior to matching.
* Thus, it should not be contained in the regex either.
* <p>
* See {@link DumpBuilder} for details on inclusion and exclusion precedence.
* @param regexes regex patterns to match token names
* @return this
* @see java.util.regex.Pattern#compile(java.lang.String)
*/
public DumpBuilder includeNonterminalAttributes(String regex, String... moreRegexes) {
updateRegexes(() -> buildConfig.getGlobalPatternCollection().getNonterminalAttributePattern(),
s -> buildConfig.getGlobalPatternCollection().setNonterminalAttributePattern(s),
regex, moreRegexes);
return this;
}
/**
* Exclude every child whose name (i.e., context) matches at least on of the given regex strings.
* This means, that the complete object and its (transitive) children will never be included in any output.
* <p>
* See {@link DumpBuilder} for details on inclusion and exclusion precedence.
* @param regexes regex patterns to match child names
* @return this
* @see java.util.regex.Pattern#compile(java.lang.String)
*/
public DumpBuilder excludeChildren(String regex, String... moreRegexes) {
updateRegexes(() -> buildConfig.getGlobalPatternCollection().getChildPattern(),
s -> buildConfig.getGlobalPatternCollection().setChildPattern(s),
regex, moreRegexes);
return this;
}
/**
* Exclude every relation whose role-name matches at least on of the given regex strings.
* This means two things: a) the relation to any potential target object(s) is never shown, and b) the target
* object(s) are not shown unless they are reachable by another relation or by containment.
* <p>
* See {@link DumpBuilder} for details on inclusion and exclusion precedence.
* @param regexes regex patterns to match child names
* @return this
* @see java.util.regex.Pattern#compile(java.lang.String)
*/
public DumpBuilder excludeRelations(String regex, String... moreRegexes) {
updateRegexes(() -> buildConfig.getGlobalPatternCollection().getRelationPattern(),
s -> buildConfig.getGlobalPatternCollection().setRelationPattern(s),
regex, moreRegexes);
return this;
}
private PatternCollection findOrCreateIncludePatternCollection(String typeRegex) {
PatternCollection result = buildConfig.findIncludePatternCollection(typeRegex);
if (result == null) {
TypePatternCollectionMapping mapping = new TypePatternCollectionMapping();
mapping.setTypeRegex(typeRegex);
result = new PatternCollection();
mapping.setPatternCollection(result);
buildConfig.addIncludeTypePattern(mapping);
}
return result;
}
private PatternCollection findOrCreateExcludePatternCollection(String typeRegex) {
PatternCollection result = buildConfig.findExcludePatternCollection(typeRegex);
if (result == null) {
TypePatternCollectionMapping mapping = new TypePatternCollectionMapping();
mapping.setTypeRegex(typeRegex);
result = new PatternCollection();
mapping.setPatternCollection(result);
buildConfig.addExcludeTypePattern(mapping);
}
return result;
}
private void updateRegexes(java.util.function.Supplier<String> getter, java.util.function.Consumer<String> setter, String regex, String... moreRegexes) {
if (getter.get().isEmpty()) {
setter.accept(regex);
} else {
setter.accept(getter.get() + "|" + regex);
}
for (String value : moreRegexes) {
setter.accept(getter.get() + "|" + value);
}
}
public DumpBuilder excludeNullNodes() {
buildConfig.setExcludeNullNodes(true);
return this;
}
public DumpBuilder includeNullNodes() {
buildConfig.setExcludeNullNodes(false);
return this;
}
public DumpBuilder customPreamble(String option) {
printConfig.addHeader(new Header(option));
return this;
}
public DumpBuilder skinParam(SkinParamStringSetting setting, String value) {
customPreamble("skinparam " + setting.toString() + " " + value);
return this;
}
public DumpBuilder skinParam(SkinParamBooleanSetting setting, boolean value) {
customPreamble("skinparam " + setting.toString() + " " + value);
return this;
}
public <ASTNODE> DumpBuilder setNameMethod(StyleMethod<ASTNODE> nameMethod) {
buildConfig.getStyleInformation().setNameMethod(nameMethod);
return this;
}
public <ASTNODE> DumpBuilder setBackgroundColorMethod(StyleMethod<ASTNODE> colorMethod) {
buildConfig.getStyleInformation().setBackgroundColorMethod(colorMethod);
return this;
}
public <ASTNODE> DumpBuilder setTextColorMethod(StyleMethod<ASTNODE> colorMethod) {
buildConfig.getStyleInformation().setTextColorMethod(colorMethod);
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;
}
public DumpBuilder orderChildren() {
printConfig.setOrderChildren(true);
return this;
}
private String readVersion() {
try {
java.util.ResourceBundle resources = java.util.ResourceBundle.getBundle("dumpAstVersion");
return resources.getString("version");
} catch (java.util.MissingResourceException e) {
return "version ?";
}
}
protected DumpAst build() {
if (result == null) {
result = new DumpAst();
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 {
result.transform(new TransformationTransferInformation(), this.target);
} catch (java.lang.reflect.InvocationTargetException e) {
throw new RuntimeException("Could not transform :(", e);
} catch (IllegalAccessException e) {
throw new RuntimeException("Could not transform :(", e);
} catch (NoSuchMethodException e) {
throw new RuntimeException("Could not transform :(", e);
}
}
return result;
}
/**
* Write out content as plantuml source code.
* @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 dumpAsSource(java.nio.file.Path destination) throws java.io.IOException {
String content = build().toPlantUml();
try (java.io.Writer writer = java.nio.file.Files.newBufferedWriter(destination)) {
writer.write(content);
}
return this;
}
public DumpBuilder dumpAsYaml(java.nio.file.Path destination, boolean prependCreationComment) throws java.io.IOException {
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;
}
}
}
...@@ -26,14 +26,6 @@ aspect GenerationToYaml { ...@@ -26,14 +26,6 @@ aspect GenerationToYaml {
return result; 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) { syn MappingElement PrintConfig.toYaml(boolean fromRelation) {
MappingElement result = new MappingElement(); MappingElement result = new MappingElement();
// children // children
...@@ -128,6 +120,7 @@ aspect GenerationToYaml { ...@@ -128,6 +120,7 @@ aspect GenerationToYaml {
result.put("name", getName()); result.put("name", getName());
result.put("computed", getComputed()); result.put("computed", getComputed());
// attributes // attributes
result.put("isList", isList());
result.put("label", label()); result.put("label", label());
result.put("isDumpValueToken", isDumpValueToken()); result.put("isDumpValueToken", isDumpValueToken());
return result; return result;
...@@ -142,8 +135,20 @@ aspect GenerationToYaml { ...@@ -142,8 +135,20 @@ aspect GenerationToYaml {
syn MappingElement DumpReferenceToken.toYaml(boolean fromRelation) { syn MappingElement DumpReferenceToken.toYaml(boolean fromRelation) {
MappingElement result = super.toYaml(fromRelation); MappingElement result = super.toYaml(fromRelation);
// tokens // attributes
result.put("innerNodeName", innerNodeName()); result.put("innerNodeName", innerNodeName());
result.put("innerNotNull", innerNotNull());
result.put("outerNodeName", outerNodeName());
return result;
}
syn MappingElement DumpReferenceListToken.toYaml(boolean fromRelation) {
MappingElement result = super.toYaml(fromRelation);
// children
addYamledList(result, "InnerRelationDumpNode", getInnerRelationDumpNodeList(), fromRelation);
// attributes
result.put("outerNodeName", outerNodeName()); result.put("outerNodeName", outerNodeName());
return result; return result;
} }
...@@ -165,13 +170,14 @@ aspect GenerationToYaml { ...@@ -165,13 +170,14 @@ aspect GenerationToYaml {
// attributes // attributes
result.put("bothVisible", bothVisible()); result.put("bothVisible", bothVisible());
result.put("innerNodeName", innerNodeName()); result.put("innerNodeName", innerNodeName());
result.put("innerNotNull", innerNotNull());
return result; return result;
} }
syn MappingElement DumpListRelation.toYaml(boolean fromRelation) { syn MappingElement DumpListRelation.toYaml(boolean fromRelation) {
MappingElement result = super.toYaml(fromRelation); MappingElement result = super.toYaml(fromRelation);
// children // children
addYamledList(result, "InnerDumpNodes", getInnerDumpNodeList(), fromRelation); addYamledList(result, "InnerRelationDumpNode", getInnerRelationDumpNodeList(), fromRelation);
return result; return result;
} }
...@@ -185,13 +191,32 @@ aspect GenerationToYaml { ...@@ -185,13 +191,32 @@ aspect GenerationToYaml {
return result; return result;
} }
syn MappingElement InnerRelationDumpNode.toYaml(boolean fromRelation) {
MappingElement result = new MappingElement();
// attributes
result.put("bothVisible", bothVisible());
result.put("innerNodeName", innerNodeName());
result.put("innerNotNull", innerNotNull());
result.put("outerNodeName", outerNodeName());
result.put("label", label());
return result;
}
syn MappingElement InvisiblePath.toYaml(boolean fromRelation) { syn MappingElement InvisiblePath.toYaml(boolean fromRelation) {
MappingElement result = super.toYaml(fromRelation); MappingElement result = super.toYaml(fromRelation);
// children // children
addYamledList(result, "InnerDumpNodes", getInnerDumpNodeList(), fromRelation); addYamledList(result, "InnerRelationDumpNode", getInnerRelationDumpNodeList(), fromRelation);
return result; 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);
}
// extension for mustache // extension for mustache
public static ValueElement ValueElement.of(double value) { public static ValueElement ValueElement.of(double value) {
return new ValueElement(false, String.valueOf(value)); return new ValueElement(false, String.valueOf(value));
......
...@@ -4,6 +4,8 @@ aspect Navigation { ...@@ -4,6 +4,8 @@ aspect Navigation {
eq DumpListChildNode.isList() = true; eq DumpListChildNode.isList() = true;
syn boolean DumpRelation.isList() = false; syn boolean DumpRelation.isList() = false;
eq DumpListRelation.isList() = true; eq DumpListRelation.isList() = true;
syn boolean DumpToken.isList() = false;
eq DumpReferenceListToken.isList() = true;
// --- buildConfig --- // --- buildConfig ---
inh BuildConfig DumpNode.buildConfig(); inh BuildConfig DumpNode.buildConfig();
...@@ -12,14 +14,20 @@ aspect Navigation { ...@@ -12,14 +14,20 @@ aspect Navigation {
// --- printConfig --- // --- printConfig ---
inh PrintConfig BuildConfig.printConfig(); inh PrintConfig BuildConfig.printConfig();
inh PrintConfig DumpNode.printConfig();
inh PrintConfig DumpNormalRelation.printConfig();
inh PrintConfig InnerRelationDumpNode.printConfig();
inh PrintConfig DumpReferenceToken.printConfig();
eq DumpAst.getChild().printConfig() = getPrintConfig(); eq DumpAst.getChild().printConfig() = getPrintConfig();
// --- containingDumpNode --- // --- containingDumpNode ---
inh DumpNode InnerDumpNode.containingDumpNode(); inh DumpNode InnerDumpNode.containingDumpNode();
inh DumpNode InnerRelationDumpNode.containingDumpNode();
inh DumpNode DumpChildNode.containingDumpNode(); inh DumpNode DumpChildNode.containingDumpNode();
inh DumpNode DumpRelation.containingDumpNode(); inh DumpNode DumpRelation.containingDumpNode();
eq DumpNode.getDumpChildNode().containingDumpNode() = this; eq DumpNode.getDumpChildNode().containingDumpNode() = this;
eq DumpNode.getDumpRelation().containingDumpNode() = this; eq DumpNode.getDumpRelation().containingDumpNode() = this;
eq DumpNode.getDumpToken().containingDumpNode() = this;
eq DumpNode.getInvisiblePath().containingDumpNode() = this; eq DumpNode.getInvisiblePath().containingDumpNode() = this;
// --- container --- // --- container ---
...@@ -69,7 +77,7 @@ aspect Navigation { ...@@ -69,7 +77,7 @@ aspect Navigation {
return java.util.Collections.emptyList(); return java.util.Collections.emptyList();
} }
java.util.List<DumpNode> result = new java.util.ArrayList<>(); java.util.List<DumpNode> result = new java.util.ArrayList<>();
getInnerDumpNodeList().forEach(inner -> { getInnerRelationDumpNodeList().forEach(inner -> {
if (!onlyVisible || !inner.getDumpNode().getInvisible()) { if (!onlyVisible || !inner.getDumpNode().getInvisible()) {
result.add(inner.getDumpNode()); result.add(inner.getDumpNode());
} }
...@@ -77,6 +85,11 @@ aspect Navigation { ...@@ -77,6 +85,11 @@ aspect Navigation {
return result; return result;
} }
// --- innerNotNull ---
syn boolean DumpNormalRelation.innerNotNull() = !printConfig().getRelationWithRank() && getDumpNode() != null && getDumpNode().getObject() != null;
syn boolean DumpReferenceToken.innerNotNull() = !printConfig().getRelationWithRank() && getValue() != null && getValue().getObject() != null;
syn boolean InnerRelationDumpNode.innerNotNull() = !printConfig().getRelationWithRank() && getDumpNode() != null && getDumpNode().getObject() != null;
// === Method naviagtion === // === Method naviagtion ===
coll java.util.List<SingleChildMethod> ClassAnalysisResult.singleChildMethods() [new java.util.ArrayList<>()] root ClassAnalysisResult; coll java.util.List<SingleChildMethod> ClassAnalysisResult.singleChildMethods() [new java.util.ArrayList<>()] root ClassAnalysisResult;
SingleChildMethod contributes this to ClassAnalysisResult.singleChildMethods(); SingleChildMethod contributes this to ClassAnalysisResult.singleChildMethods();
......
aspect Printing { aspect Printing {
// --- outerNodeName --- // --- outerNodeName ---
inh String InnerDumpNode.outerNodeName(); inh String InnerDumpNode.outerNodeName();
inh String InnerRelationDumpNode.outerNodeName();
inh String DumpChildNode.outerNodeName(); inh String DumpChildNode.outerNodeName();
inh String DumpRelation.outerNodeName(); inh String DumpRelation.outerNodeName();
inh String DumpToken.outerNodeName();
inh String DumpReferenceToken.outerNodeName(); inh String DumpReferenceToken.outerNodeName();
eq DumpNode.getChild().outerNodeName() = name(); eq DumpNode.getChild().outerNodeName() = name();
// --- innerNodeName --- // --- innerNodeName ---
syn String InnerDumpNode.innerNodeName() = getDumpNode() != null ? getDumpNode().name() : null; syn String InnerDumpNode.innerNodeName() = getDumpNode() != null ? getDumpNode().name() : null;
syn String InnerRelationDumpNode.innerNodeName() = getDumpNode() != null ? getDumpNode().name() : null;
syn String DumpNormalChildNode.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 DumpNormalRelation.innerNodeName() = getDumpNode() != null ? getDumpNode().name() : null;
syn String DumpReferenceToken.innerNodeName() = getValue().name(); syn String DumpReferenceToken.innerNodeName() = getValue().name();
...@@ -21,15 +24,46 @@ aspect Printing { ...@@ -21,15 +24,46 @@ aspect Printing {
syn String DumpToken.label() = getName() + (getComputed() ? "()" : ""); syn String DumpToken.label() = getName() + (getComputed() ? "()" : "");
syn String DumpNode.label() = getLabel(); syn String DumpNode.label() = getLabel();
inh String InnerDumpNode.label(); inh String InnerDumpNode.label();
inh String InnerRelationDumpNode.label();
eq DumpListChildNode.getInnerDumpNode(int index).label() = label() + "[" + index + "]"; eq DumpListChildNode.getInnerDumpNode(int index).label() = label() + "[" + index + "]";
eq DumpListRelation.getInnerDumpNode(int index).label() = label() + "[" + index + "]"; eq DumpListRelation.getInnerRelationDumpNode(int index).label() = label() + "[" + index + "]";
eq InvisiblePath.getInnerDumpNode(int index).label() = null; eq DumpReferenceListToken.getInnerRelationDumpNode(int index).label() = label() + "[" + index + "]";
eq InvisiblePath.getInnerRelationDumpNode(int index).label() = null;
// --- bothVisible --- // --- bothVisible ---
boolean ASTNode.bothVisible(DumpNode one, DumpNode two) { boolean ASTNode.bothVisible(DumpNode one, DumpNode two) {
return one != null && two != null && !one.getInvisible() && !two.getInvisible(); return one != null && two != null && !one.getInvisible() && !two.getInvisible();
} }
syn boolean InnerDumpNode.bothVisible() = bothVisible(containingDumpNode(), getDumpNode()); syn boolean InnerDumpNode.bothVisible() = bothVisible(containingDumpNode(), getDumpNode());
syn boolean InnerRelationDumpNode.bothVisible() = bothVisible(containingDumpNode(), getDumpNode());
syn boolean DumpNormalChildNode.bothVisible() = bothVisible(containingDumpNode(), getDumpNode()); syn boolean DumpNormalChildNode.bothVisible() = bothVisible(containingDumpNode(), getDumpNode());
syn boolean DumpNormalRelation.bothVisible() = bothVisible(containingDumpNode(), getDumpNode()); syn boolean DumpNormalRelation.bothVisible() = bothVisible(containingDumpNode(), getDumpNode());
} }
aspect Debugging {
syn String ClassAnalysisResult.prettyPrint() {
StringBuilder sb = new StringBuilder();
sb.append("ContainmentMethods:");
for (AnalysedMethod method : getContainmentMethodList()) {
sb.append(method.prettyPrint()).append(",");
}
if (getNumContainmentMethod() == 0) {
sb.append("none. ");
}
sb.append("other methods:");
for (AnalysedMethod method : getOtherMethodList()) {
sb.append(method.prettyPrint()).append(",");
}
if (getNumOtherMethod() == 0) {
sb.append("none.");
}
return sb.toString();
}
syn String AnalysedMethod.prettyPrint() {
String methodString = "?";
try {
methodString = getMethod().toString();
} catch (Exception ignore) {}
return this.getClass().getSimpleName() + "[Method: " + methodString + ", Name: " + getName() + "]";
}
}
package de.tudresden.inf.st.jastadd.dumpAst.ast;
/**
* Entrypoint for dumpAst.
*
* @author rschoene - Initial contribution
*/
public class Dumper {
/**
* Prepare to read in the given object. Use the <code>dump*</code> methods to actually dump its content.
* @param obj the object to dump
* @return a builder to adjust dump options
*/
public static DumpBuilder read(Object obj) {
return new DumpBuilder(obj);
}
}
package de.tudresden.inf.st.jastadd.dumpAst.ast;
public enum SkinParamBooleanSetting {
/**
* Print in grayscale?
*/
Monochrome,
/**
* Use shadows?
*/
Shadowing,
/**
* Use handwritten style?
*/
Handwritten
}
package de.tudresden.inf.st.jastadd.dumpAst.ast;
public enum SkinParamStringSetting {
/**
* Set color of background
*/
backgroundColor
}
...@@ -37,9 +37,18 @@ object "{{{labelAndTextColor}}}" as {{{name}}} {{{stereotypeList}}} {{#backgroun ...@@ -37,9 +37,18 @@ object "{{{labelAndTextColor}}}" as {{{name}}} {{{stereotypeList}}} {{#backgroun
{{#DumpNodes}} {{#DumpNodes}}
{{#DumpTokens}} {{#DumpTokens}}
{{^invisible}} {{^invisible}}
{{#isList}}
{{#InnerRelationDumpNode}}
{{#bothVisible}}
{{{outerNodeName}}} .[#black{{#computed}},#{{{computedColor}}}{{/computed}}{{#innerNotNull}},norank{{/innerNotNull}}].> {{{innerNodeName}}} : {{{label}}}
{{/bothVisible}}
{{/InnerRelationDumpNode}}
{{/isList}}
{{^isList}}
{{^isDumpValueToken}} {{^isDumpValueToken}}
{{{outerNodeName}}} .{{#computed}}[#{{{computedColor}}}]{{/computed}}.> {{{innerNodeName}}} : {{{label}}} {{{outerNodeName}}} .[#black{{#computed}},#{{{computedColor}}}{{/computed}}{{#innerNotNull}},norank{{/innerNotNull}}].> {{{innerNodeName}}} : {{{label}}}
{{/isDumpValueToken}} {{/isDumpValueToken}}
{{/isList}}
{{/invisible}} {{/invisible}}
{{/DumpTokens}} {{/DumpTokens}}
{{#DumpChildNodes}} {{#DumpChildNodes}}
...@@ -58,23 +67,23 @@ object "{{{labelAndTextColor}}}" as {{{name}}} {{{stereotypeList}}} {{#backgroun ...@@ -58,23 +67,23 @@ object "{{{labelAndTextColor}}}" as {{{name}}} {{{stereotypeList}}} {{#backgroun
{{/DumpChildNodes}} {{/DumpChildNodes}}
{{#DumpRelations}} {{#DumpRelations}}
{{#isList}} {{#isList}}
{{#InnerDumpNodes}} {{#InnerRelationDumpNode}}
{{#bothVisible}} {{#bothVisible}}
{{{outerNodeName}}} {{#bidirectional}}<{{/bidirectional}}--> {{{innerNodeName}}} : {{{label}}} {{{outerNodeName}}} {{#bidirectional}}<{{/bidirectional}}-{{#innerNotNull}}[norank]{{/innerNotNull}}-> {{{innerNodeName}}} : {{{label}}}
{{/bothVisible}} {{/bothVisible}}
{{/InnerDumpNodes}} {{/InnerRelationDumpNode}}
{{/isList}} {{/isList}}
{{^isList}} {{^isList}}
{{#bothVisible}} {{#bothVisible}}
{{{outerNodeName}}} {{#bidirectional}}<{{/bidirectional}}--> {{{innerNodeName}}} : {{{label}}} {{{outerNodeName}}} {{#bidirectional}}<{{/bidirectional}}-{{#innerNotNull}}[norank]{{/innerNotNull}}-> {{{innerNodeName}}} : {{{label}}}
{{/bothVisible}} {{/bothVisible}}
{{/isList}} {{/isList}}
{{/DumpRelations}} {{/DumpRelations}}
{{^invisible}} {{^invisible}}
{{#InvisiblePath}} {{#InvisiblePath}}
{{#InnerDumpNodes}} {{#InnerRelationDumpNode}}
{{{outerNodeName}}} o.. {{{innerNodeName}}} {{{outerNodeName}}} o.. {{{innerNodeName}}}
{{/InnerDumpNodes}} {{/InnerRelationDumpNode}}
{{/InvisiblePath}} {{/InvisiblePath}}
{{/invisible}} {{/invisible}}
{{#PrintConfig}}{{#orderChildren}} {{#PrintConfig}}{{#orderChildren}}
......
#Fri Mar 11 12:41:31 CET 2022 #Thu Jun 23 16:13:20 CEST 2022
version=1.0.0 version=1.1.0
...@@ -5,3 +5,4 @@ out/ ...@@ -5,3 +5,4 @@ out/
*.class *.class
/featureTest.png /featureTest.png
/featureTest.yml /featureTest.yml
/featureTest.puml
...@@ -20,16 +20,22 @@ apply plugin: 'jastadd' ...@@ -20,16 +20,22 @@ apply plugin: 'jastadd'
// --- Dependencies --- // --- Dependencies ---
repositories { repositories {
mavenCentral() mavenCentral()
maven {
name 'gitlab-maven'
url 'https://git-st.inf.tu-dresden.de/api/v4/groups/jastadd/-/packages/maven'
}
}
configurations {
relast
} }
dependencies { dependencies {
implementation project(":dumpAst") implementation project(":dumpAst")
jastadd2 group: 'org.jastadd', name: 'jastadd2', version: '2.3.5-dresden'
relast group: 'org.jastadd', name: 'relast', version: "${relast_version}"
api group: 'net.sf.beaver', name: 'beaver-rt', version: '0.9.11' implementation 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 --- // --- Preprocessors ---
...@@ -41,20 +47,16 @@ File testingGrammar = file('./src/main/jastadd/featureTest.relast') ...@@ -41,20 +47,16 @@ File testingGrammar = file('./src/main/jastadd/featureTest.relast')
task relast(type: JavaExec) { task relast(type: JavaExec) {
group = 'Build' group = 'Build'
main = "-jar" classpath = configurations.relast
mainClass = 'org.jastadd.relast.compiler.Compiler'
doFirst { doFirst {
delete "src/gen/jastadd/*.ast" delete "src/gen/jastadd/*"
delete "src/gen/jastadd/featureTest.jadd"
delete "src/gen/jastadd/featureTestRefResolver.jadd"
delete "src/gen/jastadd/featureTestResolverStubs.jrag"
mkdir "src/gen/jastadd/" mkdir "src/gen/jastadd/"
} }
args = [ args = [
"../libs/relast.jar",
testingGrammar, testingGrammar,
// "./src/main/jastadd/MustacheNodes.relast",
"--listClass=java.util.ArrayList", "--listClass=java.util.ArrayList",
"--jastAddList=JastAddList", "--jastAddList=JastAddList",
"--useJastAddNames", "--useJastAddNames",
...@@ -62,13 +64,6 @@ task relast(type: JavaExec) { ...@@ -62,13 +64,6 @@ task relast(type: JavaExec) {
"--resolverHelper", "--resolverHelper",
"--grammarName=./src/gen/jastadd/featureTest" "--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 ---
...@@ -102,7 +97,6 @@ jastadd { ...@@ -102,7 +97,6 @@ jastadd {
} }
// --- Tests --- // --- Tests ---
test.useJUnitPlatform()
// --- Versioning and Publishing --- // --- Versioning and Publishing ---
mainClassName = 'de.tudresden.inf.st.jastadd.featureTest.FeatureTestMain' mainClassName = 'de.tudresden.inf.st.jastadd.featureTest.FeatureTestMain'
......
...@@ -2,6 +2,7 @@ aspect GrammarGlobal { ...@@ -2,6 +2,7 @@ aspect GrammarGlobal {
syn A C.getCalculated() { syn A C.getCalculated() {
A result = new A(); A result = new A();
result.setName("Calculated-" + getName()); result.setName("Calculated-" + getName());
result.setB(new B().setName("B" + getName()));
D innerD = new D(); D innerD = new D();
result.setD(innerD); result.setD(innerD);
return result; return result;
...@@ -18,6 +19,7 @@ aspect GrammarGlobal { ...@@ -18,6 +19,7 @@ aspect GrammarGlobal {
syn nta A C.getCalculatedNewSyntax() { syn nta A C.getCalculatedNewSyntax() {
A result = new A(); A result = new A();
result.setName("Calculated-" + getName()); result.setName("Calculated-" + getName());
result.setB(new B().setName("B" + getName()));
return result; return result;
} }
...@@ -34,6 +36,11 @@ aspect GrammarGlobal { ...@@ -34,6 +36,11 @@ aspect GrammarGlobal {
syn boolean ASTNode.isA() = false; syn boolean ASTNode.isA() = false;
eq A.isA() = true; eq A.isA() = true;
coll java.util.Set<B> Root.collectBs() [new java.util.HashSet<>()] root Root ;
B contributes this to Root.collectBs();
C contributes nta getAlsoCalculatedList() to Root.collectBs();
C contributes nta getAlsoCalculatedListNewSyntax() to Root.collectBs();
} }
aspect GrammarTypeLevel { aspect GrammarTypeLevel {
......
// testcases with global inclusion/exclusion // testcases with global inclusion/exclusion
Nameable ::= <Name> ; Nameable ::= <Name> ;
Root : Nameable ::= A B* [C]; Root : Nameable ::= A B* [C];
A : Nameable ::= B MyC:C D; A : Nameable ::= B [MyC:C] [D];
B : Nameable ::= <OtherValue> ; B : Nameable ::= <OtherValue> ;
C : Nameable ::= [A] <Unwanted:int> <RawReference:A> /Calculated:A/ /AlsoCalculated:B*/ ; C : Nameable ::= [A] <Unwanted:int> <RawReference:A> /Calculated:A/ /AlsoCalculated:B*/ ;
SubC : C ::= <RawReference:A> <Unwanted:int> [A] /Calculated:A/ /AlsoCalculated:B*/ ; SubC : C ::= <RawReference:A> <Unwanted:int> [A] /Calculated:A/ /AlsoCalculated:B*/ ;
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment