Mime Resolver

GSF is based on mimetypes. Each GSF language plugin is associated with a particular "mime type". For example, Ruby files have the mime type text/x-ruby, JavaScript files have the mime type text/javascript, and so on.

In order for GSF to add its editing services to particular files, you have to teach the IDE how to recognize the mime type for your files. You do that using the "mime resolver" support in NetBeans. This is not particular to GSF, but it's a prerequisite.

Basically, all you have to do is write a simple XML file which tells NetBeans for example that files with the extension .foo have the mimetype text/foo. Once you've done that, you can go to the registration section and register editing services for the text/foo mime type. GSF will provide DataLoaders, DataObjects etc. for files of the mimetypes as soon as you do that.

Registering a Mime Resolver

When you create a new NetBeans plugin, it will typically create an empty layer.xml file for you. This is an XML file where you can register implementations of various IDE services. In particular, to register a mime resolver, you'll need to add something like this:

    
<filesystem>

      <folder name="Services">
        <folder name="MIMEResolver">
            <file name="javascript.xml" url="jsresolver.xml">
                <attr name="SystemFileSystem.localizingBundle" stringvalue="org.netbeans.modules.javascript.editing.Bundle"/>
                <attr name="position" intvalue="190"/>
            </file>
        </folder>
    </folder>
           
<filesystem>
            
Here, the file name javascript.xml should be something unique that no other mime resolver might have picked. The url attribute points to a filename in the same directory as the layer.xml file. I'll cover that file next. Finally, the attributes let you create a properly localized user visible description of files of this file type.

Describing File Types

For the example above, you would create a Bundle.properties file in the same package as the layer file (again, when you create a plugin you usually get one of these created for you already), and it should contain something like this:

Services/MIMEResolver/javascript.xml=JavaScript Files
            

Writing a Mime Resolver

Finally, you need to go and write the actual mime resolver file, jsresolver.xml (we could have named it anything, including javascript.xml).

    
    
<!DOCTYPE MIME-resolver PUBLIC "-//NetBeans//DTD MIME Resolver 1.0//EN" "http://www.netbeans.org/dtds/mime-resolver-1_0.dtd">
<MIME-resolver>
    <file>
        <ext name="json"/>
        <ext name="js"/>
        <resolver mime="text/javascript"/>
    </file>
</MIME-resolver>
            
Here we're saying that files of extensions json and js should be treated as JavaScript files. The DTD for mime resolvers allow more complex analysis, such as analyzing file headers and such. Take a look at the DTD for the full details. You can also write a custom class to do mime resolvers if you have really specific needs. Avoid doing that if you can, since with a custom mime resolver the IDE can't figure out what you're doing and do special optimizations. There is an example of how to do this in the ruby editing module - look for RubyMimeResolver - but again try to avoid this if you don't strictly have to do it.

Finally, note that there is a position attribute on the registration of the mime resolver above. The position attributes are used to order the mime resolvers. In case there are overlaps in registrations, order matters. For that reason, pick some number. NetBeans integration unit tests will check all the layers and make sure there aren't inconsistencies into folder orderings and will generate unit test failures if there are.

Icons and Actions

Once you register your mime type with GSF (described in the registration document, files shown in the Files and Projects views will be handled by GSF. You can also assign a custom icon to file as well as context menu actions. To do this, you need a few more registration steps. Add something like the following to your layer:

    
    <folder name="Loaders">
        <folder name="text">
            <folder name="javascript">
                <attr name="SystemFileSystem.icon" urlvalue="nbresloc:/org/netbeans/modules/javascript/editing/javascript.png"/>
                <attr name="iconBase" stringvalue="org/netbeans/modules/javascript/editing/javascript.png"/>
                <folder name="Actions">
                    <file name="OpenAction.instance">
                        <attr name="instanceClass" stringvalue="org.openide.actions.OpenAction"/>
                        <attr name="position" intvalue="100"/>
                    </file>
                    <file name="Separator1.instance">
                        <attr name="instanceClass" stringvalue="javax.swing.JSeparator"/>
                        <attr name="position" intvalue="200"/>
                    </file>
                    <file name="CutAction.instance">
                        <attr name="instanceClass" stringvalue="org.openide.actions.CutAction"/>
                        <attr name="position" intvalue="300"/>
                    </file>     
                    <file name="CopyAction.instance">
                        <attr name="instanceClass" stringvalue="org.openide.actions.CopyAction"/>
                        <attr name="position" intvalue="400"/>
                    </file>
                    <file name="PasteAction.instance">
                        <attr name="instanceClass" stringvalue="org.openide.actions.PasteAction"/>
                        <attr name="position" intvalue="500"/>
                    </file>
                    <file name="Separator2.instance">
                        <attr name="instanceClass" stringvalue="javax.swing.JSeparator"/>
                        <attr name="position" intvalue="600"/>
                    </file>
                    <file name="NewAction.instance">
                        <attr name="instanceClass" stringvalue="org.openide.actions.NewAction"/>
                        <attr name="position" intvalue="700"/>
                    </file>
                    <file name="DeleteAction.instance">
                        <attr name="instanceClass" stringvalue="org.openide.actions.DeleteAction"/>
                        <attr name="position" intvalue="800"/>
                    </file>
                    <file name="RenameAction.instance">
                        <attr name="instanceClass" stringvalue="org.openide.actions.RenameAction"/>
                        <attr name="position" intvalue="900"/>
                    </file>
                    <file name="Separator3.instance">
                        <attr name="instanceClass" stringvalue="javax.swing.JSeparator"/>
                        <attr name="position" intvalue="1000"/>
                    </file>
                    <file name="SaveAsTemplateAction.instance">
                        <attr name="instanceClass" stringvalue="org.openide.actions.SaveAsTemplateAction"/>
                        <attr name="position" intvalue="1100"/>
                    </file>
                    <file name="Separator4.instance">
                        <attr name="instanceClass" stringvalue="javax.swing.JSeparator"/>
                        <attr name="position" intvalue="1200"/>
                    </file>
                    <file name="FileSystemAction.instance">
                        <attr name="instanceClass" stringvalue="org.openide.actions.FileSystemAction"/>
                        <attr name="position" intvalue="1300"/>
                    </file>
                    <file name="Separator5.instance">
                        <attr name="instanceClass" stringvalue="javax.swing.JSeparator"/>
                        <attr name="position" intvalue="1400"/>
                    </file> 
                    <file name="ToolsAction.instance">
                        <attr name="instanceClass" stringvalue="org.openide.actions.ToolsAction"/>
                        <attr name="position" intvalue="1500"/>
                    </file> 
                    <file name="PropertiesAction.instance">
                        <attr name="instanceClass" stringvalue="org.openide.actions.PropertiesAction"/>
                        <attr name="position" intvalue="1600"/>
                    </file> 
                </folder>            
            </folder>
        </folder>
    </folder>
            
Here, the highlighted section shows how an icon is assigned to this type. You need to place an icon in the directory shown in the attribute. The rest of this section registers various common actions you might want on your files. These are all standard actions defined by NetBeans so you don't have to write these on your own.


Tor Norbye <tor@netbeans.org>