Friday August 12, 2005
Declarative MIME Type Resolvers, NetBeans IDE, and Shakespeare
So, there's this web service which searches through Shakespeare's plays for me. I send it a search string (e.g., "My kingdom for a horse!") and then the web service returns an XML string that contains the name of the play, the speaker, and the speech in which the search string is found. For example, this is what the returned XML string looks like:
<SPEECH> <PLAY>KING RICHARD III</PLAY> <SPEAKER>KING RICHARD III</SPEAKER> Slave, I have set my life upon a cast, And I will stand the hazard of the die: I think there be six Richmonds in the field; Five have I slain to-day instead of him. A horse! a horse! my kingdom for a horse! </SPEECH>As described here, it's not very hard to create a simple client in NetBeans IDE for receiving the returned XML string and printing it out to an XML file. However, sometimes the search string returns nothing. If for example, I search for "William Shakespeare", I won't get anything from the web service, because William Shakespeare never wrote himself into his plays. But, the web service doesn't really return nothing at all. Instead, I get this:
<MESSAGE>Speech not found.</MESSAGE>
But this is also written to an XML file, even though it's completely useless. I could handle that in the code, somehow, so that the XML in this case is not written to a file. However, maybe I don't want to do that. Maybe I do want to write it to a file. Let's say I name each file according to the search string I sent to the web service. And let's say that I do something that enables me to visually distinguish between the successful and unsuccessful returns. That would be pretty useful, because then, in one glance, I can see what's been successful and what hasn't. In fact, the result would be something like this:

In the above illustration, each returned XML string is printed out to an XML file that is named after the search string. (By the way, I didn't know that XML files are allowed to have spaces and punctuation marks in their names.) However, if the search string was found, the XML file is accompanied by an icon of William Shakespeare himself. If the search failed, Alfred E. Neuman is attached to the XML file instead. So now, in one glance, I can see which of my search strings were successful and which weren't. In the illustration above, you can see that not only was the search string "William Shakespeare" not found, but neither was the search string "hie thee to a nunnery", which is odd -- I'm sure there was somebody in at least one of Shakespeare's plays who was angrily advised to hie themselves to a nunnery... (By the way, if you like these icons, go here for more.)
How is this done? How do I distinguish between William Shakespeare and Alfred E. Neuman? Well, simply by using the New File Type wizard that's part of NetBeans IDE Dev. In the first panel of the New File Type wizard you are asked how your new file type should be recognized by the IDE. Of course, you can specify a specific file extension. But in this case, both the good and the bad returns end up in an XML file, i.e., both have the same file extension. So, instead, I distinguish between them by their root elements. As you can see at the start of this blog entry, successful returns have <SPEECH> as their root element, while unsuccessful returns have <MESSAGE>. So I create two separate modules -- each defining a new file type. I need to make one tweak after working through the New File Type wizard, because the wizard distinguishes between namespaces and not between the root elements themselves. (I probably should create two namespaces -- one when a successful return is printed to an XML file and another when a failed return is printed out.) The first module ("SpeechLoader") recognizes the successful returns, and gives them the William Shakespeare icon, while the second ("SpeechFailureLoader") recognizes failed search strings, and gives them the Alfred E. Neuman icon.
Everything happens in the Resolver.xml file, which is generated for you by the New File Type wizard. The Resolver.xml file is a declarative MIME type resolver, registered in the layer.xml file. This is the content for the successfully returned quotations (note that I tweaked things a bit because the New File Type wizard distinguishes, correctly, by namespace, although I want to distinguish by XML root element, which the New File Type wizard doesn't -- currently -- support):
<MIME-resolver>
<file>
<ext name="xml"/>
<resolver mime="text/x-speech+xml">
<xml-rule>
<element name="SPEECH"/>
</xml-rule>
</resolver>
</file>
</MIME-resolver>
And this is the Resolver.xml file for unsuccessful searches:
<MIME-resolver>
<file>
<ext name="xml"/>
<resolver mime="text/x-fail+xml">
<xml-rule>
<element name="MESSAGE"/>
</xml-rule>
</resolver>
</file>
</MIME-resolver>
In other words, two MIME types have been created by the New File Type wizard: text/x-speech+xml and text/x-fail+xml. It all happens in the first panel:

Note that the panel even forces you to comply with naming convention and provide a correctly formulated MIME type for XML files. (Wondering about these naming conventions? For example, what's that "+xml" all about? Go here to find out.) These MIME types are used in two other parts of the module that creates them:
- layer.xml. Specifies the actions available per MIME type. For example, in the "SpeechLoader" module, there is an entry that starts like this:
<folder name="Loaders"> <folder name="text"> <folder name="x-speech+xml"> <folder name="Actions">And then, all actions defined below that are available to the text/x-speech+xml MIME type.
- DataLoader.java. Adds the MIME type to the IDE and loads the actions from the related context. In the "SpeechLoader" module, there's this declaration at the top of the class:
public static final String REQUIRED_MIME = "text/x-speech+xml";
And in the "SpeechFailureLoader" module, there's this:public static final String REQUIRED_MIME = "text/x-fail+xml";
And then this is how the MIME type is added:
protected void initialize() { super.initialize(); getExtensions().addMimeType(REQUIRED_MIME); }And this is how the related actions are loaded:
protected String actionsContext() { return "Loaders/" + REQUIRED_MIME + "/Actions"; }
The difference between the Shakespeare files and the Alfred E. Neuman files is only in their MIME types. There are no other differences -- apart from that they don't share the same icon. And the New File Type wizard allows you to make that distinction -- it allows you to create new file types based on their MIME types. Once you've made that distinction, you can create actions that are specific to the file type -- for example, you might want to create a Visual Editor for working with the returned quotations, but make it available only for the successful returns. So you create an OpenInVisualEditor action that you'd make available to the Shakespeare type's right-click context menu and not to Alfred E. Neuman's right-click context menu. Or, if you want to make an action available in the Source Editor's right-click context menu, but only for Shakespeare files, you'd do this in the "SpeechLoader" module's layer.xml file:
<folder name="Editors">
<folder name="text">
<folder name="x-speech+xml">
<folder name="Popup">
<file name="MyAction.instance"/>
</folder>
</folder>
</folder>
</folder>
I think being able to make distinctions like this between XML files, i.e., between files that have the same file extension, is pretty cool. As a consequence, the Source Editor's tabs also show the icon related to the current file's type, as well as the name, which, since the name is the search string, is quite useful when working with the returned quotation:

So, okay, maybe this implementation of MIME type resolution is cute -- but is it useful? Not really for Shakespeare quotations because you're not actually going to edit Shakespeare, are you? But let's say that you have a web service that searches Wikipedia for you. After you've sent it your string, it returns an entry you'd like to edit, and then you go off-line and edit the entry. Then, once you're finished, possibly many hours or days later, you can go on-line again and send the updated entry back to Wikipedia. In this scenario, it would be good to be able to see which of your search strings got successful returns and which didn't. I'm sure there'd be other ways of handling this, but here's at least one way.
Aug 12 2005, 01:36:40 AM PDT Permalink


