Sorry it took me so long to respond. I tried Christians suggestion, was not very satisfied and than priorities shifted. Now I'll have another go at the problem and in order to document for others (and to clear my head) I'll write down what I did so far since it was not all that straight forward and required a fair amount of experimentation.
I will not post full classes but only the relevant parts. Feel free to ask for more detail if you need it.
My Syntax-Definition now looks like this:
Model:
stakeholders+=StakeholderDecl*
requirements+=Requirement*;
Requirement:
'As a' stakeholder=[Stakeholder] 'I want' want=('everything' | 'cookies' | 'results')
;
StakeholderDecl returns Stakeholder :
'Stakeholder' Stakeholder
;
Stakeholder:
name=ID
;
Let it be noted that everything below needed to to be done in the .ui
package.
First I created StakeholdersProvider.xtend
:
class StakeholdersProvider extends AbstractResourceDescription {
// this is the dummy for an "external source". Just raw data.
val nameList = newArrayList( "buddy", "boss" )
val cache = nameList.map[it.toDescription]
private val uri = org.eclipse.emf.common.util.URI.createPlatformResourceURI("neverland", true)
def public List<IEObjectDescription> loadAdditionalStakeholders() {
cache
}
def private IEObjectDescription toDescription(String name) {
ExternalFactoryImpl.init()
val ExternalFactory factory = new ExternalFactoryImpl()
val Stakeholder obj = factory.createStakeholder as StakeholderImpl
obj.setName(name)
new StakeholderDescription(name, obj, uri)
}
. . .
override getURI() {
uri
}
def public boolean isProvided( EObject object ) {
if( object.eClass.classifierID != ExternalPackageImpl.STAKEHOLDER ) {
false
}
else {
val stakeholder = object as Stakeholder
nameList.exists[it == stakeholder.name]
}
}
}
note that the provider is also a resourceDescription and its uri of course is nonsense.
With this provider I wrote a ScopeWrapper.xtend
:
class ScopeWrapper implements IScope {
private var IScope scope;
private var StakeholdersProvider provider
new( IScope scopeParam, StakeholdersProvider providerParam ) {
scope=scopeParam
provider = providerParam
}
override getAllElements() {
val elements = scope.allElements.toList
val ret = provider.loadAdditionalStakeholders()
ret.addAll(elements)
ret
}
override getSingleElement(QualifiedName name) {
allElements.filter[it.name == name].head
}
. . .
}
and ResourceDescriptionWrapper.xtend
class ResourceDescriptionsWrapper implements IResourceDescriptions {
private StakeholdersProvider provider;
private IResourceDescriptions descriptions;
new(IResourceDescriptions descriptionsParam, StakeholdersProvider providerParam) {
descriptions = descriptionsParam
provider = providerParam
}
override getAllResourceDescriptions() {
val resources = descriptions.allResourceDescriptions.toList
resources.add(provider)
resources
}
override getResourceDescription(URI uri) {
if( uri == provider.URI ) provider
else descriptions.getResourceDescription(uri)
}
override getExportedObjects() {
val descriptions = descriptions.exportedObjects.toList
descriptions.addAll(provider.exportedObjects)
descriptions
}
. . . some overrides for getExportedObjects-functions
}
all of this is wired together MyGlobalScopeProvider.xtend
class MyGlobalScopeProvider extends TypesAwareDefaultGlobalScopeProvider {
val provider = new StakeholdersProvider()
override getScope(Resource context, EReference reference, Predicate<IEObjectDescription> filter) {
val scope = super.getScope(context, reference, filter)
return new ScopeWrapper(scope, provider)
}
override public IResourceDescriptions getResourceDescriptions(Resource resource) {
val superDescr = super.getResourceDescriptions(resource)
return new ResourceDescriptionsWrapper(superDescr, provider)
}
}
which is registered in MyDslUiModule.java
public Class<? extends IGlobalScopeProvider> bindIGlobalScopeProvider() {
return MyGlobalScopeProvider.class;
}
So far so good. I now get boss
and buddy
suggested as stakeholders. However when I use one of those 2 I get an error in the editor complaining about a dangling reference and an error logging in the console that a stakeholder cannot be exported as the target is not contained in a resource
. Figuring those 2 might are related I tried to fix the error logging, created MyresourceDescriptionStrategy.xtend
class MyResourcesDescriptionStrategy extends DefaultResourceDescriptionStrategy {
val provider = new StakeholdersProvider()
override isResolvedAndExternal(EObject from, EObject to) {
if (provider.isProvided(to)) {
// The object is a stakeholder that was originally provided by
// our StakeholdersProvider. So we mark it as resolved.
true
} else {
super.isResolvedAndExternal(from, to)
}
}
}
and also wire it in the UiModule:
public Class<? extends IDefaultResourceDescriptionStrategy> bindDefaultResourceDescriptionStrategy() {
return MyResourcesDescriptionStrategy.class;
}
This fixes the logging error but the "dangling reference" problem remains. I searched for solutions for this and the most prominent result suggests that defining a IResourceServiceProvider
would have been the best way to solve my problem in the first place.
I'll spend a bit more time on the current approach and than try it with a ResourceProvider.
EDIT: I got the "dangling reference" problem fixed. The loadAdditionalStakeholders()
function in StakeholdersProvider.xtend
now looks like this:
override loadAdditionalStakeholders() {
val injector = Guice.createInjector(new ExternalRuntimeModule());
val rs = injector.getInstance(ResourceSet)
val resource = rs.createResource(uri)
nameList.map[it.toDescription(resource)]
}
def private IEObjectDescription toDescription(String name, Resource resource) {
ExternalFactoryImpl.init()
val ExternalFactory factory = new ExternalFactoryImpl()
val Stakeholder obj = factory.createStakeholder as StakeholderImpl
obj.setName(name)
// not sure why or how but when adding the obj to the resource, the
// the resource will be set in obj . . . thus no more dangling ref
resource.contents += obj
new StakeholderDescription(name, obj, uri)
}