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

Merge branch 'dev' into 'main'

3.0.1

See merge request !13
parents 5e0a3728 2b5428a6
Branches main
No related tags found
1 merge request!133.0.1
Pipeline #15024 passed
Showing
with 923 additions and 899 deletions
...@@ -44,8 +44,8 @@ publish_dev: ...@@ -44,8 +44,8 @@ publish_dev:
script: script:
- "./gradlew setDevVersionForCI" - "./gradlew setDevVersionForCI"
- "./gradlew publish" - "./gradlew publish"
except: only:
- main - dev
publish_main: publish_main:
image: openjdk:11 image: openjdk:11
...@@ -60,7 +60,6 @@ publish_main: ...@@ -60,7 +60,6 @@ publish_main:
ragdoc_build: ragdoc_build:
image: image:
name: "git-st.inf.tu-dresden.de:4567/jastadd/ragdoc-builder" name: "git-st.inf.tu-dresden.de:4567/jastadd/ragdoc-builder"
entrypoint: [""]
stage: ragdoc stage: ragdoc
needs: needs:
- build - build
...@@ -68,6 +67,9 @@ ragdoc_build: ...@@ -68,6 +67,9 @@ ragdoc_build:
- JAVA_FILES=$(find dumpAst.base/src/ -name '*.java') - JAVA_FILES=$(find dumpAst.base/src/ -name '*.java')
- echo $JAVA_FILES | wc -l - echo $JAVA_FILES | wc -l
- /ragdoc-builder/start-builder.sh -excludeGenerated -d data/ $JAVA_FILES - /ragdoc-builder/start-builder.sh -excludeGenerated -d data/ $JAVA_FILES
only:
- dev
- main
artifacts: artifacts:
paths: paths:
- "data/" - "data/"
...@@ -75,7 +77,6 @@ ragdoc_build: ...@@ -75,7 +77,6 @@ ragdoc_build:
ragdoc_view: ragdoc_view:
image: image:
name: "git-st.inf.tu-dresden.de:4567/jastadd/ragdoc-view:relations" name: "git-st.inf.tu-dresden.de:4567/jastadd/ragdoc-view:relations"
entrypoint: [""]
stage: ragdoc stage: ragdoc
needs: needs:
- ragdoc_build - ragdoc_build
...@@ -84,7 +85,7 @@ ragdoc_view: ...@@ -84,7 +85,7 @@ ragdoc_view:
- mkdir -p pages/docs/ragdoc - mkdir -p pages/docs/ragdoc
- OUTPUT_DIR=$(pwd -P)/pages/docs/ragdoc - OUTPUT_DIR=$(pwd -P)/pages/docs/ragdoc
- cd /ragdoc-view/src/ && rm -rf data && ln -s $DATA_DIR - cd /ragdoc-view/src/ && rm -rf data && ln -s $DATA_DIR
- /ragdoc-view/build-view.sh --output-path=$OUTPUT_DIR - BASE_HREF=/dumpAst/ragdoc/ /ragdoc-view/build-view.sh --output-path=$OUTPUT_DIR
only: only:
- dev - dev
- main - main
...@@ -92,7 +93,7 @@ ragdoc_view: ...@@ -92,7 +93,7 @@ ragdoc_view:
paths: paths:
- "pages/docs/ragdoc" - "pages/docs/ragdoc"
pages: build_pages:
image: python:3.10.0-bullseye image: python:3.10.0-bullseye
stage: publish stage: publish
needs: needs:
...@@ -105,5 +106,14 @@ pages: ...@@ -105,5 +106,14 @@ pages:
artifacts: artifacts:
paths: paths:
- public/ - public/
only:
- main pages:
stage: publish
needs:
- build_pages
script: [ "true" ]
artifacts:
paths:
- public
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
LICENSE 0 → 100644
BSD 3-Clause License
Copyright (c) 2022, TU Dresden, Software Technology Group
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
...@@ -147,7 +147,7 @@ task newVersion() { ...@@ -147,7 +147,7 @@ task newVersion() {
task setDevVersionForCI() { task setDevVersionForCI() {
doFirst { doFirst {
def props = new Properties() def props = new Properties()
props['version'] = version + "-$System.env.CI_PIPELINE_IID" props['version'] = version + "-dev-$System.env.CI_PIPELINE_IID"
props.store(file(versionFile).newWriter(), null) props.store(file(versionFile).newWriter(), null)
} }
} }
...@@ -179,6 +179,15 @@ publishing { ...@@ -179,6 +179,15 @@ publishing {
} }
} }
// --- Misc ---
task ensureNoTrainlingNewlineForRelationStyleMustache() {
doFirst {
def text = project.file("src/main/resources/RelationStyle.mustache").text
project.file("src/main/resources/RelationStyle.mustache").write(text.strip())
}
}
// --- Task order --- // --- Task order ---
compileJava.dependsOn ensureNoTrainlingNewlineForRelationStyleMustache
generateAst.dependsOn relast generateAst.dependsOn relast
publish.dependsOn jar publish.dependsOn jar
...@@ -48,29 +48,28 @@ aspect ClassAnalysis { ...@@ -48,29 +48,28 @@ aspect ClassAnalysis {
String tokenName = invokeName(annotation); String tokenName = invokeName(annotation);
if (tokenName.startsWith("_impl_")) { if (tokenName.startsWith("_impl_")) {
String relationName = titleCase(tokenName.substring(6)); String relationName = titleCase(tokenName.substring(6));
try { if (present(getMethod(clazz, "get" + relationName), relationMethod -> {
java.lang.reflect.Method relationMethod = clazz.getMethod("get" + relationName);
// normal get + token-name -> singleRelation // normal get + token-name -> singleRelation
SingleRelationMethod singleRelationMethod = new SingleRelationMethod(); SingleRelationMethod singleRelationMethod = getMethod(clazz, "has" + relationName).isPresent() ?
new OptRelationMethod() : 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) {
// ignore, but we know this is probably not a single relation
} }
// we know here this is probably not a single or opt relation
// try list-relation next // try list-relation next
try { if (present(getMethod(clazz, "get" + relationName + "List"), relationMethod -> {
java.lang.reflect.Method relationMethod = clazz.getMethod("get" + relationName + "List");
// normal get + token-name + "List" -> listRelation // normal get + token-name + "List" -> listRelation
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) {
// ignore, but we know this is probably not a relation at all
} }
// we know here this is probably not a relation at all
} }
IntrinsicTokenMethod tokenMethod = new IntrinsicTokenMethod(); IntrinsicTokenMethod tokenMethod = new IntrinsicTokenMethod();
tokenMethod.setMethod(method); tokenMethod.setMethod(method);
...@@ -201,6 +200,20 @@ aspect ClassAnalysis { ...@@ -201,6 +200,20 @@ aspect ClassAnalysis {
} }
} }
private static java.util.Optional<java.lang.reflect.Method> DumpAst.getMethod(Class<?> clazz, String methodName) {
try {
java.lang.reflect.Method method = clazz.getMethod(methodName);
return java.util.Optional.of(method);
} catch (NoSuchMethodException e) {
return java.util.Optional.empty();
}
}
private static <T> boolean DumpAst.present(java.util.Optional<T> optional, java.util.function.Consumer<T> callback) {
optional.ifPresent(callback);
return optional.isPresent();
}
// --- astNodeAnnotationPrefix --- // --- astNodeAnnotationPrefix ---
syn String DumpAst.astNodeAnnotationPrefix() = getPackageName() + ".ASTNodeAnnotation"; syn String DumpAst.astNodeAnnotationPrefix() = getPackageName() + ".ASTNodeAnnotation";
inh String DumpNode.astNodeAnnotationPrefix(); inh String DumpNode.astNodeAnnotationPrefix();
......
...@@ -5,26 +5,26 @@ DumpNode ::= DumpChildNode* DumpToken* DumpRelation* ...@@ -5,26 +5,26 @@ 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 ::= <OriginalIndex:int> <Label> <LineColor> <TextColor> ; //TODO remove colors (and label?)
rel InnerDumpNode.DumpNode <-> DumpNode.ContainerOfInner ; rel InnerDumpNode.DumpNode <-> DumpNode.ContainerOfInner? ;
InnerRelationDumpNode; InnerRelationDumpNode ::= <OriginalIndex:int> <Label> <LineColor> <TextColor> ; //TODO remove colors (and label?)
rel InnerRelationDumpNode.DumpNode -> DumpNode ; // .ContainerOfInner* rel InnerRelationDumpNode.DumpNode <-> DumpNode.ContainerOfRelationInner* ;
abstract DumpChildNode ::= <Name> <Computed:boolean> ; abstract DumpChildNode ::= <Label> <Computed:boolean> <LineColor> <TextColor> ;
DumpNormalChildNode : DumpChildNode ; DumpNormalChildNode : DumpChildNode ::= ;
rel DumpNormalChildNode.DumpNode <-> DumpNode.ContainerOfNormalChild ; rel DumpNormalChildNode.DumpNode <-> DumpNode.ContainerOfNormalChild? ;
DumpListChildNode : DumpChildNode ::= InnerDumpNode* ; DumpListChildNode : DumpChildNode ::= InnerDumpNode* <Name> ;
abstract DumpToken ::= <Name> <Computed:boolean> ; abstract DumpToken ::= <Label> <Computed:boolean> ;
DumpReferenceToken : DumpToken ; DumpReferenceToken : DumpToken ::= <LineColor> <TextColor> ;
rel DumpReferenceToken.Value -> DumpNode ; rel DumpReferenceToken.DumpNode -> DumpNode ;
DumpReferenceListToken : DumpToken ::= InnerRelationDumpNode* ; DumpReferenceListToken : DumpToken ::= InnerRelationDumpNode* <Name> <LineColor> <TextColor> ;
DumpValueToken : DumpToken ::= <Value:Object> ; DumpValueToken : DumpToken ::= <Value:Object> ;
abstract DumpRelation ::= <Name> <Bidirectional:boolean> ; abstract DumpRelation ::= <Label> <Bidirectional:boolean> <LineColor> <TextColor> ;
DumpNormalRelation : DumpRelation ; DumpNormalRelation : DumpRelation ::= ;
rel DumpNormalRelation.DumpNode -> DumpNode ; rel DumpNormalRelation.DumpNode -> DumpNode ;
DumpListRelation : DumpRelation ::= InnerRelationDumpNode* ; DumpListRelation : DumpRelation ::= InnerRelationDumpNode* <Name> ;
// type of NTA // type of NTA
InvisiblePath ::= InnerRelationDumpNode* ; InvisiblePath ::= InnerRelationDumpNode* ;
...@@ -45,6 +45,7 @@ NormalListChildMethod : ListChildMethod ; ...@@ -45,6 +45,7 @@ NormalListChildMethod : ListChildMethod ;
NTAListChildMethod : ListChildMethod ; NTAListChildMethod : ListChildMethod ;
SingleRelationMethod : AnalysedMethod ; SingleRelationMethod : AnalysedMethod ;
OptRelationMethod : SingleRelationMethod ;
ListRelationMethod : AnalysedMethod ; ListRelationMethod : AnalysedMethod ;
// TODO can the refine bug also happen for refined attributes? // TODO can the refine bug also happen for refined attributes?
...@@ -53,9 +54,6 @@ IntrinsicTokenMethod : TokenMethod ::= <Refined:boolean> ; ...@@ -53,9 +54,6 @@ IntrinsicTokenMethod : TokenMethod ::= <Refined:boolean> ;
AttributeMethod : TokenMethod ; AttributeMethod : TokenMethod ;
BuildConfig ::= StyleInformation BuildConfig ::= StyleInformation
GlobalPatternCollection:PatternCollection
ExcludeTypePattern:TypePatternCollectionMapping*
IncludeTypePattern:TypePatternCollectionMapping*
<IncludeRelationMethod:IncludeRelationMethod> <IncludeRelationMethod:IncludeRelationMethod>
<IncludeChildMethod:IncludeChildMethod> <IncludeChildMethod:IncludeChildMethod>
<IncludeAttributeMethod:IncludeAttributeMethod> <IncludeAttributeMethod:IncludeAttributeMethod>
...@@ -64,13 +62,16 @@ BuildConfig ::= StyleInformation ...@@ -64,13 +62,16 @@ BuildConfig ::= StyleInformation
<IncludeEmptyString:boolean> <IncludeEmptyString:boolean>
<ExcludeNullNodes:boolean> <ExcludeNullNodes:boolean>
<Debug:boolean>; <Debug:boolean>;
TypePatternCollectionMapping ::= <TypeRegex> PatternCollection ; StyleInformation ::= <ComputedColor>;
PatternCollection ::= <TokenPattern> <ChildPattern> <RelationPattern> <AttributePattern> <NonterminalAttributePattern> ;
StyleInformation ::= <NameMethod:StyleMethod> <BackgroundColorMethod:StyleMethod> <TextColorMethod:StyleMethod> <StereotypeMethod:StyleMethod> <ComputedColor>;
PrintConfig ::= Header* PrintConfig ::= Header*
<Scale:double> <Scale:double>
<Version> <Version>
<RelationWithRank:boolean> <RelationWithRank:boolean>
<OrderChildren:boolean> ; <OrderChildren:boolean>
<NodeStyleDefinition:NodeStyleDefinition>
<RelationStyleDefinition:RelationStyleDefinition>;
Header ::= <Value> ; Header ::= <Value> ;
NodeStyle ::= <Label> <BackgroundColor> <TextColor> <StereoTypes> ;
RelationStyle ::= <Label> <LineColor> <TextColor> ;
aspect Frontend { aspect Frontend {
// --- match{In,Ex}cludePatternCollection ---
syn PatternCollection BuildConfig.matchIncludePatternCollection(String typeName) {
for (TypePatternCollectionMapping mapping : getIncludeTypePatternList()) {
if (matches(mapping.typePattern(), typeName)) {
return mapping.getPatternCollection();
}
}
return null;
}
syn PatternCollection BuildConfig.matchExcludePatternCollection(String typeName) {
for (TypePatternCollectionMapping mapping : getExcludeTypePatternList()) {
if (matches(mapping.typePattern(), typeName)) {
return mapping.getPatternCollection();
}
}
return null;
}
static StyleInformation StyleInformation.createDefault() { static StyleInformation StyleInformation.createDefault() {
StyleInformation result = new StyleInformation(); 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"); result.setComputedColor("blue");
return result; return result;
} }
@FunctionalInterface @FunctionalInterface
public interface StyleMethod<ASTNODE> { public interface NodeStyleDefinition<ASTNODE> {
String get(ASTNODE node); void style(ASTNODE node, NodeStyle style);
}
@FunctionalInterface
public interface RelationStyleDefinition<ASTNODE> {
void style(ASTNODE source, ASTNODE target, boolean isComputed, boolean isContainment, RelationStyle style);
}
public void NodeStyle.useSimpleLabel() {
setLabel(node.getObject() == null ? "null" : node.getObject().getClass().getSimpleName());
}
public void NodeStyle.useSimpleLabelWithHashCode() {
setLabel(node.getObject() == null ? "null" :
node.getObject().getClass().getSimpleName() + "@" + Integer.toHexString(node.getObject().hashCode()));
} }
@FunctionalInterface @FunctionalInterface
......
...@@ -36,6 +36,12 @@ aspect Navigation { ...@@ -36,6 +36,12 @@ aspect Navigation {
syn boolean AnalysedMethod.isSingleRelationMethod() = false; syn boolean AnalysedMethod.isSingleRelationMethod() = false;
eq SingleRelationMethod.isSingleRelationMethod() = true; eq SingleRelationMethod.isSingleRelationMethod() = true;
/** Tests if SingleRelationMethod is a OptRelationMethod.
* @return 'true' if this is a OptRelationMethod, otherwise 'false'
*/
syn boolean SingleRelationMethod.isOptRelationMethod() = false;
eq OptRelationMethod.isOptRelationMethod() = true;
/** Tests if AnalysedMethod is a ListRelationMethod. /** Tests if AnalysedMethod is a ListRelationMethod.
* @return 'true' if this is a ListRelationMethod, otherwise 'false' * @return 'true' if this is a ListRelationMethod, otherwise 'false'
*/ */
...@@ -102,6 +108,12 @@ aspect Navigation { ...@@ -102,6 +108,12 @@ aspect Navigation {
syn boolean DumpToken.isDumpReferenceToken() = false; syn boolean DumpToken.isDumpReferenceToken() = false;
eq DumpReferenceToken.isDumpReferenceToken() = true; eq DumpReferenceToken.isDumpReferenceToken() = true;
/** Tests if DumpToken is a DumpReferenceListToken.
* @return 'true' if this is a DumpReferenceListToken, otherwise 'false'
*/
syn boolean DumpToken.isDumpReferenceListToken() = false;
eq DumpReferenceListToken.isDumpReferenceListToken() = true;
/** Tests if DumpToken is a DumpValueToken. /** Tests if DumpToken is a DumpValueToken.
* @return 'true' if this is a DumpValueToken, otherwise 'false' * @return 'true' if this is a DumpValueToken, otherwise 'false'
*/ */
...@@ -227,6 +239,13 @@ aspect Navigation { ...@@ -227,6 +239,13 @@ aspect Navigation {
eq DumpToken.asDumpReferenceToken() = null; eq DumpToken.asDumpReferenceToken() = null;
eq DumpReferenceToken.asDumpReferenceToken() = this; eq DumpReferenceToken.asDumpReferenceToken() = this;
/** casts a DumpToken into a DumpReferenceListToken if possible.
* @return 'this' cast to a DumpReferenceListToken or 'null'
*/
syn DumpReferenceListToken DumpToken.asDumpReferenceListToken();
eq DumpToken.asDumpReferenceListToken() = null;
eq DumpReferenceListToken.asDumpReferenceListToken() = this;
/** casts a DumpToken into a DumpValueToken if possible. /** casts a DumpToken into a DumpValueToken if possible.
* @return 'this' cast to a DumpValueToken or 'null' * @return 'this' cast to a DumpValueToken or 'null'
*/ */
......
...@@ -10,6 +10,11 @@ aspect Navigation { ...@@ -10,6 +10,11 @@ aspect Navigation {
// --- buildConfig --- // --- buildConfig ---
inh BuildConfig DumpNode.buildConfig(); inh BuildConfig DumpNode.buildConfig();
inh BuildConfig PrintConfig.buildConfig(); inh BuildConfig PrintConfig.buildConfig();
inh BuildConfig InnerDumpNode.buildConfig();
inh BuildConfig InnerRelationDumpNode.buildConfig();
inh BuildConfig DumpChildNode.buildConfig();
inh BuildConfig DumpRelation.buildConfig();
inh BuildConfig DumpToken.buildConfig();
eq DumpAst.getChild().buildConfig() = getBuildConfig(); eq DumpAst.getChild().buildConfig() = getBuildConfig();
// --- printConfig --- // --- printConfig ---
...@@ -25,11 +30,23 @@ aspect Navigation { ...@@ -25,11 +30,23 @@ aspect Navigation {
inh DumpNode InnerRelationDumpNode.containingDumpNode(); inh DumpNode InnerRelationDumpNode.containingDumpNode();
inh DumpNode DumpChildNode.containingDumpNode(); inh DumpNode DumpChildNode.containingDumpNode();
inh DumpNode DumpRelation.containingDumpNode(); inh DumpNode DumpRelation.containingDumpNode();
inh DumpNode DumpReferenceToken.containingDumpNode();
inh DumpNode DumpReferenceListToken.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.getDumpToken().containingDumpNode() = this;
eq DumpNode.getInvisiblePath().containingDumpNode() = this; eq DumpNode.getInvisiblePath().containingDumpNode() = this;
// --- containingDumpListChildNode ---
inh DumpListChildNode InnerDumpNode.containingDumpListChildNode();
eq DumpListChildNode.getInnerDumpNode().containingDumpListChildNode() = this;
// --- containingDumpListRelation ---
inh DumpListRelation InnerRelationDumpNode.containingDumpListRelation();
eq DumpListRelation.getInnerRelationDumpNode().containingDumpListRelation() = this;
eq DumpReferenceListToken.getInnerRelationDumpNode().containingDumpListRelation() = null;
eq InvisiblePath.getInnerRelationDumpNode().containingDumpListRelation() = null;
// --- container --- // --- container ---
syn DumpNode DumpNode.container() { syn DumpNode DumpNode.container() {
if (getContainerOfNormalChild() != null) { if (getContainerOfNormalChild() != null) {
...@@ -85,10 +102,10 @@ aspect Navigation { ...@@ -85,10 +102,10 @@ aspect Navigation {
return result; return result;
} }
// --- innerNotNull --- // --- innerNotNullOrEmpty ---
syn boolean DumpNormalRelation.innerNotNull() = !printConfig().getRelationWithRank() && getDumpNode() != null && getDumpNode().getObject() != null; syn boolean DumpNormalRelation.innerNotNullOrEmpty() = !printConfig().getRelationWithRank() && getDumpNode() != null && getDumpNode().getObject() != null && getDumpNode().getObject() != DumpAst.EMPTY;
syn boolean DumpReferenceToken.innerNotNull() = !printConfig().getRelationWithRank() && getValue() != null && getValue().getObject() != null; syn boolean DumpReferenceToken.innerNotNullOrEmpty() = !printConfig().getRelationWithRank() && getDumpNode() != null && getDumpNode().getObject() != null && getDumpNode().getObject() != DumpAst.EMPTY;
syn boolean InnerRelationDumpNode.innerNotNull() = !printConfig().getRelationWithRank() && getDumpNode() != null && getDumpNode().getObject() != null; syn boolean InnerRelationDumpNode.innerNotNullOrEmpty() = !printConfig().getRelationWithRank() && getDumpNode() != null && getDumpNode().getObject() != null && getDumpNode().getObject() != DumpAst.EMPTY;
// === 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;
......
aspect Printing { aspect Printing {
// --- outerNodeName ---
inh String InnerDumpNode.outerNodeName();
inh String InnerRelationDumpNode.outerNodeName();
inh String DumpChildNode.outerNodeName();
inh String DumpRelation.outerNodeName();
inh String DumpToken.outerNodeName();
inh String DumpReferenceToken.outerNodeName();
eq DumpNode.getChild().outerNodeName() = name();
// --- innerNodeName ---
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 DumpNormalRelation.innerNodeName() = getDumpNode() != null ? getDumpNode().name() : null;
syn String DumpReferenceToken.innerNodeName() = getValue().name();
// --- name --- // --- name ---
syn String DumpNode.name() = getName(); // might change in the future syn String DumpNode.name() = getName(); // might change in the future
// --- label ---
syn String DumpChildNode.label() = getName() + (getComputed() ? "()" : "");
syn String DumpRelation.label() = getName();
syn String DumpToken.label() = getName() + (getComputed() ? "()" : "");
syn String DumpNode.label() = getLabel();
inh String InnerDumpNode.label();
inh String InnerRelationDumpNode.label();
eq DumpListChildNode.getInnerDumpNode(int index).label() = label() + "[" + index + "]";
eq DumpListRelation.getInnerRelationDumpNode(int index).label() = label() + "[" + index + "]";
eq DumpReferenceListToken.getInnerRelationDumpNode(int index).label() = label() + "[" + index + "]";
eq InvisiblePath.getInnerRelationDumpNode(int index).label() = null;
// --- bothVisible ---
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 InnerRelationDumpNode.bothVisible() = bothVisible(containingDumpNode(), getDumpNode());
syn boolean DumpNormalChildNode.bothVisible() = bothVisible(containingDumpNode(), getDumpNode());
syn boolean DumpNormalRelation.bothVisible() = bothVisible(containingDumpNode(), getDumpNode());
} }
aspect Debugging { aspect Debugging {
......
...@@ -8,6 +8,11 @@ aspect TemplateContext { ...@@ -8,6 +8,11 @@ aspect TemplateContext {
return getObject() == null; return getObject() == null;
} }
// --- isEmpty ---
syn boolean DumpNode.isEmpty() {
return getObject() == DumpAst.EMPTY;
}
// --- isAstNode --- // --- isAstNode ---
syn boolean DumpNode.isAstNode() { syn boolean DumpNode.isAstNode() {
if (getObject() == null) { if (getObject() == null) {
...@@ -22,11 +27,63 @@ aspect TemplateContext { ...@@ -22,11 +27,63 @@ aspect TemplateContext {
return false; return false;
} }
// --- outerNodeName ---
inh String InnerDumpNode.outerNodeName();
inh String InnerRelationDumpNode.outerNodeName();
inh String DumpChildNode.outerNodeName();
inh String DumpRelation.outerNodeName();
inh String DumpToken.outerNodeName();
inh String DumpReferenceToken.outerNodeName();
eq DumpNode.getChild().outerNodeName() = name();
// --- innerNodeName ---
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 DumpNormalRelation.innerNodeName() = getDumpNode() != null ? getDumpNode().name() : null;
syn String DumpReferenceToken.innerNodeName() = getDumpNode().name();
// --- label ---
syn String DumpChildNode.label() = safeLabel(getLabel() + (getComputed() ? "()" : ""));
syn String DumpRelation.label() = safeLabel(getLabel());
syn String DumpToken.label() = safeLabel(getLabel() + (getComputed() ? "()" : ""));
syn String DumpNode.label() = safeLabel(getLabel());
inh String InnerDumpNode.label();
inh String InnerRelationDumpNode.label();
eq DumpListChildNode.getInnerDumpNode(int index).label() {
InnerDumpNode inner = getInnerDumpNode(index);
return safeLabel(
(inner.getDumpNode().isEmpty() ? "" : "[" + chooseIndex(inner.getOriginalIndex(), index) + "]"));
}
eq DumpListRelation.getInnerRelationDumpNode(int index).label() {
InnerRelationDumpNode inner = getInnerRelationDumpNode(index);
return safeLabel(inner.getLabel() +
(inner.getDumpNode().isEmpty() ? "" : "[" + chooseIndex(inner.getOriginalIndex(), index) + "]"));
}
eq DumpReferenceListToken.getInnerRelationDumpNode(int index).label() = "[" + index + "]";
eq InvisiblePath.getInnerRelationDumpNode(int index).label() = null;
protected int ASTNode.chooseIndex(int originalIndex, int inheritedIndex) {
return originalIndex != 0 ? originalIndex : inheritedIndex;
}
static String ASTNode.safeLabel(String s) {
return s.isEmpty() ? " " : s;
}
// --- bothVisible ---
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 InnerRelationDumpNode.bothVisible() = bothVisible(containingDumpNode(), getDumpNode());
syn boolean DumpNormalChildNode.bothVisible() = bothVisible(containingDumpNode(), getDumpNode());
syn boolean DumpNormalRelation.bothVisible() = bothVisible(containingDumpNode(), getDumpNode());
// --- NTA: InvisiblePath --- // --- NTA: InvisiblePath ---
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.addInnerRelationDumpNode(new InnerRelationDumpNode(successor)); result.addInnerRelationDumpNode(new InnerRelationDumpNode().setDumpNode(successor));
} }
return result; return result;
} }
...@@ -49,9 +106,9 @@ aspect TemplateContext { ...@@ -49,9 +106,9 @@ aspect TemplateContext {
// --- labelAndTextColor --- // --- labelAndTextColor ---
syn String DumpNode.labelAndTextColor() { syn String DumpNode.labelAndTextColor() {
if (getTextColor().isEmpty()) { if (getTextColor().isEmpty()) {
return getLabel(); return label();
} else { } else {
return "<color:" + getTextColor() + ">" + getLabel() + "</color>"; return "<color:" + getTextColor() + ">" + label() + "</color>";
} }
} }
......
...@@ -32,9 +32,9 @@ aspect ToYaml { ...@@ -32,9 +32,9 @@ aspect ToYaml {
addYamledList(result, "Headers", getHeaderList(), fromRelation); addYamledList(result, "Headers", getHeaderList(), fromRelation);
// tokens // tokens
result.put("scale", getScale()); result.put("OrderChildren", getOrderChildren());
result.put("version", getVersion()); result.put("Scale", getScale());
result.put("orderChildren", getOrderChildren()); result.put("Version", getVersion());
// attributes // attributes
result.put("debug", debug()); result.put("debug", debug());
...@@ -44,7 +44,7 @@ aspect ToYaml { ...@@ -44,7 +44,7 @@ aspect ToYaml {
syn MappingElement Header.toYaml(boolean fromRelation) { syn MappingElement Header.toYaml(boolean fromRelation) {
MappingElement result = new MappingElement(); MappingElement result = new MappingElement();
// tokens // tokens
result.put("value", getValue()); result.put("Value", getValue());
return result; return result;
} }
...@@ -59,19 +59,19 @@ aspect ToYaml { ...@@ -59,19 +59,19 @@ aspect ToYaml {
} }
// tokens // tokens
result.put("name", getName()); result.put("Name", getName());
if (!fromRelation) { if (!fromRelation) {
result.put("computed", getComputed()); result.put("BackgroundColor", getBackgroundColor());
result.put("label", getLabel()); result.put("Invisible", getInvisible());
result.put("backgroundColor", getBackgroundColor()); result.put("TextColor", getTextColor());
result.put("textColor", getTextColor());
result.put("invisible", getInvisible());
} }
// attributes // attributes
if (!fromRelation) { if (!fromRelation) {
result.put("isNull", isNull()); result.put("isNull", isNull());
result.put("isEmpty", isEmpty());
result.put("isAstNode", isAstNode()); result.put("isAstNode", isAstNode());
result.put("label", label());
result.put("labelAndTextColor", labelAndTextColor()); result.put("labelAndTextColor", labelAndTextColor());
result.put("stereotypeList", stereotypeList()); result.put("stereotypeList", stereotypeList());
addYamledList(result, "myChildren", myChildren(), true); addYamledList(result, "myChildren", myChildren(), true);
...@@ -88,12 +88,15 @@ aspect ToYaml { ...@@ -88,12 +88,15 @@ aspect ToYaml {
syn MappingElement DumpChildNode.toYaml(boolean fromRelation) { syn MappingElement DumpChildNode.toYaml(boolean fromRelation) {
MappingElement result = new MappingElement(); MappingElement result = new MappingElement();
// tokens // tokens
result.put("name", getName()); result.put("LineColor", getLineColor());
result.put("computed", getComputed()); result.put("TextColor", getTextColor());
// attributes // attributes
result.put("label", label());
result.put("isList", isList()); result.put("isList", isList());
result.put("label", label());
result.put("needRelationStyling", needRelationStyling());
result.put("outerNodeName", outerNodeName()); result.put("outerNodeName", outerNodeName());
return result; return result;
...@@ -101,6 +104,7 @@ aspect ToYaml { ...@@ -101,6 +104,7 @@ aspect ToYaml {
syn MappingElement DumpNormalChildNode.toYaml(boolean fromRelation) { syn MappingElement DumpNormalChildNode.toYaml(boolean fromRelation) {
MappingElement result = super.toYaml(fromRelation); MappingElement result = super.toYaml(fromRelation);
// attributes // attributes
result.put("bothVisible", bothVisible()); result.put("bothVisible", bothVisible());
result.put("innerNodeName", innerNodeName()); result.put("innerNodeName", innerNodeName());
...@@ -109,6 +113,10 @@ aspect ToYaml { ...@@ -109,6 +113,10 @@ aspect ToYaml {
syn MappingElement DumpListChildNode.toYaml(boolean fromRelation) { syn MappingElement DumpListChildNode.toYaml(boolean fromRelation) {
MappingElement result = super.toYaml(fromRelation); MappingElement result = super.toYaml(fromRelation);
// tokens
result.put("Name", getName());
// children // children
addYamledList(result, "InnerDumpNodes", getInnerDumpNodeList(), fromRelation); addYamledList(result, "InnerDumpNodes", getInnerDumpNodeList(), fromRelation);
return result; return result;
...@@ -116,28 +124,33 @@ aspect ToYaml { ...@@ -116,28 +124,33 @@ aspect ToYaml {
syn MappingElement DumpToken.toYaml(boolean fromRelation) { syn MappingElement DumpToken.toYaml(boolean fromRelation) {
MappingElement result = new MappingElement(); MappingElement result = new MappingElement();
// tokens
result.put("name", getName());
result.put("computed", getComputed());
// attributes // attributes
result.put("isDumpValueToken", isDumpValueToken());
result.put("isList", isList()); result.put("isList", isList());
result.put("label", label()); result.put("label", label());
result.put("isDumpValueToken", isDumpValueToken());
return result; return result;
} }
syn MappingElement DumpValueToken.toYaml(boolean fromRelation) { syn MappingElement DumpValueToken.toYaml(boolean fromRelation) {
MappingElement result = super.toYaml(fromRelation); MappingElement result = super.toYaml(fromRelation);
// tokens // tokens
result.put("value", getValue().toString()); result.put("Value", getValue().toString());
return result; return result;
} }
syn MappingElement DumpReferenceToken.toYaml(boolean fromRelation) { syn MappingElement DumpReferenceToken.toYaml(boolean fromRelation) {
MappingElement result = super.toYaml(fromRelation); MappingElement result = super.toYaml(fromRelation);
// tokens
result.put("LineColor", getLineColor());
result.put("TextColor", getTextColor());
// attributes // attributes
result.put("innerNodeName", innerNodeName()); result.put("innerNodeName", innerNodeName());
result.put("innerNotNull", innerNotNull()); result.put("innerNotNullOrEmpty", innerNotNullOrEmpty());
result.put("needRelationStyling", needRelationStyling());
result.put("outerNodeName", outerNodeName()); result.put("outerNodeName", outerNodeName());
return result; return result;
} }
...@@ -145,37 +158,52 @@ aspect ToYaml { ...@@ -145,37 +158,52 @@ aspect ToYaml {
syn MappingElement DumpReferenceListToken.toYaml(boolean fromRelation) { syn MappingElement DumpReferenceListToken.toYaml(boolean fromRelation) {
MappingElement result = super.toYaml(fromRelation); MappingElement result = super.toYaml(fromRelation);
// tokens
result.put("Name", getName());
result.put("LineColor", getLineColor());
result.put("TextColor", getTextColor());
// children // children
addYamledList(result, "InnerRelationDumpNode", getInnerRelationDumpNodeList(), fromRelation); addYamledList(result, "InnerRelationDumpNode", getInnerRelationDumpNodeList(), fromRelation);
// attributes // attributes
result.put("needRelationStyling", needRelationStyling());
result.put("outerNodeName", outerNodeName()); result.put("outerNodeName", outerNodeName());
return result; return result;
} }
syn MappingElement DumpRelation.toYaml(boolean fromRelation) { syn MappingElement DumpRelation.toYaml(boolean fromRelation) {
MappingElement result = new MappingElement(); MappingElement result = new MappingElement();
// tokens // tokens
result.put("name", getName()); result.put("Bidirectional", getBidirectional());
result.put("bidirectional", getBidirectional()); result.put("LineColor", getLineColor());
result.put("TextColor", getTextColor());
// attributes // attributes
result.put("isList", isList()); result.put("isList", isList());
result.put("label", label()); result.put("label", label());
result.put("needRelationStyling", needRelationStyling());
result.put("outerNodeName", outerNodeName()); result.put("outerNodeName", outerNodeName());
return result; return result;
} }
syn MappingElement DumpNormalRelation.toYaml(boolean fromRelation) { syn MappingElement DumpNormalRelation.toYaml(boolean fromRelation) {
MappingElement result = super.toYaml(fromRelation); MappingElement result = super.toYaml(fromRelation);
// attributes // attributes
result.put("bothVisible", bothVisible()); result.put("bothVisible", bothVisible());
result.put("innerNodeName", innerNodeName()); result.put("innerNodeName", innerNodeName());
result.put("innerNotNull", innerNotNull()); result.put("innerNotNullOrEmpty", innerNotNullOrEmpty());
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);
// tokens
result.put("Name", getName());
// children // children
addYamledList(result, "InnerRelationDumpNode", getInnerRelationDumpNodeList(), fromRelation); addYamledList(result, "InnerRelationDumpNode", getInnerRelationDumpNodeList(), fromRelation);
return result; return result;
...@@ -183,27 +211,40 @@ aspect ToYaml { ...@@ -183,27 +211,40 @@ aspect ToYaml {
syn MappingElement InnerDumpNode.toYaml(boolean fromRelation) { syn MappingElement InnerDumpNode.toYaml(boolean fromRelation) {
MappingElement result = new MappingElement(); MappingElement result = new MappingElement();
// tokens
result.put("LineColor", getLineColor());
result.put("TextColor", getTextColor());
// attributes // attributes
result.put("bothVisible", bothVisible()); result.put("bothVisible", bothVisible());
result.put("innerNodeName", innerNodeName()); result.put("innerNodeName", innerNodeName());
result.put("outerNodeName", outerNodeName());
result.put("label", label()); result.put("label", label());
result.put("needRelationStyling", needRelationStyling());
result.put("outerNodeName", outerNodeName());
return result; return result;
} }
syn MappingElement InnerRelationDumpNode.toYaml(boolean fromRelation) { syn MappingElement InnerRelationDumpNode.toYaml(boolean fromRelation) {
MappingElement result = new MappingElement(); MappingElement result = new MappingElement();
// tokens
result.put("LineColor", getLineColor());
result.put("TextColor", getTextColor());
// attributes // attributes
result.put("bothVisible", bothVisible()); result.put("bothVisible", bothVisible());
result.put("innerNodeName", innerNodeName()); result.put("innerNodeName", innerNodeName());
result.put("innerNotNull", innerNotNull()); result.put("innerNotNullOrEmpty", innerNotNullOrEmpty());
result.put("outerNodeName", outerNodeName());
result.put("label", label()); result.put("label", label());
result.put("needRelationStyling", needRelationStyling());
result.put("outerNodeName", outerNodeName());
return result; 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, "InnerRelationDumpNode", getInnerRelationDumpNodeList(), fromRelation); addYamledList(result, "InnerRelationDumpNode", getInnerRelationDumpNodeList(), fromRelation);
return result; return result;
...@@ -237,7 +278,7 @@ aspect ToYaml { ...@@ -237,7 +278,7 @@ aspect ToYaml {
if (value == null || value.equals("null")) { if (value == null || value.equals("null")) {
return StringElement.of("null"); return StringElement.of("null");
} }
if (value.isEmpty()) { if (value.isBlank()) {
return StringElement.of(value); return StringElement.of(value);
} }
return containsAny(value, ",#[{\"\n") ? return containsAny(value, ",#[{\"\n") ?
......
...@@ -53,6 +53,8 @@ aspect Transform { ...@@ -53,6 +53,8 @@ aspect Transform {
return new TransformationOptions(Source.NORMAL, false, false); return new TransformationOptions(Source.NORMAL, false, false);
} }
public static final Object DumpAst.EMPTY = new Object();
// --- transform --- (need to be a method, because it alters the AST while traversing the object structure) // --- transform --- (need to be a method, because it alters the AST while traversing the object structure)
protected DumpNode DumpAst.transform(TransformationTransferInformation tti, Object obj) protected DumpNode DumpAst.transform(TransformationTransferInformation tti, Object obj)
throws java.lang.reflect.InvocationTargetException, IllegalAccessException, NoSuchMethodException { throws java.lang.reflect.InvocationTargetException, IllegalAccessException, NoSuchMethodException {
...@@ -81,7 +83,7 @@ aspect Transform { ...@@ -81,7 +83,7 @@ aspect Transform {
} }
DumpNode node; DumpNode node;
String objClassName; String objClassName;
if (obj == null) { if (obj == null || obj == EMPTY) {
node = null; node = null;
objClassName = "null"; objClassName = "null";
} else { } else {
...@@ -97,7 +99,7 @@ aspect Transform { ...@@ -97,7 +99,7 @@ aspect Transform {
} else { } else {
node = new DumpNode(); node = new DumpNode();
node.setObject(obj); node.setObject(obj);
node.setName("node" + (tti.nodeCounter++)); node.setName(tti.nextName());
node.setComputed(options.computed); node.setComputed(options.computed);
tti.transformed.put(obj, node); tti.transformed.put(obj, node);
this.addDumpNode(node); this.addDumpNode(node);
...@@ -114,8 +116,8 @@ aspect Transform { ...@@ -114,8 +116,8 @@ aspect Transform {
if (options.invisible || !isTypeEnabled(objClassName)) { if (options.invisible || !isTypeEnabled(objClassName)) {
node.setInvisible(true); node.setInvisible(true);
} }
if (obj == null) { if (obj == null || obj == EMPTY) {
// for a null object, we do not need any further analysis // for a null or empty object, we do not need any further analysis
return node; return node;
} }
final ClassAnalysisResult car = analyzeClass(obj.getClass()); final ClassAnalysisResult car = analyzeClass(obj.getClass());
...@@ -124,12 +126,39 @@ aspect Transform { ...@@ -124,12 +126,39 @@ aspect Transform {
} }
for (AnalysedMethod containmentMethod : car.getContainmentMethodList()) { for (AnalysedMethod containmentMethod : car.getContainmentMethodList()) {
if (containmentMethod.isSingleChildMethod() || containmentMethod.isOptChildMethod()) { if (containmentMethod.isSingleChildMethod() || containmentMethod.isOptChildMethod()) {
handleContainmentSingleOrOptChild(node, containmentMethod, obj, tti, options);
} else if (containmentMethod.isListChildMethod()) {
handleContainmentListChild(node, containmentMethod, obj, tti, options);
} else {
throw new RuntimeException("Unknown containment method type " + containmentMethod);
}
}
for (AnalysedMethod otherMethod : car.getOtherMethodList()) {
if (otherMethod.isSingleChildMethod()) {
handleOtherSingleChild(node, otherMethod, obj, tti, options);
} else if (otherMethod.isListChildMethod()) {
handleOtherListChild(node, otherMethod, obj, tti, options);
} else if (otherMethod.isSingleRelationMethod()) {
handleOtherSingleRelation(node, otherMethod, obj, tti, options);
} else if (otherMethod.isListRelationMethod()) {
handleOtherListRelation(node, otherMethod, obj, tti, options);
} else if (otherMethod.isTokenMethod()) {
handleOtherToken(node, otherMethod, obj, tti, options);
} else {
throw new RuntimeException("Unknown other method type " + otherMethod);
}
}
return node;
}
private boolean DumpAst.handleContainmentSingleOrOptChild(DumpNode node, AnalysedMethod containmentMethod,
Object obj, TransformationTransferInformation tti, TransformationOptions options)
throws java.lang.reflect.InvocationTargetException, IllegalAccessException, NoSuchMethodException {
// -- singleChild or optChild -- // -- singleChild or optChild --
Object target; Object target;
if (containmentMethod.isOptChildMethod() && !((boolean) containmentMethod.asOptChildMethod().getCheckMethod().invoke(obj))) { if (containmentMethod.isOptChildMethod() && !((boolean) containmentMethod.asOptChildMethod().getCheckMethod().invoke(obj))) {
if (getBuildConfig().getExcludeNullNodes()) { if (getBuildConfig().getExcludeNullNodes()) {
continue; return false;
//target = containmentMethod.getMethod().invoke(obj);
} else { } else {
target = null; target = null;
} }
...@@ -138,41 +167,58 @@ aspect Transform { ...@@ -138,41 +167,58 @@ aspect Transform {
} }
String childName = containmentMethod.getName(); String childName = containmentMethod.getName();
if (!getBuildConfig().getIncludeChildMethod().shouldInclude(obj, target, childName)) { if (!getBuildConfig().getIncludeChildMethod().shouldInclude(obj, target, childName)) {
continue; return false;
} }
DumpNode targetNode = transform(tti, target, options.asNormal(false).allowNullObjectsOnce()); 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.setLabel(childName + (containmentMethod.isOptChildMethod() ? "?" : ""));
normalChild.setDumpNode(targetNode); normalChild.setDumpNode(targetNode);
normalChild.setComputed(false); normalChild.setComputed(false);
node.addDumpChildNode(normalChild); node.addDumpChildNode(normalChild);
applyStyle(normalChild);
} }
} else if (containmentMethod.isListChildMethod()) { return true;
}
private boolean DumpAst.handleContainmentListChild(DumpNode node, AnalysedMethod containmentMethod,
Object obj, TransformationTransferInformation tti, TransformationOptions options)
throws java.lang.reflect.InvocationTargetException, IllegalAccessException, NoSuchMethodException {
// -- listChild -- // -- listChild --
Iterable<?> targetList = (Iterable<?>) containmentMethod.getMethod().invoke(obj); Iterable<?> targetList = (Iterable<?>) containmentMethod.getMethod().invoke(obj);
DumpListChildNode listChild = new DumpListChildNode(); DumpListChildNode listChild = new DumpListChildNode();
listChild.setComputed(false); listChild.setComputed(false);
String childName = containmentMethod.getName(); String childName = containmentMethod.getName();
listChild.setName(childName); listChild.setLabel(childName);
listChild.setName(tti.nextName());
node.addDumpChildNode(listChild);
RelationStyle style = applyStyle(listChild);
int index = -1;
for (Object target : targetList) { for (Object target : targetList) {
index++;
if (!getBuildConfig().getIncludeChildMethod().shouldInclude(obj, target, childName)) { if (!getBuildConfig().getIncludeChildMethod().shouldInclude(obj, target, childName)) {
continue; continue;
} }
DumpNode targetNode = transform(tti, target, options.asNormal(false)); DumpNode targetNode = transform(tti, target, options.asNormal(false));
if (target != null && targetNode != null) { if (target != null && targetNode != null) {
listChild.addInnerDumpNode(new InnerDumpNode().setDumpNode(targetNode)); InnerDumpNode inner = new InnerDumpNode().setDumpNode(targetNode).setOriginalIndex(index);
} listChild.addInnerDumpNode(inner);
inner.applyStyle(style);
} }
if (listChild.getNumInnerDumpNode() > 0) {
node.addDumpChildNode(listChild);
} }
} else { if (listChild.getNumInnerDumpNode() == 0) {
throw new RuntimeException("Unknown containment method type " + containmentMethod); InnerDumpNode inner = new InnerDumpNode().setDumpNode(transform(tti, EMPTY,
options.asNormal(false).allowNullObjectsOnce()));
listChild.addInnerDumpNode(inner);
inner.applyStyle(style);
} }
return true;
} }
for (AnalysedMethod otherMethod : car.getOtherMethodList()) {
if (otherMethod.isSingleChildMethod()) { private boolean DumpAst.handleOtherSingleChild(DumpNode node, AnalysedMethod otherMethod,
Object obj, TransformationTransferInformation tti, TransformationOptions options)
throws java.lang.reflect.InvocationTargetException, IllegalAccessException, NoSuchMethodException {
// -- singleChild -- // -- singleChild --
String childName = otherMethod.getName(); String childName = otherMethod.getName();
boolean computed = otherMethod.asSingleChildMethod().isNTASingleChildMethod(); boolean computed = otherMethod.asSingleChildMethod().isNTASingleChildMethod();
...@@ -180,85 +226,127 @@ aspect Transform { ...@@ -180,85 +226,127 @@ aspect Transform {
getBuildConfig().getIncludeAttributeMethod().shouldInclude(obj, childName, true, () -> catchedInvoke(otherMethod.getMethod(), obj)) : getBuildConfig().getIncludeAttributeMethod().shouldInclude(obj, childName, true, () -> catchedInvoke(otherMethod.getMethod(), obj)) :
getBuildConfig().getIncludeChildMethod().shouldInclude(obj, otherMethod.getMethod().invoke(obj), childName); getBuildConfig().getIncludeChildMethod().shouldInclude(obj, otherMethod.getMethod().invoke(obj), childName);
if (!shouldInclude) { if (!shouldInclude) {
continue; return false;
} }
DumpNode targetNode = transform(tti, otherMethod.getMethod().invoke(obj), options.asNormal(false).computed(computed).allowNullObjectsOnce()); DumpNode targetNode = transform(tti, otherMethod.getMethod().invoke(obj), options.asNormal(false).computed(computed).allowNullObjectsOnce());
if (targetNode != null) { if (targetNode != null) {
DumpNormalChildNode normalChild = new DumpNormalChildNode(); DumpNormalChildNode normalChild = new DumpNormalChildNode();
normalChild.setName(childName); normalChild.setLabel(childName);
normalChild.setDumpNode(targetNode); normalChild.setDumpNode(targetNode);
normalChild.setComputed(computed); normalChild.setComputed(computed);
node.addDumpChildNode(normalChild); node.addDumpChildNode(normalChild);
applyStyle(normalChild);
} }
} else if (otherMethod.isListChildMethod()) { return true;
}
private boolean DumpAst.handleOtherListChild(DumpNode node, AnalysedMethod otherMethod,
Object obj, TransformationTransferInformation tti, TransformationOptions options)
throws java.lang.reflect.InvocationTargetException, IllegalAccessException, NoSuchMethodException {
// -- listChild -- // -- listChild --
// it is always a NTAListChildMethod // it is always a NTAListChildMethod
String childName = otherMethod.getName(); String childName = otherMethod.getName();
if (!getBuildConfig().getIncludeAttributeMethod().shouldInclude(obj, childName, true, () -> catchedInvoke(otherMethod.getMethod(), obj))) { if (!getBuildConfig().getIncludeAttributeMethod().shouldInclude(obj, childName, true, () -> catchedInvoke(otherMethod.getMethod(), obj))) {
continue; return false;
} }
Iterable<?> targetList = (Iterable<?>) otherMethod.getMethod().invoke(obj); Iterable<?> targetList = (Iterable<?>) otherMethod.getMethod().invoke(obj);
DumpListChildNode listChild = new DumpListChildNode(); DumpListChildNode listChild = new DumpListChildNode();
boolean computed = otherMethod.asListChildMethod().isNTAListChildMethod(); boolean computed = otherMethod.asListChildMethod().isNTAListChildMethod();
listChild.setComputed(computed); listChild.setComputed(computed);
listChild.setName(childName); listChild.setLabel(childName);
listChild.setName(tti.nextName());
node.addDumpChildNode(listChild);
RelationStyle style = applyStyle(listChild);
for (Object target : targetList) { for (Object target : targetList) {
DumpNode targetNode = transform(tti, target, options.asNormal(false).computed(computed)); 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)); InnerDumpNode inner = new InnerDumpNode().setDumpNode(targetNode);
listChild.addInnerDumpNode(inner);
inner.applyStyle(style);
} }
} }
if (listChild.getNumInnerDumpNode() > 0) { return true;
node.addDumpChildNode(listChild);
} }
} else if (otherMethod.isSingleRelationMethod()) {
private boolean DumpAst.handleOtherSingleRelation(DumpNode node, AnalysedMethod otherMethod,
Object obj, TransformationTransferInformation tti, TransformationOptions options)
throws java.lang.reflect.InvocationTargetException, IllegalAccessException, NoSuchMethodException {
// -- singleRelation -- // -- singleRelation --
Object target = otherMethod.getMethod().invoke(obj); Object target = otherMethod.getMethod().invoke(obj);
if (!getBuildConfig().getIncludeRelationMethod().shouldInclude(obj, target, otherMethod.getName())) { if (!getBuildConfig().getIncludeRelationMethod().shouldInclude(obj, target, otherMethod.getName())) {
continue; return false;
} }
DumpNode targetNode = transform(tti, target, options.asRelation()); DumpNode targetNode = transform(tti, target, options.asRelation());
if (targetNode != null) { if (targetNode != null) {
DumpNormalRelation normalRelation = new DumpNormalRelation(); DumpNormalRelation normalRelation = new DumpNormalRelation();
normalRelation.setName(otherMethod.getName()); normalRelation.setLabel(otherMethod.getName() +
(otherMethod.asSingleRelationMethod().isOptRelationMethod() ? "?" : ""));
normalRelation.setDumpNode(targetNode); normalRelation.setDumpNode(targetNode);
node.addDumpRelation(normalRelation); node.addDumpRelation(normalRelation);
applyStyle(normalRelation);
} }
} else if (otherMethod.isListRelationMethod()) { return true;
}
private boolean DumpAst.handleOtherListRelation(DumpNode node, AnalysedMethod otherMethod,
Object obj, TransformationTransferInformation tti, TransformationOptions options)
throws java.lang.reflect.InvocationTargetException, IllegalAccessException, NoSuchMethodException {
// -- listRelation -- // -- listRelation --
Iterable<?> targetList = (Iterable<?>) otherMethod.getMethod().invoke(obj); Iterable<?> targetList = (Iterable<?>) otherMethod.getMethod().invoke(obj);
DumpListRelation listRelation = new DumpListRelation(); DumpListRelation listRelation = new DumpListRelation();
listRelation.setName(otherMethod.getName()); listRelation.setLabel(otherMethod.getName());
listRelation.setName(tti.nextName());
node.addDumpRelation(listRelation);
RelationStyle style = applyStyle(listRelation);
int index = -1;
for (Object target : targetList) { for (Object target : targetList) {
index++;
if (!getBuildConfig().getIncludeRelationMethod().shouldInclude(obj, target, otherMethod.getName())) { if (!getBuildConfig().getIncludeRelationMethod().shouldInclude(obj, target, otherMethod.getName())) {
continue; 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.addInnerRelationDumpNode(new InnerRelationDumpNode(targetNode)); InnerRelationDumpNode inner = new InnerRelationDumpNode().setDumpNode(targetNode).setOriginalIndex(index);
listRelation.addInnerRelationDumpNode(inner);
inner.applyStyle(style);
} }
} }
if (listRelation.getNumInnerRelationDumpNode() > 0) { if (listRelation.getNumInnerRelationDumpNode() == 0) {
node.addDumpRelation(listRelation); InnerRelationDumpNode inner = new InnerRelationDumpNode().setDumpNode(transform(tti, EMPTY,
options.asNormal(false).allowNullObjectsOnce()));
listRelation.addInnerRelationDumpNode(inner);
inner.applyStyle(style);
} }
} else if (otherMethod.isTokenMethod()) { return true;
}
private boolean DumpAst.handleOtherToken(DumpNode node, AnalysedMethod otherMethod,
Object obj, TransformationTransferInformation tti, TransformationOptions options)
throws java.lang.reflect.InvocationTargetException, IllegalAccessException, NoSuchMethodException {
// -- token -- // -- token --
TokenMethod tokenMethod = otherMethod.asTokenMethod(); TokenMethod tokenMethod = otherMethod.asTokenMethod();
boolean shouldInclude = tokenMethod.isAttributeMethod() ? boolean shouldInclude = tokenMethod.isAttributeMethod() ?
getBuildConfig().getIncludeAttributeMethod().shouldInclude(obj, otherMethod.getName(), false, () -> catchedInvoke(otherMethod.getMethod(), obj)) : getBuildConfig().getIncludeAttributeMethod().shouldInclude(obj, otherMethod.getName(), false, () -> catchedInvoke(otherMethod.getMethod(), obj)) :
getBuildConfig().getIncludeTokenMethod().shouldInclude(obj, otherMethod.getName(), otherMethod.getMethod().invoke(obj)); getBuildConfig().getIncludeTokenMethod().shouldInclude(obj, otherMethod.getName(), otherMethod.getMethod().invoke(obj));
if (!shouldInclude) { if (!shouldInclude) {
continue; return false;
} }
Object target = otherMethod.getMethod().invoke(obj); Object target = otherMethod.getMethod().invoke(obj);
// local function to add common member and add the token-node to the given node
java.util.function.Consumer<DumpToken> setMemberAndAdd = token -> {
token.setLabel(otherMethod.getName());
token.setComputed(otherMethod.asTokenMethod().isAttributeMethod());
node.addDumpToken(token);
};
if (target != null) { if (target != null) {
DumpToken token = null;
boolean atLeastOneASTNode = false; boolean atLeastOneASTNode = false;
if (Iterable.class.isAssignableFrom(target.getClass())) { if (Iterable.class.isAssignableFrom(target.getClass())) {
java.util.List<DumpNode> nodes = new java.util.ArrayList<>(); java.util.List<DumpNode> nodes = new java.util.ArrayList<>();
Iterable iterable = (Iterable) target; Iterable<?> iterable = (Iterable<?>) target;
for (Object element : iterable) { for (Object element : iterable) {
// TODO check if isAstNode for first non-null. if yes, use DumpReferenceListToken, other DumpValueToken // check if isAstNode for first non-null. if yes, use DumpReferenceListToken, otherwise DumpValueToken
DumpNode nodeForElement = transform(tti, element, options.asRelation()); DumpNode nodeForElement = transform(tti, element, options.asRelation());
nodes.add(nodeForElement); nodes.add(nodeForElement);
if (nodeForElement != null && nodeForElement.isAstNode()) { if (nodeForElement != null && nodeForElement.isAstNode()) {
...@@ -267,35 +355,32 @@ aspect Transform { ...@@ -267,35 +355,32 @@ aspect Transform {
} }
if (atLeastOneASTNode) { if (atLeastOneASTNode) {
DumpReferenceListToken listToken = new DumpReferenceListToken(); DumpReferenceListToken listToken = new DumpReferenceListToken();
listToken.setName(tti.nextName());
setMemberAndAdd.accept(listToken);
RelationStyle style = applyStyle(listToken);
nodes.forEach(element -> { nodes.forEach(element -> {
listToken.addInnerRelationDumpNode(new InnerRelationDumpNode().setDumpNode(element)); InnerRelationDumpNode inner = new InnerRelationDumpNode().setDumpNode(element);
listToken.addInnerRelationDumpNode(inner);
inner.applyStyle(style);
}); });
token = listToken;
} }
} }
if (!atLeastOneASTNode) { if (!atLeastOneASTNode) {
DumpNode targetNode = transform(tti, target, options.asRelation()); DumpNode targetNode = transform(tti, target, options.asRelation());
if (targetNode != null && targetNode.isAstNode()) { if (targetNode != null && targetNode.isAstNode()) {
token = new DumpReferenceToken().setValue(targetNode); DumpReferenceToken token = new DumpReferenceToken().setDumpNode(targetNode);
setMemberAndAdd.accept(token);
applyStyle(token.asDumpReferenceToken());
} else { } else {
if (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; setMemberAndAdd.accept(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; return true;
} }
private Object DumpAst.catchedInvoke(java.lang.reflect.Method method, Object obj) { private Object DumpAst.catchedInvoke(java.lang.reflect.Method method, Object obj) {
...@@ -308,38 +393,103 @@ aspect Transform { ...@@ -308,38 +393,103 @@ aspect Transform {
} }
} }
// === NodeStyle ===
DumpNode NodeStyle.node;
NodeStyle DumpNode.createDefaultStyle() {
NodeStyle result = new NodeStyle();
result.node = this;
result.useSimpleLabelWithHashCode();
return result;
}
private void DumpAst.applyStyle(DumpNode node) { private void DumpAst.applyStyle(DumpNode node) {
Object obj = node.getObject(); NodeStyle style = node.createDefaultStyle();
node.setLabel(getBuildConfig().getStyleInformation().getNameMethod().get(obj)); getPrintConfig().getNodeStyleDefinition().style(node.getObject(), style);
node.setBackgroundColor(getBuildConfig().getStyleInformation().getBackgroundColorMethod().get(obj)); node.setLabel(style.getLabel());
node.setTextColor(getBuildConfig().getStyleInformation().getTextColorMethod().get(obj)); node.setBackgroundColor(style.getBackgroundColor());
node.setManualStereotypes(getBuildConfig().getStyleInformation().getStereotypeMethod().get(obj)); node.setTextColor(style.getTextColor());
node.setManualStereotypes(style.getStereoTypes());
} }
// TODO: add new attributes for: {token,child,relation,attribute,nta}Enabled(String parentType, String name). 1) just move implementation into this attribute. 2) add include/exclude on type-level to it. // === RelationStyle ===
interface RelationStyleDefinable {
// more methods in TransformPlus.jadd
// --- isTypeEnabled --- // attributes
syn boolean DumpAst.isTypeEnabled(String typeName) { BuildConfig buildConfig();
return !matches(getBuildConfig().typeIgnorePattern(), typeName); boolean isComputed();
boolean isContainment();
String initialLabel();
DumpNode containingDumpNode();
DumpNode getDumpNode();
}
interface RelationStylable<SELF> {
// more methods in TransformPlus.jadd
// tokens
String getTextColor();
String getLineColor();
SELF setLabel(String name);
SELF setTextColor(String textColor);
SELF setLineColor(String lineColor);
} }
// --- {typeIgnore,child,token,relation,attribute,nta}Pattern --- DumpChildNode implements RelationStyleDefinable, RelationStylable<DumpChildNode>;
syn java.util.regex.Pattern BuildConfig.typeIgnorePattern() = java.util.regex.Pattern.compile(getTypeIgnorePattern()); syn String DumpChildNode.initialLabel() = getLabel();
syn java.util.regex.Pattern PatternCollection.childPattern() = java.util.regex.Pattern.compile(getChildPattern()); syn boolean DumpChildNode.isComputed() = getComputed();
syn java.util.regex.Pattern PatternCollection.tokenPattern() = java.util.regex.Pattern.compile(getTokenPattern()); syn boolean DumpChildNode.isContainment() = true;
syn java.util.regex.Pattern PatternCollection.relationPattern() = java.util.regex.Pattern.compile(getRelationPattern());
syn java.util.regex.Pattern PatternCollection.attributePattern() = java.util.regex.Pattern.compile(getAttributePattern()); syn DumpNode DumpListChildNode.getDumpNode() = null;
syn java.util.regex.Pattern PatternCollection.ntaPattern() = java.util.regex.Pattern.compile(getNonterminalAttributePattern());
syn java.util.regex.Pattern TypePatternCollectionMapping.typePattern() = java.util.regex.Pattern.compile(getTypeRegex()); DumpRelation implements RelationStyleDefinable, RelationStylable<DumpRelation>;
syn String DumpRelation.initialLabel() = getLabel();
syn boolean DumpRelation.isComputed() = false;
syn boolean DumpRelation.isContainment() = false;
syn DumpNode DumpListRelation.getDumpNode() = null;
InnerDumpNode implements RelationStylable<InnerDumpNode>;
InnerRelationDumpNode implements RelationStylable<InnerRelationDumpNode>;
// --- matches --- DumpReferenceListToken implements RelationStyleDefinable, RelationStylable<DumpReferenceListToken>;
static boolean ASTNode.matches(java.util.regex.Pattern p, String input) { syn String DumpReferenceListToken.initialLabel() = getLabel();
return p.matcher(input).matches(); syn boolean DumpReferenceListToken.isComputed() = getComputed();
syn boolean DumpReferenceListToken.isContainment() = false;
syn DumpNode DumpReferenceListToken.getDumpNode() = null;
DumpReferenceToken implements RelationStyleDefinable, RelationStylable<DumpReferenceToken>;
syn String DumpReferenceToken.initialLabel() = getLabel();
syn boolean DumpReferenceToken.isComputed() = getComputed();
syn boolean DumpReferenceToken.isContainment() = false;
private void DumpAst.applyStyle(RelationStylable<?> stylable, RelationStyle style) {
stylable.applyStyle(style);
}
private void DumpAst.applyStyle(DumpToken token, RelationStyle style) {
if (token.isDumpReferenceListToken()) {
token.asDumpReferenceListToken().applyStyle(style);
} else if (token.isDumpReferenceToken()) {
token.asDumpReferenceToken().applyStyle(style);
}
// otherwise ignore
} }
// --- isTypeEnabled ---
syn boolean DumpAst.isTypeEnabled(String typeName) {
return !getBuildConfig().typeIgnorePattern().matcher(typeName).matches();
}
// --- typeIgnorePattern ---
syn java.util.regex.Pattern BuildConfig.typeIgnorePattern() = java.util.regex.Pattern.compile(getTypeIgnorePattern());
class TransformationTransferInformation { class TransformationTransferInformation {
java.util.Map<Object, DumpNode> transformed = new java.util.HashMap<>(); java.util.Map<Object, DumpNode> transformed = new java.util.HashMap<>();
java.util.Map<DumpNode, Boolean> relationTargetsUnprocessed = new java.util.HashMap<>(); java.util.Map<DumpNode, Boolean> relationTargetsUnprocessed = new java.util.HashMap<>();
int nodeCounter = 0; int nodeCounter = 0;
String nextName() {
return "node" + (nodeCounter++);
}
} }
} }
// more stuff from Transform that is not correctly handled by the IntelliJ plugin
aspect Transform {
interface RelationStyleDefinable {
default RelationStyle createDefaultStyle() {
return new RelationStyle()
.setLabel(initialLabel())
.setLineColor(isComputed() ? buildConfig().getStyleInformation().getComputedColor() : "")
.setTextColor(isComputed() ? buildConfig().getStyleInformation().getComputedColor() : "");
}
default RelationStyle getStyle(RelationStyleDefinition relationStyleDefinition) {
RelationStyle style = createDefaultStyle();
relationStyleDefinition.style(containingDumpNode().getObject(),
getDumpNode() != null ? getDumpNode().getObject() : null,
isComputed(), isContainment(), style);
return style;
}
}
interface RelationStylable<SELF> {
default boolean needRelationStyling() {
return !getLineColor().isEmpty() || !getTextColor().isEmpty();
}
default void applyStyle(RelationStyle style) {
setLabel(style.getLabel());
setTextColor(style.getTextColor());
setLineColor(style.getLineColor());
}
}
private <T extends RelationStyleDefinable & RelationStylable<?>> RelationStyle DumpAst.applyStyle(T styleDefinable) {
RelationStyle result = styleDefinable.getStyle(getPrintConfig().getRelationStyleDefinition());
styleDefinable.applyStyle(result);
return result;
}
}
aspect Util { aspect Util {
// --- find{In,Ex}cludePatternCollection ---
syn PatternCollection BuildConfig.findIncludePatternCollection(String typeRegex) {
for (TypePatternCollectionMapping mapping : getIncludeTypePatternList()) {
if (mapping.getTypeRegex().equals(typeRegex)) {
return mapping.getPatternCollection();
}
}
return null;
}
syn PatternCollection BuildConfig.findExcludePatternCollection(String typeRegex) {
for (TypePatternCollectionMapping mapping : getExcludeTypePatternList()) {
if (mapping.getTypeRegex().equals(typeRegex)) {
return mapping.getPatternCollection();
}
}
return null;
}
private String DumpAst.titleCase(String s) { private String DumpAst.titleCase(String s) {
if (s.isEmpty()) { if (s.isEmpty()) {
return s; return s;
......
...@@ -5,6 +5,7 @@ import net.sourceforge.plantuml.FileFormatOption; ...@@ -5,6 +5,7 @@ import net.sourceforge.plantuml.FileFormatOption;
import net.sourceforge.plantuml.SourceStringReader; import net.sourceforge.plantuml.SourceStringReader;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream;
import java.io.Writer; import java.io.Writer;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.nio.file.Files; import java.nio.file.Files;
...@@ -13,114 +14,37 @@ import java.util.ResourceBundle; ...@@ -13,114 +14,37 @@ import java.util.ResourceBundle;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Supplier; import java.util.function.Supplier;
import static de.tudresden.inf.st.jastadd.dumpAst.ast.ASTNode.matches;
/** /**
* Building a dump. * Creating a snapshot of an AST.
* <p>
*
* <h3>Inclusion and Exclusion of Types</h3>
* Types can be only be disabled, see {@link #disableTypes(String, String...)}.
*
* <h3>Inclusion and Exclusion of children, tokens and relations</h3>
* Children, 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>
*/ */
@SuppressWarnings("UnusedReturnValue") @SuppressWarnings("UnusedReturnValue")
public class DumpBuilder { public class DumpBuilder {
private final Object target; private final Object target;
private String packageName; private String packageName;
private DumpAst result; private DumpAst dumpAst;
private final BuildConfig buildConfig;
private final PrintConfig printConfig; private boolean built;
protected DumpBuilder(Object target) { protected DumpBuilder(Object target) {
this.target = target; this.target = target;
buildConfig = new BuildConfig(); this.built = false;
buildConfig.setIncludeChildMethod((parentNode, childNode, contextName) -> { this.dumpAst = new DumpAst();
// level 4: excluded for type? -> return no dumpAst.setBuildConfig(new BuildConfig());
PatternCollection excludeOnType = buildConfig.matchExcludePatternCollection(parentNode.getClass().getSimpleName()); dumpAst.getBuildConfig().setIncludeChildMethod((parentNode, childNode, contextName) -> true);
if (excludeOnType != null && matches(excludeOnType.childPattern(), contextName)) { dumpAst.getBuildConfig().setIncludeRelationMethod((sourceNode, targetNode, roleName) -> true);
return false; dumpAst.getBuildConfig().setIncludeTokenMethod((node, tokenName, value) -> true);
} dumpAst.getBuildConfig().setIncludeAttributeMethod((node, attributeName, isNTA, supplier) -> false);
// level 3: included for type? -> return yes dumpAst.getBuildConfig().setStyleInformation(StyleInformation.createDefault());
PatternCollection includeOnType = buildConfig.matchIncludePatternCollection(parentNode.getClass().getSimpleName()); dumpAst.setPrintConfig(new PrintConfig());
if (includeOnType != null && matches(includeOnType.childPattern(), contextName)) { dumpAst.getPrintConfig().setScale(1);
return true; dumpAst.getPrintConfig().setVersion(readVersion());
} dumpAst.getPrintConfig().setNodeStyleDefinition((node, style) -> {});
// level 2: globally excluded? -> return no dumpAst.getPrintConfig().setRelationStyleDefinition((sourceNode, targetNode, isComputed, isContainment, style) -> {});
// level 1: otherwise return yes }
return !matches(buildConfig.getGlobalPatternCollection().childPattern(), contextName);
}); private DumpBuilder thisWithResetBuilt() {
buildConfig.setIncludeRelationMethod((sourceNode, targetNode, roleName) -> { this.built = false;
// level 4: excluded for type? -> return no return this;
PatternCollection excludeOnType = buildConfig.matchExcludePatternCollection(sourceNode.getClass().getSimpleName());
if (excludeOnType != null && matches(excludeOnType.relationPattern(), roleName)) {
return false;
}
// level 3: included for type? -> return yes
PatternCollection includeOnType = buildConfig.matchIncludePatternCollection(sourceNode.getClass().getSimpleName());
if (includeOnType != null && matches(includeOnType.relationPattern(), roleName)) {
return true;
}
// level 2: globally excluded? -> return no
// level 1: otherwise return yes
return !matches(buildConfig.getGlobalPatternCollection().relationPattern(), roleName);
});
buildConfig.setIncludeTokenMethod((node, tokenName, value) -> {
// level 4: excluded for type? -> return no
PatternCollection excludeOnType = buildConfig.matchExcludePatternCollection(node.getClass().getSimpleName());
if (excludeOnType != null && matches(excludeOnType.tokenPattern(), tokenName)) {
return false;
}
// level 3: included for type? -> return yes
PatternCollection includeOnType = buildConfig.matchIncludePatternCollection(node.getClass().getSimpleName());
if (includeOnType != null && matches(includeOnType.tokenPattern(), tokenName)) {
return true;
}
// level 2: globally excluded? -> return no
// level 1: otherwise return yes
return !matches(buildConfig.getGlobalPatternCollection().tokenPattern(), tokenName);
});
buildConfig.setIncludeAttributeMethod((node, attributeName, isNTA, supplier) -> {
// level 4: included for type? -> return yes
PatternCollection includeOnType = buildConfig.matchIncludePatternCollection(node.getClass().getSimpleName());
if (includeOnType != null && matches(isNTA ? includeOnType.ntaPattern() : includeOnType.attributePattern(), attributeName)) {
return true;
}
// level 3: excluded for type? -> return no
PatternCollection excludeOnType = buildConfig.matchExcludePatternCollection(node.getClass().getSimpleName());
if (excludeOnType != null && matches(isNTA ? excludeOnType.ntaPattern() : excludeOnType.attributePattern(), attributeName)) {
return false;
}
// level 2: globally included? -> return yes
// level 1: otherwise return no
PatternCollection global = buildConfig.getGlobalPatternCollection();
return matches(isNTA ? global.ntaPattern() : global.attributePattern(), attributeName);
});
buildConfig.setGlobalPatternCollection(new PatternCollection());
buildConfig.setStyleInformation(StyleInformation.createDefault());
printConfig = new PrintConfig();
printConfig.setScale(1);
printConfig.setVersion(readVersion());
} }
/** /**
...@@ -130,8 +54,8 @@ public class DumpBuilder { ...@@ -130,8 +54,8 @@ public class DumpBuilder {
* @return this * @return this
*/ */
public DumpBuilder enableDebug() { public DumpBuilder enableDebug() {
buildConfig.setDebug(true); dumpAst.getBuildConfig().setDebug(true);
return this; return thisWithResetBuilt();
} }
/** /**
...@@ -141,7 +65,7 @@ public class DumpBuilder { ...@@ -141,7 +65,7 @@ public class DumpBuilder {
*/ */
public DumpBuilder setPackageName(String packageName) { public DumpBuilder setPackageName(String packageName) {
this.packageName = packageName; this.packageName = packageName;
return this; return thisWithResetBuilt();
} }
/** /**
...@@ -150,8 +74,8 @@ public class DumpBuilder { ...@@ -150,8 +74,8 @@ public class DumpBuilder {
* @return this * @return this
*/ */
public DumpBuilder includeEmptyStringsOnTokens() { public DumpBuilder includeEmptyStringsOnTokens() {
buildConfig.setIncludeEmptyString(true); dumpAst.getBuildConfig().setIncludeEmptyString(true);
return this; return thisWithResetBuilt();
} }
/** /**
...@@ -159,8 +83,8 @@ public class DumpBuilder { ...@@ -159,8 +83,8 @@ public class DumpBuilder {
* @return this * @return this
*/ */
public DumpBuilder enableRelationWithRank() { public DumpBuilder enableRelationWithRank() {
printConfig.setRelationWithRank(true); dumpAst.getPrintConfig().setRelationWithRank(true);
return this; return thisWithResetBuilt();
} }
// --- Types --- // --- Types ---
...@@ -177,353 +101,42 @@ public class DumpBuilder { ...@@ -177,353 +101,42 @@ public class DumpBuilder {
* @see java.util.regex.Pattern#compile(String) * @see java.util.regex.Pattern#compile(String)
*/ */
public DumpBuilder disableTypes(String regex, String... moreRegexes) { public DumpBuilder disableTypes(String regex, String... moreRegexes) {
updateRegexes(buildConfig::getTypeIgnorePattern, updateRegexes(dumpAst.getBuildConfig()::getTypeIgnorePattern,
buildConfig::setTypeIgnorePattern, dumpAst.getBuildConfig()::setTypeIgnorePattern,
regex, moreRegexes); regex, moreRegexes);
return this; return thisWithResetBuilt();
} }
// --- Tokens --- // --- Tokens ---
public <ASTNODE> DumpBuilder includeTokensWhen(IncludeTokenMethod<ASTNODE> spec) { public <ASTNODE> DumpBuilder includeToken(IncludeTokenMethod<ASTNODE> spec) {
buildConfig.setIncludeTokenMethod(spec); dumpAst.getBuildConfig().setIncludeTokenMethod(spec);
if (!buildConfig.getGlobalPatternCollection().getTokenPattern().isEmpty()) { return thisWithResetBuilt();
System.err.println("Overriding previous filters for tokens");
}
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 regex first pattern to match token names
* @param moreRegexes more patterns to match token names
* @return this
* @see java.util.regex.Pattern#compile(String)
*/
public DumpBuilder excludeTokens(String regex, String... moreRegexes) {
updateRegexes(() -> buildConfig.getGlobalPatternCollection().getTokenPattern(),
s -> buildConfig.getGlobalPatternCollection().setTokenPattern(s),
regex, moreRegexes);
return this;
}
/**
* Exclude tokens and their value within a type 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 typeRegex pattern to match a nonterminal name
* @param regex first pattern to match token names
* @param moreRegexes more patterns to match token names
* @return this
* @see java.util.regex.Pattern#compile(String)
*/
public DumpBuilder excludeTokensFor(String typeRegex, String regex, String... moreRegexes) {
PatternCollection collection = findOrCreateExcludePatternCollection(typeRegex);
updateRegexes(collection::getTokenPattern,
collection::setTokenPattern,
regex, moreRegexes);
return this;
}
/**
* Include tokens (again) and their value within a type 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 typeRegex pattern to match a nonterminal name
* @param regex first pattern to match token names
* @param moreRegexes more patterns to match token names
* @return this
* @see java.util.regex.Pattern#compile(String)
*/
public DumpBuilder includeTokensFor(String typeRegex, String regex, String... moreRegexes) {
PatternCollection collection = findOrCreateIncludePatternCollection(typeRegex);
updateRegexes(collection::getTokenPattern,
collection::setTokenPattern,
regex, moreRegexes);
return this;
} }
// --- Children --- // --- Children ---
public <ASTNODE> DumpBuilder includeChildWhen(IncludeChildMethod<ASTNODE> spec) { public <ASTNODE> DumpBuilder includeChild(IncludeChildMethod<ASTNODE> spec) {
buildConfig.setIncludeChildMethod(spec); dumpAst.getBuildConfig().setIncludeChildMethod(spec);
if (!buildConfig.getGlobalPatternCollection().getChildPattern().isEmpty()) { return thisWithResetBuilt();
System.err.println("Overriding previous filters for children");
}
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 regex first pattern to match child name
* @param moreRegexes more patterns to match child names
* @return this
* @see java.util.regex.Pattern#compile(String)
*/
public DumpBuilder excludeChildren(String regex, String... moreRegexes) {
updateRegexes(() -> buildConfig.getGlobalPatternCollection().getChildPattern(),
s -> buildConfig.getGlobalPatternCollection().setChildPattern(s),
regex, moreRegexes);
return this;
}
/**
* Exclude every child within a type 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 typeRegex pattern to match a nonterminal name
* @param regex first pattern to match child name
* @param moreRegexes more patterns to match child names
* @return this
* @see java.util.regex.Pattern#compile(String)
*/
public DumpBuilder excludeChildrenFor(String typeRegex, String regex, String... moreRegexes) {
PatternCollection collection = findOrCreateExcludePatternCollection(typeRegex);
updateRegexes(collection::getChildPattern,
collection::setChildPattern,
regex, moreRegexes);
return this;
}
/**
* Include every child (again) within a type 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 typeRegex pattern to match a nonterminal name
* @param regex first pattern to match child name
* @param moreRegexes more patterns to match child names
* @return this
* @see java.util.regex.Pattern#compile(String)
*/
public DumpBuilder includeChildrenFor(String typeRegex, String regex, String... moreRegexes) {
PatternCollection collection = findOrCreateIncludePatternCollection(typeRegex);
updateRegexes(collection::getChildPattern,
collection::setChildPattern,
regex, moreRegexes);
return this;
} }
// --- Attributes --- // --- Attributes ---
public <ASTNODE> DumpBuilder includeAttributeWhen(IncludeAttributeMethod<ASTNODE> spec) { public <ASTNODE> DumpBuilder includeAttribute(IncludeAttributeMethod<ASTNODE> spec) {
buildConfig.setIncludeAttributeMethod(spec); dumpAst.getBuildConfig().setIncludeAttributeMethod(spec);
if (!buildConfig.getGlobalPatternCollection().getAttributePattern().isEmpty()) { return thisWithResetBuilt();
System.err.println("Overriding previous filters for attributes");
}
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 regex first pattern to match attribute name
* @param moreRegexes more patterns to match attribute names
* @return this
* @see java.util.regex.Pattern#compile(String)
*/
public DumpBuilder includeAttributes(String regex, String... moreRegexes) {
updateRegexes(() -> buildConfig.getGlobalPatternCollection().getAttributePattern(),
s -> buildConfig.getGlobalPatternCollection().setAttributePattern(s),
regex, moreRegexes);
return this;
}
/**
* Include attributes within a type (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 typeRegex pattern to match a nonterminal name
* @param regex first pattern to match attribute name
* @param moreRegexes more patterns to match attribute names
* @return this
* @see java.util.regex.Pattern#compile(String)
*/
public DumpBuilder includeAttributesFor(String typeRegex, String regex, String... moreRegexes) {
PatternCollection collection = findOrCreateIncludePatternCollection(typeRegex);
updateRegexes(collection::getAttributePattern,
collection::setAttributePattern,
regex, moreRegexes);
return this;
}
/**
* Exclude attributes within a type (as tokens) and their value (again) 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 typeRegex pattern to match a nonterminal name
* @param regex first pattern to match attribute name
* @param moreRegexes more patterns to match attribute names
* @return this
* @see java.util.regex.Pattern#compile(String)
*/
public DumpBuilder excludeAttributesFor(String typeRegex, String regex, String... moreRegexes) {
PatternCollection collection = findOrCreateExcludePatternCollection(typeRegex);
updateRegexes(collection::getAttributePattern,
collection::setAttributePattern,
regex, moreRegexes);
return this;
}
// --- Nonterminal-Attributes ---
/**
* 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 regex first pattern to match attribute name
* @param moreRegexes more patterns to match attribute names
* @return this
* @see java.util.regex.Pattern#compile(String)
*/
public DumpBuilder includeNonterminalAttributes(String regex, String... moreRegexes) {
updateRegexes(() -> buildConfig.getGlobalPatternCollection().getNonterminalAttributePattern(),
s -> buildConfig.getGlobalPatternCollection().setNonterminalAttributePattern(s),
regex, moreRegexes);
return this;
}
/**
* Includes nonterminal-attributes (as children) and their values within a type 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 typeRegex pattern to match a nonterminal name
* @param regex first pattern to match attribute name
* @param moreRegexes more patterns to match attribute names
* @return this
* @see java.util.regex.Pattern#compile(String)
*/
public DumpBuilder includeNonterminalAttributesFor(String typeRegex, String regex, String... moreRegexes) {
PatternCollection collection = findOrCreateIncludePatternCollection(typeRegex);
updateRegexes(collection::getNonterminalAttributePattern,
collection::setNonterminalAttributePattern,
regex, moreRegexes);
return this;
}
/**
* Excludes nonterminal-attributes (as children) and their values (again) within a type 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 typeRegex pattern to match a nonterminal name
* @param regex first pattern to match attribute name
* @param moreRegexes more patterns to match attribute names
* @return this
* @see java.util.regex.Pattern#compile(String)
*/
public DumpBuilder excludeNonterminalAttributesFor(String typeRegex, String regex, String... moreRegexes) {
PatternCollection collection = findOrCreateExcludePatternCollection(typeRegex);
updateRegexes(collection::getNonterminalAttributePattern,
collection::setNonterminalAttributePattern,
regex, moreRegexes);
return this;
} }
// --- Relations --- // --- Relations ---
public <ASTNODE> DumpBuilder includeRelationsWhen(IncludeRelationMethod<ASTNODE> spec) { public <ASTNODE> DumpBuilder includeRelation(IncludeRelationMethod<ASTNODE> spec) {
buildConfig.setIncludeRelationMethod(spec); dumpAst.getBuildConfig().setIncludeRelationMethod(spec);
if (!buildConfig.getGlobalPatternCollection().getRelationPattern().isEmpty()) { return thisWithResetBuilt();
System.err.println("Overriding previous filters for relations");
}
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 regex first pattern to match child name
* @param moreRegexes more patterns to match child names
* @return this
* @see java.util.regex.Pattern#compile(String)
*/
public DumpBuilder excludeRelations(String regex, String... moreRegexes) {
updateRegexes(() -> buildConfig.getGlobalPatternCollection().getRelationPattern(),
s -> buildConfig.getGlobalPatternCollection().setRelationPattern(s),
regex, moreRegexes);
return this;
}
/**
* Exclude every relation within a type 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 typeRegex pattern to match a nonterminal name
* @param regex first pattern to match child name
* @param moreRegexes more patterns to match child names
* @return this
* @see java.util.regex.Pattern#compile(String)
*/
public DumpBuilder excludeRelationsFor(String typeRegex, String regex, String... moreRegexes) {
PatternCollection collection = findOrCreateExcludePatternCollection(typeRegex);
updateRegexes(collection::getRelationPattern,
collection::setRelationPattern,
regex, moreRegexes);
return this;
}
/**
* Include every relation (again) within a type 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 typeRegex pattern to match a nonterminal name
* @param regex first pattern to match child name
* @param moreRegexes more patterns to match child names
* @return this
* @see java.util.regex.Pattern#compile(String)
*/
public DumpBuilder includeRelationsFor(String typeRegex, String regex, String... moreRegexes) {
PatternCollection collection = findOrCreateIncludePatternCollection(typeRegex);
updateRegexes(collection::getRelationPattern,
collection::setRelationPattern,
regex, moreRegexes);
return this;
} }
// --- Settings --- // --- Settings ---
/** /**
* Omit children that are <code>null</code> (in a full AST, there are no such nodes). * Omit children that are <code>null</code> (in a full AST, there are no such nodes).
* *
...@@ -531,8 +144,8 @@ public class DumpBuilder { ...@@ -531,8 +144,8 @@ public class DumpBuilder {
* @return this * @return this
*/ */
public DumpBuilder excludeNullNodes() { public DumpBuilder excludeNullNodes() {
buildConfig.setExcludeNullNodes(true); dumpAst.getBuildConfig().setExcludeNullNodes(true);
return this; return thisWithResetBuilt();
} }
/** /**
...@@ -542,8 +155,8 @@ public class DumpBuilder { ...@@ -542,8 +155,8 @@ public class DumpBuilder {
* @return this * @return this
*/ */
public DumpBuilder includeNullNodes() { public DumpBuilder includeNullNodes() {
buildConfig.setExcludeNullNodes(false); dumpAst.getBuildConfig().setExcludeNullNodes(false);
return this; return thisWithResetBuilt();
} }
/** /**
...@@ -552,8 +165,8 @@ public class DumpBuilder { ...@@ -552,8 +165,8 @@ public class DumpBuilder {
* @return this * @return this
*/ */
public DumpBuilder customPreamble(String option) { public DumpBuilder customPreamble(String option) {
printConfig.addHeader(new Header(option)); dumpAst.getPrintConfig().addHeader(new Header(option));
return this; return thisWithResetBuilt();
} }
/** /**
...@@ -564,7 +177,7 @@ public class DumpBuilder { ...@@ -564,7 +177,7 @@ public class DumpBuilder {
*/ */
public DumpBuilder skinParam(SkinParamStringSetting setting, String value) { public DumpBuilder skinParam(SkinParamStringSetting setting, String value) {
customPreamble("skinparam " + setting.toString() + " " + value); customPreamble("skinparam " + setting.toString() + " " + value);
return this; return thisWithResetBuilt();
} }
/** /**
...@@ -575,63 +188,29 @@ public class DumpBuilder { ...@@ -575,63 +188,29 @@ public class DumpBuilder {
*/ */
public DumpBuilder skinParam(SkinParamBooleanSetting setting, boolean value) { public DumpBuilder skinParam(SkinParamBooleanSetting setting, boolean value) {
customPreamble("skinparam " + setting.toString() + " " + value); customPreamble("skinparam " + setting.toString() + " " + value);
return this; return thisWithResetBuilt();
}
/**
* Set the method defining, what name a node has (default: {@code n -> n == null ? "null" : n.getClass().getSimpleName() + "@" + Integer.toHexString(n.hashCode())}).
*
* <p>Example:<br>
* {@code builder.<ASTNode<?>>setNameMethod(n -> n.isA() ? "A" : "Not A")}
* @param nameMethod a style method
* @param <ASTNODE> the type of ASTNode
* @return this
*/
public <ASTNODE> DumpBuilder setNameMethod(StyleMethod<ASTNODE> nameMethod) {
buildConfig.getStyleInformation().setNameMethod(nameMethod);
return this;
}
/**
* Set the method defining, what background color a node has (default: {@code n -> ""}).
*
* <p>Example:<br>
* {@code builder.<ASTNode<?>>setBackgroundColorMethod(n -> n.isA() ? "red" : "blue")}
* @param colorMethod a style method
* @param <ASTNODE> the type of ASTNode
* @return this
*/
public <ASTNODE> DumpBuilder setBackgroundColorMethod(StyleMethod<ASTNODE> colorMethod) {
buildConfig.getStyleInformation().setBackgroundColorMethod(colorMethod);
return this;
} }
/** /**
* Set the method defining, what text color a node has (default: {@code n -> ""}). * Set the styling definition for all nodes to change appearance, e.g., of background color.
* * @param styleDefinition the new style definition
* <p>Example:<br>
* {@code builder.<ASTNode<?>>setTextColorMethod(n -> n.isA() ? "black" : "white")}
* @param colorMethod a style method
* @param <ASTNODE> the type of ASTNode
* @return this * @return this
* @param <ASTNODE> type of AstNode
*/ */
public <ASTNODE> DumpBuilder setTextColorMethod(StyleMethod<ASTNODE> colorMethod) { public <ASTNODE> DumpBuilder nodeStyle(NodeStyleDefinition<ASTNODE> styleDefinition) {
buildConfig.getStyleInformation().setTextColorMethod(colorMethod); dumpAst.getPrintConfig().setNodeStyleDefinition(styleDefinition);
return this; return thisWithResetBuilt();
} }
/** /**
* Set the method defining, what stereotype a node has (default: {@code n -> ""}). * Set the styling definition for all relations to change appearance, e.g., of its label.
* * @param styleDefinition the new style definition
* <p>Example:<br>
* {@code builder.<ASTNode<?>>setStereotypeMethod(n -> n.isA() ? "MyStereoType" : "")}
* @param stereotypeMethod a style method
* @param <ASTNODE> the type of ASTNode
* @return this * @return this
* @param <ASTNODE> type of AstNode
*/ */
public <ASTNODE> DumpBuilder setStereotypeMethod(StyleMethod<ASTNODE> stereotypeMethod) { public <ASTNODE> DumpBuilder relationStyle(RelationStyleDefinition<ASTNODE> styleDefinition) {
buildConfig.getStyleInformation().setStereotypeMethod(stereotypeMethod); dumpAst.getPrintConfig().setRelationStyleDefinition(styleDefinition);
return this; return thisWithResetBuilt();
} }
/** /**
...@@ -641,8 +220,8 @@ public class DumpBuilder { ...@@ -641,8 +220,8 @@ public class DumpBuilder {
* @see <a href="https://plantuml.com/en/color">https://plantuml.com/en/color</a> * @see <a href="https://plantuml.com/en/color">https://plantuml.com/en/color</a>
*/ */
public DumpBuilder setComputedColor(String color) { public DumpBuilder setComputedColor(String color) {
buildConfig.getStyleInformation().setComputedColor(color); dumpAst.getBuildConfig().getStyleInformation().setComputedColor(color);
return this; return thisWithResetBuilt();
} }
/** /**
...@@ -651,8 +230,8 @@ public class DumpBuilder { ...@@ -651,8 +230,8 @@ public class DumpBuilder {
* @return this * @return this
*/ */
public DumpBuilder setScale(double value) { public DumpBuilder setScale(double value) {
printConfig.setScale(value); dumpAst.getPrintConfig().setScale(value);
return this; return thisWithResetBuilt();
} }
/** /**
...@@ -660,8 +239,8 @@ public class DumpBuilder { ...@@ -660,8 +239,8 @@ public class DumpBuilder {
* @return this * @return this
*/ */
public DumpBuilder orderChildren() { public DumpBuilder orderChildren() {
printConfig.setOrderChildren(true); dumpAst.getPrintConfig().setOrderChildren(true);
return this; return thisWithResetBuilt();
} }
// --- Dump methods --- // --- Dump methods ---
...@@ -690,6 +269,16 @@ public class DumpBuilder { ...@@ -690,6 +269,16 @@ public class DumpBuilder {
} }
} }
/**
* Return content as intermediate data structure used by the template engine.
*
* @return the intermediate data structure used by the template engine
* @throws TransformationException if {@link DumpAst#transform(TransformationTransferInformation, Object) transform} was not successful
*/
public String dumpAsYaml(boolean prependCreationComment) throws TransformationException {
return build().printYaml(prependCreationComment);
}
/** /**
* Write out content as intermediate data structure used by the template engine. * Write out content as intermediate data structure used by the template engine.
* *
...@@ -705,6 +294,17 @@ public class DumpBuilder { ...@@ -705,6 +294,17 @@ public class DumpBuilder {
} }
} }
/**
* Write out content as PNG image generated by plantuml.
*
* @param os a stream to write to
* @throws IOException if an I/O error happened during writing in that stream
* @throws TransformationException if {@link DumpAst#transform(TransformationTransferInformation, Object) transform} was not successful
*/
public void dumpAsPNG(OutputStream os) throws TransformationException, IOException {
dumpAs(os, FileFormat.PNG);
}
/** /**
* Write out content as PNG image generated by plantuml. * Write out content as PNG image generated by plantuml.
* *
...@@ -716,6 +316,17 @@ public class DumpBuilder { ...@@ -716,6 +316,17 @@ public class DumpBuilder {
dumpAs(destination, FileFormat.PNG); dumpAs(destination, FileFormat.PNG);
} }
/**
* Write out content as SVG image generated by plantuml.
*
* @param os a stream to write to
* @throws IOException if an I/O error happened during writing in that stream
* @throws TransformationException if {@link DumpAst#transform(TransformationTransferInformation, Object) transform} was not successful
*/
public void dumpAsSVG(OutputStream os) throws TransformationException, IOException {
dumpAs(os, FileFormat.SVG);
}
/** /**
* Write out content as SVG image generated by plantuml. * Write out content as SVG image generated by plantuml.
* *
...@@ -727,6 +338,20 @@ public class DumpBuilder { ...@@ -727,6 +338,20 @@ public class DumpBuilder {
dumpAs(destination, FileFormat.SVG); dumpAs(destination, FileFormat.SVG);
} }
/**
* Write out content as PDF image generated by plantuml.
*
* <br>
* <b>Note:</b> This requires additional dependencies, see <a href="https://plantuml.com/pdf">https://plantuml.com/pdf</a>
*
* @param os a stream to write to
* @throws IOException if an I/O error happened during writing in that stream
* @throws TransformationException if {@link DumpAst#transform(TransformationTransferInformation, Object) transform} was not successful
*/
public void dumpAsPDF(OutputStream os) throws TransformationException, IOException {
dumpAs(os, FileFormat.PDF);
}
/** /**
* Write out content as PDF generated by plantuml. * Write out content as PDF generated by plantuml.
* *
...@@ -747,11 +372,16 @@ public class DumpBuilder { ...@@ -747,11 +372,16 @@ public class DumpBuilder {
reader.outputImage(Files.newOutputStream(destination), new FileFormatOption(fileFormat)); reader.outputImage(Files.newOutputStream(destination), new FileFormatOption(fileFormat));
} }
private void dumpAs(OutputStream os, FileFormat fileFormat) throws IOException, TransformationException {
String content = build().toPlantUml();
SourceStringReader reader = new SourceStringReader(content);
reader.outputImage(os, new FileFormatOption(fileFormat));
}
// --- Helper methods --- // --- Helper methods ---
protected DumpAst build() throws TransformationException { protected DumpAst build() throws TransformationException {
if (result == null) { if (!built) {
result = new DumpAst();
final String packageNameToUse; final String packageNameToUse;
if (this.packageName != null) { if (this.packageName != null) {
packageNameToUse = this.packageName; packageNameToUse = this.packageName;
...@@ -762,41 +392,15 @@ public class DumpBuilder { ...@@ -762,41 +392,15 @@ public class DumpBuilder {
packageNameToUse = this.target.getClass().getPackage().getName(); packageNameToUse = this.target.getClass().getPackage().getName();
} }
} }
result.setPackageName(packageNameToUse); dumpAst.setPackageName(packageNameToUse);
result.setBuildConfig(this.buildConfig);
result.setPrintConfig(this.printConfig);
try { try {
result.transform(new TransformationTransferInformation(), this.target); dumpAst.transform(new TransformationTransferInformation(), this.target);
} catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) { } catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) {
result = null; dumpAst = null;
throw new TransformationException(e); throw new TransformationException(e);
} }
} }
return result; return dumpAst;
}
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(Supplier<String> getter, Consumer<String> setter, String regex, String... moreRegexes) { private void updateRegexes(Supplier<String> getter, Consumer<String> setter, String regex, String... moreRegexes) {
......
{{#TextColor}}#text:{{{TextColor}}}{{/TextColor}}{{#LineColor}}{{#TextColor}};{{/TextColor}}{{^TextColor}}#{{/TextColor}}line:{{{LineColor}}}{{/LineColor}}
\ No newline at end of file
...@@ -11,57 +11,64 @@ skinparam object<<NTA>> { ...@@ -11,57 +11,64 @@ skinparam object<<NTA>> {
hide <<NTA>> stereotype hide <<NTA>> stereotype
{{#PrintConfig}} {{#PrintConfig}}
scale {{{scale}}} scale {{{Scale}}}
{{#Headers}} {{#Headers}}
{{{value}}} {{{Value}}}
{{/Headers}} {{/Headers}}
{{/PrintConfig}} {{/PrintConfig}}
{{#DumpNodes}} {{#DumpNodes}}
{{^invisible}} {{^Invisible}}
{{#isNull}} {{#isNull}}
object "null" as {{{name}}}<<null>> object "null" as {{{Name}}}<<null>>
{{/isNull}} {{/isNull}}
{{#isEmpty}}
object "[]" as {{{Name}}}<<null>>
{{/isEmpty}}
{{#isAstNode}} {{#isAstNode}}
object "{{{labelAndTextColor}}}" as {{{name}}} {{{stereotypeList}}} {{#backgroundColor}}#{{{backgroundColor}}}{{/backgroundColor}} { object "{{{labelAndTextColor}}}" as {{{Name}}} {{{stereotypeList}}} {{#BackgroundColor}}#{{{BackgroundColor}}}{{/BackgroundColor}} {
{{#DumpTokens}} {{#DumpTokens}}
{{#isDumpValueToken}} {{#isDumpValueToken}}
{{{label}}} = {{{value}}} {{{label}}} = {{{Value}}}
{{/isDumpValueToken}} {{/isDumpValueToken}}
{{/DumpTokens}} {{/DumpTokens}}
} }
{{/isAstNode}} {{/isAstNode}}
{{/invisible}} {{/Invisible}}
{{/DumpNodes}} {{/DumpNodes}}
{{#DumpNodes}} {{#DumpNodes}}
{{#DumpTokens}} {{#DumpTokens}}
{{^invisible}} {{^Invisible}}
{{#isList}} {{#isList}}
circle " " as {{{Name}}}
{{{outerNodeName}}} .-[norank]-> {{{Name}}} {{#needRelationStyling}}{{>RelationStyle}}{{/needRelationStyling}} : "{{{label}}}"
{{#InnerRelationDumpNode}} {{#InnerRelationDumpNode}}
{{#bothVisible}} {{#bothVisible}}
{{{outerNodeName}}} .[#black{{#computed}},#{{{computedColor}}}{{/computed}}{{#innerNotNull}},norank{{/innerNotNull}}].> {{{innerNodeName}}} : {{{label}}} {{{Name}}} .{{#innerNotNullOrEmpty}}[norank]{{/innerNotNullOrEmpty}}.> {{{innerNodeName}}} {{#needRelationStyling}}{{>RelationStyle}}{{/needRelationStyling}} : "{{{label}}}"
{{/bothVisible}} {{/bothVisible}}
{{/InnerRelationDumpNode}} {{/InnerRelationDumpNode}}
{{/isList}} {{/isList}}
{{^isList}} {{^isList}}
{{^isDumpValueToken}} {{^isDumpValueToken}}
{{{outerNodeName}}} .[#black{{#computed}},#{{{computedColor}}}{{/computed}}{{#innerNotNull}},norank{{/innerNotNull}}].> {{{innerNodeName}}} : {{{label}}} {{{outerNodeName}}} .{{#innerNotNullOrEmpty}}[norank]{{/innerNotNullOrEmpty}}.> {{{innerNodeName}}} {{#needRelationStyling}}{{>RelationStyle}}{{/needRelationStyling}} : "{{{label}}}"
{{/isDumpValueToken}} {{/isDumpValueToken}}
{{/isList}} {{/isList}}
{{/invisible}} {{/Invisible}}
{{/DumpTokens}} {{/DumpTokens}}
{{#DumpChildNodes}} {{#DumpChildNodes}}
{{#isList}} {{#isList}}
circle " " as {{{Name}}}
{{{outerNodeName}}} *-- {{{Name}}} {{#needRelationStyling}}{{>RelationStyle}}{{/needRelationStyling}} : "{{{label}}}"
{{#InnerDumpNodes}} {{#InnerDumpNodes}}
{{#bothVisible}} {{#bothVisible}}
{{{outerNodeName}}} *-{{#computed}}[#{{{computedColor}}}]{{/computed}}- {{{innerNodeName}}} : {{{label}}} {{{Name}}} -- {{{innerNodeName}}} {{#needRelationStyling}}{{>RelationStyle}}{{/needRelationStyling}} : "{{{label}}}"
{{/bothVisible}} {{/bothVisible}}
{{/InnerDumpNodes}} {{/InnerDumpNodes}}
{{/isList}} {{/isList}}
{{^isList}} {{^isList}}
{{#bothVisible}} {{#bothVisible}}
{{{outerNodeName}}} *-{{#computed}}[#{{{computedColor}}}]{{/computed}}- {{{innerNodeName}}} : {{{label}}} {{{outerNodeName}}} *-- {{{innerNodeName}}} {{#needRelationStyling}}{{>RelationStyle}}{{/needRelationStyling}} : "{{{label}}}"
{{/bothVisible}} {{/bothVisible}}
{{/isList}} {{/isList}}
{{/DumpChildNodes}} {{/DumpChildNodes}}
...@@ -69,36 +76,36 @@ object "{{{labelAndTextColor}}}" as {{{name}}} {{{stereotypeList}}} {{#backgroun ...@@ -69,36 +76,36 @@ object "{{{labelAndTextColor}}}" as {{{name}}} {{{stereotypeList}}} {{#backgroun
{{#isList}} {{#isList}}
{{#InnerRelationDumpNode}} {{#InnerRelationDumpNode}}
{{#bothVisible}} {{#bothVisible}}
{{{outerNodeName}}} {{#bidirectional}}<{{/bidirectional}}-{{#innerNotNull}}[norank]{{/innerNotNull}}-> {{{innerNodeName}}} : {{{label}}} {{{outerNodeName}}} {{#Bidirectional}}<{{/Bidirectional}}-{{#innerNotNullOrEmpty}}[norank]{{/innerNotNullOrEmpty}}-> {{{innerNodeName}}} {{#needRelationStyling}}{{>RelationStyle}}{{/needRelationStyling}} : "{{{label}}}"
{{/bothVisible}} {{/bothVisible}}
{{/InnerRelationDumpNode}} {{/InnerRelationDumpNode}}
{{/isList}} {{/isList}}
{{^isList}} {{^isList}}
{{#bothVisible}} {{#bothVisible}}
{{{outerNodeName}}} {{#bidirectional}}<{{/bidirectional}}-{{#innerNotNull}}[norank]{{/innerNotNull}}-> {{{innerNodeName}}} : {{{label}}} {{{outerNodeName}}} {{#Bidirectional}}<{{/Bidirectional}}-{{#innerNotNullOrEmpty}}[norank]{{/innerNotNullOrEmpty}}-> {{{innerNodeName}}} {{#needRelationStyling}}{{>RelationStyle}}{{/needRelationStyling}} : "{{{label}}}"
{{/bothVisible}} {{/bothVisible}}
{{/isList}} {{/isList}}
{{/DumpRelations}} {{/DumpRelations}}
{{^invisible}} {{^Invisible}}
{{#InvisiblePath}} {{#InvisiblePath}}
{{#InnerRelationDumpNode}} {{#InnerRelationDumpNode}}
{{{outerNodeName}}} o.. {{{innerNodeName}}} {{{outerNodeName}}} o.. {{{innerNodeName}}}
{{/InnerRelationDumpNode}} {{/InnerRelationDumpNode}}
{{/InvisiblePath}} {{/InvisiblePath}}
{{/invisible}} {{/Invisible}}
{{#PrintConfig}}{{#orderChildren}} {{#PrintConfig}}{{#OrderChildren}}
{{#myChildren}} {{#myChildren}}
{{#hasSuccessor}} {{#hasSuccessor}}
{{{name}}} -[hidden]right-> {{#successor}}{{{name}}}{{/successor}} {{{Name}}} -[hidden]right-> {{#successor}}{{{Name}}}{{/successor}}
{{/hasSuccessor}} {{/hasSuccessor}}
{{/myChildren}} {{/myChildren}}
{{/orderChildren}}{{/PrintConfig}} {{/OrderChildren}}{{/PrintConfig}}
{{/DumpNodes}} {{/DumpNodes}}
{{#PrintConfig}} {{#PrintConfig}}
{{#debug}} {{#debug}}
legend right legend right
%date() %date()
dumpAst: {{{version}}} dumpAst: {{{Version}}}
plantuml: %version() plantuml: %version()
endlegend endlegend
{{/debug}} {{/debug}}
......
#Thu Sep 08 16:46:11 CEST 2022 #Tue Nov 22 10:54:37 CET 2022
version=2.0.0 version=3.0.1
...@@ -24,3 +24,9 @@ T3 : AbstractT ; ...@@ -24,3 +24,9 @@ T3 : AbstractT ;
rel AbstractT.oneA -> A ; rel AbstractT.oneA -> A ;
rel AbstractT.maybeA? -> A ; rel AbstractT.maybeA? -> A ;
rel AbstractT.manyA* -> A ; rel AbstractT.manyA* -> A ;
OtherRoot : Nameable ::= E* F*;
E : Nameable ;
F : Nameable ;
rel E.myF* <-> F.myE* ;
...@@ -9,6 +9,7 @@ import org.jastadd.featureTest.ast.*; ...@@ -9,6 +9,7 @@ import org.jastadd.featureTest.ast.*;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.Arrays;
/** /**
* Main class of feature test. * Main class of feature test.
...@@ -18,44 +19,78 @@ import java.nio.file.Paths; ...@@ -18,44 +19,78 @@ import java.nio.file.Paths;
public class FeatureTestMain { public class FeatureTestMain {
public static void main(String[] args) throws IOException, TransformationException { public static void main(String[] args) throws IOException, TransformationException {
new FeatureTestMain().run(args);
}
private void run(String[] args) throws TransformationException, IOException {
String mode = args.length == 0 ? "default" : args[0];
System.out.println(Arrays.toString(args) + " -> " + mode);
final DumpBuilder builder;
switch (mode) {
case "default":
case "withRoot":
builder = withRoot();
break;
case "withOtherRoot":
builder = withOtherRoot();
break;
default:
throw new UnsupportedOperationException("Unknown mode: " + mode);
}
Path pathToYaml = Paths.get("featureTest.yml");
Path pathToPng = Paths.get("featureTest.png");
Path pathToSvg = Paths.get("featureTest.svg");
builder.dumpAsYaml(pathToYaml, true);
builder.dumpAsPNG(pathToPng);
builder.dumpAsSVG(pathToSvg);
builder.dumpAsSource(Paths.get("featureTest.puml"));
}
private DumpBuilder withRoot() {
Root root = new Root(); Root root = new Root();
root.setName("Root1"); root.setName("Root1");
A a = new A().setName("A2"); A a = new A().setName("A2");
a.setB(new B().setName("B2.1")); a.setB(new B().setName("B2.1"));
a.setD(new D());
// a.setMyC(new C().setName("C2.1")); // a.setMyC(new C().setName("C2.1"));
// B b1 = new B().setName("B3").setOtherValue("some long text"); B b1 = new B().setName("B3").setOtherValue("some long text");
// C c = new C().setName("C4"); // C c = new C().setName("C4");
// c.setA(new A().setName("A4.1").setB(new B().setName("B4.1.1"))); // c.setA(new A().setName("A4.1").setB(new B().setName("B4.1.1")));
// c.setRawReference(a); // c.setRawReference(a);
// b1.setOneA(a); // b1.setOneA(a);
// B b2 = new B().setName("B5").setOtherValue("#ff00ff"); B b2 = new B().setName("B5").setOtherValue("#ff00ff");
// C myC = new C().setName("C6"); // C myC = new C().setName("C6");
// c.setA(new A().setName("A6.1").setB(new B().setName("B6.1.1"))); // c.setA(new A().setName("A6.1").setB(new B().setName("B6.1.1")));
// a.setMyC(myC); // a.setMyC(myC);
root.setA(a); root.setA(a);
// root.addB(b1); root.addB(b1);
// root.addB(b2); root.addB(b2);
// root.setC(c); // root.setC(c);
Path pathToYaml = Paths.get("featureTest.yml"); return Dumper
Path pathToPng = Paths.get("featureTest.png");
Path pathToSvg = Paths.get("featureTest.svg");
DumpBuilder builder = Dumper
// .read(null) // .read(null)
.read(root) .read(root)
// .enableRelationWithRank() // .enableRelationWithRank()
// .customPreamble("hide empty members") // .customPreamble("hide empty members")
.enableDebug() .enableDebug()
.customPreamble("title My fancy title") .customPreamble("title My fancy title")
.includeChildWhen((parentNode, childNode, contextName) -> { .includeChild((parentNode, childNode, contextName) -> {
if (parentNode instanceof A && ((A) parentNode).getName().equals("A2")) { // if (parentNode instanceof A && ((A) parentNode).getName().equals("A2")) {
return false; // return false;
// }
if (parentNode instanceof Root && childNode instanceof B) {
return !"B3".equals(((B) childNode).getName());
} }
return !contextName.equals("MyC"); // return !contextName.equals("MyC");
return true;
}) })
.includeRelationsWhen((sourceNode, targetNode, roleName) -> .includeRelation((sourceNode, targetNode, roleName) ->
!(sourceNode instanceof B) || !((B) sourceNode).getName().equals("B6.1.1")) !(sourceNode instanceof B) || !((B) sourceNode).getName().equals("B6.1.1"))
.includeAttributeWhen((node, attributeName, isNTA, supplier) -> { .includeAttribute((node, attributeName, isNTA, supplier) -> {
switch (attributeName) { switch (attributeName) {
case "referenceAttr": case "referenceAttr":
case "collectBs": case "collectBs":
...@@ -69,11 +104,66 @@ public class FeatureTestMain { ...@@ -69,11 +104,66 @@ public class FeatureTestMain {
} }
}) })
.skinParam(SkinParamBooleanSetting.Shadowing, false) .skinParam(SkinParamBooleanSetting.Shadowing, false)
.setNameMethod(node -> node.getClass().getSimpleName() + ASTNode.counter++); .relationStyle((source, target, isComputed, isContainment, style) -> {
System.out.println(style.getLabel() + ", computed: " + isComputed + ", containment: " + isContainment + ", textC: " + style.getTextColor() + ", lineC: " + style.getLineColor());
if (isContainment && target != null && style.getLabel().equals(target.getClass().getSimpleName())) {
style.setLabel("");
}
switch (style.getLabel()) {
case "ManyA":
style.setLabel("ManyA of " + ((Nameable) source).getName());
break;
case "OneA":
case "MaybeC?":
style.setTextColor("red");
style.setLineColor("green");
break;
case "B":
style.setLineColor("orange");
style.setTextColor("orange");
break;
case "collectBs":
style.setLineColor("purple");
style.setTextColor("purple");
break;
}
})
.<ASTNode<?>>nodeStyle((node, style) -> {
if (node.isA()) {
style.setBackgroundColor("yellow");
}
if (node instanceof B && ((B) node).getOtherValue().startsWith("#")) {
style.setBackgroundColor(((B) node).getOtherValue().substring(1));
}
style.setLabel(node.getClass().getSimpleName() + ASTNode.counter++);
});
}
builder.dumpAsYaml(pathToYaml, true); private DumpBuilder withOtherRoot() {
builder.dumpAsPNG(pathToPng); OtherRoot root = new OtherRoot();
builder.dumpAsSVG(pathToSvg); E e1 = new E().setName("E1");
builder.dumpAsSource(Paths.get("featureTest.puml")); E e2 = new E().setName("E2");
E e3 = new E().setName("E3");
E e4 = new E().setName("E4");
F f1 = new F().setName("F1");
F f2 = new F().setName("F2");
F f3 = new F().setName("F3");
F f4 = new F().setName("F4");
for (E e : new E[]{e1, e2, e3, e4}) {
root.addE(e);
}
for (F f : new F[]{f1, f2, f3, f4}) {
root.addF(f);
}
e1.addMyF(f1);
e1.addMyF(f2);
e2.addMyF(f2);
e3.addMyF(f2);
return Dumper.read(root)
.includeToken((node, tokenName, value) -> false)
.nodeStyle((node, style) -> {
style.setLabel(((Nameable) node).getName());
});
} }
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment