[jboss-user] [JBoss Tools Development] - Eclipse File Content Types

Robert (Bob) Brodt do-not-reply at jboss.com
Mon Apr 19 14:50:52 EDT 2010

Robert (Bob) Brodt [http://community.jboss.org/people/bbrodt] modified the document:

"Eclipse File Content Types"

To view the document, visit: http://community.jboss.org/docs/DOC-15183

h1. The Big Picture
 http://community.jboss.org/servlet/JiveServlet/showImage/2580/soup.jpg  http://community.jboss.org/servlet/JiveServlet/downloadImage/2580/57-97/soup.jpg When you pick up a can of soup and the label reads “TOMATO SOUP”, you expect tomato soup to be inside, right? What if soup manufacturers all decided not to label their products and simply used generic containers with the label “SOUP”? One could argue that even if the soup cans were clearly labeled, who's to say that what's inside really is TOMATO SOUP? The only way to be absolutely certain is to open the can and peek inside!

Since the dawn of MS-DOS, file name extensions have been used, much like soup can labels, to identify file contents and, typically which application is associated with that file. Eclipse has been following this same tried-and-true philosophy to launch the proper editor or viewer plugin when you double-click on a resource in the eclipse Navigator. But with the proliferation of XML formats that describe everything from  http://graffitianalysis.com/gml/ graffiti to  http://xml.coverpages.org/geml.html DNA patterns, the file name extension “XML” has become somewhat ambiguous. OK, we know it's SOUP but what *kind* of SOUP?

That's essentially the idea behind the eclipse  http://help.eclipse.org/help32/index.jsp?topic=/org.eclipse.platform.doc.isv/guide/runtime_content_contributing.htm content-type extension point. When the eclipse workbench opens a folder resource for the first time, it looks at the folder's contained IFile resources and then all plugins that contribute to this extension point are allowed to “peek inside the can” to see if it contains a SOUP that they can consume. The extension point then provides an ID which is is associated with the IFile and cached by the workbench. If the name of the IFile changes, the workbench goes through the same content type checking again.

As you can imagine, the time it takes for the workbench to run through this process increases (roughly) with the number of plugins that contribute to content-type multiplied by the number of files in your projects. So, as a plugin developer you need to ensure that content type checking is as fast as possible: peek inside the can, take a quick sniff and move on; don't grab a spoon and try to take a taste!
h1. The Details
If you have been around eclipse long enough you probably already know that the SDK documentation was written by Captain Obvious and is of little help when you're trying figure out how to do something that should be so...simple. Case-in-point:
h2. Package org.eclipse.core.runtime.content
Provides core support for content types.
           http://help.eclipse.org/help32/topic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/core/runtime/content/package-summary.html?resultof=%22%49%43%6F%6E%74%65%6E%74%44%65%73%63%72%69%62%65%72%22%20%22%69%63%6F%6E%74%65%6E%74%64%65%73%63%72%69%62%22%20#package_description *Description*
   ||  %1,2% *Interface Summary* ||
| *IContentDescriber* (http://help.eclipse.org/help32/topic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/core/runtime/content/IContentDescriber.html) | Content describers describe contents. |
| *IContentDescription* (http://help.eclipse.org/help32/topic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/core/runtime/content/IContentDescription.html) | A description for content. |
| *IContentTypeManager* (http://help.eclipse.org/help32/topic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/core/runtime/content/IContentTypeManager.html) | Content type managers manage content types. |

   ||  %1,2% *Class Summary* ||
| *BinarySignatureDescriber* (http://help.eclipse.org/help32/topic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/core/runtime/content/BinarySignatureDescriber.html) | A describer for binary signatures. |
| *XMLRootElementContentDescriber* (http://help.eclipse.org/help32/topic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/core/runtime/content/XMLRootElementContentDescriber.html) | A content describer for XML root elements. |

Luckily there are a lot of folks out there doing amazing work with eclipse and publishing their experiences. I just wanted to give something back by describing the steps I took to add this feature to the eclipse  http://eclipse.org/bpel/ BPEL editor.
The “org.eclipse.ui.editors” extension point in your plugin.xml probably looks something like this:

There are a couple of things wrong with this if you intend to do content type checking: first, do not specify file extensions here since this defeats the purpose of the content-type extension point; second, do not specify an icon for your editor here either, since the workbench will slap this decorator on all files based on their file extension instead of their content. Instead, replace these attributes with the following:


The contentTypeBinding element references a type ID which is defined in the content-type extension point:

 name="BPEL Editor File"

I'm not going to repeat the eclipse reference documentation here, but I did want to make a few comments.

1. Notice the file-extensions attribute is specified here, and you will still need to provide at least one file name extension for your content type. As of eclipse 3.5.2  https://bugs.eclipse.org/bugs/show_bug.cgi?id=46297 file name extensions are still required – you can not, for example, create an XML file that has no file name extension and expect the XML editor to correctly recognize it based on content.
2. The eclipse framework already provides an XML root element content describer and there are lots of examples out there that demonstrate how to use it. Note however, that a lot fo these examples refer to the deprecated class XMLRootElementContentDescriber - you should use XMLRootElementContentDescriber2 defined in the org.eclipse.core.runtime.content package instead.
3. Whenever possible, provide a namespace for your root element. This will avoid possible conflicts with other plugins. For example, I discovered (after much gnashing of teeth) that JBoss Drools Flow editor files (*.rf files) also have a “process” root element.

The icon attribute in the org.eclipse.editors extension point should be removed and specified in an org.eclipse.ui.decorators extension point instead, like so:

       label="BPEL Resource Decorator"

This will ensure that your editor's files will always display the correct icon regardless of what the file name extension is, as long as it's one of the extensions declared by your content-type.

The last point I wanted to discuss was EMF support for content types. As far as I can tell this has been around since EMF 2.2.2 (possibly earlier). If your editor uses an EMF model, you also need to contribute an EMF content parser extension point:

 <extension point="org.eclipse.emf.ecore.content_parser">

The resource factory is then responsible for constructing the correct XMI parser for your EMF model.
Finally, don't forget to go through your editor code and replace all of the file name extension checking with content type checking! For example, stuff like this:

    IResource res;
    *if* (res.getType() == IResource.+FILE+) {
        *if* ("bpel".equalsIgnoreCase(((IFile)res).getFileExtension())) {
            // load it...

should be replaced with this:

     IResource res;
 *    if* (res.getType() == IResource.+FILE+) {
         IContentDescription desc = ((IFile)res).getContentDescription();
 *        if* (desc != *null*) {
             IContentType type = desc.getContentType();
 *            if* ("org.eclipse.bpel.contenttype".equals(type.getId())) {
                 // load it...
h1. Going Further
The XMLRootElementContentDescriber2 provided by eclipse should fill most of your content type checking needs. But, if you need to drill down deeper into the XML document, you may need to write your own custom content describer class. Keep in mind though that your implementation should only do the absolute bare minimum required to make its determination – for instance don't read the entire document using a DOM parser if you only need to check a couple of elements!

Techniques that work well are to use either an  http://www.xmlpull.org/ XML pull parser or use the SAX parser and throw exceptions to terminate parsing after the first element.

I've provided an example of my BPEL content describer using a SAX parser for your reading enjoyment:

*public* *class* BPELContentDescriber *implements* ITextContentDescriber {

*  private* *static* *final* String +WSBPEL_2_NAMESPACE+ = "http://docs.oasis-open.org/wsbpel/2.0/process/executable"; //$NON-NLS-1$
 *  private* *static* *final* String +ROOT_ELEMENT+ = "process"; //$NON-NLS-1$

 *  private* RootElementParser parser = *new* RootElementParser();

 *  public* BPELContentDescriber() {

  *public* *int* describe(Reader contents, IContentDescription description) *throws* IOException {
    *return* doDescribe(contents) == *null* ? +INVALID+ : +VALID+;

  *public* *int* describe(InputStream contents, IContentDescription description) *throws* IOException {
    *return* describe(*new* InputStreamReader(contents), description);

  *private* String doDescribe(Reader contents) *throws* IOException {
    InputSource source = *new* InputSource(contents);
    *try* {
    } *catch* (SAXException e) {
      *return* *null*;
    } *catch* (AcceptedException e) {
      *return* e.acceptedRootElement;
    } *catch* (RejectedException e) {
      *return* *null*;

    *return* *null*;

  *public* QualifiedName[] getSupportedOptions() {
    *return* *null*;

  *private* *class* RootElementParser *extends* SAXParser {
    *public* *void* startElement(QName qName, XMLAttributes attributes, Augmentations augmentations) *throws* XNIException {
      *super*.startElement(qName, attributes, augmentations);

      *if* (+ROOT_ELEMENT+.equals(qName.localpart)) {
        String namespace = fNamespaceContext.getURI(qName.prefix);
        *if* (namespace != *null* && +WSBPEL_2_NAMESPACE+.equals(namespace))
          *throw* *new* AcceptedException(qName.localpart);
          *throw* *new* RejectedException();
      } *else*
        *throw* *new* RejectedException();

  *private* *class* AcceptedException *extends* RuntimeException {
    *public* String acceptedRootElement;

    *public* AcceptedException(String acceptedRootElement) {
      *this*.acceptedRootElement = acceptedRootElement;

    *private* *static* *final* *long* +serialVersionUID+ = 1L;

  *private* *class* RejectedException *extends* RuntimeException {
    *private* *static* *final* *long* +serialVersionUID+ = 1L;
h1. References
 http://wiki.bioclipse.net/index.php?title=Contenttypes_and_describers Contenttypes and describers
Eclipse Tip: Define Custom Content Types to Identify Your Data Files (http://www.developer.com/java/other/article.php/3648736/Eclipse-Tip-Define-Custom-Content-Types-to-Identify-Your-Data-Files.htm)
Lessons from Behind The Curtain (http://cvalcarcel.wordpress.com/2009/10/25/writing-an-eclipse-plug-in-part-9-custom-project-defining-a-custom-file-type/)
EMF Bits (http://www.eclipticalsoftware.com/emf/#contenttype)
Eclipse Zone (http://www.eclipsezone.com/eclipse/forums/t107605.html)


Comment by going to Community

Create a new document in JBoss Tools Development at Community
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.jboss.org/pipermail/jboss-user/attachments/20100419/97cca3f3/attachment.html 

More information about the jboss-user mailing list