Geertjan's Blog

  • March 3, 2006

JavaHelp Map IDs for Lazy People

Geertjan Wielenga
Product Manager
I solved the problem that I blogged about yesterday. The problem, in a nutshell, is that I am too lazy to manually create JavaHelp map IDs for all the Javadoc that I want to see in my JavaHelp viewer. To automate the process, I'm now using DOM parsing. (By far the most helpful on-line resource, that explains everything and is very clear, including understandable code samples, is in Red Squirrel Reflections.) So what happens now is this—if no map ID is found for a MultipartId (see yesterday's blog entry for info about MultipartId), a test is done to see whether the MultipartId belongs to the java package (because I only have a plug-in module with that package's Javadoc). If the test succeeds, the following dialog box is displayed:

And then, when you click OK, the MultipartId is sent to MapIdCreator.create(receivedWord):

public class MapIdCreator {
public static void create(String receivedWord)
throws ParserConfigurationException, SAXException, IOException,
TransformerConfigurationException, TransformerException {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse
("C:/HelpForJavaKeywords/javahelp/org/netbeans/modules/" +
Element mapRoot = document.getDocumentElement();
Element mapID = document.createElement("mapID");
mapID.setAttribute("url","nbdocs://org.netbeans.modules.javadoc.java/" +
"org/netbeans/modules/javadoc/java/docs/api/" + receivedWord + ".html");

Transformer transformer = TransformerFactory.newInstance().newTransformer();
Source source = new DOMSource(document);
Result result = new StreamResult(new FileOutputStream("C:/HelpForJavaKeywords/" +
"javahelp/org/netbeans/modules/sendlinetogoogle/" +
transformer.transform(source, result);
NotifyDescriptor.Confirmation msg = new NotifyDescriptor.Confirmation
("'" + receivedWord + "' has been mapped to a help topic.",
"Map Id Successfully Created",NotifyDescriptor.OK_CANCEL_OPTION);

When you look at the section that is in bold above, you see how and what is added to the map file. I'm also working on checking whether the map ID exists locally—in other words, maybe you've used the above method, but you haven't rebuilt the plug-in module yet. In this scenario, you might recreate the map ID by accident, without realizing you've already created it (without rebuilding the plug-in module). That wouldn't be a problem for the functioning of the plug-in module—duplicate map IDs have (so far) worked fine for me (since they point to the same Javadoc HTML file here). However, creating duplicate map IDs means that you end up with a messy map file, so it's better to have some safeguard against that. (I guess there must be a way of updating the map file within the plug-in module, instead of outside of it, which is what I am currently doing. However, somehow I wasn't able to get DOM to parse my file, or even find it, except when pointing to a location outside of the plug-in module.)

Speaking of messy, I also need to work out how to add a linebreak to the map file. In the highlighted code above, no linebreak is added, so that each subsequent map ID is just appended to the closing tag of the previous map ID. (Anyone out there who knows the answer to this question?)

Finally, whenever a map ID is added, for it to take effect you'd need to rebuild the plug-in module. For this reason, you'd need the sources. And for that reason, this is not something that the end-user would be doing—only the provider of the plug-in module (i.e., me) or someone interested in expanding the plug-in module (i.e., possibly, whoever happens to be reading this) would be able to expand the Javadoc HTML files supported by the plug-in module. (Also, if you provide support for additional sets of Javadoc, you'd have to change the condition that checks whether the MultipartId is supported for Javadoc by this plug-in module.) Still, it is really cool to not have to type all those map IDs myself—thanks to the IDE's MultipartId, coupled with DOM's parsing functionality, this is a pretty neat solution to an otherwise Sisyphusian nightmare.

By the way, one incidental, yet beneficial, side effect of this approach is that I only end up with map IDs for the Javadoc that I am actually interested in (as opposed to having masses and masses of map IDs for Javadoc that I have little or no use for). That's actually pretty cool too—it means that my map file is nice and concise, while simultaneously, at least potentially, enabling me to expose Javadoc for any MultipartId whatsoever in my JavaHelp viewer.

Join the discussion

Comments ( 2 )
  • Jesse Glick Friday, March 3, 2006
    Re. indentation: use org.openide.xml.XMLUtil.write and it will be taken care of for you.
    Why don't you just add a custom build step to the module that generates IDs for every Javadoc page right away? Then you don't need to bother with manual addition of each and every ID, which is insane. You say you want to keep your map file concise, but all you need to do is put the generated IDs into a separate map file (created fresh each time by the build) and your custom IDs into a hand-written map file. The help set can refer to them both.
  • Geertjan Sunday, March 5, 2006
    Sure. But it was fun to work on this. (I guess it shouldn't be recommended for anyone trying this at home. Parental guidance advised.)
Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.