Domainmodel: entities += Entity* ; Entity: 'entity' name = ID '{' features += Feature* '}' ; Feature: 'feature' name = ID ':' type = Type ; Type: baseType = BaseType | reference = [Entity] ; enum BaseType: String | Int ;
IScope scope_Feature_type(Type ctx, EReference ref)
public class EntityDslJavaValidator extends AbstractEntityDslJavaValidator { @Check public void checkEntityRecursion(Feature feature) { checkEntityRecursion(feature, (Entity)feature.eContainer()); } public void checkEntityRecursion(Feature feature, Entity rootEntity) { if (feature.getType() == null) return; Entity referencedEntity = feature.getType().getReference(); if (referencedEntity == rootEntity) { error("A type is not allowed to be used recursively by its feature list", EntityDslPackage.Literals.FEATURE__TYPE); } else if (referencedEntity != null) { for (Feature f: referencedEntity.getFeatures()) { checkEntityRecursion(f, rootEntity); } } } }
public class EntityDslProposalProvider extends AbstractEntityDslProposalProvider { @Override public void completeType_Reference(EObject model, Assignment assignment, ContentAssistContext context, ICompletionProposalAcceptor acceptor) { lookupCrossReference(((CrossReference)assignment.getTerminal()), context, acceptor, new RecursiveReferenceDetector((Feature)model)); } } class RecursiveReferenceDetector implements Predicate<IEObjectDescription> { private final Entity rootEntity; public RecursiveReferenceDetector(Feature feature) { rootEntity = (Entity)feature.eContainer(); } @Override public boolean apply(IEObjectDescription input) { return checkEntityRecursion((Entity)input.getEObjectOrProxy()); } public boolean checkEntityRecursion(Entity referencedEntity) { if (referencedEntity == rootEntity) { return false; } else if (referencedEntity != null) { for (Feature f: referencedEntity.getFeatures()) { if (f.getType() == null) continue; if (!checkEntityRecursion(f.getType().getReference())) { return false; } } } return true; } }
entity A extends C {} entity B extends A {} entity C extends B {} entity D extends D {}
@Check public void checkEntityInheritance(Entity entity) { Entity hierarchyLoopElement = getHierarchyLoop(entity); if (hierarchyLoopElement != null) { error(String.format("Cycle detected: a cycle exists in the type hierarchy between %s and %s", entity.getName(), entity.getSuperType().getName()), BachLangPackage.Literals.ENTITY__SUPER_TYPE, INHERITANCE_LOOP, hierarchyLoopElement.getName()); } } private static Entity getHierarchyLoop(Entity element) { Set<Entity> visited = new HashSet<Entity>(); visited.add(element); Entity currentSubType = element; Entity currentSuperType = getSuperType(element); while (currentSuperType != null) { if (visited.contains(currentSuperType)) { return currentSubType; } visited.add(currentSuperType); currentSubType = currentSuperType; currentSuperType = getSuperType(currentSubType); } return null; } private static Entity getSuperType(Entity element) { return element.getSuperType(); }
regarding 2) Look at the community projects, the examples shipped with Xtext, search for AbstractDeclarativeScopeProvider... Search this forum for threads on scoping. And, as you did in your original post, ask specific questions about what you want to achieve. Then you are likely to get helpful pointers.
I have an addition to this problem/solution: Think of a referenced child/parent customer or a bidirectional association you try to prevent.