XText交叉引用非DSL资源

时间:2015-03-31 10:03:39

标签: xtext

请考虑这个最小的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的一部分。

我非常高兴能够大致了解需要做些什么。

1 个答案:

答案 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;
}

到目前为止一切顺利。我现在将bossbuddy建议为利益相关者。但是,当我使用其中一个时,我在编辑器中收到一个错误,抱怨悬挂引用并在控制台中记录了一个利益相关者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)
}