From f91fc9bb24e679eddec77685443e50a3dadc7984 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20L=C3=BCpges?= Date: Mon, 27 Apr 2026 13:27:55 +0200 Subject: [PATCH 1/2] Start on the visitor decorator --- .../monticore/cd/codegen/DecoratorConfig.java | 8 + .../codegen/decorators/GetterDecorator.java | 151 ++++++++++--- .../codegen/decorators/VisitorDecorator.java | 120 ++++++++++ .../VisitorImplementationDecorator.java | 206 ++++++++++++++++++ .../decorators/data/AbstractDecorator.java | 2 + .../main/resources/cd2java/init/CD2Pojo.ftl | 3 + .../main/resources/methods/visitor/Accept.ftl | 3 + .../methods/visitor/DefaultTraverseMan.ftl | 3 + .../methods/visitor/DefaultTraverseOpt.ftl | 5 + .../methods/visitor/DefaultTraverseSet.ftl | 5 + .../methods/visitor/DefaultVisit.ftl | 11 + doc/CDGen.md | 55 +++-- 12 files changed, 512 insertions(+), 60 deletions(-) create mode 100644 cdlang/src/main/java/de/monticore/cd/codegen/decorators/VisitorDecorator.java create mode 100644 cdlang/src/main/java/de/monticore/cd/codegen/decorators/VisitorImplementationDecorator.java create mode 100644 cdlang/src/main/resources/methods/visitor/Accept.ftl create mode 100644 cdlang/src/main/resources/methods/visitor/DefaultTraverseMan.ftl create mode 100644 cdlang/src/main/resources/methods/visitor/DefaultTraverseOpt.ftl create mode 100644 cdlang/src/main/resources/methods/visitor/DefaultTraverseSet.ftl create mode 100644 cdlang/src/main/resources/methods/visitor/DefaultVisit.ftl 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..09921c911 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(); @@ -69,16 +83,20 @@ protected void decorateMandatory(ASTCDClass decoratedClazz, ASTCDAttribute attri glexOpt.ifPresent(glex -> glex.replaceTemplate(EMPTY_BODY, method, new TemplateHookPoint( "methods.Get", attribute))); method.getModifier().setAbstract(attribute.getModifier().isDerived()); - + 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(); - + String generatedErrorCode = getCDGenService().getGeneratedErrorCode(attribute.getName() + attribute.getMCType().printType()); ASTCDMethod getMethod = CDMethodFacade.getInstance().createMethod(attribute.getModifier() @@ -89,53 +107,65 @@ protected void decorateOptional(ASTCDClass decoratedClazz, ASTCDAttribute attrib "methods.opt.Get4Opt", attribute, nativeAttributeName, generatedErrorCode))); getMethod.getModifier().setAbstract(attribute.getModifier().isDerived()); 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())); glexOpt.ifPresent(glex -> glex.replaceTemplate(EMPTY_BODY, isPresentMethod, new TemplateHookPoint("methods.opt.IsPresent4Opt", attribute))); 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(); - + ASTCDMethod getListMethod = CDMethodFacade.getInstance().createMethod(attribute.getModifier() .deepClone(), MCTypeFacade.getInstance().createSetTypeOf(type), name); glexOpt.ifPresent(glex -> glex.replaceTemplate(EMPTY_BODY, getListMethod, new TemplateHookPoint( "methods.Get", attribute))); getListMethod.getModifier().setAbstract(attribute.getModifier().isDerived()); 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(); - + ASTCDMethod getListMethod = CDMethodFacade.getInstance().createMethod(attribute.getModifier() .deepClone(), MCTypeFacade.getInstance().createListTypeOf(type), name); glexOpt.ifPresent(glex -> glex.replaceTemplate(EMPTY_BODY, getListMethod, new TemplateHookPoint( "methods.Get", attribute))); getListMethod.getModifier().setAbstract(attribute.getModifier().isDerived()); addToClass(decoratedClazz, getListMethod); - + this.updateModifier(attribute); + return new MethodInformation(GetterMethodKind.GET_COLLECTION, getListMethod, "methods.Get", + attribute.getName()); } - + protected void decorateWithAssocFunctions(ASTCDClass decoratedClazz, ASTCDAttribute attribute, boolean isList) { ASTMCType type = getCDGenService().getFirstTypeArgument(attribute.getMCType()).deepClone(); - + String attributeType = type.printType(); - + String capitalizedAttributeNameWithS = StringUtils.capitalize(getCDGenService() .getNativeAttributeName(attribute.getName())); String capitalizedAttributeNameWithOutS; @@ -148,7 +178,7 @@ protected void decorateWithAssocFunctions(ASTCDClass decoratedClazz, ASTCDAttrib else { capitalizedAttributeNameWithOutS = capitalizedAttributeNameWithS; } - + if (!attribute.getModifier().isDerived()) { for (String signature : Arrays.asList(String.format(CONTAINS, capitalizedAttributeNameWithOutS), String.format(CONTAINS_ALL, @@ -185,7 +215,7 @@ protected void decorateWithAssocFunctions(ASTCDClass decoratedClazz, ASTCDAttrib } } } - + protected HookPoint createListImplementation(final ASTCDMethod method, String capitalizedAttributeNameWithOutS) { String attributeName = StringUtils.uncapitalize(capitalizedAttributeNameWithOutS); @@ -195,11 +225,11 @@ protected HookPoint createListImplementation(final ASTCDMethod method, .collect(Collectors.joining(", ")); String returnType = (new CD4CodeFullPrettyPrinter(new IndentPrinter())).prettyprint(method .getMCReturnType()); - + return new TemplateHookPoint("methods.AnyMethodDelegate", attributeName, methodName, parameterCall, returnType); } - + protected static final String CONTAINS = "public boolean contains%s(Object element);"; protected static final String CONTAINS_ALL = "public boolean containsAll%s(java.util.Collection collection);"; @@ -223,17 +253,66 @@ protected HookPoint createListImplementation(final ASTCDMethod method, "public java.util.ListIterator<%s> listIterator%s(int index);"; protected static final String SUBLIST = "public java.util.List<%s> subList%s(int start, int end);"; - + protected void updateModifier(ASTCDAttribute attribute) { var decoratedModifier = decoratorData.getAsDecorated(attribute).getModifier(); decoratedModifier.setProtected(true); decoratedModifier.setPublic(false); decoratedModifier.setPrivate(false); } - + @Override 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..130dfd875 --- /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); + + 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: From f6c649a265aa921bf0fdcdd4ef72cccefff1028f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20L=C3=BCpges?= Date: Mon, 27 Apr 2026 13:37:57 +0200 Subject: [PATCH 2/2] format --- .../codegen/decorators/GetterDecorator.java | 86 +++++++++---------- .../VisitorImplementationDecorator.java | 52 +++++------ 2 files changed, 69 insertions(+), 69 deletions(-) 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 09921c911..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 @@ -34,15 +34,15 @@ */ 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 @@ -72,7 +72,7 @@ else if (MCCollectionSymTypeRelations.isOptional(attribute.getSymbol().getType() } } } - + protected MethodInformation decorateMandatory(ASTCDClass decoratedClazz, ASTCDAttribute attribute) { String name = (MCTypeFacade.getInstance().isBooleanType(attribute.getMCType()) ? "is" : "get") @@ -83,20 +83,20 @@ protected MethodInformation decorateMandatory(ASTCDClass decoratedClazz, glexOpt.ifPresent(glex -> glex.replaceTemplate(EMPTY_BODY, method, new TemplateHookPoint( "methods.Get", attribute))); method.getModifier().setAbstract(attribute.getModifier().isDerived()); - + addToClass(decoratedClazz, method); - + this.updateModifier(attribute); - + return new MethodInformation(GetterMethodKind.GET_MANDATORY_OR_OPT, method, "methods.Get", attribute.getName()); } - + protected MethodInformation decorateOptional(ASTCDClass decoratedClazz, ASTCDAttribute attribute) { String name = "get" + StringTransformations.capitalize(attribute.getName()); ASTMCType type = getCDGenService().getFirstTypeArgument(attribute.getMCType()).deepClone(); - + String generatedErrorCode = getCDGenService().getGeneratedErrorCode(attribute.getName() + attribute.getMCType().printType()); ASTCDMethod getMethod = CDMethodFacade.getInstance().createMethod(attribute.getModifier() @@ -107,12 +107,12 @@ protected MethodInformation decorateOptional(ASTCDClass decoratedClazz, "methods.opt.Get4Opt", attribute, nativeAttributeName, generatedErrorCode))); getMethod.getModifier().setAbstract(attribute.getModifier().isDerived()); 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() @@ -121,51 +121,51 @@ protected MethodInformation decorateOptionalIsPresent(ASTCDClass decoratedClazz, glexOpt.ifPresent(glex -> glex.replaceTemplate(EMPTY_BODY, isPresentMethod, new TemplateHookPoint("methods.opt.IsPresent4Opt", attribute))); addToClass(decoratedClazz, isPresentMethod); - + this.updateModifier(attribute); return new MethodInformation(GetterMethodKind.IS_PRESENT, isPresentMethod, "methods.opt.IsPresent4Opt", attribute.getName()); } - + protected MethodInformation decorateSet(ASTCDClass decoratedClazz, ASTCDAttribute attribute) { String name = "get" + StringTransformations.capitalize(attribute.getName()); ASTMCType type = getCDGenService().getFirstTypeArgument(attribute.getMCType()).deepClone(); - + ASTCDMethod getListMethod = CDMethodFacade.getInstance().createMethod(attribute.getModifier() .deepClone(), MCTypeFacade.getInstance().createSetTypeOf(type), name); glexOpt.ifPresent(glex -> glex.replaceTemplate(EMPTY_BODY, getListMethod, new TemplateHookPoint( "methods.Get", attribute))); getListMethod.getModifier().setAbstract(attribute.getModifier().isDerived()); addToClass(decoratedClazz, getListMethod); - + this.updateModifier(attribute); - + return new MethodInformation(GetterMethodKind.GET_COLLECTION, getListMethod, "methods.Get", attribute.getName()); } - + protected MethodInformation decorateList(ASTCDClass decoratedClazz, ASTCDAttribute attribute) { String name = "get" + StringTransformations.capitalize(attribute.getName()); ASTMCType type = getCDGenService().getFirstTypeArgument(attribute.getMCType()).deepClone(); - + ASTCDMethod getListMethod = CDMethodFacade.getInstance().createMethod(attribute.getModifier() .deepClone(), MCTypeFacade.getInstance().createListTypeOf(type), name); glexOpt.ifPresent(glex -> glex.replaceTemplate(EMPTY_BODY, getListMethod, new TemplateHookPoint( "methods.Get", attribute))); getListMethod.getModifier().setAbstract(attribute.getModifier().isDerived()); addToClass(decoratedClazz, getListMethod); - + this.updateModifier(attribute); return new MethodInformation(GetterMethodKind.GET_COLLECTION, getListMethod, "methods.Get", attribute.getName()); } - + protected void decorateWithAssocFunctions(ASTCDClass decoratedClazz, ASTCDAttribute attribute, boolean isList) { ASTMCType type = getCDGenService().getFirstTypeArgument(attribute.getMCType()).deepClone(); - + String attributeType = type.printType(); - + String capitalizedAttributeNameWithS = StringUtils.capitalize(getCDGenService() .getNativeAttributeName(attribute.getName())); String capitalizedAttributeNameWithOutS; @@ -178,7 +178,7 @@ protected void decorateWithAssocFunctions(ASTCDClass decoratedClazz, ASTCDAttrib else { capitalizedAttributeNameWithOutS = capitalizedAttributeNameWithS; } - + if (!attribute.getModifier().isDerived()) { for (String signature : Arrays.asList(String.format(CONTAINS, capitalizedAttributeNameWithOutS), String.format(CONTAINS_ALL, @@ -215,7 +215,7 @@ protected void decorateWithAssocFunctions(ASTCDClass decoratedClazz, ASTCDAttrib } } } - + protected HookPoint createListImplementation(final ASTCDMethod method, String capitalizedAttributeNameWithOutS) { String attributeName = StringUtils.uncapitalize(capitalizedAttributeNameWithOutS); @@ -225,11 +225,11 @@ protected HookPoint createListImplementation(final ASTCDMethod method, .collect(Collectors.joining(", ")); String returnType = (new CD4CodeFullPrettyPrinter(new IndentPrinter())).prettyprint(method .getMCReturnType()); - + return new TemplateHookPoint("methods.AnyMethodDelegate", attributeName, methodName, parameterCall, returnType); } - + protected static final String CONTAINS = "public boolean contains%s(Object element);"; protected static final String CONTAINS_ALL = "public boolean containsAll%s(java.util.Collection collection);"; @@ -253,24 +253,24 @@ protected HookPoint createListImplementation(final ASTCDMethod method, "public java.util.ListIterator<%s> listIterator%s(int index);"; protected static final String SUBLIST = "public java.util.List<%s> subList%s(int start, int end);"; - + protected void updateModifier(ASTCDAttribute attribute) { var decoratedModifier = decoratorData.getAsDecorated(attribute).getModifier(); decoratedModifier.setProtected(true); decoratedModifier.setPublic(false); decoratedModifier.setPrivate(false); } - + @Override 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) { @@ -279,20 +279,20 @@ public List getMethods(ASTCDAttribute node) { } 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; @@ -300,19 +300,19 @@ public MethodInformation(GetterMethodKind kind, ASTCDMethod setMethod, String te 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/VisitorImplementationDecorator.java b/cdlang/src/main/java/de/monticore/cd/codegen/decorators/VisitorImplementationDecorator.java index 130dfd875..199d1fabb 100644 --- a/cdlang/src/main/java/de/monticore/cd/codegen/decorators/VisitorImplementationDecorator.java +++ b/cdlang/src/main/java/de/monticore/cd/codegen/decorators/VisitorImplementationDecorator.java @@ -34,9 +34,9 @@ */ public class VisitorImplementationDecorator extends AbstractDecorator implements CDBasisVisitor2 { - + protected GetterDecorator.GetterData getterData; - + @Override @SuppressWarnings("rawtypes") public Iterable> getMustRunAfter() { @@ -44,32 +44,32 @@ public Iterable> getMustRunAfter() { // 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"); @@ -78,14 +78,14 @@ public void visit(ASTCDDefinition clazz) { 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)) { @@ -93,7 +93,7 @@ public void visit(ASTCDClass clazz) { ASTMCType classType = MCTypeFacade.getInstance().createQualifiedType(clazz.getName()); ASTCDParameter classParameter = CD4CodeMill.cDParameterBuilder().setName("node").setMCType( classType).build(); - + @Nullable String parentClass; if (clazz.isPresentCDExtendUsage()) { @@ -102,36 +102,36 @@ public void visit(ASTCDClass clazz) { 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(); @@ -173,9 +173,9 @@ else if (attrInfo.getMultiplicity() == AttrHelper.Multiplicity.SET) { .getName()))); } } - + } - + protected void warnMissingGetter(ASTCDAttribute attribute) { String message = "Attribute `" + attribute.getSymbol().getFullName() + "` has no getter information provided. Unable to include in " + visitorImplementationStack @@ -185,7 +185,7 @@ protected void warnMissingGetter(ASTCDAttribute attribute) { 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)) { @@ -193,14 +193,14 @@ public void endVisit(ASTCDClass clazz) { 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); } - + }