请考虑这个最小的Xtext语法。
Model:
"As a" stackeholder=Stakeholder "I want" want=Want;
Stakeholder:
'client' | 'developer' | 'manager';
Want:
'everything' | 'cookies' | 'fame';
现在我需要做的是将利益相关者的定义(让我们忘记want
)移到 SOME 外部数据源。此“外部数据源”可能是CSV文件,可能是数据库或Web服务。但我不太可能是某些Xtext文件或带有EMF模型。但我仍然希望交叉引用它,就像你可以在你的DSL中交叉引用java类型一样。
除了手动解析和缓存(出于性能)之外的问题:这是否可行?
我在范围和资源提供者的主题中挖了一点,但我发现的一切都要求外部源至少是另一个DSL的一部分。
我非常高兴能够大致了解需要做些什么。
答案 0 :(得分:2)
抱歉,我花了这么长时间回复。我试过基督徒的建议,不是很满意,而是优先考虑转移。现在我还有另外一个问题,为了记录其他人(并清楚我的头脑),我会记下我到目前为止所做的事情,因为它不是那么直接,需要大量的实验。
我不会发布完整课程,只会发布相关部分。如果需要,请随时询问更多细节。
我的语法定义现在看起来像这样:
Model:
stakeholders+=StakeholderDecl*
requirements+=Requirement*;
Requirement:
'As a' stakeholder=[Stakeholder] 'I want' want=('everything' | 'cookies' | 'results')
;
StakeholderDecl returns Stakeholder :
'Stakeholder' Stakeholder
;
Stakeholder:
name=ID
;
请注意,以下所有内容都需要在.ui
包中完成。
首先我创建了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]
}
}
}
请注意,提供程序也是一个resourceDescription,它的uri当然是无意义的。
有了这个提供者,我写了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
}
. . .
}
和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
}
所有这些都连接在一起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)
}
}
在MyDslUiModule.java中注册
public Class<? extends IGlobalScopeProvider> bindIGlobalScopeProvider() {
return MyGlobalScopeProvider.class;
}
到目前为止一切顺利。我现在将boss
和buddy
建议为利益相关者。但是,当我使用其中一个时,我在编辑器中收到一个错误,抱怨悬挂引用并在控制台中记录了一个利益相关者cannot be exported as the target is not contained in a resource
。确定那些2可能是相关的我试图修复错误记录,创建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)
}
}
}
并将其连接到UiModule:
public Class<? extends IDefaultResourceDescriptionStrategy> bindDefaultResourceDescriptionStrategy() {
return MyResourcesDescriptionStrategy.class;
}
这修复了日志记录错误,但仍存在“悬空引用”问题。我搜索了这方面的解决方案,most prominent result 表明定义IResourceServiceProvider
本来是解决问题的最佳方法。
我将花费更多时间在当前的方法上,而不是尝试使用ResourceProvider。
编辑:我修复了“悬空参考”问题。 loadAdditionalStakeholders()
中的StakeholdersProvider.xtend
功能现在看起来像这样:
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)
}