We had EMF generated model classes and a Util class with lot of helper methods. Both were exposed to clients as an API. Most of the methods in the Util classes were like:
//API - Util class
Book getBook(Writer writer, String title);
//Customer code
Util.getBook(daveSteinberg, "Eclipse Modeling Framework");
These methods would have been more appropriate in the Writer class itself rather than in a Util class, and it will be more natural to code like:
daveSteinberg.getBook("Eclipse Modeling Framework");
This tip is about how to add such methods in the generated code.
The first way is simple. Just edit the generated java code and add these methods. EMF is smart enough to identify this method and keeps it safe during regeneration of code. But if you were like me, who consider the generated Java files are as good as class files and don't want them checked into the repository, you can follow the second way.
In the eCore Editor for your model, right click the EClass and add a new child EOperation.
Go to the Properties view and specify the name, this will be the name of your method. The EType represents the return type of the method.
If your method has any parameters, then add EParameter children to the EOperation & specify their types and names.

To add the code, add an EAnnotation to the EOperation.
In the properties view, set the Source to "http://www.eclipse.org/emf/2002/GenModel".
Add a details entry to the EAnnotation.
In the properties view set the key to 'body' and value to the code that you want to be generated.
Reload your .genmodel from .ecore and generate the code:
May 5, 2008
Adding util methods to the generated EMF classes
Posted by
Prakash G.R.
at
10:26 PM
0
comments
Labels: eclipse, EMF, guidelines
Apr 24, 2008
Save hours of EMF coding ...
Questions:
- How would you perform a deep copy an EObject with all it references?
- How would you resolve all the proxies of an EObject?
- How would you find whether one EObject is a child of other (directly/indirectly)?
- How would you get the root containder of a given EObject?
These are common problems which you would face in your EMF code. Before you try to write the generic code using powerful EMF reflections, resist yourself. Somehow in between posting 5 millionth news group message and shooting 100k photographs (both the numbers are my guess, and true values should be more than that :-P ), Ed Merks finds time to do all that work for us. This nice solution, which is available in EMF itself: ECoreUtil.
Take a look at the other methods in the class. Will surely come handy sometimes.
Posted by
Prakash G.R.
at
3:27 PM
0
comments
Labels: EMF
Mar 15, 2008
How does EMF finds the right parser?
ResourceSet resourceSet = new ResourceSetImpl();
Resource resource = resourceSet.createResource(someUri);
The API can't get any simpler. But the implementation details behind this is not simple. Resources are generally loaded by Resource.Factory. Your ECore model would have generated an appropriate Factory. You can find it in the util package with the name {your model name}ResourceFactoryImpl. Lets take a journey from the ResourceSet to this Factory.
ResourceSetImpl class delegates the job of finding the right Factory to the Resource.Factory.Registry. The default implementation of the registry (ResourceFactoryRegistryImpl) tries to find the factory by:
* lookup in the protocol map for the uri
* lookup in the extension map for the uri
* lookup in the content type map for the uri
* lookup in the extension map for the default extension (*)
* lookup in the content type map for the default content type (*)
If the above lookups did not find a factory then the delegatedGetFactory() method is called.
All three maps (protocol, extension & content type) are by default empty in the ResourceFactoryRegistryImpl. Either you can subclass and initialize in your constructor or call the resourceFactory.get{map type}ToFactoryMap().put({key}, {value}) to add your entries.
Since we have not added any entries in the registry, the delegatedGetFactory() method is called. The ResourceSetImpl overrides that method (thru an anonymous subclass of ResourceFactoryRegistryImpl) and calls again the getFactory() method with the maps from Resource.Factory.Registry.INSTANCE. BTW, all three maps in that instance are filled with the details from the extension points:
org.eclipse.emf.ecore.protocol_parser
org.eclipse.emf.ecore.extension_parser
org.eclipse.emf.ecore.content_parser
You should be using these extensions to register the right parser (the generated ResourceFactoryImpl) for your resource.
Phew! If you think its done, you should look into the EMF code. All the above said stuff are optimized to the core. The ResourceSetImpl creates the ResourceFactoryRegistryImpl instance only when the getResourceFactoryRegistry() is called for the first time; all the above lookups are performed only when the map is not empty; and so on. EMF takes all the pain and finally we end up with:
ResourceSet resourceSet = new ResourceSetImpl();
Resource resource = resourceSet.createResource(someUri);
See, you got to love EMF. Or in general, Eclipse for making our life easier :-)
Related:
EMF Packing changes in 2.4
Using EMF with XML Catalog
Converting EMF Resource to Platform Resource (IFile)
Posted by
Prakash G.R.
at
9:18 PM
0
comments
Mar 11, 2008
Converting EMF Resource to Platform Resource (IFile)
Simple one:
EObject eObject = ...;
Resource eResource = eObject.eResource();
URI eUri = eResource.getURI();
if (eUri.isPlatformResource()) {
String platformString = eUri.toPlatformString(true);
return ResourcesPlugin.getWorkspace().getRoot().findMember(platformString);
}
return null;
Posted by
Prakash G.R.
at
1:53 PM
0
comments
Labels: EMF
Mar 6, 2008
Eclipse CVS
The CVS Repositories view in Eclipse allows you to paste a CVS URL in the "New Repository Location" Wizard. (If you are on 3.3, you can directly paste on the view without the Wizard) In case you want to browse thru the Eclipse CVS repository, here are the URLs you can copy and paste into the wizard.
Platform, SWT, JFace, Equinox, JDT, launcher, UI, update, search, debug, team, etc) are available here:
:pserver:anonymous@dev.eclipse.org:/cvsroot/eclipse
Mylyn, GEF, CDT, COBOL, VE:
:pserver:anonymous@dev.eclipse.org:/cvsroot/tools
AspectJ, Nebula, EPP:
:pserver:anonymous@dev.eclipse.org:/cvsroot/technology
EMF, GMF, MDT:
:pserver:anonymous@dev.eclipse.org:/cvsroot/modeling
WTP:
:pserver:anonymous@dev.eclipse.org:/cvsroot/webtools
Data Tools:
:pserver:anonymous@dev.eclipse.org:/cvsroot/datatools
If you want to browse thru the CVS, you can use the web based viewer also: http://dev.eclipse.org/viewcvs/
More info on connecting to Eclipse CVS: http://wiki.eclipse.org/index.php/CVS_Howto
Related:
Searching Eclipse Sources
Feb 19, 2008
EMF Packing changes in 2.4
EMF has changed the packaging in 2.4. You need to download an additional jar for docs and source for EMF and XSD. While this might be a good news for those who are bundling EMF in RCP apps, not for the early adopters of WTP. If you are an early adopter, then probably you may want to do vote for the all-in-one package for the milestone builds.
Related:
How does EMF finds the right parser?
Converting EMF Resource to Platform Resource (IFile)
EMF Packing changes in 2.4
Using EMF with XML Catalog
Feb 15, 2008
Using EMF with XML Catalog
When you try to load an XML document using EMF, you might have noticed that the XML Catalog is not respected and might throw parse exceptions. First I thought I had made a mistake in extending org.eclipse.wst.xml.core.catalogContributions and was debugging that. Only after a while I realized that the XML Catalog is contributed by WTP and EMF is not depending on that. So there is no way for EMF to know the existence of such catalog. I found a quick solution using some internal classes. I'm not sure whether its the best, but it works (at least for my requirement)
The idea is when EMF fails to resolve when loading an XML we should get the WTP Catalog and try to resolve with that:
loadOptions.put(XMLResource.OPTION_USE_PARSER_POOL, new XMLParserPoolImpl() {
@Override
public synchronized XMLDefaultHandler getDefaultHandler(XMLResource resource, XMLLoad xmlLoad, XMLHelper helper, Map options) {
return
new SAXXMLHandler(resource, helper, options)
{
@Override
public InputSource resolveEntity(String publicId, String systemId) throws SAXException {
InputSource result;
try {
result = super.resolveEntity(publicId, systemId);
}catch(SAXException e) {
try {
ICatalog catalog = XMLCorePlugin.getDefault().getDefaultXMLCatalog();
String resolvePublic = catalog.resolvePublic(publicId, systemId).substring(5);
InputStream inputStream = getURIConverter().createInputStream(URI.createFileURI(resolvePublic));
result = new InputSource(inputStream);
result.setPublicId(publicId);
result.setSystemId(systemId);
return result;
} catch (Exception exception) {
throw new SAXException(exception);
}
}
return result;
}
};
}
});
There are many cases where this code will fail (like if you had added the catalog entry by uri/systemId) and there might be a much better way of doing that without using any internal classes. Till I find that, I'm going to live with this hack.
