The index framework supports the indexing of instance models from arbitrary Ecore models and supports queries to retrieve information from these models. Queries can provide information as EObjects that reference a particular EObject, EObjects that are of a certain EClass among many other types of queries. This indexing part of the framework provides an extension point for clients to contribute indexable information to support indexing of their artifacts. By default, the framework indexes the type (EClass of any EObject) and all reference information (containment and non-containment reference information) for every EObject. The infrastructure can optionally index attributes for an EObject (EAttributes). The goal of this section is to enable clients to index their artifacts and query for information from these files.
The code samples indicated in this section are demonstrated in the example Index Example.
The indexing framework defines the com.ibm.xtools.emf.index.configurationEntries
extension point
for clients to configure entries for the indexing platform. To start with the framework will
process only files associated with the content types declared against this extension point.
The following example registers the special content type associated with files (.extlibrary) that are produced with the "Extended Library" metamodel. The plugins for the example metamodel are installed in the workspace as part of installing the index example.
<extension point="com.ibm.xtools.emf.index.configurationEntries"> <contentType class="com.ibm.xtools.emf.index.providers.XMIIndexProviderFactory" typeIds="org.eclipse.emf.examples.library.extendedLibrary"/> </extension>
The class attribute defined in the extension above specifies the
IIndexProviderFactory
instance used to create the IIndexProvider
instances. The index providers are
used for creating index entries for files associated with these content type ids. The framework
provides a factory class to handle xmi serialized files. This class is a SAX handler for parsing and creating index entries
for files created using the org.eclipse.emf.ecore.xmi.impl.XMIResourceImpl
class with ids.
The index entries for a given resource should include a ResourceEntry
for the resource level information and every eObject in the resource should have a corresponding
EObjectEntry
.
For creating index entries for XMI resources refer to IndexSAXXMIHandler
for usage of these classes.
//Create a resource entry: ResourceEntry resourceEntry = new ResourceEntry(resourceURI); //Add imports to the entry: resourceEntry.addImport(importingResourceURI); //Create an EObject entry: EObjectEntry eObjectEntry = new EObjectEntry(containerURI, eClassOfEObject); //Add reference value to the entry: eObjectEntry.addEReferenceValue(eReference, referencingEObjectURI); //Add attribute value to the entry: eObjectEntry.addEAttributeValue(eAttribute, value);
When the framework indexes a file it will record all of the EReferences, and the EClass of an EObject. Every resource will have its imports/exports recorded. The framework will not index EAttribute values unless specified in the index extension. Note that the framework can only index information that is persisted. EAttribute features that are derived and/or transient cannot be indexed. The following example registers some of the EAttributes "Extended Library" metamodel.
<extension point="com.ibm.xtools.emf.index.configurationEntries"> <structuralFeature features="Book.title,Writer.firstName,Writer.lastName,BookOnTape.title,Library.name" nsURI="http:///org/eclipse/emf/examples/library/extlibrary.ecore/1.0.0"/> </extension>
As a start we will construct a simple query using the
IIndexSearchManager
instance
(IIndexSearchManager
).
The scope for this query will be the entire workspace. We are querying
for any books that have a specific title.
IndexContext context = IndexContext.createWorkspaceContext(myResourceSet); try { Collection objects = IIndexSearchManager.INSTANCE.findEObjects(indexContext, "War and Peace", false, EXTLibraryPackage.eINSTANCE.getBook_Title(), EXTLibraryPackage.eINSTANCE.getBook(), monitor); } catch (IndexException e) { // handle exception } if (objects.isEmpty()) { print("No books with that name."); //$NON-NLS-1$ } else { print("Books are:"); //$NON-NLS-1$ Iterator iter = objects.iterator(); while (iter.hasNext()) { EObject eObject = (EObject) iter.next(); print(eObject); } }
Note that the returned objects could be proxies because our scope includes resources that are not loaded but are located in the workspace. It is for this reason that we do not attempt to access a book's information without first calling eIsProxy() on the eObject.
We could rewrite our query so that all of the returned objects will not be proxies so that we can print out the authors of the books. The side-effect will be that all of the required resources will be loaded into the resource set.
IndexContext context = IndexContext.createWorkspaceContext(myResourceSet); context.getOptions().put(IndexContext.RESOLVE_PROXIES, Boolean.TRUE); try { Collection objects = IIndexSearchManager.INSTANCE.findEObjects(indexContext, "War and Peace", false, EXTLibraryPackage.eINSTANCE.getBook_Title(), EXTLibraryPackage.eINSTANCE.getBook(), monitor); } catch (IndexException e) { // handle exception } if (objects.isEmpty()) { print("No books with that name."); //$NON-NLS-1$ } else { print("War and Peace authors are:"); //$NON-NLS-1$ Iterator iter = objects.iterator(); while (iter.hasNext()) { Book book = (Book) iter.next(); Writer author = book.getAuthor(); if (author != null && !author.eIsProxy()) { String lastName = null, firstName = null; lastName = author.getLastName(); firstName = author.getFirstName(); if (lastName != null && firstName != null) { print(lastName+", "+firstName); } } } }
It is important to note that we are only able to query for information based on the book's title and the writer's first and last name because we had included those EAttributes in our plugin's extension listed at the beginning of this section. When we queried for the author of the book we get this information automatically because it is an EReference.
One of the many useful facets of the indexing framework is that we could try to get more information about a book proxy without having to load its resource.
Let's try rewriting the last section of the above code to get the names of the authors of each book even if the book object is a proxy and remove our use of the "RESOLVE_PROXIES" option.
IndexContext context = IndexContext.createWorkspaceContext(myResourceSet); try { Collection objects = IIndexSearchManager.INSTANCE.findEObjects(indexContext, "War and Peace", false, EXTLibraryPackage.eINSTANCE.getBook_Title(), EXTLibraryPackage.eINSTANCE.getBook(), monitor); } catch (IndexException e) { // handle exception } if (objects.isEmpty()) { print("No books with that name."); //$NON-NLS-1$ } else { print("War and Peace authors are:"); //$NON-NLS-1$ Iterator iter = objects.iterator(); while (iter.hasNext()) { Book book = (Book)iter.next(); Writer author = null; if (book.eIsProxy()) { // Check the index for the author of this book proxy Collection results = IIndexSearchManager.INSTANCE.findReferencedObjects( context, book, EXTLibraryPackage.eINSTANCE.getBook_Author(), EXTLibraryPackage.eINSTANCE.getWriter(), monitor); // The result size will be one or zero because the author EReference // has a multiplicity of 0..1 if (results.size() == 1) { author = (Writer)results.iterator().next(); } } else { author = book.getAuthor(); } if (author != null) { String lastName = null, firstName = null; if (author.eIsProxy()) { // The author could be another proxy. We will use the // index search manager to find its first and last name // without loading any resources. lastName = (String) IIndexSearchManager.INSTANCE.findValue( context, author, EXTLibraryPackage.eINSTANCE.getAuthor_LastName(), monitor); firstName = (String) IIndexSearchManager.INSTANCE.findValue( context, author, EXTLibraryPackage.eINSTANCE.getAuthor_FirstName(), monitor); } else { lastName = author.getLastName(); firstName = author.getFirstName(); } if (lastName != null && firstName != null) { print(lastName+", "+firstName); } } } }
Let's try rewriting the last section of the above code
to get the names of the authors of each book even if the book object
is a proxy and use the "PROVIDE_INDEXED_DATA_FOR_PROXIES_AND_PARENTS" option.
Setting this option will populate the IProxyData
cache within the context
that contains attribute and reference information on proxies and their parents returned
as part of the result set.
IndexContext context = IndexContext.createWorkspaceContext(myResourceSet); context.getOptions().put( IndexContext.PROVIDE_INDEXED_DATA_FOR_PROXIES_AND_PARENTS, Boolean.TRUE); try { Collection objects = IIndexSearchManager.INSTANCE.findEObjects(indexContext, "War and Peace", false, EXTLibraryPackage.eINSTANCE.getBook_Title(), EXTLibraryPackage.eINSTANCE.getBook(), monitor); } catch (IndexException e) { // handle exception } if (objects.isEmpty()) { print("No books with that name."); //$NON-NLS-1$ } else { print("War and Peace authors are:"); //$NON-NLS-1$ Iterator iter = objects.iterator(); // Get the proxy data class IProxyData proxyData = context.getProxyData(); while (iter.hasNext()) { Book book = (Book)iter.next(); Writer author = null; if (book.eIsProxy()) { // Get the URI for the book URI uriBook = EcoreUtil.getURI(book); // Get the author of the book URI uriWriter = (URI) proxyData.getValue(uriBook, EXTLibraryPackage.eINSTANCE.getBook_Author()); if (uriWriter != null) { // get the names of the author String lastName = (String) proxyData.getValue(uriWriter, EXTLibraryPackage.eINSTANCE.getAuthor_LastName()); String firstName = (String) proxyData.getValue(uriWriter, EXTLibraryPackage.eINSTANCE.getAuthor_FirstName()); if (lastName != null && firstName != null) { print(lastName+", "+firstName); } } } else { author = book.getAuthor(); String lastName = author.getLastName(); String firstName = author.getFirstName(); if (lastName != null && firstName != null) { print(lastName+", "+firstName); } } } }
Some queries can be constructed to discover information regarding an entire resource, namely imports and exports. Imports occur whenever an EObject within a resource makes a reference to an EObject in another resource. An export occurs when a resource contains an EObject that is referenced by an EObject from another resource.
For example, whenever deleting a resource from the workspace it would be useful to know what other resources are importing this resource. The following example demonstrates formulating a query to find the exports.
IndexContext context = IndexContext.createWorkspaceContext(myResourceSet); Collection exports = IIndexSearchManager.INSTANCE.findExports(indexContext, resourceToDelete, monitor); if (exports.size() == 0) { // no exports } else { Iterator iter = exports.iterator(); while (iter.hasNext()) { Resource resource = (Resource)iter.next(); print("The importing resource is " + resource.getURI().toString()); } }
Queries may sometimes take some time to complete their search
through the index. In these cases the clients could execute the
query as a job so that it can be run in the background while the
user works on other things. To facilitate these workflows the
index framework has provided a special QueryJob
class.
//Create the job: QueryJob job = new QueryJob("Example Query") { protected Collection doRun(IProgressMonitor monitor) { // create the index context IndexContext context = IndexContext.createDefaultContext(new ResourceSetImpl()); // run the query Collection result = Collections.EMPTY_LIST; result = IIndexSearchManager.INSTANCE.findEObjects(context, EXTLibraryPackage.eINSTANCE. .getBookOnTape(), monitor); return result; }; }; // Schedule the job: job.schedule(); // Get the results: job.getResults();
The indexing framework supports wildcard matches for string EAttribute values. For example, we could search for books that start with the word "War" in the workspace. Note that the patterns used are not true regular expressions.
IndexContext context = IndexContext.createWorkspaceContext(myResourceSet); boolean ignoreCase = false; Collection warBooks = IIndexSearchManager.INSTANCE.findEObjects(indexContext, "War*", ignoreCase, EXTLibraryPackage.eINSTANCE.getBook_Title(), null, monitor);