【问题标题】:Inverse reference lookup反向参考查找
【发布时间】:2015-03-19 00:30:59
【问题描述】:

出于某些原因,我的JvmModelInferrer 需要 搜索满足条件的特殊类型的所有元素。这些元素是完全推断模型所必需的。但是所有这些元素都可以分布在项目的所有源代码文件中。更准确地说:有一个元素引入了一个类和几个元素修改了这个类。这个语法看起来像这样(简化到最小深度):

DeltaAction:
    AddsUnit | ModifiesUnit | RemovesUnit;

AddsUnit:
    {AddsUnit} 'adds' '{' unit=JavaCompilationUnit? '}';

JavaCompilationUnit:
    ('package' name=QualifiedName EOL)?
    importSection=XImportSection?
    // ...
    typeDeclarations=ClassOrInterface;

ClassOrInterface:
    ClassDeclaration /* | ... */;

ClassDeclaration:
    'class' name=QualifiedName
    // ...
    ;

ModifiesUnit:
    'modifies' unit=[ClassOrInterface|QualifiedName] '{'
    // ...
    '}';

如果我现在推断类pkg.A 的jvm 模型,我需要找到所有引用pkg.AModifiesUnit 单元来生成这个类。

这或多或少是一个问题:如何找到所有引用pkg.A 的元素?我找到了一个灵魂,但我认为它的性能非常低下,也许有任何 API 对我来说效率更高。

类 DeltaJJvmModelInferrer 扩展 AbstractModelInferrer {

@Inject ResourceDescriptionsProvider descsProvider
@Inject ResourceSet set
@Inject IQualifiedNameProvider qnameProvider

def dispatch void infer(DeltaJUnit unit, IJvmDeclaredTypeAcceptor acceptor, boolean isPreIndexingPhase) {
    descsProvider.createResourceDescriptions.allResourceDescriptions.forEach [ rd |
            val res = set.getResource(rd.URI, true)
            res.unload
            res.load(null)
            EcoreUtil2.resolveAll(res)
        ]
        try {
            set.allContents.filter(typeof(ModifiesUnit)).filter [ mu |
                qnameProvider.getFullyQualifiedName(mu.unit).equals(qnameProvider.getFullyQualifiedName(cd))
            ].forEach [ mu |
                // Do the stuff I need to do!
            ]
        } catch (Exception e) {
            return
        }
    ]
}

【问题讨论】:

  • 用想要的名称询问所有 ModifiesUnit 的索引然后看看?
  • 看看索引中的参考说明
  • 我还没有使用索引。我明天看看。有没有关于它的使用的文档?
  • descsProvider.createResourceDescriptions 是索引 - 不,没有文档 - 但您可以向 iresourcedescription 询问信息
  • 另一种可能性是将信息直接存储在索引中(修改 DefaultResourceDEscriptionStrategy 并将信息存储在用户数据映射中) - 使用 nodemodelutils 检索信息,因为模型未链接)

标签: java xtext emf xtend


【解决方案1】:

谢谢,克里斯蒂安·迪特里希!你的想法很有效。

我的快速、专门的反向引用查找解决方案如下所示:

  1. 我扩展了XbaseResourceDescriptionStrategy 以将自定义数据添加到索引中。自定义数据是一个键/值对,以 'ModifiesUnit' 为键,引用类的限定名称 (qnp.getFullyQualifiedName(mu.unit)) 作为值:

    class DeltaJResourceDescriptionStrategy extends XbaseResourceDescriptionStrategy {
    
        public static val TYPE = 'ModifiesUnit'
    
        override def createEObjectDescriptions(EObject eObject, IAcceptor<IEObjectDescription> acceptor) {
            var custom = true
            try {
                if (eObject instanceof ModifiesUnit) {
                    if (!eObject.eIsProxy) {
                        val qname = qnp.getFullyQualifiedName(eObject.unit)
                        acceptor.accept(EObjectDescription.create(qname, eObject, eObject.createModifiesUnitUserData))
                    }
                }
            } catch (Exception e) {
                custom = false
            }
            super.createEObjectDescriptions(eObject, acceptor) && custom
        }
    
        def createModifiesUnitUserData(ModifiesUnit mu) {
            val map = newHashMap
            map.put(TYPE, qualifiedNameProvider.getFullyQualifiedName(mu.unit).toString)
            map
        }
    }
    
  2. 我创建了一个索引包装类,它目前只提供一个返回所有修改给定类的ModifiesUnits 列表的方法。它使用限定名称来标识我想要的修改单元:

    class DeltaJIndex {
    
        @Inject extension ResourceDescriptionsProvider
        @Inject extension QualifiedNameProvider
        @Inject extension ResourceSet
    
        def getAllResourceDescriptions() {
            createResourceDescriptions.allResourceDescriptions
        }
    
        def getAllModifyUnitsOf(ClassOrInterface ci) {
            val Set<ModifiesUnit> units = newHashSet
            val Set<Resource> resources = newHashSet
            val ciQn = qnProvider.getFullyQualifiedName(ci).toString
    
            rdProvider.getResourceDescriptions(ci.eResource).allResourceDescriptions.forEach [ list |
                list.exportedObjects.forEach [ object |
                    if (object.userDataKeys.contains(TYPE) && object.getUserData(TYPE) == ciQn) {
                        val res = set.getResource(object.EObjectURI, true)
                        if (!resources.contains(res)) {
                            res.unload
                            res.load(null)
                            resources.add(res)
                        }
                        units.add(res.getEObject(object.EObjectURI.fragment) as ModifiesUnit)
                    }
                ]
            ]
            units
        }
    }
    

    唯一的问题是,我必须卸载所有资源并重新加载。否则,任何资源的内容如果自上次 Eclipse 启动后被编辑过,它们的状态就会不同。

  3. 现在访问所有修改某个类的ModifiesUnits 就是这么简单:val modifiesUnits = index.allModifyUnitsForCi(cd)

【讨论】:

  • 嗯,我有一个新问题...加载的资源不会与我对ModfifiesUnits 执行的更改同步...该列表包含所有资源,它们在 Eclipse 之后直接具有的状态已开始。
  • “加载的资源”是什么意思
  • 顺便说一句,你应该使用 org.eclipse.xtext.resource.impl.ResourceDescriptionsProvider.getResourceDescriptions(Resource)
  • 好的,我现在使用您提供的方法。我现在的问题如下:
  • 我已经解决了我的问题。看看我的回答,我已经更新了。
猜你喜欢
  • 2021-02-25
  • 1970-01-01
  • 1970-01-01
  • 2017-12-01
  • 2018-08-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多