Geertjan's Blog

  • June 5, 2012

HTML Tidy in NetBeans IDE (Part 1)

Geertjan Wielenga
Product Manager

First step in integrating HTML Tidy (via its JTidy implementation) into NetBeans IDE:

The reason why I started doing this is because I want to integrate this into the pluggable analyzer functionality of NetBeans IDE that I recently blogged about, i.e., where the FindBugs functionality is found.

So a logical first step is to get it working in an Action class, after which I can port it into the analyzer infrastructure:

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import org.openide.awt.ActionID;
import org.openide.awt.ActionReference;
import org.openide.awt.ActionReferences;
import org.openide.awt.ActionRegistration;
import org.openide.cookies.LineCookie;
import org.openide.loaders.DataObject;
import org.openide.text.Line;
import org.openide.text.Line.ShowOpenType;
import org.openide.util.Exceptions;
import org.openide.util.NbBundle.Messages;
import org.openide.windows.IOProvider;
import org.openide.windows.InputOutput;
import org.openide.windows.OutputEvent;
import org.openide.windows.OutputListener;
import org.openide.windows.OutputWriter;
import org.w3c.tidy.Tidy;
category = "Tools",
id = "org.jtidy.TidyAction")
displayName = "#CTL_TidyAction")
@ActionReference(path = "Loaders/text/html/Actions", position = 150),
@ActionReference(path = "Editors/text/html/Popup", position = 750)
@Messages("CTL_TidyAction=Run HTML Tidy")
public final class TidyAction implements ActionListener {
private final DataObject context;
public TidyAction(DataObject context) {
this.context = context;
public void actionPerformed(ActionEvent ev) {
InputOutput io = IOProvider.getDefault().getIO("HTML Tidy", false);
OutputWriter writer = io.getOut();
Tidy tidy = new Tidy();
try {
StringWriter stringWriter = new StringWriter();
PrintWriter errorWriter = new PrintWriter(stringWriter);
tidy.parse(context.getPrimaryFile().getInputStream(), System.out);
String[] split = stringWriter.toString().split("\n");
for (final String string : split) {
final int end = string.indexOf(" c");
if (string.startsWith("line")) {
writer.println(string, new OutputListener() {
public void outputLineAction(OutputEvent oe) {
LineCookie lc = context.getLookup().lookup(LineCookie.class);
int lineNumber = Integer.parseInt(string.substring(0, end).replace("line ", ""));
Line line = lc.getLineSet().getOriginal(lineNumber - 1);
line.show(ShowOpenType.OPEN, Line.ShowVisibilityType.FOCUS);
public void outputLineSelected(OutputEvent oe) {}
public void outputLineCleared(OutputEvent oe) {}
} catch (IOException ex) {

The string parsing above is ugly but gets the job done for now.

A problem integrating this into the pluggable analyzer functionality is the limitation of its scope. The analyzer lets you select one or more projects, or individual files, but not a folder. So it doesn't work on folders in the Favorites window, for example, which is where I'd like to apply HTML Tidy, across multiple folders via the analyzer functionality. That's a bit of a bummer that I'm hoping to get around somehow.

Join the discussion

Comments ( 3 )
  • Jesse Glick Tuesday, June 5, 2012

    You should not call IOProvider.getIO or InputOutput.select from the action's constructor. The constructor should do nothing except store the context - only actionPerformed should have a visible effect.

    Parsing based on FileObject.getInputStream will only work so long as the file is unmodified.

  • Geertjan Tuesday, June 5, 2012

    Thanks, cleaned up the constructor. But what must I do instead of FileObject.getInputStream?

  • alex Tuesday, June 5, 2012

    Probably nothing. If you're running the code analyzer, I guess it's highly unlikely you're modifying the files at the same time.

Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.Captcha