Skip to main content


Eclipse Community Forums
Forum Search:

Search      Help    Register    Login    Home
Home » Modeling » TMF (Xtext) » [Xtext] ScopeProvider - How to scope differently two calls to the same parser rule ?
[Xtext] ScopeProvider - How to scope differently two calls to the same parser rule ? [message #660459] Fri, 18 March 2011 14:24 Go to next message
Didier Villevalois is currently offline Didier VillevaloisFriend
Messages: 86
Registered: July 2009
Member
Hello newsgroup,

I've got the following grammar (extract):
[the ... in the ClassMapping rule stands for what I stripped down from
my real grammar :p]

<code>
grammar org.clinigrid.capillary.mapping.Mapping
with org.eclipse.xtext.common.Terminals

generate model "http://www.clinigrid.org/Capillary/Mapping"

import "http://www.eclipse.org/emf/2002/Ecore" as ecore

Transformation returns Transformation:
'transformation' name=MappingID
metamodelDeclarations+=MetamodelDeclaration*
children+=AbstractMapping*;

MappingID returns ecore::EString:
ID ('.' ID)*;

MetamodelDeclaration :
SourceMetamodel | TargetMetamodel;

SourceMetamodel :
'source' ePackage=[ecore::EPackage|STRING] ('as' alias=ID)?;

TargetMetamodel :
'target' ePackage=[ecore::EPackage|STRING] ('as' alias=ID)?;

TypeRef returns TypeRef :
{TypeRef} (metamodel=[MetamodelDeclaration] '::')?
classifier=[ecore::EClassifier];

AbstractMapping : ClassMapping;

ClassMapping returns ClassMapping:
'mapping' source=TypeRef '->' target=TypeRef
'{' ... '}';
</code>

My problem lies in the ClassMapping definition. There I have two
assignations/calls to the same TypeRef parser rule.

I want to customize my scope provider to provide different proposals for
the source and target features. The source TypeRef could only reference
classifiers from a SourceMetamodel's package and the target TypeRef
could only reference a classifier from a TargetMetamodel's package.

I first thought getScope(contex, reference) would be called with a
TypeRef context EObject and the "classifier" feature as reference. But
it seems that the TypeRef object is not yet created at the moment where
I ask for content proposal in my editor. Thus, getScope(...) is in fact
called with a ClassMapping context EObject and the "classifier" feature
as reference. So I have no way to check if I am scoping the source or
target feature.

Am I missing something ?

Is there a way to force the creation of the TypeRef object as that part
of my grammar is completely deterministic ('mapping', '->' and '{'
surroundings) ? I tried to force the TypeRef creation with {TypeRef} in
the TypeRef parser rule, but this does not make it.

In last resort, is there a way to rewrite the grammar to have a hint in
the scope provider ?

Thanks for your help. (Xtext rocks!)
Didier Villevalois.
Re: [Xtext] ScopeProvider - How to scope differently two calls to the same parser rule ? [message #660477 is a reply to message #660459] Fri, 18 March 2011 14:40 Go to previous messageGo to next message
Alexander Nittka is currently offline Alexander NittkaFriend
Messages: 1193
Registered: July 2009
Senior Member
Hi,

are you aware that you can use different scope method signatures. For
you (something like - check the documentation)
IScope scope_ClassMapping_source(...)
and IScope scope_ClassMapping_target

in your case the two calls should also differ in the reference
parameter, though.

Alex


Need training, onsite consulting or any other kind of help for Xtext?
Go visit http://xtext.itemis.com or send a mail to xtext@itemis.de
Re: [Xtext] ScopeProvider - How to scope differently two calls to the same parser rule ? [message #660498 is a reply to message #660477] Fri, 18 March 2011 16:06 Go to previous messageGo to next message
Didier Villevalois is currently offline Didier VillevaloisFriend
Messages: 86
Registered: July 2009
Member
Hi Alex,

On Fri, 2011-03-18 at 15:40 +0100, Alexander Nittka wrote:
> are you aware that you can use different scope method signatures. For
> you (something like - check the documentation)
> IScope scope_ClassMapping_source(...)
> and IScope scope_ClassMapping_target

Yeah, I do use the declarative way to express the scopes. However I
spoke in terms of getScope(...) because I've overridden it to put a
breakpoint on it and check what calls Xtext was really doing.

> in your case the two calls should also differ in the reference
> parameter, though.

And it appears that it only calls

getScope(the parent ClassMapping instance,
ModelPackage.getTypeRef_Metamodel())

and then

getScope(the parent ClassMapping instance,
ModelPackage.getTypeRef_Classifier())

I can't do anything with that. I would like to be called with:

getScope(the TypeRef instance, ModelPackage.getTypeRef_Classifier())

With that I could resolve by checking it the TypeRef instance is the one
in ((ClassMapping) eContainer()).getSource() or getTarget()...

Didier.
Re: [Xtext] ScopeProvider - How to scope differently two calls to the same parser rule ? [message #660519 is a reply to message #660498] Fri, 18 March 2011 20:08 Go to previous messageGo to next message
Alexander Nittka is currently offline Alexander NittkaFriend
Messages: 1193
Registered: July 2009
Senior Member
Hi,

sorry, I read and answered your original post in a hurry and misinterpreted your grammar (thinking, that source and target are already references rather than containments). Now, I see the actual problem.

2 possible approaches:
-You could use NodeUtil to navigate to the parse tree in order to see whether the rule call to the TypeRef rule was done from source or target. But this could get a bit messy.
-Have two copies of the TypeRef-Rule (SourceTypeRef and TargetTypeRef creating elements with the the common super type TypeRef). This would give you a way of distinguishing the two, while still keep the common features etc.

Alex
Re: [Xtext] ScopeProvider - How to scope differently two calls to the same parser rule ? [message #660524 is a reply to message #660519] Fri, 18 March 2011 20:38 Go to previous messageGo to next message
Christian Dietrich is currently offline Christian DietrichFriend
Messages: 14665
Registered: July 2009
Senior Member
Hello Didier,

i just tested an approach without changing the grammar or working with the NodeModel and it seems to work

public class MyDslScopeProvider extends AbstractDeclarativeScopeProvider {
	
	public IScope scope_TypeRef_classifier(TypeRef typeRef, EReference ref) {
		List<EClassifier> allClasses = new ArrayList<EClassifier>();
		Transformation transformation = (Transformation) typeRef.eContainer().eContainer();
		if ("source".equals(typeRef.eContainingFeature().getName())) {
			for (MetamodelDeclaration decl : transformation.getMetamodelDeclarations()) {
				if (decl instanceof SourceMetamodel) {
					allClasses.addAll(decl.getEPackage().getEClassifiers());
				}
			}
		}
		if ("target".equals(typeRef.eContainingFeature().getName())) {
			for (MetamodelDeclaration decl : transformation.getMetamodelDeclarations()) {
				if (decl instanceof TargetMetamodel) {
					allClasses.addAll(decl.getEPackage().getEClassifiers());
				}
			}
		}
		return Scopes.scopeFor(allClasses);
	}
	
	public IScope scope_TypeRef_classifier(ClassMapping classMapping, EReference ref) {
		List<EClassifier> allClasses = new ArrayList<EClassifier>();
		Transformation transformation = (Transformation) classMapping.eContainer();
		if (classMapping.getSource() == null || classMapping.getSource().getClassifier() == null) {
			for (MetamodelDeclaration decl : transformation.getMetamodelDeclarations()) {
				if (decl instanceof SourceMetamodel) {
					allClasses.addAll(decl.getEPackage().getEClassifiers());
				}
			}
		} else if (classMapping.getTarget() == null || classMapping.getTarget().getClassifier() == null) {
			for (MetamodelDeclaration decl : transformation.getMetamodelDeclarations()) {
				if (decl instanceof TargetMetamodel) {
					allClasses.addAll(decl.getEPackage().getEClassifiers());
				}
			}
		}
		return Scopes.scopeFor(allClasses);
	}

}


~Christian


Twitter : @chrdietrich
Blog : https://www.dietrich-it.de
Re: [Xtext] ScopeProvider - How to scope differently two calls to the same parser rule ? [message #660546 is a reply to message #660498] Sat, 19 March 2011 04:55 Go to previous messageGo to next message
Alexander Nittka is currently offline Alexander NittkaFriend
Messages: 1193
Registered: July 2009
Senior Member
Hi,

two more note. Even if the modified scoping works, some consider it
better practice to have more elements referalble than semantically
valid. You would check that in validation and provide meaningful
errormessages ("only source references are allowed in this position"
rather than "could not resolve reference") and adapt content assist
(where you can make use of several lookupReferences methods that use the
scoping but allow filtering).

What would you think about editor that complains: could not resolve
reference to java.lang.String?
It is quite strange to a user getting this error message, seeing that
the referenced element is defined directly above. If you don't restrict
the scope, navigation to the element is possible and the user can check
that it indeed has the wrong type.

Another approach from the grammar point of view would be to actually
eliminate the need for the TypeRef element and write
"source=[ecore::EClassifier|QualifiedName]" instead.
QualifiedName hidden(): (ID'::')? ID;

And then use scoping in order to give Classifiers the "correct" namens.
It basically depends on the wanted "navigation" behaviour of the editor.

Alex


Need training, onsite consulting or any other kind of help for Xtext?
Go visit http://xtext.itemis.com or send a mail to xtext@itemis.de
Re: [Xtext] ScopeProvider - How to scope differently two calls to the same parser rule ? [message #660924 is a reply to message #660546] Tue, 22 March 2011 11:15 Go to previous message
Didier Villevalois is currently offline Didier VillevaloisFriend
Messages: 86
Registered: July 2009
Member
Hi Alex, Hi Christian,

Thank you both for your help. My comments bellow...

On Sat, 2011-03-19 at 05:55 +0100, Alexander Nittka wrote:
> Hi,
>
> two more note. Even if the modified scoping works, some consider it
> better practice to have more elements referalble than semantically
> valid. You would check that in validation and provide meaningful
> errormessages ("only source references are allowed in this position"
> rather than "could not resolve reference") and adapt content assist
> (where you can make use of several lookupReferences methods that use the
> scoping but allow filtering).
>
> What would you think about editor that complains: could not resolve
> reference to java.lang.String?
> It is quite strange to a user getting this error message, seeing that
> the referenced element is defined directly above. If you don't restrict
> the scope, navigation to the element is possible and the user can check
> that it indeed has the wrong type.

The whole point of my question was to narrow the possible/proposed
source and target references. However I had tested before adding
SourceTypeRef and TargetTypeRef without any success.

Also I found the solution proposed by Christian a bit dangerous as a
user might have put the '->' without temporarily put a target classifier
reference before the '->'. I know my own habits to sometime omit some
stuff when I code (and leave a visible parser error) until I cleared my
mind.

> Another approach from the grammar point of view would be to actually
> eliminate the need for the TypeRef element and write
> "source=[ecore::EClassifier|QualifiedName]" instead.
> QualifiedName hidden(): (ID'::')? ID;
>
> And then use scoping in order to give Classifiers the "correct" namens.
> It basically depends on the wanted "navigation" behaviour of the editor.

So I finally choose that QualifiedName solution which I find better from
an EMF model point of view. This is way more closer to what I need when
I generate. This gave me the possibility to have in my scope provider:

<code>
IScope scope_ClassMapping_source(ClassMapping classMapping, EReference
reference) {
Transformation transformation =
MappingUtil.getTransformation(classMapping);
return createPackagesClassifiersScope(transformation,
MappingUtil.getSourceMetamodels(transformation));
}

IScope scope_ClassMapping_target(ClassMapping classMapping, EReference
reference) {
Transformation transformation =
MappingUtil.getTransformation(classMapping);
return createPackagesClassifiersScope(transformation,
MappingUtil.getTargetMetamodels(transformation));
}

protected IScope createPackagesClassifiersScope(Transformation
transformation, List<MetamodelDeclaration> metamodels) {
Collection<EClassifier> allClassifiers = new ArrayList<EClassifier>();
for (MetamodelDeclaration declaration : metamodels) {
if (declaration.getPackage() != null)
allClassifiers.addAll(declaration.getPackage().getEClassifie rs());
}
return scopeFor(allClassifiers, new
QualifiedClassifiersResolver(transformation), IScope.NULLSCOPE);
}

private class QualifiedClassifiersResolver implements
com.google.common.base.Function<EClassifier, String> {

QualifiedClassifiersResolver(Transformation transformation) {
this.transformation = transformation;
}

private Transformation transformation;

@Override
public String apply(EClassifier from) {
return MappingUtil.getQualifiedName(transformation, from);
}
}
</code>

This works correctly as expected.

Thank you both for your help.
Didier Villevalois.
Previous Topic:What is this error about?
Next Topic:Does XText 1.0.1 support syntactic predicates?
Goto Forum:
  


Current Time: Thu Apr 25 01:33:36 GMT 2024

Powered by FUDForum. Page generated in 0.03425 seconds
.:: Contact :: Home ::.

Powered by: FUDforum 3.0.2.
Copyright ©2001-2010 FUDforum Bulletin Board Software

Back to the top