|
|
|
|
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 |
|
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 #660924 is a reply to message #660546] |
Tue, 22 March 2011 11:15 |
Didier Villevalois 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.
|
|
|
Powered by
FUDForum. Page generated in 0.03425 seconds