Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/* (c) https://github.com/MontiCore/monticore */
package de.monticore.cd.codegen.trafo;

import de.monticore.cd4code.CD4CodeMill;
import de.monticore.cdassociation._ast.ASTCDRole;
import de.monticore.cdassociation._symboltable.ICDAssociationScope;
import de.monticore.cdassociation._visitor.CDAssociationVisitor2;
import de.monticore.cdbasis._ast.ASTCDClass;
import de.monticore.cdbasis._ast.ASTCDCompilationUnit;
import de.monticore.cdbasis._visitor.CDBasisVisitor2;
import de.monticore.symbols.oosymbols._symboltable.OOTypeSymbol;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/** Adapts association roles to Java identifiers before fields and methods are generated. */
public class JavaAssociationRoleNameTrafo implements CDAssociationVisitor2, CDBasisVisitor2 {

// Avoid SourceVersion because Gradle's isolated generator classloader does not expose
// the java.compiler module.
// Includes reserved keywords, the underscore, and literals; contextual keywords are legal fields.
protected static final Set<String> JAVA_RESERVED_NAMES = Set.of("abstract", "assert", "boolean",
"break", "byte", "case", "catch", "char", "class", "const", "continue", "default", "do",
"double", "else", "enum", "extends", "final", "finally", "float", "for", "goto", "if",
"implements", "import", "instanceof", "int", "interface", "long", "native", "new", "package",
"private", "protected", "public", "return", "short", "static", "strictfp", "super", "switch",
"synchronized", "this", "throw", "throws", "transient", "try", "void", "volatile", "while",
"_", "true", "false", "null");

protected final List<ASTCDRole> roles = new ArrayList<>();
protected final Map<OOTypeSymbol, Set<OOTypeSymbol>> hierarchies = new LinkedHashMap<>();

@Override
public void visit(ASTCDClass node) {
Set<OOTypeSymbol> ancestors = new LinkedHashSet<>();
collectAncestors(node.getSymbol(), ancestors);
hierarchies.put(node.getSymbol(), ancestors);
}

protected void collectAncestors(OOTypeSymbol type, Set<OOTypeSymbol> ancestors) {
if (ancestors.add(type)) {
type.getSuperTypesList().forEach(superType -> {
if (superType.getTypeInfo() instanceof OOTypeSymbol ooSuperTypeInfo) {
collectAncestors(ooSuperTypeInfo, ancestors);
}
});
}
}

@Override
public void visit(ASTCDRole node) {
if (JAVA_RESERVED_NAMES.contains(node.getName())) {
roles.add(node);
}
}

public void transform(Collection<ASTCDCompilationUnit> asts) {
roles.clear();
hierarchies.clear();
var traverser = CD4CodeMill.inheritanceTraverser();
traverser.add4CDAssociation(this);
asts.forEach(ast -> ast.accept(traverser));
if (roles.isEmpty()) {
return;
}
// Only inspect inheritance when a role actually needs a Java identifier.
var hierarchyTraverser = CD4CodeMill.inheritanceTraverser();
hierarchyTraverser.add4CDBasis(this);
asts.forEach(ast -> ast.accept(hierarchyTraverser));
for (ASTCDRole role : roles) {
var symbol = role.getSymbol();
var scope = symbol.getEnclosingScope();
Set<String> occupied = new LinkedHashSet<>();
scope.getLocalFieldSymbols().forEach(field -> occupied.add(field.getName()));
scope.getLocalCDRoleSymbols().forEach(other -> occupied.add(other.getName()));
// Reserve inherited members and members of subclasses, independent of traversal order.
hierarchies.forEach((type, ancestors) -> {
if (ancestors.stream().anyMatch(ancestor -> ancestor.getSpannedScope() == scope)) {
ancestors.forEach(ancestor -> collectNames(ancestor, occupied));
}
});
String name = role.getName() + "_";
while (occupied.contains(name)) {
name += "_";
}
// Re-index the scope as well as updating the AST and symbol used by navigable setters.
scope.remove(symbol);
role.setName(name);
symbol.setName(name);
symbol.setFullName(null);
scope.add(symbol);
}
}

protected void collectNames(OOTypeSymbol type, Set<String> names) {
type.getFieldList().forEach(field -> names.add(field.getName()));
if (type.getSpannedScope() instanceof ICDAssociationScope) {
((ICDAssociationScope) type.getSpannedScope()).getLocalCDRoleSymbols().forEach(role -> names
.add(role.getName()));
}
}

}
2 changes: 2 additions & 0 deletions cdlang/src/main/java/de/monticore/cdgen/CDGenTool.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import de.monticore.cd.codegen.CdUtilsPrinter;
import de.monticore.cd.codegen.DecoratorConfig;
import de.monticore.cd.codegen.trafo.DefaultVisibilityPublicTrafo;
import de.monticore.cd.codegen.trafo.JavaAssociationRoleNameTrafo;
import de.monticore.cd.codegen.trafo.TOPTrafo;
import de.monticore.cd.methodtemplates.CD4C;
import de.monticore.cd4analysis._util.CD4AnalysisTypeDispatcher;
Expand Down Expand Up @@ -331,6 +332,7 @@ public GeneratorSetup newConfiguredGeneratorSetup(List<File> additionalTemplateP

public CDAssociationCreateFieldsFromAllRoles performFieldsFromRolesTrafo(
Collection<ASTCDCompilationUnit> asts) {
new JavaAssociationRoleNameTrafo().transform(asts);
CDAssociationCreateFieldsFromAllRoles roleTrafo =
new CDAssociationCreateFieldsFromNavigableRoles();
final CD4CodeTraverser traverser = CD4CodeMill.inheritanceTraverser();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/* (c) https://github.com/MontiCore/monticore */
package de.monticore.cdgen.cocos;

import de.monticore.cdassociation._ast.ASTCDAssocSide;
import de.monticore.cdassociation._ast.ASTCDAssociation;
import de.monticore.cdbasis._ast.ASTCDDefinition;
import de.monticore.symbols.basicsymbols._symboltable.TypeSymbol;
import org.apache.commons.lang3.StringUtils;

import java.util.*;

/**
* Checks that generated association references are unique.
*/
public class CDAssociationReferenceUnique extends CDAssociationUniqueInHierarchy {

@Override
public void check(ASTCDDefinition node) {
List<ASTCDAssociation> alreadyChecked = new ArrayList<>();

// we check for each pair of associations
for (ASTCDAssociation assoc2 : node.getCDAssociationsList()) {
for (ASTCDAssociation assoc1 : alreadyChecked) {
for (AssociationReference ref1 : getAssociationReferences(assoc1)) {
for (AssociationReference ref2 : getAssociationReferences(assoc2)) {
if (ref1.name.equals(ref2.name)) {
checkRef(node, ref1.sourceType, ref2.sourceType, assoc2);
}
}
}
}

alreadyChecked.add(assoc2);
}
}

@Override
protected void checkRef(ASTCDDefinition node, TypeSymbol type1, TypeSymbol type2,
ASTCDAssociation assoc1) {
if (type1 == null || type2 == null) {
return;
}
super.checkRef(node, type1, type2, assoc1);
}

protected List<AssociationReference> getAssociationReferences(ASTCDAssociation assoc) {
List<AssociationReference> references = new ArrayList<>();

boolean navigableLeft = assoc.getCDAssocDir().isDefinitiveNavigableLeft();
boolean navigableRight = assoc.getCDAssocDir().isDefinitiveNavigableRight();
boolean undirected = !navigableLeft && !navigableRight;

if (navigableRight || undirected) {
references.add(new AssociationReference(findTypeByFullName(assoc.getLeftQualifiedName()),
deriveReferenceName(assoc, AssocSide.RIGHT)));
}
if (navigableLeft || undirected) {
references.add(new AssociationReference(findTypeByFullName(assoc.getRightQualifiedName()),
deriveReferenceName(assoc, AssocSide.LEFT)));
}

return references;
}

protected String deriveReferenceName(ASTCDAssociation assoc, AssocSide side) {
ASTCDAssocSide assocSide;
if (side.equals(AssocSide.LEFT)) {
assocSide = assoc.getLeft();
}
else {
assocSide = assoc.getRight();
}
if (assoc.isPresentName()) {
return StringUtils.uncapitalize(assoc.getName());
}
else if (assocSide.isPresentCDRole()) {
return assocSide.getCDRole().getName();
}
else {
return StringUtils.uncapitalize(assocSide.getMCQualifiedType().getMCQualifiedName()
.getBaseName());
}
}

protected enum AssocSide {
LEFT, RIGHT;
}

protected static class AssociationReference {

protected final TypeSymbol sourceType;

protected final String name;

protected AssociationReference(TypeSymbol sourceType, String name) {
this.sourceType = sourceType;
this.name = name;
}

}

}
Loading
Loading