Sunday, December 9, 2012

Emulating RDoc's Toggle Source in JavaDoc

Yesterday I thought I'd see if I could emulate RDoc's source code toggling feature in JavaDoc.  If you're not familiar with that feature, here's what I tried to accomplish:

Click show to display source code.

Click hide to make source code hidden again.

But unlike RDoc, I wanted to have control over which methods have their source code displayed.  To accomplish this I created my own Taglet.  That way I could mark individual methods with a @toggle-source tag in it's doc-comment.

Note: The Taglet interface is in the tools jar in jdk's lib folder.

Next I needed to figure out how to mark the end of the method.  I could have probably done some tricky regular expressions to match curly braces, but I wanted this to be a quick hack.  So instead I decided the length of the method would be passed as an argument to the @toggle-source tag.  For example:
/**
 * @toggle-source 3
*/
public MyClass(String message){
   this.message = message;
}

Another problem I had to solve was what id to give each code section.  To keep things simple, I used a static counter variable and then appended it to an appropriately named prefix.  For example, link's have an id prefix of "link-"
StringBuilder str = new StringBuilder();
// ...
str.append("<a href='javascript:toggleSource(\"" + count +    "\");' id='link-" + count + "'>show</a>");
str.append("<br" + "/>");
str.append("<div style='border-style: dashed; visibility: hidden;' id='code-" + count + "'>");
Note: I used String concatenation inside of StringBuilder's append method to keep things logically together.

The last issue I had to deal with was how to include the javascript code that does the actual toggling of the source code.  I ended up using javadoc's -bottom option to pass the desired javascript code.
-bottom "<script type='text/javascript'>function toggleSource(id){var    code_element = document.getElementById('code-' +
    id);if(code_element.style.visibility ==    'hidden'){code_element.style.visibility =
    'visible';}else{code_element.style.visibility = 'hidden';}var link_element =    document.getElementById('link-' + id);if(link_element.innerHTML ==
    'show'){link_element.innerHTML = 'hide';}else {link_element.innerHTML =    'show';}}</script>"
It looks ugly, but it was the only way I could think of to ensure the code was included in the output only once.  Here's the expanded version of the code (sans the script tags):

function toggleSource(id) {
    var code_element = document.getElementById('code-' + id);

    if(code_element.style.visibility == 'hidden') {
        code_element.style.visibility = 'visible';
    }
    else {
        code_element.style.visibility = 'hidden';
    }

    var link_element = document.getElementById('link-' + id);

    if(link_element.innerHTML == 'show') {
        link_element.innerHTML = 'hide';
    }
    else {
        link_element.innerHTML = 'show';
    }
}

The source code for my custom Taglet is on my github  page.

To run it, you'll need to pass the fully qualified class name to javadoc's taglet option and and also where the class is located.
-taglet my.independent.studies.ToggleSource
-tagletpath    C:\Users\Todd\Documents\NetBeansProjects\CustomTaglet\build\classes
-bottom "<script type='text/javascript'>function toggleSource(id){var    code_element = document.getElementById('code-' +
    id);if(code_element.style.visibility ==    'hidden'){code_element.style.visibility =
    'visible';}else{code_element.style.visibility = 'hidden';}var link_element =    document.getElementById('link-' + id);if(link_element.innerHTML ==
    'show'){link_element.innerHTML = 'hide';}else {link_element.innerHTML =    'show';}}</script>"
Here's what the resulting HTML looks like:

Click show to display source code.

Click hide to make source code hidden again

It's not exactly like RDoc's, but I think I did a pretty good job.

No comments:

Post a Comment