Blog
Navigating/Querying EMF-Models using XPath
To make the new e4 way of building a complete model from small model pieces - named fragments - more flexible in 4.1, I've been developing a JXPath extension which works ontop of the reflective EMF-API. I know that there's already the possibility to query models using OCL but there are many more people familiar with XPath than the sometimes very cryptic OCL syntax. Some code samples might make clear why I think querying and navigating through EMF-Models using XPath is a very useful thing. As an example model I'm using the Library-Model which is well known to most people who've worked with EMF.
An instance of the model would probably look like this:
The XML-Source helps probably to understand the references:
<?xml version="1.0" encoding="ASCII"?>
<extlib:Library xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:extlib="http:///org/eclipse/emf/examples/library/extlibrary.ecore/1.0.0" xsi:schemaLocation="http:///org/eclipse/emf/examples/library/extlibrary.ecore/1.0.0 ../org.eclipse.emf.examples.library/model/extlibrary.ecore">
<stock xsi:type="extlib:Book" borrowers="//@borrowers.1" title="Mystery Book 1" author="//@writers.0"/>
<stock xsi:type="extlib:Book" borrowers="//@borrowers.0" title="Sience Book 1" category="ScienceFiction" author="//@writers.0"/>
<stock xsi:type="extlib:Book" borrowers="//@borrowers.1" title="Mystery Book 2" author="//@writers.1"/>
<stock xsi:type="extlib:Book" borrowers="//@borrowers.0" title="Sience Book 2" category="ScienceFiction" author="//@writers.1"/>
<writers address="Hometown" firstName="Tom" lastName="Schindl" books="//@stock.0 //@stock.1"/>
<writers address="Homecity" firstName="Boris" lastName="Bokowski" books="//@stock.2 //@stock.3"/>
<borrowers address="Hometown" firstName="Paul" lastName="Webster" borrowed="//@stock.1 //@stock.3"/>
<borrowers address="Homecity" firstName="Remy" lastName="Suen" borrowed="//@stock.0 //@stock.2"/>
</extlib:Library>
Now let's try to answer some questions:
- Find all "Mystery Books"
- Authors of "Mystery Books"
- Find all writers and borrowers in "Hometown"
- Find all borrowers "Mystery books"
The XPath-Code one can use with the new support is like this.
Load the model and setup a context for the XPath-Query
public class Application implements IApplication { public Object start(IApplicationContext context) throws Exception { ResourceSet resourceSet = new ResourceSetImpl(); resourceSet.getResourceFactoryRegistry() .getExtensionToFactoryMap() .put(Resource.Factory.Registry.DEFAULT_EXTENSION,new XMIResourceFactoryImpl()); URI uri = URI.createPlatformPluginURI("/testxpath/Library.xmi",true); Resource resource = resourceSet.getResource(uri, true); Library l = (Library) resource.getContents().get(0); XPathContextFactory<EObject> f = EcoreXPathContextFactory.newInstance(); XPathContext xPathContext = f.newContext(l); // Execute the XPaths } }Find all "Mystery Books"
{ System.out.println("Mystery Books:"); Iterator<Book> it = xPathContext.iterate("/books[category='Mystery']"); while( it.hasNext() ) { System.out.println(" " + it.next().getTitle()); } }Authors of "Mystery Books"
{ System.out.println("Mystery Book Authors:"); Iterator<Writer> it = xPathContext.iterate("/books[category='Mystery']/author"); while( it.hasNext() ) { Writer w = it.next(); System.out.println(" " + w.getFirstName() + "," + w.getLastName()); } }Find all writers and borrowers in "Hometown"
{ System.out.println("Borrower/Writer in Hometown:"); Iterator<Person> it = xPathContext.iterate( "/borrowers[address='Hometown']|/writers[address='Hometown']" ); while( it.hasNext() ) { Person b = it.next(); System.out.println(" " + b.getFirstName() + "," + b.getLastName()); } }Find all borrowers "Mystery books"
{ System.out.println("Borrower of Mystery books:"); Iterator<Borrower> it = xPathContext.iterate( "/borrowers[borrowed/category='Mystery']"); while( it.hasNext() ) { Borrower b = it.next(); System.out.println(" " + b.getFirstName() + "," + b.getLastName()); } }
Executing the code leads to the following output:
Mystery Books:
Mystery Book 1
Mystery Book 2
Mystery Book Authors:
Tom,Schindl
Boris,Bokowski
Borrower/Writer in Hometown:
Paul,Webster
Tom,Schindl
Borrower of Mystery books:
Remy,Suen
I think the above shows how easy it is to navigate/query an EMF-Model-Instance using JXPath and using this new EMF-extension. I hope we'll manage to integrate this support into one of the next Eclipse 4.1 I-builds until then you can access the source from the e4-cvs-repository. I'm also thinking about moving the code at some point to EMF directly because there's no dependency on e4 and such an implementation could be of use for others as well.