diff --git a/cdlang/src/main/java/de/monticore/cd/codegen/DecoratorConfig.java b/cdlang/src/main/java/de/monticore/cd/codegen/DecoratorConfig.java
index de28a754b..a578c8d89 100644
--- a/cdlang/src/main/java/de/monticore/cd/codegen/DecoratorConfig.java
+++ b/cdlang/src/main/java/de/monticore/cd/codegen/DecoratorConfig.java
@@ -67,6 +67,14 @@ public ChainableGenSetup withObservers() {
return this.withDecorator(new ObserverDecorator());
}
+ public ChainableGenSetup withVisitors() {
+ return this.withDecorator(new VisitorDecorator());
+ }
+
+ public ChainableGenSetup withVisitorImplementations() {
+ return this.withDecorator(new VisitorImplementationDecorator());
+ }
+
@SuppressWarnings("unchecked")
public ChainableGenSetup withDecorator(String className) {
IDecorator> newObj = (IDecorator>) ObjectFactory.createObject(className);
diff --git a/cdlang/src/main/java/de/monticore/cd/codegen/decorators/GetterDecorator.java b/cdlang/src/main/java/de/monticore/cd/codegen/decorators/GetterDecorator.java
index c1d55f96e..ee5e61ee4 100644
--- a/cdlang/src/main/java/de/monticore/cd/codegen/decorators/GetterDecorator.java
+++ b/cdlang/src/main/java/de/monticore/cd/codegen/decorators/GetterDecorator.java
@@ -4,6 +4,7 @@
import static de.monticore.cd.codegen.CD2JavaTemplates.EMPTY_BODY;
import de.monticore.cd.codegen.decorators.data.AbstractDecorator;
+import de.monticore.cd.codegen.decorators.data.DecoratorData;
import de.monticore.cd.facade.CDMethodFacade;
import de.monticore.cd.methodtemplates.CD4C;
import de.monticore.cd4code._prettyprint.CD4CodeFullPrettyPrinter;
@@ -13,6 +14,7 @@
import de.monticore.cdbasis._ast.ASTCDAttribute;
import de.monticore.cdbasis._ast.ASTCDClass;
import de.monticore.cdbasis._visitor.CDBasisVisitor2;
+import de.monticore.generating.templateengine.GlobalExtensionManagement;
import de.monticore.generating.templateengine.HookPoint;
import de.monticore.generating.templateengine.TemplateHookPoint;
import de.monticore.prettyprint.IndentPrinter;
@@ -21,7 +23,8 @@
import de.monticore.types.mccollectiontypes.types3.MCCollectionSymTypeRelations;
import de.se_rwth.commons.StringTransformations;
import de.se_rwth.commons.logging.Log;
-import java.util.Arrays;
+
+import java.util.*;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
@@ -29,9 +32,17 @@
* Add get methods to all attributes methodic
*/
-public class GetterDecorator extends AbstractDecorator implements
+public class GetterDecorator extends AbstractDecorator implements
CDBasisVisitor2 {
+ protected GetterData getterData;
+
+ @Override
+ public void init(DecoratorData util, Optional glexOpt) {
+ super.init(util, glexOpt);
+ this.getterData = this.decoratorData.createDataIfAbsent(this.getClass(), GetterData::new);
+ }
+
@Override
public void visit(ASTCDAttribute attribute) {
// First, check if we should decorate the given object
@@ -41,26 +52,29 @@ public void visit(ASTCDAttribute attribute) {
//
var decClazz = (ASTCDClass) decoratorData.getAsDecorated(originalClazz);
if (MCTypeFacade.getInstance().isBooleanType(attribute.getMCType())) {
- decorateMandatory(decClazz, attribute);
+ this.getterData.getOrCreateMethods(attribute).add(decorateMandatory(decClazz, attribute));
}
else if (MCCollectionSymTypeRelations.isList(attribute.getSymbol().getType())) {
- decorateList(decClazz, attribute);
+ this.getterData.getOrCreateMethods(attribute).add(decorateList(decClazz, attribute));
decorateWithAssocFunctions(decClazz, attribute, true);
}
else if (MCCollectionSymTypeRelations.isSet(attribute.getSymbol().getType())) {
- decorateSet(decClazz, attribute);
+ this.getterData.getOrCreateMethods(attribute).add(decorateSet(decClazz, attribute));
decorateWithAssocFunctions(decClazz, attribute, false);
}
else if (MCCollectionSymTypeRelations.isOptional(attribute.getSymbol().getType())) {
- decorateOptional(decClazz, attribute);
+ this.getterData.getOrCreateMethods(attribute).add(decorateOptional(decClazz, attribute));
+ this.getterData.getOrCreateMethods(attribute).add(decorateOptionalIsPresent(decClazz,
+ attribute));
}
else {
- decorateMandatory(decClazz, attribute);
+ this.getterData.getOrCreateMethods(attribute).add(decorateMandatory(decClazz, attribute));
}
}
}
- protected void decorateMandatory(ASTCDClass decoratedClazz, ASTCDAttribute attribute) {
+ protected MethodInformation decorateMandatory(ASTCDClass decoratedClazz,
+ ASTCDAttribute attribute) {
String name = (MCTypeFacade.getInstance().isBooleanType(attribute.getMCType()) ? "is" : "get")
+ StringTransformations.capitalize(attribute.getName());
ASTMCType type = attribute.getMCType().deepClone();
@@ -73,9 +87,13 @@ protected void decorateMandatory(ASTCDClass decoratedClazz, ASTCDAttribute attri
addToClass(decoratedClazz, method);
this.updateModifier(attribute);
+
+ return new MethodInformation(GetterMethodKind.GET_MANDATORY_OR_OPT, method, "methods.Get",
+ attribute.getName());
}
- protected void decorateOptional(ASTCDClass decoratedClazz, ASTCDAttribute attribute) {
+ protected MethodInformation decorateOptional(ASTCDClass decoratedClazz,
+ ASTCDAttribute attribute) {
String name = "get" + StringTransformations.capitalize(attribute.getName());
ASTMCType type = getCDGenService().getFirstTypeArgument(attribute.getMCType()).deepClone();
@@ -91,7 +109,12 @@ protected void decorateOptional(ASTCDClass decoratedClazz, ASTCDAttribute attrib
CD4C.getInstance().addImport(decoratedClazz, Log.class.getName());
addToClass(decoratedClazz, getMethod);
-
+ return new MethodInformation(GetterMethodKind.GET_MANDATORY_OR_OPT, getMethod,
+ "methods.opt.Get4Opt", attribute.getName());
+ }
+
+ protected MethodInformation decorateOptionalIsPresent(ASTCDClass decoratedClazz,
+ ASTCDAttribute attribute) {
ASTCDMethod isPresentMethod = CDMethodFacade.getInstance().createMethod(attribute.getModifier()
.deepClone(), MCTypeFacade.getInstance().createBooleanType(), "isPresent"
+ StringTransformations.capitalize(attribute.getName()));
@@ -100,9 +123,11 @@ protected void decorateOptional(ASTCDClass decoratedClazz, ASTCDAttribute attrib
addToClass(decoratedClazz, isPresentMethod);
this.updateModifier(attribute);
+ return new MethodInformation(GetterMethodKind.IS_PRESENT, isPresentMethod,
+ "methods.opt.IsPresent4Opt", attribute.getName());
}
- protected void decorateSet(ASTCDClass decoratedClazz, ASTCDAttribute attribute) {
+ protected MethodInformation decorateSet(ASTCDClass decoratedClazz, ASTCDAttribute attribute) {
String name = "get" + StringTransformations.capitalize(attribute.getName());
ASTMCType type = getCDGenService().getFirstTypeArgument(attribute.getMCType()).deepClone();
@@ -114,9 +139,12 @@ protected void decorateSet(ASTCDClass decoratedClazz, ASTCDAttribute attribute)
addToClass(decoratedClazz, getListMethod);
this.updateModifier(attribute);
+
+ return new MethodInformation(GetterMethodKind.GET_COLLECTION, getListMethod, "methods.Get",
+ attribute.getName());
}
- protected void decorateList(ASTCDClass decoratedClazz, ASTCDAttribute attribute) {
+ protected MethodInformation decorateList(ASTCDClass decoratedClazz, ASTCDAttribute attribute) {
String name = "get" + StringTransformations.capitalize(attribute.getName());
ASTMCType type = getCDGenService().getFirstTypeArgument(attribute.getMCType()).deepClone();
@@ -128,6 +156,8 @@ protected void decorateList(ASTCDClass decoratedClazz, ASTCDAttribute attribute)
addToClass(decoratedClazz, getListMethod);
this.updateModifier(attribute);
+ return new MethodInformation(GetterMethodKind.GET_COLLECTION, getListMethod, "methods.Get",
+ attribute.getName());
}
protected void decorateWithAssocFunctions(ASTCDClass decoratedClazz, ASTCDAttribute attribute,
@@ -236,4 +266,53 @@ public void addToTraverser(CD4CodeTraverser traverser) {
traverser.add4CDBasis(this);
}
+ public static class GetterData {
+
+ protected final Map> attributesData =
+ new LinkedHashMap<>();
+
+ public List getMethods(ASTCDAttribute node) {
+ var ret = attributesData.get(node);
+ if (ret == null) {
+ Log.warn("Requested setter of " + node.getSymbol().getFullName()
+ + ", which has no setters!", node.get_SourcePositionStart());
+ }
+ return ret == null ? List.of() : ret;
+ }
+
+ protected List getOrCreateMethods(ASTCDAttribute node) {
+ return this.attributesData.computeIfAbsent(node, a -> new ArrayList<>());
+ }
+
+ }
+
+ public static class MethodInformation {
+
+ private final GetterMethodKind kind;
+ private final ASTCDMethod getMethod;
+ private final String templateName;
+ private final String paramName;
+
+ public MethodInformation(GetterMethodKind kind, ASTCDMethod setMethod, String templateName,
+ String paramName) {
+ this.kind = kind;
+ this.getMethod = setMethod;
+ this.templateName = templateName;
+ this.paramName = paramName;
+ }
+
+ public GetterMethodKind getKind() { return kind; }
+
+ public ASTCDMethod getGetMethod() { return getMethod; }
+
+ public String getTemplateName() { return templateName; }
+
+ public String getParamName() { return paramName; }
+
+ }
+
+ public enum GetterMethodKind {
+ GET_MANDATORY_OR_OPT, IS_PRESENT, GET_COLLECTION
+ }
+
}
diff --git a/cdlang/src/main/java/de/monticore/cd/codegen/decorators/VisitorDecorator.java b/cdlang/src/main/java/de/monticore/cd/codegen/decorators/VisitorDecorator.java
new file mode 100644
index 000000000..1d72498b9
--- /dev/null
+++ b/cdlang/src/main/java/de/monticore/cd/codegen/decorators/VisitorDecorator.java
@@ -0,0 +1,120 @@
+/* (c) https://github.com/MontiCore/monticore */
+package de.monticore.cd.codegen.decorators;
+
+import com.google.common.collect.Iterables;
+import de.monticore.ast.ASTNode;
+import de.monticore.cd.codegen.decorators.data.AbstractDecorator;
+import de.monticore.cd.facade.CDMethodFacade;
+import de.monticore.cd.methodtemplates.CD4C;
+import de.monticore.cd4code.CD4CodeMill;
+import de.monticore.cd4code._visitor.CD4CodeTraverser;
+import de.monticore.cd4codebasis._ast.ASTCDInterface;
+import de.monticore.cd4codebasis._ast.ASTCDMethod;
+import de.monticore.cd4codebasis._ast.ASTCDParameter;
+import de.monticore.cdbasis._ast.ASTCDClass;
+import de.monticore.cdbasis._ast.ASTCDDefinition;
+import de.monticore.cdbasis._visitor.CDBasisVisitor2;
+import de.monticore.generating.templateengine.TemplateHookPoint;
+import de.monticore.types.MCTypeFacade;
+import de.monticore.types.mcbasictypes._ast.ASTMCQualifiedType;
+import de.monticore.types.mcbasictypes._ast.ASTMCType;
+
+import java.util.*;
+
+import static de.monticore.cd.codegen.CD2JavaTemplates.EMPTY_BODY;
+
+/**
+ * Applies the Visitor-Pattern to the CD
+ */
+public class VisitorDecorator extends AbstractDecorator implements
+ CDBasisVisitor2 {
+
+ protected static final String SUFFIX = "Visitor";
+
+ @Override
+ @SuppressWarnings("rawtypes")
+ public Iterable> getMustRunAfter() {
+ //We check that the SetterDecorator has added a Getter for an attribute,
+ // thus the Getter decorator has to run before.
+ return Iterables.concat(super.getMustRunAfter(), List.of(GetterDecorator.class));
+ }
+
+ @Override
+ public void visit(ASTCDDefinition clazz) {
+ if (decoratorData.shouldDecorate(this.getClass(), clazz)) {
+ // Get the parent (package or CDDef)
+ ASTNode origParent = this.decoratorData.getParent(clazz).get();
+ ASTNode decParent = this.decoratorData.getAsDecorated(origParent);
+
+ // create a Visitor interface for the class
+ ASTCDInterface interfaceVisitorArtifact = CD4CodeMill.cDInterfaceBuilder().setName("I" + clazz
+ .getName() + SUFFIX).setModifier(CD4CodeMill.modifierBuilder().PUBLIC().build()).build();
+
+ addElementToParent(decParent, interfaceVisitorArtifact);
+
+ visitorInterfaceStack.push(interfaceVisitorArtifact);
+
+ }
+ }
+
+ @Override
+ public void endVisit(ASTCDDefinition clazz) {
+ if (decoratorData.shouldDecorate(this.getClass(), clazz)) {
+ visitorInterfaceStack.pop();
+ }
+ }
+
+ @Override
+ public void visit(ASTCDClass clazz) {
+ if (decoratorData.shouldDecorate(this.getClass(), clazz)) {
+ ASTCDClass decClazz = decoratorData.getAsDecorated(clazz);
+ String packageName = clazz.getSymbol().getPackageName();
+
+ String visitorInterfaceName = packageName.isEmpty() ? visitorInterfaceStack.peek().getName()
+ : packageName + "." + visitorInterfaceStack.peek().getName();
+
+ ASTMCQualifiedType visitorInterfaceQualifiedType = MCTypeFacade.getInstance()
+ .createQualifiedType(visitorInterfaceName);
+ ASTCDParameter visitorParameter = CD4CodeMill.cDParameterBuilder().setName("visitor")
+ .setMCType(visitorInterfaceQualifiedType).build();
+ //create a type of the class
+ ASTMCType classType = MCTypeFacade.getInstance().createQualifiedType(clazz.getName());
+ ASTCDParameter classParameter = CD4CodeMill.cDParameterBuilder().setName("node").setMCType(
+ classType).build();
+
+ // construct visitor handling methods
+ ASTCDMethod acceptMethod = CDMethodFacade.getInstance().createMethod(CD4CodeMill
+ .modifierBuilder().PUBLIC().build(), "accept", visitorParameter);
+
+ // add the interface methods to the pojo class
+ addToClass(decClazz, acceptMethod);
+ glexOpt.ifPresent(glex -> glex.replaceTemplate(EMPTY_BODY, acceptMethod,
+ new TemplateHookPoint("methods.visitor.Accept", clazz.getName())));
+ // new StringHookPoint("visitor.visit ((" + clazz.getName() + ")this);")));
+
+ CD4C.getInstance().addImport(decClazz, visitorInterfaceName);
+ this.decParent.push(decClazz);
+
+ // add visit method
+ ASTCDMethod visitMethod = CDMethodFacade.getInstance().createMethod(CD4CodeMill
+ .modifierBuilder().PUBLIC().ABSTRACT().build(), "visit", classParameter);
+ visitorInterfaceStack.peek().addCDMember(visitMethod);
+ }
+ }
+
+ @Override
+ public void endVisit(ASTCDClass clazz) {
+ if (decoratorData.shouldDecorate(this.getClass(), clazz)) {
+ decParent.pop();
+ }
+ }
+
+ protected Stack visitorInterfaceStack = new Stack<>();
+ protected Stack decParent = new Stack<>();
+
+ @Override
+ public void addToTraverser(CD4CodeTraverser traverser) {
+ traverser.add4CDBasis(this);
+ }
+
+}
diff --git a/cdlang/src/main/java/de/monticore/cd/codegen/decorators/VisitorImplementationDecorator.java b/cdlang/src/main/java/de/monticore/cd/codegen/decorators/VisitorImplementationDecorator.java
new file mode 100644
index 000000000..199d1fabb
--- /dev/null
+++ b/cdlang/src/main/java/de/monticore/cd/codegen/decorators/VisitorImplementationDecorator.java
@@ -0,0 +1,206 @@
+/* (c) https://github.com/MontiCore/monticore */
+package de.monticore.cd.codegen.decorators;
+
+import com.google.common.collect.Iterables;
+import de.monticore.ast.ASTNode;
+import de.monticore.cd.codegen.decorators.data.AbstractDecorator;
+import de.monticore.cd.codegen.decorators.data.DecoratorData;
+import de.monticore.cd.facade.CDAttributeFacade;
+import de.monticore.cd.facade.CDMethodFacade;
+import de.monticore.cd4code.CD4CodeMill;
+import de.monticore.cd4code._visitor.CD4CodeTraverser;
+import de.monticore.cd4codebasis._ast.ASTCDMethod;
+import de.monticore.cd4codebasis._ast.ASTCDParameter;
+import de.monticore.cdbasis._ast.ASTCDAttribute;
+import de.monticore.cdbasis._ast.ASTCDClass;
+import de.monticore.cdbasis._ast.ASTCDDefinition;
+import de.monticore.cdbasis._visitor.CDBasisVisitor2;
+import de.monticore.generating.templateengine.GlobalExtensionManagement;
+import de.monticore.generating.templateengine.StringHookPoint;
+import de.monticore.generating.templateengine.TemplateHookPoint;
+import de.monticore.types.MCTypeFacade;
+import de.monticore.types.mcbasictypes._ast.ASTMCType;
+import de.se_rwth.commons.logging.Log;
+
+import javax.annotation.Nullable;
+import java.util.List;
+import java.util.Optional;
+import java.util.Stack;
+
+import static de.monticore.cd.codegen.CD2JavaTemplates.EMPTY_BODY;
+
+/**
+ * Applies the Visitor-Pattern to the CD
+ */
+public class VisitorImplementationDecorator extends AbstractDecorator
+ implements CDBasisVisitor2 {
+
+ protected GetterDecorator.GetterData getterData;
+
+ @Override
+ @SuppressWarnings("rawtypes")
+ public Iterable> getMustRunAfter() {
+ //We check that the SetterDecorator has added a Getter for an attribute,
+ // thus the Getter decorator has to run before.
+ return Iterables.concat(super.getMustRunAfter(), List.of(VisitorDecorator.class));
+ }
+
+ @Override
+ public void init(DecoratorData util, Optional glexOpt) {
+ super.init(util, glexOpt);
+
+ // Pre-fetch the data of the getter decorator
+ this.getterData = util.getDecoratorData(GetterDecorator.class);
+ }
+
+ @Override
+ public void visit(ASTCDDefinition clazz) {
+ if (decoratorData.shouldDecorate(this.getClass(), clazz)) {
+ // Get the parent (package or CDDef)
+ ASTNode origParent = this.decoratorData.getParent(clazz).get();
+ ASTNode decParent = this.decoratorData.getAsDecorated(origParent);
+
+ var visitorImplementationClass = CD4CodeMill.cDClassBuilder().setName(clazz.getName()
+ + "VisitorImplementation").setModifier(CD4CodeMill.modifierBuilder().PUBLIC().build())
+ .setCDInterfaceUsage(CD4CodeMill.cDInterfaceUsageBuilder().addInterface(MCTypeFacade
+ .getInstance().createQualifiedType("I" + clazz.getName() + "Visitor")).build())
+ .build();
+
+ addElementToParent(decParent, visitorImplementationClass);
+
+ visitorImplementationStack.push(visitorImplementationClass);
+
+ ASTCDAttribute traversedElements = CDAttributeFacade.getInstance().createAttribute(CD4CodeMill
+ .modifierBuilder().PROTECTED().build(), MCTypeFacade.getInstance().createCollectionTypeOf(
+ "Object"), "traversedElements");
+ glexOpt.ifPresent(glex -> glex.replaceTemplate("cd2java.Value", traversedElements,
+ new StringHookPoint("= new java.util.LinkedHashSet<>()")));
+ addToClass(visitorImplementationClass, traversedElements);
+ }
+ }
+
+ @Override
+ public void endVisit(ASTCDDefinition clazz) {
+ if (decoratorData.shouldDecorate(this.getClass(), clazz)) {
+ visitorImplementationStack.pop();
+ }
+ }
+
+ @Override
+ public void visit(ASTCDClass clazz) {
+ if (decoratorData.shouldDecorate(this.getClass(), clazz)) {
+ ASTCDClass decClazz = decoratorData.getAsDecorated(clazz);
+ ASTMCType classType = MCTypeFacade.getInstance().createQualifiedType(clazz.getName());
+ ASTCDParameter classParameter = CD4CodeMill.cDParameterBuilder().setName("node").setMCType(
+ classType).build();
+
+ @Nullable
+ String parentClass;
+ if (clazz.isPresentCDExtendUsage()) {
+ parentClass = clazz.getCDExtendUsage().getSuperclass(0).printType();
+ }
+ else {
+ parentClass = null;
+ }
+
+ this.decParent.push(decClazz);
+
+ // add visit method
+ ASTCDMethod visitMethod = CDMethodFacade.getInstance().createMethod(CD4CodeMill
+ .modifierBuilder().PUBLIC().build(), "visit", classParameter);
+ visitorImplementationStack.peek().addCDMember(visitMethod);
+ glexOpt.ifPresent(glex -> glex.replaceTemplate(EMPTY_BODY, visitMethod, new TemplateHookPoint(
+ "methods.visitor.DefaultVisit", parentClass)));
+
+ visitMethodStack.push(visitMethod);
+ }
+ }
+
+ @Override
+ public void visit(ASTCDAttribute attribute) {
+ if (!decoratorData.shouldDecorate(this.getClass(), attribute) || visitorImplementationStack
+ .isEmpty()) {
+ return;
+ }
+
+ var attrInfo = decoratorData.getAttrHelper().getFromSymTypeExpr(attribute.getSymbol()
+ .getType());
+
+ if (attrInfo.getTypeKind() != AttrHelper.TypeKind.DOMAIN) {
+ return;
+ }
+
+ // node.getChild().accept(this);
+
+ if (attrInfo.getMultiplicity() == AttrHelper.Multiplicity.MANDATORY) {
+ var getter = this.getterData.getMethods(attribute).stream().filter(m -> m.getKind()
+ == GetterDecorator.GetterMethodKind.GET_MANDATORY_OR_OPT).findAny();
+ if (getter.isEmpty()) {
+ warnMissingGetter(attribute);
+ }
+ else {
+ glexOpt.ifPresent(glex -> glex.addAfterTemplate("VisitorImplementation:Traverse",
+ visitMethodStack.peek(), new TemplateHookPoint("methods.visitor.DefaultTraverseMan",
+ getter.get().getGetMethod().getName(), visitorImplementationStack.peek()
+ .getName())));
+ }
+ }
+ else if (attrInfo.getMultiplicity() == AttrHelper.Multiplicity.OPTIONAL) {
+ var getter = this.getterData.getMethods(attribute).stream().filter(m -> m.getKind()
+ == GetterDecorator.GetterMethodKind.GET_MANDATORY_OR_OPT).findAny();
+ var isPresent = this.getterData.getMethods(attribute).stream().filter(m -> m.getKind()
+ == GetterDecorator.GetterMethodKind.IS_PRESENT).findAny();
+ if (getter.isEmpty() || isPresent.isEmpty()) {
+ warnMissingGetter(attribute);
+ }
+ else {
+ glexOpt.ifPresent(glex -> glex.addAfterTemplate("VisitorImplementation:Traverse",
+ visitMethodStack.peek(), new TemplateHookPoint("methods.visitor.DefaultTraverseOpt",
+ getter.get().getGetMethod().getName(), isPresent.get().getGetMethod().getName(),
+ visitorImplementationStack.peek().getName())));
+ }
+ }
+ else if (attrInfo.getMultiplicity() == AttrHelper.Multiplicity.SET) {
+ var getter = this.getterData.getMethods(attribute).stream().filter(m -> m.getKind()
+ == GetterDecorator.GetterMethodKind.GET_COLLECTION).findAny();
+ if (getter.isEmpty()) {
+ warnMissingGetter(attribute);
+ }
+ else {
+ glexOpt.ifPresent(glex -> glex.addAfterTemplate("VisitorImplementation:Traverse",
+ visitMethodStack.peek(), new TemplateHookPoint("methods.visitor.DefaultTraverseSet",
+ getter.get().getGetMethod().getName(), visitorImplementationStack.peek()
+ .getName())));
+ }
+ }
+
+ }
+
+ protected void warnMissingGetter(ASTCDAttribute attribute) {
+ String message = "Attribute `" + attribute.getSymbol().getFullName()
+ + "` has no getter information provided. Unable to include in " + visitorImplementationStack
+ .peek().getName();
+ Log.warn("0xTODO: " + message, attribute.get_SourcePositionStart(), attribute
+ .get_SourcePositionEnd());
+ glexOpt.ifPresent(glex -> glex.addAfterTemplate("VisitorImplementation:Traverse",
+ visitMethodStack.peek(), new StringHookPoint("//" + message + "\n")));
+ }
+
+ @Override
+ public void endVisit(ASTCDClass clazz) {
+ if (decoratorData.shouldDecorate(this.getClass(), clazz)) {
+ decParent.pop();
+ visitMethodStack.pop();
+ }
+ }
+
+ protected Stack visitorImplementationStack = new Stack<>();
+ protected Stack visitMethodStack = new Stack<>();
+ protected Stack decParent = new Stack<>();
+
+ @Override
+ public void addToTraverser(CD4CodeTraverser traverser) {
+ traverser.add4CDBasis(this);
+ }
+
+}
diff --git a/cdlang/src/main/java/de/monticore/cd/codegen/decorators/data/AbstractDecorator.java b/cdlang/src/main/java/de/monticore/cd/codegen/decorators/data/AbstractDecorator.java
index 98b61af79..002e693f2 100644
--- a/cdlang/src/main/java/de/monticore/cd/codegen/decorators/data/AbstractDecorator.java
+++ b/cdlang/src/main/java/de/monticore/cd/codegen/decorators/data/AbstractDecorator.java
@@ -30,6 +30,8 @@ protected void addElementToParent(ASTNode decoratedParent, ASTCDElement newElem)
((ASTCDDefinition) decoratedParent).addCDElement(newElem);
else if (decoratedParent instanceof ASTCDPackage)
((ASTCDPackage) decoratedParent).addCDElement(newElem);
+ else if (decoratedParent instanceof ASTCDCompilationUnit)
+ ((ASTCDCompilationUnit) decoratedParent).getCDDefinition().addCDElement(newElem);
else
throw new IllegalStateException("Unhandled addElementToParent " + decoratedParent.getClass()
.getName());
diff --git a/cdlang/src/main/resources/cd2java/init/CD2Pojo.ftl b/cdlang/src/main/resources/cd2java/init/CD2Pojo.ftl
index d912fb818..324c41efc 100644
--- a/cdlang/src/main/resources/cd2java/init/CD2Pojo.ftl
+++ b/cdlang/src/main/resources/cd2java/init/CD2Pojo.ftl
@@ -37,6 +37,9 @@ ${decConfig.withAbstractMethodSignatures().applyOnName("abstractMethod").ignoreO
${decConfig.withBuilders().applyOnName("builder").ignoreOnName("noBuilder")}
<#-- Similarly, the Observable decorator is NOT applied by default, unless an element or its parents are marked with observable -->
${decConfig.withObservers().applyOnName("observable").ignoreOnName("notObservable")}
+<#-- Similarly, the Visitor decorator is NOT applied by default, unless an element or its parents are marked with visitor -->
+${decConfig.withVisitors().applyOnName("visitor").ignoreOnName("noVisitor")}
+${decConfig.withVisitorImplementations().applyOnName("visitor").ignoreOnName("noVisitor").ignoreOnName("noDefaultVisitor")}
<#--
diff --git a/cdlang/src/main/resources/methods/visitor/Accept.ftl b/cdlang/src/main/resources/methods/visitor/Accept.ftl
new file mode 100644
index 000000000..fe26ceac1
--- /dev/null
+++ b/cdlang/src/main/resources/methods/visitor/Accept.ftl
@@ -0,0 +1,3 @@
+<#-- (c) https://github.com/MontiCore/monticore -->
+${tc.signature("className")}
+visitor.visit((${className}) this);
diff --git a/cdlang/src/main/resources/methods/visitor/DefaultTraverseMan.ftl b/cdlang/src/main/resources/methods/visitor/DefaultTraverseMan.ftl
new file mode 100644
index 000000000..2385005d7
--- /dev/null
+++ b/cdlang/src/main/resources/methods/visitor/DefaultTraverseMan.ftl
@@ -0,0 +1,3 @@
+<#-- (c) https://github.com/MontiCore/monticore -->
+${tc.signature("getter", "clazzname")}
+node.${getter}().accept((${clazzname})this);
diff --git a/cdlang/src/main/resources/methods/visitor/DefaultTraverseOpt.ftl b/cdlang/src/main/resources/methods/visitor/DefaultTraverseOpt.ftl
new file mode 100644
index 000000000..e4481c8ff
--- /dev/null
+++ b/cdlang/src/main/resources/methods/visitor/DefaultTraverseOpt.ftl
@@ -0,0 +1,5 @@
+<#-- (c) https://github.com/MontiCore/monticore -->
+${tc.signature("getter", "isPresent", "clazzname")}
+if (node.${isPresent}()) {
+ node.${getter}().accept((${clazzname})this);
+}
diff --git a/cdlang/src/main/resources/methods/visitor/DefaultTraverseSet.ftl b/cdlang/src/main/resources/methods/visitor/DefaultTraverseSet.ftl
new file mode 100644
index 000000000..0383aeb41
--- /dev/null
+++ b/cdlang/src/main/resources/methods/visitor/DefaultTraverseSet.ftl
@@ -0,0 +1,5 @@
+<#-- (c) https://github.com/MontiCore/monticore -->
+${tc.signature("getter", "clazzname")}
+for (var elem : node.${getter}()) {
+ elem.accept((${clazzname})this);
+}
diff --git a/cdlang/src/main/resources/methods/visitor/DefaultVisit.ftl b/cdlang/src/main/resources/methods/visitor/DefaultVisit.ftl
new file mode 100644
index 000000000..9afb086ba
--- /dev/null
+++ b/cdlang/src/main/resources/methods/visitor/DefaultVisit.ftl
@@ -0,0 +1,11 @@
+<#-- (c) https://github.com/MontiCore/monticore -->
+${tc.signature("superClass")}
+if (!traversedElements.contains(node)) {
+<#if superClass??>
+ // Call visit of parent class
+ this.visit((${superClass}) node);
+#if>
+ traversedElements.add(node);
+ ${defineHookPoint("VisitorImplementation:Traverse")}
+
+}
diff --git a/doc/CDGen.md b/doc/CDGen.md
index 7f192eb4c..134a4cf0b 100644
--- a/doc/CDGen.md
+++ b/doc/CDGen.md
@@ -12,7 +12,6 @@ An example Gradle configuration can be found below:
By adding the `de.rwth.se.cdgen` Gradle plugin to your project,
all class diagrams in the _cds_ source-directory-set (e.g., _src/main/cds_, _src/test/cds_) are generated to Java code.
-
```groovy
// build.gradle
plugins {
@@ -45,14 +44,24 @@ It includes the following transformations:
* CD4CodeAfterParseTrafo:
* DefaultVisibilityPublicTrafo: absent visibility means *public*
- It includes the following decorators:
-* CopyCreator: Include all elements of the original CD in the output
-* GetterDecorator: By default, getters are added to all elements
-* SetterDecorator: By default, setters are added to all elements
-* CardinalityDefaultDecorator: By default, optional and list attributes are initialized with an empty default
-* NavigableSetterDecorator: By default, the setters of bidirectional associations are also bidirectional
-* BuilderDecorator: If elements are marked with `<>`, a builder is added
-* ObserverDecorator: If elements are marked with `<>`, an observer is added
+
+It includes the following decorators:
+
+| Decorator | Description | To Enable | To Disable |
+|-----------------------------|--------------------------------------------------------------------|--------------------------|-------------------------------------------|
+| CopyCreator | Include all elements of the original CD in the output | always | - |
+| GetterDecorator | Add Getter Methods | 🟩 `<>` | `<>` |
+| SetterDecorator | Add Setter Methods | 🟩 `<>` | `<>` |
+| CardinalityDefaultDecorator | Optional and list attributes are initialized with an empty default | 🟩 | `<>` |
+| NavigableSetterDecorator | Setters of bidirectional associations are also bidirectional | 🟩 `<>` | `<>` |
+| AbstractMethodDecorator | Defined methods are made abstract | 🟩 `<>` | `<>` |
+| BuilderDecorator | Add a builder class | 🟨 `<>` | `<>` |
+| ObserverDecorator | Turn the class observable | 🟨 `<>` | `<>` |
+| VisitorDecorator | Include a visitor | 🟨 `<>` | `<>` or `<>` |
+
+In the default configuration,
+🟩 means the decorator is applied unless disabled.
+🟨 means the decorator is not applied unless enabled.
### Element Configuration
@@ -71,6 +80,7 @@ Modelers should select a suitable config template and use stereotypes for class
Tool-developers should provide their own config template.
### Configuring via Gradle
+
```groovy
// build.gradle
// optionally: continue to configure the generate Task for the main sourceset (src/main/cds)
@@ -120,9 +130,8 @@ The following example of a decorator adds a class `XFancy` for every class `X`.
Via a template replacement, a start method is added.
```java
-public class MyFancyDecorator
- extends AbstractDecorator
- implements CDBasisVisitor2 {
+public class MyFancyDecorator extends AbstractDecorator implements CDBasisVisitor2 {
+
@Override
public void visit(ASTCDClass node) {
// Only act if we should decorate the class
@@ -141,16 +150,8 @@ public class MyFancyDecorator
addElementToParent(decParent, additionalClass);
// Add a public start() method to the builder class
- ASTCDMethod myMethod =
- CDMethodFacade.getInstance()
- .createMethod(
- CD4CodeMill.modifierBuilder().PUBLIC().build(), node.getName(), "start");
- glexOpt.ifPresent(
- glex ->
- glex.replaceTemplate(
- EMPTY_BODY,
- myMethod,
- new TemplateHookPoint("methods.fancy.Start", node.getName())));
+ ASTCDMethod myMethod = CDMethodFacade.getInstance().createMethod(CD4CodeMill.modifierBuilder().PUBLIC().build(), node.getName(), "start");
+ glexOpt.ifPresent(glex -> glex.replaceTemplate(EMPTY_BODY, myMethod, new TemplateHookPoint("methods.fancy.Start", node.getName())));
addToClass(additionalClass, myMethod);
}
}
@@ -160,6 +161,7 @@ public class MyFancyDecorator
// Our decorator is only interested in CDBasis elements and thus a CDBasisVisitor2
traverser.add4CDBasis(this);
}
+
}
```
@@ -169,8 +171,7 @@ By providing an explicit ordering via the `getMustRunAfter`, the original class
```java
public Iterable> getMustRunAfter() {
// We require data of the Setter Decorator, thus it has to run before this decorator
- return Iterables.concat(
- super.getMustRunAfter(), Collections.singletonList(SetterDecorator.class));
+ return Iterables.concat(super.getMustRunAfter(), Collections.singletonList(SetterDecorator.class));
}
```
@@ -226,6 +227,12 @@ tasks.named('generateClassDiagrams') {
When using the CLI tool, the following arguments can be used: `-ct CD2OwnDecorator -fp src/main/configTemplate`.
When using the API, the *DecoratorConfig* can be modified directly, as seen in the following tests.
+#### Using in Code
+
+* Tag-Adder (tag-like?)
+* Output CD
+* Also generate
+
#### Testing
All decorators should be tested by at least the following: