Archive
Writing an Eclipse Plug-in (Part 9): Custom Project: Defining a Custom File Type
So things have been going swimmingly for the last 8 parts. Time to return to our roots: the original project.
But first: time to floss. Open customplugin MANIFEST.MF and click on the light bulb to fix the warning. It should now display:
MANIFEST.MF ... Export-Package: customplugin, customplugin.natures, customplugin.perspectives, customplugin.projects, customplugin.wizards
Also, return to the Overview tab and click on the Externalize Strings Wizard. That should take care of externalizing 4 strings from the MANIFEST.MF and plugin.xml files. Click Finish.
There. My teeth feel so much better.
Okay, now on to the serious stuff.
The custom project will use 3 files types (for now):
- Schema definition
- Deployment descriptor
- Java source code
The two descriptor files will be XML files. They are what we need to define.
The current tasks are to:
- Define a custom project file type for the Schema Definition.
- Define a custom project file type for the Deployment Definition.
There are two things we need to know before defining the file type:
- how can we determine the type of a file?
- can we leverage existing Eclipse functionality?
The butler did it. Metaphorically anyway. There is no need to wait until the end of the story to be told what the answer is: Eclipse can associate the content type of an XML file by looking inside it and reading the root element.
By configuring the content type extension with a base-type of org.eclipse.core.runtime.xml and selecting a parser of type org.eclipse.core.runtime.content.XMLRootElementContentDescriber2 we can create an extensive list of content types that are all XML files, but have different file formats. Very convenient. Very cool. That’s two verys.
Define a Custom Project File Type for the Schema Definition.
First:
- Open customplugin –> plugin.xml –> Extensions –> Add –> contenttypes
- Select org.eclipse.core.contenttype.contentTypes
- Click Finish
- Enter:
- ID: customplugin.contenttype
- Select org.eclipse.core.contenttype.contentTypes –> new –> content-type
- Enter:
- id: customplugin.contenttype.schema
- name: Hidden Clause Schema Definition
- base-type: org.eclipse.core.runtime.xml
- file-extensions: xml
- priority: normal
- Select Schema Definition –> new –> describer
- Enter:
- class: org.eclipse.core.runtime.content.XMLRootElementContentDescriber2
- Select org.eclipse.core.runtime.content.XMLRootElementContentDescriber2 –> new –> parameter
- Enter:
- name: element
- value: hc-schema
Start the runtime workbench and open Window –> Preferences –> General –> Content Types –> Text –> XML. Hidden Clause Schema Definition should be listed as an XML content type.

From the Resource Perspective create a project, custom or otherwise. Create a file and name it special-schema.xml. Enter the following into the file:
<hc-schema> <tables> </tables> </hc-schema>
Right click on the file and select Properties –> Resource. The file should be listed as Hidden Clause Schema Definition (the name we gave it above).

Define a Custom Project File Type for the Deployment Definition.
Time for the second file type:
- Select org.eclipse.core.contenttype.contentTypes –> new –> content-type
- Enter:
- id: customplugin.contenttype.deployment
- name: Hidden Clause Deployment Definition
- base-type: org.eclipse.core.runtime.xml
- file-extensions: xml
- priority: normal
- Select Schema Definition –> new –> describer
- Enter:
- class: org.eclipse.core.runtime.content.XMLRootElementContentDescriber2
- Select org.eclipse.core.runtime.content.XMLRootElementContentDescriber2 –> new –> parameter
- Enter:
- name: element
- value: hc-deployment
Start the runtime workbench and open Window –> Preferences –> General –> Content Types –> Text –> XML. Hidden Clause Deployment Definition should be listed as an XML content type.

From the Resource Perspective, using the same project as before create another file and name it special-dep.xml. Enter the following into the file:
<hc-deployment> <tables> </tables> </hc-deployment>
Right click on the file and select Properties –> Resource. The file should be listed as Hidden Clause Deployment Definition.

Take a bow.
Cleaning Up
Return to the Overview tab of plugin.xml and click on the Externalize Strings Wizard. Oh, look: the two custom file type names are there.
- Assign the key content-type.name.schema to Hidden Clause Schema Definition
- Assign the key content-type.name.deployment to Hidden Clause Deployment Definition
- Click Finish
I think we might have missed a spot behind the ears, but they feel dry so I think we are okay.
What Just Happened?
Well, adding two new content types was pretty straightforward. Some would say that we should have done this directly in XML. Others think we should learn latin in school. Others think we should go back to the oceans.
Personally, I prefer to use editors to control configuration files; it keeps me from doing something complicated like…forgetting a closing brace, or misspelling an element name.
References
The Eclipse help files were somewhat useful, but not as useful as just trying it out. This is one of the few times where stumbling around in the room is actually useful.
Of course the Eclipse Tip: Define Custom Content Types to Identify Your Data Files was rather helpful in pointing out the use of org.eclipse.core.runtime.content.XMLRootElementContentDescriber2 as an existing content type XML parser that also supports namespaces. Very cool. Very useful.
The cat is alive.
Using XSLT to Replace Arbitrary XML Located Within An XML Document
I spent some time today working on an interesting XML/XSL problem. Not interesting like cold fusion research, but interesting like what-a-boring-day interesting.
Inside of two document elements are strings with embedded XML that needed to be changed into something else. For example:
<legal-xml>String with<arbitrary-xml/> inside <arbitrary2-xml>of</arbitrary2-xml></legal-xml>
XSL has no problem finding the <legal-xml> element, but allowing me to find and change the <arbitrary-xml> elements is interesting.
Here is the XML input:
<?xml version="1.0" encoding="UTF-8"?>
<documents>
<document>
<title>This is <bold-term>my</bold-term> title</title>
<description>
<separator/>This is the <bold-term>book's</bold-term><separator/>description<separator/>
</description>
</document>
</documents>
In the interest of full disclosure it is safe for me to tell you that the file I was working on did not look anything like the XML document above. Contain your disappointment.
In the document the <title> and <description> elements contain the strings with additional embedded XML tags; in this case <bold-term> and <separator>. What I needed to do was change <bold-term> into the HTML <b> tag and the <separator> into asterisks.
My first thought was to use fn:replace(), but since the strings I wanted to replace are XML elements fn:replace() would not work in this case. I was going to have to use <template>s. The solution is in the XSL below.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" />
<xsl:template match="/">
<html>
<head>
<title>An XSL example</title>
</head>
<body>
Title: <xsl:apply-templates select="/documents/document/title" />
Description: <xsl:apply-templates select="/documents/document/description" />
</body>
</html>
</xsl:template>
<xsl:template match="bold-term" >
<b><xsl:value-of select="." /></b>
</xsl:template>
<xsl:template match="separator" >***</xsl:template>
</xsl:stylesheet>
By taking the contents of <title> and <description> and processing them as standalone XML nodes I was able to use <template>s to do the dirty work for me. For example, the template for the <bold-term> element:
<xsl:template match="bold-term" >
<b><xsl:value-of select="." /></b>
</xsl:template>
takes care of pulling the text surrounded by <bold-term> and prints it between the <b> tags. The template for <separator> is only slightly different; all I wanted to do was put 3 asterisks in place of the element which I did quite unceremoniously.
Yes, there is a reason why the <separator> template is on one line: I wanted the asterisks on the same line as the description. If I had formatted the template by putting the asterisk on a separate line:
<xsl:template match="separator" > *** </xsl:template>
the output would have looked more like:
<?xml version="1.0" encoding="UTF-8"?>
<html>
<head>
<title>An XSL example</title>
</head>
<body>
Title: This is <b>my</b> title
Description:
***
This is the <b>book's</b>
***
description
***
</body>
</html>
Instead, by keeping the <separator> template on one line, I was able to generate the output I wanted.
<?xml version="1.0" encoding="UTF-8"?>
<html>
<head>
<title>An XSL example</title>
</head>
<body>
Title: This is <b>my</b> title
Description: ***This is the <b>book's</b> ***description***</body>
</html>
For those of you new to XSL, you might be wondering how the text that was not surrounded by any elements was output.
This is <bold-term>my</bold-term> title
Since the template only cared about the <bold-term> element it should have only printed the word “my”. Somehow the entire string was printed instead. The answer is in the default XSL templates. XSL has a template for plain text which was automatically applied when text was being processed so instead of generating:
<b>my</b>
it generated:
This is <b>my</b> title
I worked on this using the XML Copy Editor which lets me create an XML file in one tab, an XSL file in another and view the output of the transform in another simply by pressing F8 in the XML tab. Simplicity at its best. I highly recommend it.