Saturday July 07, 2007
Simultaneous Multi File Link Check Feature
My link checker has turned into a pretty useful feature already, despite some minor flaws. Either on an HTML document's node, or in the HTML editor, or on a folder containing at least one HTML document, I can now select the new 'Link Check' menu item. When chosen on document-level, the Output window then immediately contains a list of hyperlinks, one for each HREF attribute in the document, prefaced either by 'ok' or 'broken'. When an 'ok' hyperlink is clicked, the corrctly linked document opens in the editor. When a 'broken' hyperlink is clicked, the document in which the HREF attribute is defined opens and the cursor lands on the offending line.
But one doesn't want to do a document-by-document link check, does one? One would rather right-click a folder and choose 'Link Check'. In that case, all HTML documents within the folder are link checked and only the broken links are written to the Output window (otherwise the list of hyperlinks would be too long, i.e., if the 'ok' hyperlinks were also written there). The result can be seen in the screenshot below:

The folder-level "Link Check" menu item is only enabled if at least one HTML document is found within the folder. This is how to do that:
@Override
protected boolean enable(Node[] activatedNodes) {
if (super.enable(activatedNodes)) {
//Look up the node's data folder:
DataFolder dFo = activatedNodes[0].getLookup().lookup(org.openide.loaders.DataFolder.class);
if (!(dFo == null)) {
DataObject[] objs;
//Get the children of the data folder:
objs = dFo.getChildren();
//If there is at least one HTML file, return true:
for (int i = 0; i < objs.length; i++) {
DataObject dataObject = objs[i];
if (dataObject.getPrimaryFile().getMIMEType().equals("text/html")) {
return true;
}
}
}
}
return false;
}
And that's it. Notice that here we are overriding the CookieAction.enable method. This is an extremely powerful method. Even before the user invokes the menu item, this method is called. As a result, you can use it to determine whether the menu item is enabled or disabled, depending on whatever conditions you set, such as, in the above case, the content of the folder. In fact, it is a filter that narrows the scope of the CookieAction. Here, although the menu item can be seen in the contextual menu item of all folders, it will only be enabled when at least one HTML file is found within the folder.
The handleStartTag method, in the HTMLEditorKit.ParserCallback class, which was mentioned yesterday, is filled out to handle various values that come from identified HREF attributes. A variety of different kinds of values could be returned, such as internal links (prefaced by #) and "nbdocs" links. These are excluded. Other values are relative links. For these, the data object's parent, or parent's parent, is needed to resolve the link. My solution here is incomplete, but gets the job done for my needs.
public void handleStartTag(HTML.Tag t, MutableAttributeSet a, int pos) {
if (t.toString().equals("a")) {
try {
//Get the HREF attribute's value:
java.lang.String value = (java.lang.String) a.getAttribute(HTML.Attribute.HREF);
//Exclude internal links and 'nbdocs' links:
if (value.startsWith("nbdocs") || value.startsWith("#")) {
return;
}
//Create the full URL to the HREF value,
//solution here is imperfect and incomplete:
java.lang.String url;
if (value.startsWith("../..")) {
url = "/" + dataObject.getPrimaryFile().getParent().getParent().getParent().getPath() + value.substring(5);
} else if (value.startsWith("..")) {
url = "/" + dataObject.getPrimaryFile().getParent().getParent().getPath() + value.substring(2);
} else {
url = "/" + dataObject.getPrimaryFile().getParent().getPath() + "/" + value;
}
//Get line number and subtract 1:
int lineNo = NbDocument.findLineNumber(ec, pos);
int realLineNo = lineNo - 1;
//Create the file object from the string:
org.openide.filesystems.FileObject fo = null;
fo = org.openide.filesystems.FileUtil.toFileObject(new java.io.File(url));
//If the file object is null, then the
//link is broken:
String found = null;
if (fo == null) {
found = "broken";
//Write to the Output window,
//creating hyperlinks:
writer.println(found + ": " + dataObject.getNodeDelegate().getDisplayName() + ", line " + (realLineNo + 2) + ": " + value, new HTMLOutputListener(found, url, dataObject, fo, realLineNo, pos));
}
} catch (IOException ex) {
Exceptions.printStackTrace(ex);
}
}
}
Then, in the HTMLOutputListener class, as shown yesterday, use the LineCookie to open the document and jump the cursor to the place where the HREF attribute is defined. Because that's where the problem is and the help author needs to fix that link.
Jul 07 2007, 09:33:48 AM PDT Permalink


