XText 对非 DSL 资源的交叉引用
XText cross-reference to an non-DSL resource
请考虑这个最小的 Xtext 语法。
Model:
"As a" stackeholder=Stakeholder "I want" want=Want;
Stakeholder:
'client' | 'developer' | 'manager';
Want:
'everything' | 'cookies' | 'fame';
现在我需要做的是将利益相关者的定义(让我们忘记 want
)到 SOME 外部数据源。这个 "external data source" 可能是一个 CSV 文件,可能是一个数据库,也可能是一个网络服务。但我不太可能是某个 Xtext 文件或带有 EMF 模型。但我仍然想交叉引用它,就像您可以在 DSL 中交叉引用 java 类型一样。
抛开手动解析和缓存(出于性能考虑)等问题:这是否可行?
我深入研究了范围和资源提供者的主题,但我发现的所有内容都要求外部源至少是另一个 DSL 的一部分。
我很乐意大致概述需要完成的工作。
抱歉这么久才回复。我尝试了基督徒的建议,不是很满意,然后优先事项发生了变化。现在我将再次解决这个问题,为了为其他人记录(并清醒我的头脑),我将写下我到目前为止所做的事情,因为这并不是那么简单,需要大量的实验。
我不会 post 完整 类 而是只包含相关部分。如果需要,请随时询问更多详细信息。
我的语法定义现在看起来像这样:
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]
}
}
}
注意provider也是一个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
在控制台中记录的错误。弄清楚这两个可能是相关的我试图修复错误日志记录,创建 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;
}
这修复了日志记录错误,但 "dangling reference" 问题仍然存在。我为此搜索了解决方案,most prominent result 建议首先定义 IResourceServiceProvider
是解决我的问题的最佳方法。
我将在当前方法上花费更多时间,而不是使用 ResourceProvider 进行尝试。
编辑:我解决了 "dangling reference" 问题。 StakeholdersProvider.xtend
中的 loadAdditionalStakeholders()
函数现在看起来像这样:
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)
}
请考虑这个最小的 Xtext 语法。
Model:
"As a" stackeholder=Stakeholder "I want" want=Want;
Stakeholder:
'client' | 'developer' | 'manager';
Want:
'everything' | 'cookies' | 'fame';
现在我需要做的是将利益相关者的定义(让我们忘记 want
)到 SOME 外部数据源。这个 "external data source" 可能是一个 CSV 文件,可能是一个数据库,也可能是一个网络服务。但我不太可能是某个 Xtext 文件或带有 EMF 模型。但我仍然想交叉引用它,就像您可以在 DSL 中交叉引用 java 类型一样。
抛开手动解析和缓存(出于性能考虑)等问题:这是否可行?
我深入研究了范围和资源提供者的主题,但我发现的所有内容都要求外部源至少是另一个 DSL 的一部分。
我很乐意大致概述需要完成的工作。
抱歉这么久才回复。我尝试了基督徒的建议,不是很满意,然后优先事项发生了变化。现在我将再次解决这个问题,为了为其他人记录(并清醒我的头脑),我将写下我到目前为止所做的事情,因为这并不是那么简单,需要大量的实验。
我不会 post 完整 类 而是只包含相关部分。如果需要,请随时询问更多详细信息。
我的语法定义现在看起来像这样:
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]
}
}
}
注意provider也是一个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
在控制台中记录的错误。弄清楚这两个可能是相关的我试图修复错误日志记录,创建 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;
}
这修复了日志记录错误,但 "dangling reference" 问题仍然存在。我为此搜索了解决方案,most prominent result 建议首先定义 IResourceServiceProvider
是解决我的问题的最佳方法。
我将在当前方法上花费更多时间,而不是使用 ResourceProvider 进行尝试。
编辑:我解决了 "dangling reference" 问题。 StakeholdersProvider.xtend
中的 loadAdditionalStakeholders()
函数现在看起来像这样:
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)
}