反向引用查找

Inverse reference lookup

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

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] '{'
    // ...
    '}';

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

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

class 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
        }
    ]
}

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

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

  1. 我扩展了 XbaseResourceDescriptionStrategy 以将自定义数据添加到索引中。自定义数据是一个 key/value 对,其中 'ModifiesUnit' 作为键,引用的 class (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. 我创建了一个索引包装器 class,它目前只提供一种方法,该方法 returns 修改给定 [=39= 的所有 ModifiesUnit 的列表].它使用限定名称来标识我想要的修改单位:

    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. 访问所有修改某个 class 的 ModifiesUnit 现在就这么简单:val modifiesUnits = index.allModifyUnitsForCi(cd).