For some reasons my JvmModelInferrer
needs to search for all elements of a special type which fulfill a criterion. These elements are necessary to infer the model completely. But all these elements can be spread over all source code files of the project. More precise: There is an element which introduces a class and several elements which modify this class. The grammar for this looks like this (simplified to a minimum depth):
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] '{'
// ...
'}';
If I now infer the jvm model for a class pkg.A
, I need to find all ModifiesUnit
units which reference pkg.A
to generate this class.
This is more or less the question: How can I find all elements referencing pkg.A
? I found a soultion, but I think it is very inperformant and maybe there is any API which does i for me much more efficient.
class DeltaJJvmModelInferrer extends 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
}
]
}
Thanks, Christian Dietrich! Your idea works very good.
My solution for a fast, specialised reverse reference lookup looks like this:
I extended the XbaseResourceDescriptionStrategy
to add custom data to the index. The custom data is a key/value pair which has 'ModifiesUnit'
as key and the qualified name of the referenced class (qnp.getFullyQualifiedName(mu.unit)
) as value:
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
}
}
I created an index wrapper class which currently only provides a method which returns a list of all ModifiesUnit
s which modify a given class. It uses the qualified name to identify the modifies units I want to have:
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
}
}
The only problem is, that I have to unload every resource and load it again. Otherwise the content of any resource is not in the same state if they were edited since the last Eclipse start-up.
Accessing all ModifiesUnit
s which modify a certain class is now that simple: val modifiesUnits = index.allModifyUnitsForCi(cd)
.