Enriching Xtext Index API

In Eclipse, it is quiet common to use the Open Type (for Java types) and Open Resource (for files) selection dialogs. The types are indexed by the JDT indexing, and the files by the Eclipse platform indexing mechanism. Xtext supports indexing of DSL elements and provides the Open Model Element (Ctrl + Shift + F3) dialog to quickly open the indexed elements.

Xtext Index (implemented by IResourceDescriptions) is a data structure that holds information/metadata about all the EMF Resources and the contained EObjects. The metadata of a Resource is stored using IResourceDescription and of EObject using IEObjectDescription. The later provides the following details - Simple name, Qualified name, EObject URI, EClass, additional user data.

The index is global, all the indexed objects are globally accessible/referenceable. Visibility is handled using IContainer. Objects having name (string attribute name) will land into the index. The name computation is delegated to the IQualifiedNameProvider and the decision to create an IEObjectDescription instance is delegated to the IDefaultResourceDescriptionStrategy. As a DSL author, you have the flexibility to decide what goes into the index by defining a custom ResourceDescriptionStrategy.

The type IResourceDescriptions provides a very basic API to get hold of the exported objects and the Resource descriptions. As your DSL grows or the number of DSL’s in your project increase, the API might look insufficient. In such situations, a “local index” can come handy. It basically wraps up the global index and provides a much richer API, abstracting the API consumer from the complexity of the actual index. You could have one local index per dsl.

import com.google.inject.Inject
import org.eclipse.emf.ecore.EClass
import org.eclipse.emf.ecore.EObject
import org.eclipse.xtext.resource.IContainer
import org.eclipse.xtext.resource.impl.ResourceDescriptionsProvider
import org.xtext.example.mydsl.myDsl.MyDslPackage
import org.eclipse.xtext.naming.QualifiedName

class EntityDslIndex {

	@Inject ResourceDescriptionsProvider indexProvider

	@Inject IContainer.Manager containerManager
	
	// Returns all EOD's with EClass matching Entity EClass
	def getAllEntities(EObject context) {
		val index = indexProvider.getResourceDescriptions(context.eResource)
		index.getExportedObjectsByType(MyDslPackage.Literals.ENTITY)
	}

	// Returns all EOD's from the current resource with EClass matching Entity EClass
	def getEntities(EObject context) {
		val resource = context.eResource
		val index = indexProvider.getResourceDescriptions(resource)
		val resourceDescription = index.getResourceDescription(resource.URI)
		resourceDescription.getExportedObjectsByType(MyDslPackage.Literals.ENTITY)
	}
	
	// Return EOD's with EClass matching Entity EClass and qualifiedName matching qName 
	def getEntity(EObject context, QualifiedName qName) {
		context.entities.filter[it.qualifiedName.equals(qName)]?.head
	}
	
	// Return EOD's from the current resource and all the visible resources
	def getAllVisibleEntities(EObject context) {
		context.getVisibleEObjectsByType(MyDslPackage.Literals.ENTITY)
	}

	private def getVisibleEObjectsByType(EObject context, EClass type) {
		context.visibleContainers.map[getExportedObjectsByType(type)].flatten
	}

	private def getVisibleContainers(EObject context) {
		val resource = context.eResource
		val index = indexProvider.getResourceDescriptions(resource)
		val resourceDescription = index.getResourceDescription(resource.URI)
		if (resourceDescription === null) {
			return emptyList
		}
		val visibleContainers = containerManager.getVisibleContainers(resourceDescription, index)
		visibleContainers
	}
}

The call to indexProvider.getResourceDescriptions returns a index instance based on the context in which the resource set is used. The context is indicated by the load options (resourceSet.getLoadOptions()).

The call to containerManager.getVisibleContainers on a IContainer.Manager returns the IContainer and all the visible containers. Following which, the call to getExportedObjects on all the visible containers returns the IEObjectDescription elements that are externally visible (globally exported) from a given resource.


PS - The code above is written using Eclipse Xtend
References - Documentation, Implementing Domain-Specific Languages with Xtext and Xtend