HTML Tidy in NetBeans IDE (Part 1)

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;

@ActionID(
    category = "Tools",
id = "org.jtidy.TidyAction")
@ActionRegistration(
    displayName = "#CTL_TidyAction")
@ActionReferences({
    @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;
    }

    @Override
    public void actionPerformed(ActionEvent ev) {
        InputOutput io = IOProvider.getDefault().getIO("HTML Tidy", false);
        io.select();
        OutputWriter writer = io.getOut();
        Tidy tidy = new Tidy();
        try {
            writer.reset();
            StringWriter stringWriter = new StringWriter();
            PrintWriter errorWriter = new PrintWriter(stringWriter);
            tidy.setErrout(errorWriter);
            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() {
                        @Override
                        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);
                        }
                        @Override
                        public void outputLineSelected(OutputEvent oe) {}
                        @Override
                        public void outputLineCleared(OutputEvent oe) {}
                    });
                }
            }
        } catch (IOException ex) {
            Exceptions.printStackTrace(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.

Comments:

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.

Posted by Jesse Glick on June 04, 2012 at 05:53 PM PDT #

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

Posted by Geertjan on June 04, 2012 at 09:57 PM PDT #

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

Posted by alex on June 05, 2012 at 01:41 AM PDT #

Post a Comment:
  • HTML Syntax: NOT allowed
About

Geertjan Wielenga (@geertjanw) is a Principal Product Manager in the Oracle Developer Tools group living & working in Amsterdam. He is a Java technology enthusiast, evangelist, trainer, speaker, and writer. He blogs here daily.

The focus of this blog is mostly on NetBeans (a development tool primarily for Java programmers), with an occasional reference to NetBeans, and sometimes diverging to topics relating to NetBeans. And then there are days when NetBeans is mentioned, just for a change.

Search

Archives
« August 2015
SunMonTueWedThuFriSat
      
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
     
Today