Home » Modeling » TMF (Xtext) » Qualified Name according to the hierarchical level
Qualified Name according to the hierarchical level [message #1840204] |
Thu, 08 April 2021 15:23  |
John Henbergs Messages: 227 Registered: October 2020 |
Senior Member |
|
|
Hi all,
I am writing the following grammar in Xtext which also allows inheritance. A country can extend another country and inherit all its attributes.
grammar org.xtext.example.forest.Forest with org.eclipse.xtext.common.Terminals
generate forest "http://www.xtext.org/example/forest/Forest"
Country:
"country" name=ID ("extends" supercountry=[Country|QualifiedName])? "{"
forests=Forest
"}" ;
Forest:
"forest" "{"
("park" parks+=Park ("," parks+=Park)*)*
("workingarea" workingarea=WorkingArea)*
"}";
Park:
name=ID ("{"
"enter" enter+=Enter
("park" parks+=Park ("," parks+=Park)*)*
("workingarea" workingarea=WorkingArea)*
"}")* ;
WorkingArea:
name=ID "from" from=[ParkEnter|QualifiedName] ;
Enter:
name=ID;
ParkEnter:
Park | Enter;
QualifiedName:
ID ('.' ID)*;
In order to make this inheritance possible I had to customize the scope provider so that it also collects the elements from the inherited country. Furthermore, in the scope provider is a function to calculate the qualified name.
However, my issue now is with the qualified name. The current one has two major issues:
1. When I have the following example (please read the comments in the code):
park P4 {
enter EN3
park P5
workingarea W4 from P4.EN3 // Here it should only be EN3, not P4.EN3 because EN3 and W4 are both contained by P4
}
So if I cross reference an element that is in the same hierarchical level, then there should not be a Park.Enter, only Enter.
2. The second issue is that it only goes as far as Park.Enter, but it might also need to go more in depth such as in this example.
country C1 {
forest {
park P1 {
enter EN1
park P2 {
enter EN2
park P3
park P4 {
enter EN3
park P5
workingarea W4 from P4.EN3 // Only EN3, not P4.EN3 because EN3 and W4 are both contained by P4
}
workingarea W2 from P4.EN3 // Here it is fine as it is because P4 and W2 are both contained inside P2 and EN3 is contained inside P4
}
workingarea W1 from P1.EN1 // Only EN1, not P1.EN1 because EN1 and W1 are both contained by P1
}
park P6 {
enter EN4
}
workingarea WF from P5.P5 //here it should be P1.P2.P4.P5
}
}
And this is the scope provider:
public class ForestScopeProvider extends AbstractForestScopeProvider {
@Override
public IScope getScope(EObject context, EReference reference) {
if (reference == ForestPackage.Literals.WORKING_AREA__FROM) {
Country rootCountry = (Country) EcoreUtil2.getRootContainer(context);
List<EObject> workingarea = new ArrayList<>();
while (rootCountry != null) {
workingarea.addAll(EcoreUtil2.getAllContentsOfType(rootCountry, ParkEnter.class));
rootCountry = rootCountry.getSupercountry();
}
System.out.println(workingarea);
Function<EObject, QualifiedName> nameComputation = (EObject e) -> {
if (e instanceof Enter)
{
Enter en = (Enter) e;
Park p = EcoreUtil2.getContainerOfType(en, Park.class);
return QualifiedName.create(p.getName(), en.getName());
}
else if (e instanceof Park)
{
Park p1 = (Park) e;
Park p2 = EcoreUtil2.getContainerOfType(p1, Park.class);
return QualifiedName.create(p2.getName(), p1.getName());
}
return QualifiedName.create(((ParkEnter)e).getName());
};
return Scopes.scopeFor(workingarea, nameComputation, IScope.NULLSCOPE);
}
return super.getScope(context, reference);
}
}
So in this scope provider the segments of the qualified name are somehow "hardcoded", where there could only be two segments where the first is the Park and the second is the Enter. But it should not, it should depend on the hierarchical levels.
If in workingarea name from:
I reference elements (they can either be park or enter) that are part of the same Park as the working area, then I should only get the element.
If I reference sub-elements (they can either be park or enter) of the park in which the working area is located, then I should only get Park.Park or Park.Enter (depends on if I am referencing a Park or an Enter).
If I reference sub-sub-elements (they can either be park or enter) of the park in which the working area is located, then I should only get Park.Park.Park or Park.Park.Enter (depends on if I am referencing a Park or an Enter) and so on...
So there should probably be some sort of a method that find the hierarchical level in which the working area is in and creates the qualified name based on the hierarchical level in which the element to be referenced is, but I am not really sure on how to implement this, so any help would be deeply appreciated.
Many thanks
|
|
| |
Re: Qualified Name according to the hierarchical level [message #1840234 is a reply to message #1840205] |
Fri, 09 April 2021 09:37   |
John Henbergs Messages: 227 Registered: October 2020 |
Senior Member |
|
|
Hi Christian,
Thank you for your help!
I customised the scope provider according to your advice both for enter and park. This is the scope provider:
public class ForestScopeProvider extends AbstractForestScopeProvider {
@Override
public IScope getScope(EObject context, EReference reference) {
if (reference == ForestPackage.Literals.WORKING_AREA__FROM) {
Country rootCountry = (Country) EcoreUtil2.getRootContainer(context);
List<EObject> workingarea = new ArrayList<>();
while (rootCountry != null) {
workingarea.addAll(EcoreUtil2.getAllContentsOfType(rootCountry, ParkEnter.class));
rootCountry = rootCountry.getSupercountry();
}
System.out.println(workingarea);
Function<EObject, QualifiedName> nameComputation = (EObject e) -> {
if (e instanceof Enter)
{
Enter en = (Enter) e;
Park parkTheEnterIsIn = (Park)context.eContainer();
List <String> allParks=new ArrayList<>();
EObject p = en.eContainer();
while (p instanceof Park && !p.equals(parkTheEnterIsIn)) {
allParks.add(((Park) p).getName());
p = p.eContainer();
}
QualifiedName nameWithAllParks = QualifiedName.create(ListExtensions.reverse(allParks)).append(en.getName());
return nameWithAllParks;
}
else if (e instanceof Park)
{
Park p = (Park) e;
Park parkTheParkIsIn = (Park)context.eContainer();
List <String> allParks=new ArrayList<>();
EObject pl = p.eContainer();
while (pl instanceof Park && !p.equals(parkTheParkIsIn)) {
allParks.add(((Park) pl).getName());
pl = pl.eContainer();
}
while (pl instanceof Park&& !pl.equals(parkTheParkIsIn)) {
allParks.add(((Park) pl).getName());
pl = pl.eContainer();
}
QualifiedName nameWithAllParks = QualifiedName.create(ListExtensions.reverse(allParks)).append(p.getName());
return nameWithAllParks;
}
return QualifiedName.create(((ParkEnter)e).getName());
};
return Scopes.scopeFor(workingarea, nameComputation, IScope.NULLSCOPE);
}
return super.getScope(context, reference);
}
}
However I get two issues with this:
1. When I try to cross reference a park or an enter from a working area that is contained inside a forest and not a park, I do not get suggestions for any of them.
2. For the park I get the wrong qualified name.
For more details on what I get and what I should get I am putting here the following example:
country C1 {
forest {
park P1 {
enter EN1
park P2 {
enter EN2
park P3
park P4 {
enter EN3
park P5
workingarea W4 from P1.P2.P4.P5 // Enter is fine but the park should be P5 not P1.P2.P5.P5
}
workingarea W2 from P4.EN3 // Enter is fine but in park I get P1.P2.P3 instead of P3, P1.P2.P4 instead of P4, P1.P2.P4.P5 instead of P4.P5
}
workingarea W1 from P2.EN2 // Enter is fine but in park I get P1.P2 instead of P2, P1.P2.P3 instead of P2.P3, P1.P2.P4 instead of P2.P4, P1.P2.P4.P5 instead of P2.P4.P5
}
park P6 {
enter EN4
}
workingarea WF from //here I don't get any suggestions nor for the enter and not for parks. Maybe because we only consider when the container is a park while here it is a forest.
}
}
Thank you!
|
|
| | |
Re: Qualified Name according to the hierarchical level [message #1840239 is a reply to message #1840238] |
Fri, 09 April 2021 12:16   |
|
if you check the console (as i told you before)
you see this exception:
4 [main] WARN org.eclipse.xtext.ui.editor.contentassist.AbstractJavaBasedContentProposalProvider - Error in polymorphic dispatcher : class org.xtext.example.mydsl.myDsl.impl.ForestImpl cannot be cast to class org.xtext.example.mydsl.myDsl.Park (org.xtext.example.mydsl.myDsl.impl.ForestImpl and org.xtext.example.mydsl.myDsl.Park are in unnamed module of loader org.eclipse.osgi.internal.loader.EquinoxClassLoader @64314da3)
java.lang.ClassCastException: class org.xtext.example.mydsl.myDsl.impl.ForestImpl cannot be cast to class org.xtext.example.mydsl.myDsl.Park (org.xtext.example.mydsl.myDsl.impl.ForestImpl and org.xtext.example.mydsl.myDsl.Park are in unnamed module of loader org.eclipse.osgi.internal.loader.EquinoxClassLoader @64314da3)
at org.xtext.example.mydsl.scoping.MyDslScopeProvider.lambda$0(MyDslScopeProvider.java:62)
at org.eclipse.xtext.scoping.Scopes$2.apply(Scopes.java:94)
at org.eclipse.xtext.scoping.Scopes$2.apply(Scopes.java:91)
at com.google.common.collect.Iterators$6.transform(Iterators.java:783)
at com.google.common.collect.TransformedIterator.next(TransformedIterator.java:47)
at com.google.common.collect.Iterators$5.computeNext(Iterators.java:636)
at com.google.common.collect.AbstractIterator.tryToComputeNext(AbstractIterator.java:141)
at com.google.common.collect.AbstractIterator.hasNext(AbstractIterator.java:136)
at com.google.common.collect.Iterators$ConcatenatedIterator.hasNext(Iterators.java:1324)
fix it!
Need professional support for Xtext, Xpand, EMF?
Go to: https://www.itemis.com/en/it-services/methods-and-tools/xtext
Twitter : @chrdietrich
Blog : https://www.dietrich-it.de
|
|
|
Re: Qualified Name according to the hierarchical level [message #1840241 is a reply to message #1840239] |
Fri, 09 April 2021 14:22   |
John Henbergs Messages: 227 Registered: October 2020 |
Senior Member |
|
|
Hi Christian,
I added an instanceof to the scopeprovider to check if the context.eContainer() instanceof Park and now I do not get any more errors, but now in working area WF I get everything but not by their qualified name.
public class ForestScopeProvider extends AbstractForestScopeProvider {
@Override
public IScope getScope(EObject context, EReference reference) {
if (reference == ForestPackage.Literals.WORKING_AREA__FROM) {
Country rootCountry = (Country) EcoreUtil2.getRootContainer(context);
List<EObject> workingarea = new ArrayList<>();
while (rootCountry != null) {
workingarea.addAll(EcoreUtil2.getAllContentsOfType(rootCountry, ParkEnter.class));
rootCountry = rootCountry.getSupercountry();
}
System.out.println(workingarea);
Function<EObject, QualifiedName> nameComputation = (EObject e) -> {
if (e instanceof Enter)
{
Enter en = (Enter) e;
if (context.eContainer() instanceof Park) {
Park parkTheEnterIsIn = (Park)context.eContainer();
List <String> allParks=new ArrayList<>();
EObject p = en.eContainer();
while (p instanceof Park && !p.equals(parkTheEnterIsIn)) {
allParks.add(((Park) p).getName());
p = p.eContainer();
}
QualifiedName nameWithAllParks = QualifiedName.create(ListExtensions.reverse(allParks)).append(en.getName());
return nameWithAllParks;
}
}
else if (e instanceof Park)
{
Park p = (Park) e;
if (context.eContainer() instanceof Park)
{
Park parkTheParkIsIn = (Park)context.eContainer();
List <String> allParks=new ArrayList<>();
EObject pl = p.eContainer();
while (pl instanceof Park && !pl.equals(parkTheParkIsIn)) {
allParks.add(((Park) pl).getName());
pl = pl.eContainer();
}
QualifiedName nameWithAllParks = QualifiedName.create(ListExtensions.reverse(allParks)).append(p.getName());
return nameWithAllParks;
}
return QualifiedName.create(((ParkEnter)e).getName());
}
return QualifiedName.create(((ParkEnter)e).getName());
};
return Scopes.scopeFor(workingarea, nameComputation, IScope.NULLSCOPE);
}
return super.getScope(context, reference);
}
}
PS: The forest doesn't have a name. Could that be an issue?
[Updated on: Fri, 09 April 2021 14:43] Report message to a moderator
|
|
| |
Goto Forum:
Current Time: Tue Jun 06 17:46:13 GMT 2023
Powered by FUDForum. Page generated in 0.02135 seconds
|