Skip to main content


Eclipse Community Forums
Forum Search:

Search      Help    Register    Login    Home
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 Go to next message
John Henbergs is currently offline John HenbergsFriend
Messages: 239
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 #1840205 is a reply to message #1840204] Thu, 08 April 2021 15:31 Go to previous messageGo to next message
Christian Dietrich is currently offline Christian DietrichFriend
Messages: 14661
Registered: July 2009
Senior Member
from the enter go up (eContainer()) until you dont find any parks anymore

e.g. PseudoCode

Enter e = ...;
Park parkTheEngterIsIn = (Park)context.eContainer();

List<String> allParks = new ArrayList<>();
EObject p = e.eContainer();
while (p instanceof Park && !p.equals(parkTheEngterIsIn)) {
allParks.add(((Park) p).getName());
p = p.eContainer();
}
QualifiedName nameWithAllParks = QualifiedName.create(ListExtensions.reverse(allParks)).append(e.getName());


Twitter : @chrdietrich
Blog : https://www.dietrich-it.de

[Updated on: Thu, 08 April 2021 15:40]

Report message to a moderator

Re: Qualified Name according to the hierarchical level [message #1840234 is a reply to message #1840205] Fri, 09 April 2021 09:37 Go to previous messageGo to next message
John Henbergs is currently offline John HenbergsFriend
Messages: 239
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 #1840237 is a reply to message #1840234] Fri, 09 April 2021 11:43 Go to previous messageGo to next message
Christian Dietrich is currently offline Christian DietrichFriend
Messages: 14661
Registered: July 2009
Senior Member
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 && !pl.equals(parkTheParkIsIn)) {
allParks.add(((Park) pl).getName());
pl = pl.eContainer();
}

you adapted the while loop wrong

ps: please debug your code


Twitter : @chrdietrich
Blog : https://www.dietrich-it.de

[Updated on: Fri, 09 April 2021 11:48]

Report message to a moderator

Re: Qualified Name according to the hierarchical level [message #1840238 is a reply to message #1840237] Fri, 09 April 2021 12:05 Go to previous messageGo to next message
John Henbergs is currently offline John HenbergsFriend
Messages: 239
Registered: October 2020
Senior Member
Hi Christian,

Thanks for that!

This resolves the first issue, but I am still not able to cross reference parks or enters in the working are WF which is contained in the Forest and not in a Park.

It is the last line of the previous example.

Many thanks!
Re: Qualified Name according to the hierarchical level [message #1840239 is a reply to message #1840238] Fri, 09 April 2021 12:16 Go to previous messageGo to next message
Christian Dietrich is currently offline Christian DietrichFriend
Messages: 14661
Registered: July 2009
Senior Member
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!


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 Go to previous messageGo to next message
John Henbergs is currently offline John HenbergsFriend
Messages: 239
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

Re: Qualified Name according to the hierarchical level [message #1840246 is a reply to message #1840241] Fri, 09 April 2021 15:19 Go to previous message
Christian Dietrich is currently offline Christian DietrichFriend
Messages: 14661
Registered: July 2009
Senior Member
sorry i cant do the work for you.
you need to decide and implement

if (context.eContainer() instanceof Park) {

} else {
// you dont have this else you just have
return QualifiedName.create(((ParkEnter)e).getName());
// maybe, but just maybe you need a while loop here too
}

please ask someone with java knowledge to help you


Twitter : @chrdietrich
Blog : https://www.dietrich-it.de
Previous Topic:Customize Scoping for inheritance
Next Topic:Split XText Grammar into multiple files
Goto Forum:
  


Current Time: Fri Mar 29 00:11:39 GMT 2024

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

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

Back to the top