|
|
|
Re: dangling reference with models from different files [message #1707339 is a reply to message #1707287] |
Thu, 03 September 2015 20:09 |
YU WANG 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 #1707346 is a reply to message #1707343] |
Thu, 03 September 2015 20:34 |
|
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 |
YU WANG 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
|
|
|
|
|
Powered by
FUDForum. Page generated in 0.03703 seconds