Home » Modeling » TMF (Xtext) » Restricting the scope / code suggestion(Understanding the scope provider)
Restricting the scope / code suggestion [message #697799] |
Mon, 18 July 2011 04:47 |
Moritz Messages: 22 Registered: July 2011 |
Junior Member |
|
|
Hi,
first I want to say that I am very very happy about this framework. Thank you!
I read through many blog posts and documentations for some time, most of them very helpful. Unfortunately, some are quite hard to follow and lack of some good examples imo (i.e. the chapter about scoping[1]). I would love if someone could help me understanding this part better.
Domainmodel:
entities += Entity*
;
Entity:
'entity' name = ID '{'
features += Feature*
'}'
;
Feature:
'feature' name = ID ':' type = Type
;
Type:
baseType = BaseType | reference = [Entity]
;
enum BaseType:
String | Int
;
So here is a simple DSL similar to the one of the documentation. My questions are:
Image:[4]
- The type of a feature must not be the name of the entity wherein it is defined (so, no recursion allowed). This constraint can be easily implemented with a @Check in the validator, but it is then still contained in the proposal list.
I assume one just has to adapt the scope provider, but I am still puzzled on how to use it properly. I think one has to start with a function as described in the documentation[2]:
IScope scope_Feature_type(Type ctx, EReference ref)
But how can I restrict the automatically created scope, or simply create a new one? Does the injected IScopeProvider offer help here? I think I nee something similar to this[3], but this code seems to use a rather old version.
- The type of the feature can either be a predefined (static) type, or a reference to an entity. Is there a more easy or elegant way to express this rule? How would you do it?
Any help is very appreciated. Thank you.
(I cannot use links yet)
[1]http //www.eclipse.org/Xtext/documentation/2_0_0/080-scoping.php
[2]http //www.eclipse.org/Xtext/documentation/2_0_0/080-scoping.php#local_scoping
[3]http //chilifreak.wordpress.com/2010/02/22/extending-proposals-in-xtext-using-scoping/
[4]http //s4.postimage.org/5zfdmtfbh/Reference1.png
|
|
| | |
Re: Restricting the scope / code suggestion [message #697828 is a reply to message #697824] |
Mon, 18 July 2011 07:14 |
Alexander Nittka Messages: 1193 Registered: July 2009 |
Senior Member |
|
|
Hi,
you could ship the model file containing the base types along with the language plugin or within a separate project. The second alternative has the advantage that the user knows, why there should be a dependency to that project - if you name it something like mydsl.basetypes or mydsl.library.
You just have to make sure that the model file is on the classpath of the project that is to reference the base types, so that it is picked up by the index. You can check that it is visible at all by inspecting the open-model-element-dialog (shift-ctrl-F3) which tells you which elements are contained in the index.
Alex
[Updated on: Mon, 18 July 2011 07:44] Report message to a moderator
|
|
|
Re: Restricting the scope / code suggestion [message #697982 is a reply to message #697828] |
Mon, 18 July 2011 14:37 |
Moritz Messages: 22 Registered: July 2011 |
Junior Member |
|
|
Hi Alex,
thank you very much for your explanation and the hint to the code completion and the filtering methods.
In fact, I tried it the way you proposed and the concept for Content Assist[1] is much easier to understand than scoping (combined with debugging)
I want to show the code that I used to make it more understandable for people coming to a similar issue. With the Validator and the ProposalProvider it pretty much does exactly what I was hoping for. Anyway I have a few further quetions.
At first, the code for the validator:
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);
}
}
}
}
And the code for the ProposalProvider:
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;
}
}
- In the completeType_Reference function, both the context.currentModel and the model refer to the same object of type FeatureImpl. I indeed need this Feature (or its container) to complete the Type_Reference, but how does Xtext decides which model is passed? What if there are more Rules with references to Type?
- Is there a good source or example that makes the usage of the scope provider more clear? Maybe one where the scope is restricted or just somehow modified.
Moritz
[1]http //www.eclipse.org/Xtext/documentation/2_0_0/150-contentassist.php
|
|
|
Re: Restricting the scope / code suggestion [message #698064 is a reply to message #697982] |
Mon, 18 July 2011 17:51 |
Alexander Nittka Messages: 1193 Registered: July 2009 |
Senior Member |
|
|
Hi,
regarding 1) I can't give you the definition, but I can give you an idea. Usually when typing and using code completion, you have an incomplete model. The framework determines all possible continuations, i.e. valid semantic models with the given "document prefix". This is how different context objects are possible.
If there are different rules referring to Type, they would cause a different completion function to be invoked. Your job here would be to extract code to be used by all of them to helper methods.
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.
Alex
|
|
|
Re: Restricting the scope / code suggestion [message #698265 is a reply to message #698064] |
Tue, 19 July 2011 06:38 |
Daniel Missing name Messages: 101 Registered: July 2011 |
Senior Member |
|
|
I have an addition to this problem/solution: Think of a referenced child/parent customer or a bidirectional association you try to prevent. Your problem also regards to a recursive inheritance. Think of:
entity A extends C {}
entity B extends A {}
entity C extends B {}
entity D extends D {}
I've written a small algorithm (find cycle in a directed graph) which checks this:
@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();
}
If you need a more generic way you can modify the getHierarchyLoop and getSuperType to use your base-object which provides a getSuperType. Or you can use reflection for calling a method.
[Updated on: Tue, 19 July 2011 06:38] Report message to a moderator
|
|
| |
Goto Forum:
Current Time: Fri Apr 26 16:52:24 GMT 2024
Powered by FUDForum. Page generated in 0.03595 seconds
|