0

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
        }
    ]
}
Joko
  • 600
  • 3
  • 15
  • what avout asking the index for all ModifiesUnit with the wanted name and then have a look? – Christian Dietrich Jan 20 '15 at 17:32
  • and have a look at the reference descriptions in the index – Christian Dietrich Jan 20 '15 at 20:00
  • I haven't used the index yet. I will have a look at it tomorrow. Is there any documentation about its usage? – Joko Jan 20 '15 at 20:32
  • descsProvider.createResourceDescriptions is the index - an no there is no docs on that - but you can ask a iresourcedescription for information – Christian Dietrich Jan 20 '15 at 20:58
  • the other possibility is to store the information directly in the index (modify DefaultResourceDEscriptionStrategy and store the information in the user data map) - use the nodemodelutils to retrieve the information since the model is not linked then) – Christian Dietrich Jan 21 '15 at 05:31

1 Answers1

0

Thanks, Christian Dietrich! Your idea works very good.

My solution for a fast, specialised reverse reference lookup looks like this:

  1. 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
        }
    }
    
  2. I created an index wrapper class which currently only provides a method which returns a list of all ModifiesUnits 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.

  3. Accessing all ModifiesUnits which modify a certain class is now that simple: val modifiesUnits = index.allModifyUnitsForCi(cd).

Joko
  • 600
  • 3
  • 15