Skip to main content


Eclipse Community Forums
Forum Search:

Search      Help    Register    Login    Home
Home » Modeling » TMF (Xtext) » dangling reference with models from different files
dangling reference with models from different files [message #1620562] Tue, 17 February 2015 09:17 Go to next message
Stefan Scheiber is currently offline Stefan ScheiberFriend
Messages: 18
Registered: February 2015
Junior Member
hello.

with the following grammar
Model:
	classes+=Class*
;
Class:
	'class' name=ID '{'
		variables += Variable*
		statements += Statement*
	'}';	
Variable:
	name=ID ':' type=Type ';'
;
Type:
	PrimitiveType | ClassType
;
PrimitiveType:
	type=PrimitiveTypes
;
ClassType:
	refClass = [Class]
;
enum PrimitiveTypes:
	BOOL | INT | REAL 
;
Statement:
	lValue=LValue '=' rValue=RValue ';'
;
LValue:
	VariableRef
;
RValue:
	VariableRef | ConstantLiteral
;
VariableRef:
	variable=[Variable] ({VariableRef.qualifier=current} '.' variable=[Variable])*	
;
ConstantLiteral:
	value=INT
;

and the following scope provider
override getScope(EObject context, EReference reference) {
	if (reference == MyDslPackage.eINSTANCE.variableRef_Variable) {
		if (context instanceof VariableRef) {
			val type = context.qualifier?.variable?.type
			if (type instanceof ClassType) {
				return Scopes.scopeFor(type.refClass.variables)
			}
		}
	}
	return super.getScope(context, reference)
}

i have a project with two files
file1 with
class A {
	x: INT;
        y: INT;
}
class B {
	vc: C;
}
class D {
	vb: B;
		
	vb.vc.va.x = 2;
}

and file2 with
class C {
	va: A;
}

if i edit the line vb.vc.va.x = 2; in file1 (e.g. delete x, activate content assist and choose y) i get a dangling reference error.

this happens because the file1 is reparsed and new model objects are instantiated. on the other hand file2 isn't reparsed and the model object keeps a reference to the old model objects from file1. How can I force file2 to be reparsed as well? The DefaultResourceDescriptionManager.isAffected is not called when editing and even after saving it isn't called with file2 as candidate.
I was able to solve the problem by adding the following code to the scope provider but I'm not sure if this is the way to go.

override getScope(EObject context, EReference reference) {
	if (reference == MyDslPackage.eINSTANCE.variableRef_Variable) {
		if (context instanceof VariableRef) {
			val type = context.qualifier?.variable?.type
			if (type instanceof ClassType) {
				var class = type.refClass
				if (class.eResource == null) {
					val uriFragment = EcoreUtil.getURI(class).fragment
					class = context.eResource.getEObject(uriFragment) as org.xtext.example.mydsl.myDsl.Class
				}
				return Scopes.scopeFor(class.variables)
			}
		}
	}
	return super.getScope(context, reference)
}

How would you solve the problem?
Thanks in advance!
Re: dangling reference with models from different files [message #1707251 is a reply to message #1620562] Thu, 03 September 2015 09:59 Go to previous messageGo to next message
YU WANG is currently offline YU WANGFriend
Messages: 3
Registered: September 2015
Location: Taiwan
Junior Member
This post has yet been replied over several months.
I wonder if anyone found a solution on this issue?

I faced the same situations in the last few days, and finally conclude to the same cause.

I made a workaround by checking the eResource of the EObject, and the eResource of the referenced EObject. If they are from different resources, I will unload the other resource, then resolveAll the other resource right after the unload. Since doing this will make all references in the other resource up-to-date. A pseudo code might look like this:

VariableRef:
variable=[Variable] ({VariableRef.qualifier=current} '.' variable=[Variable])*
-------
someDispatchFunction(VariableRef variableRef){
...
if(!variableRef.eResource.equals(variableRef.variable.eResource)){
variableRef.variable.eResource.unload()
//rset.createResource(rsURI)
variableRef.variable.eResource.resolveAll
}
....
}

This inefficient workaround temporarily solves this issue. But as you can clearly see, this method is very not a good choice for large project. I wonder if anyone has better solution on this issue? Or any suggestions??

A better method I can think of is to to override default behavior the lazy-linker, through checking all references in other resources which are referencing the current resource in edit.

Thank you.

Yu-Jiu
Re: dangling reference with models from different files [message #1707287 is a reply to message #1707251] Thu, 03 September 2015 13:34 Go to previous messageGo to next message
Christian Dietrich is currently offline Christian DietrichFriend
Messages: 14665
Registered: July 2009
Senior Member
did you try my solution for that problem: https://christiandietrich.wordpress.com/2013/05/18/xtext-and-dot-expressions/

Twitter : @chrdietrich
Blog : https://www.dietrich-it.de
Re: dangling reference with models from different files [message #1707339 is a reply to message #1707287] Thu, 03 September 2015 20:09 Go to previous messageGo to next message
YU WANG is currently offline YU WANGFriend
Messages: 3
Registered: September 2015
Location: Taiwan
Junior Member
Dear Christian,

I had read many of your posts over the year. Your articles help me a lot. Thank you.

The problem I faced is a different one. It only happen when we have 'Entity' defined across several files. Let's extend your "Xtext and Dot/Path-Expressions" post to repeat this issue.

First, let's try to add a custom ResourceDescriptionStrategy to export 'Entity' to index.

package org.xtext.example.mydsl.scoping

import org.eclipse.xtext.resource.impl.DefaultResourceDescriptionStrategy
import com.google.inject.Singleton
import com.google.inject.Inject
import org.eclipse.xtext.naming.IQualifiedNameProvider
import org.xtext.example.mydsl.myDsl.Entity
import org.eclipse.xtext.resource.EObjectDescription
import org.eclipse.emf.ecore.EObject
import org.eclipse.xtext.util.IAcceptor
import org.eclipse.xtext.resource.IEObjectDescription
import static extension org.eclipse.xtext.EcoreUtil2.*
import org.xtext.example.mydsl.myDsl.Model

@Singleton
class myDslResourceDescriptionStrategy extends DefaultResourceDescriptionStrategy{

	@Inject extension IQualifiedNameProvider 
	override createEObjectDescriptions(EObject eObj, IAcceptor<IEObjectDescription> acceptor){
		if(eObj instanceof Model){
			(eObj as Model).getAllContentsOfType(typeof(Entity)).forEach[it|
				acceptor.accept(EObjectDescription::create((it as Entity).fullyQualifiedName, (it as Entity)))
			]
			true
		} 	else false
	}

}


Then, let's improve the MyDslScopeProvider to access the global index.
 
	def IScope scope_EntityRef_entity(EntityRef entity, EReference ref) {
		val model = entity.getContainerOfType(typeof(Model)) as Model
		val outerScope=delegateGetScope(model, ref)
		val currentScope=Scopes::scopeFor(model.getAllContentsOfType(typeof(Entity)), outerScope)
		return currentScope
		
	}
	def IScope scope_Reference_type(Reference context, EReference ref) {
		val model = context.getContainerOfType(typeof(Model)) as Model		
		val outerScope=delegateGetScope(context.getContainerOfType(typeof(Model)), ref)
		val currentScope=Scopes::scopeFor(context.getAllContentsOfType(typeof(Entity)), outerScope)
		return currentScope
	}

And don't forget to change the MyDslRuntimeModule.
org.xtext.example.mydsl.AbstractMyDslRuntimeModule {
    public Class<? extends IDefaultResourceDescriptionStrategy> bindIDefaultResourceDescriptionStrategy() {
        return myDslResourceDescriptionStrategy.class;
      }
}

----
Then, let's separate the test code into two separate files:
1. test1.mydsl
entity A {
    attr a1 : int
    attr a2 : string
    ref b : B
    ref c : C
}
  
   
entity C {
    attr c1 : string
    attr c2 : int
}

2. test2.mydsl
entity B { 
    attr b1 : string
    attr b2 : string
    ref a : A
    ref c : C
} 

use A.b.b2
use A.b.c.c1 
use A.a1
use A.b.a.a1   


Now, you can see in the editor of the second test file, where the b2 of A.b.b2; c of A.b.c.c1, the a of A.b.a showing dangling reference error. This is due to when we update test2.mydsl in the editor, the eResource of test2.mydsl is reparsed, which make all references in test1.mydsl back to Entity B in test1.mydsl out-of-date.

Obviously my previous workaround cannot be put inside the scope-provider; otherwise it will result in a endless job.

Any suggestions on these?

Yu-Jiu
Re: dangling reference with models from different files [message #1707342 is a reply to message #1707339] Thu, 03 September 2015 20:21 Go to previous messageGo to next message
Christian Dietrich is currently offline Christian DietrichFriend
Messages: 14665
Registered: July 2009
Senior Member
hi,

why do you add these methods to the scope provider?
delegateGetScope already adds all local and global entities


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

[Updated on: Thu, 03 September 2015 20:22]

Report message to a moderator

Re: dangling reference with models from different files [message #1707343 is a reply to message #1707342] Thu, 03 September 2015 20:24 Go to previous messageGo to next message
Christian Dietrich is currently offline Christian DietrichFriend
Messages: 14665
Registered: July 2009
Senior Member
=> the problem is that a is not reparsed. so let us force it to do so?

Twitter : @chrdietrich
Blog : https://www.dietrich-it.de
Re: dangling reference with models from different files [message #1707346 is a reply to message #1707343] Thu, 03 September 2015 20:34 Go to previous messageGo to next message
Christian Dietrich is currently offline Christian DietrichFriend
Messages: 14665
Registered: July 2009
Senior Member
class MyDslResourceDescriptionManager extends DefaultResourceDescriptionManager implements IResourceDescription.Manager.AllChangeAware {
	
	override isAffectedByAny(Collection<Delta> deltas, IResourceDescription candidate,
		IResourceDescriptions context) throws IllegalArgumentException {
		for (delta : deltas) {
			val Set<QualifiedName> names = Sets.newHashSet()
			addExportedNames(names, delta.getOld())
			addExportedNames(names, delta.getNew())
			val result = !Collections.disjoint(names, getImportedNames(candidate))
			println(candidate)
			println(result)
			if (result) {
				return true
			}
		}
		return false

	}

}


Twitter : @chrdietrich
Blog : https://www.dietrich-it.de
Re: dangling reference with models from different files [message #1707446 is a reply to message #1707346] Fri, 04 September 2015 18:30 Go to previous messageGo to next message
YU WANG is currently offline YU WANGFriend
Messages: 3
Registered: September 2015
Location: Taiwan
Junior Member
Thank you Christian, the codes you provide really works!
Overriding the default DefaultResourceDescriptionManager by implementing the isAffectedByAny(Collection<Delta> deltas, IResourceDescription candidate, IResourceDescriptions context) changes the default builder behavior to reflect the resources that are affected by the current file in edit!

I try to go through some related codes, and I found that the default method called by builder:
isAffected(Collection<Delta> deltas, IResourceDescription candidate, IResourceDescriptions context) in the DefaultResourceDescriptionManager shares the same input parameters as the method:
isAffectedByAny() of IResourceDescription.Manager.AllChangeAware

So, I try to track the execution of the default isAffected() method:
    @Override
	public boolean isAffected(Collection<Delta> deltas, IResourceDescription candidate, IResourceDescriptions context) {
        Set<URI> outgoingReferences = descriptionUtils.collectOutgoingReferences(candidate);
        if (!outgoingReferences.isEmpty()) {
	        for (IResourceDescription.Delta delta : deltas)
	            if (hasChanges(delta, candidate) && outgoingReferences.contains(delta.getUri()))
	                return true;
        }
        // this is a tradeoff - we could either check whether a given delta uri is contained
        // in a reachable container and check for intersecting names afterwards, or we can do
        // the other way round
        // unfortunately there is no way to decide reliably which algorithm scales better
        // note that this method is called for each description so we have something like a 
        // number of deltas x number of resources which is not really nice
        List<IContainer> containers = null;
        Collection<QualifiedName> importedNames = getImportedNames(candidate);
        for (IResourceDescription.Delta delta : deltas) {
			if (hasChanges(delta, candidate)) {
				// not a java resource - delta's resource should be contained in a visible container
				// as long as we did not delete the resource
				URI uri = delta.getUri();
				if ((uri.isPlatform() || uri.isArchive()) && delta.getNew() != null) { 
					if (containers == null)
						containers = containerManager.getVisibleContainers(candidate, context);
					boolean descriptionIsContained = false;
					for(int i = 0; i < containers.size() && !descriptionIsContained; i++) {
						descriptionIsContained = containers.get(i).hasResourceDescription(uri);
					}
					if (!descriptionIsContained)
						return false;
				}
				if (isAffected(importedNames, delta.getNew()) || isAffected(importedNames, delta.getOld())) {
					return true;
				}
			}
        }
        return false;
    }


What I found is that the two occurrences of the hasChanges(delta, candidate) inside the conditionals of the above codes always return false for our test codes. So, the default
isAffected(Collection<Delta> deltas, IResourceDescription candidate, IResourceDescriptions context) always return a false, which prevent the builder to reparse the affected resource. And eventually causes the problem that we have here.

So, for the default hasChanges() method; instead checking whether the file in-edit is changed using hasChanges(delta, candidate), might it be more correct to check whether the file in edit is reparsed and its eResource is recreated? Since hasChanges() eventually calls DefaultResourceDescriptionDelta.haveEObjectDescriptionsChanged(), and it only compares the changes of EObjectDescriptions of the old/new versoin of an eResource, but not the reparse and re-creation of the EObjects for the resource in edit?

Any comments or suggestions?

Yu-Jiu
Re: dangling reference with models from different files [message #1707454 is a reply to message #1707446] Fri, 04 September 2015 19:06 Go to previous messageGo to next message
Christian Dietrich is currently offline Christian DietrichFriend
Messages: 14665
Registered: July 2009
Senior Member
this is why i implement IResourceDescription.Manager.AllChangeAware

you could try to modify hasChanged as well.
the code is an optimization and trys to reparse as few as possible.


Twitter : @chrdietrich
Blog : https://www.dietrich-it.de
Re: dangling reference with models from different files [message #1716476 is a reply to message #1707454] Fri, 04 December 2015 09:35 Go to previous message
Stefan Scheiber is currently offline Stefan ScheiberFriend
Messages: 18
Registered: February 2015
Junior Member
Thank you. After replacing the DefaultResourceDescriptionManager as instructed it works.
Previous Topic:Change Quickfix order
Next Topic:Getting started
Goto Forum:
  


Current Time: Fri Apr 26 08:08:21 GMT 2024

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

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

Back to the top