Add automatic link detection to your Swing apps

Extend JEditorPane functionality

Java supports HTML editing through JEditorPane, a text component similar to TextBox and TextArea. The JEditorPane is typically used in association with EditorKit, which decides what special text settings (or tags for HTML) to recognize apart from the regular text. HTMLEditorKit extends EditorKit and is capable of recognizing HTML text. The code below shows how to initialize a JEditorPane for editing HTML:

 JEditorPane txt = new JEditorPane();
txt.setEditorKit(new HTMLEditorKit()); 

The instance of the JEditorPane component can now recognize any HTML tag present in its model (text). For example, if the text is <b>This is Bold</b>, the JEditorPane will display This is Bold. Similarly, if the text is <a href=http://mail.yahoo.com>yahoo mail</a>, JEditorPane will then convert this text to a link. However, if a user types a link directly into the editing area, the component displays it as plain text only, until the link is decorated with proper HTML tags.

To make JEditorPane display links properly, we must detect links as they are being typed and decorate these links with proper HTML tags.

Displaying user-typed links in JEditorPane

As Amy Fowler explains in "A Swing Architecture Overview," Swing is a "model-based framework." That is, every component is controlled by a backend model class. This brings HTMLDocument into the picture as the model class for JEditorPane, which can help us access the text being written onto the JEditorPane and alter it at runtime.

So what do we have to do? As the user types a link in the EditorPane, we need to modify this text programmatically to a correct HTML anchor tag with appropriate value for the HREF attribute. For example, a normal HTML link should have the URL as the HREF attribute and the email addresses should use the "mailto:<email address>" syntax.

To achieve this functionality, we can create a new component that extends HTMLDocument so that it intercepts the user-entered string and converts it to a link. The main Swing component that extends from JEditorPane can use the new model class to implement the required functionality.

Some of the features that the new component should support are:

  1. Automatic detection of links, based on some predefined patterns
  2. Automatic rewriting of URL when user modifies a URL
  3. Showing a tool tip when the mouse hovers over a link
  4. Opening the link in user's preferred browser

Link detector: Design

The figure below illustrates our link detector's design.

Design. Click on thumbnail to view full-sized image.

As shown in the figure above, the class EditorPaneLinkDetector extends JEditorPane and is the main class that supports automatic recognition of hyperlinks. Also, HTMLDocLinkDetector, which extends HTMLDocument, is the model class and will detect and recognize the hyperlinks using regular expressions as the user types them. These detected links are then converted into HTML links using the <a> tag. Later, we address the problem of listening to the mouse click on the hyperlink.

Text typed in by the user is passed to our new model class, HTMLDocLinkDetector, through the insertString() method. This method is overridden in the model class to look for link patterns and modify the text with <a> tags.

Now, we get into the details of the implementation. Let's divide our discussion into two subtopics:

  • Detecting and converting the link to a hyperlink
  • Listening to the hyperlink (forcing the system to respond when the link is clicked)

Detect and convert the link text to an HTML anchor tag

As already discussed, our main component extends JEditorPane, which uses the associated EditorKit to implement its behavior. The EditorKit is responsible for configuring the editor to accept different kinds of content. In other words, EditorKit decides which content type the editor can handle. In our case, we use HTMLEditorKit to make it understand HTML. The following code goes to the constructor of our main class EditorPaneLinkDetector, which extends JEditorPane:

 HTMLEditorKit htmlkit = new HTMLEditorKit();
...
HTMLDocument doc = new HTMLDocLinkDetector(ss);
setEditorKit(htmlkit);
setDocument(doc); 

The HTMLDocLinkDetector class, our model class, is a subclass of HTMLDocument. This is where we'll search and activate links.

While extending HTMLDocument, we override the function insertString(), which is responsible for writing to the document, and we call a function from inside insertString() to check for links:

 public void insertString(int offs, String str, AttributeSet a)
            throws BadLocationException {
   super.insertString(offs, str, a);
   Element e = getCharacterElement(offs);
   computeLinks(e);
}

As shown in bold, we get the character element of the string to be inserted and pass it to another function, computeLinks(), wherein we check whether the entered string is already a link, with the help of the following code:

  e.getAttributes().getAttribute(HTML.Tag.A) != null 

Thus, if the element already contains HTML tag <a>, i.e., we are trying to edit an existing link, then we replace the earlier link with the one entered now. We'll get into the details of replacing an existing link once we finish our discussion on how to convert a string into a link.

Once we get the details of the string entered and, if it is not a link,—i.e., it has not yet been converted to a link—we run the following pattern matcher on it:

 Matcher matcher = Pattern.compile("(?i)
   (\\b(http://|https://|www.|ftp://|file:/|mailto:)\\S+)(\\s+)").matcher(text); 

As you can see, the above pattern will match for any string starting with http://, https://, etc. Here, we use the or construct (|) to handle the different HTML link patterns. Once a link is identified, we run the following pattern matcher to avoid adding any non-alpha characters like (?,.) at the link's end to the anchor tag:

 Matcher dotEndMatcher = Pattern.compile("([\\W&&[^/]]+)$").matcher(url); 

Once the link is identified, it is replaced with a properly formatted HTML hyperlink using the insertString() method.

Editing an existing link can be tricky. In our implementation, we remove the anchor tag when the user starts editing an existing link. That is, the link converts to plain text. The appropriate HTML tags are then rewritten as if the original link were newly typed.

We now look at how to handle mouse hover and click-over hyperlinks.

Display a tool tip with mouse hover

When a mouse hovers over a link, we want to display a tool tip describing how to open the link. We implement this by adding a MouseMotionListener. We'll implement the logic in the mouseMoved() method:

 AccessibleJTextComponent context = (AccessibleJTextComponent) getAccessibleContext().getAccessibleEditableText();
AccessibleHypertext accText = (AccessibleHypertext) context
               .getAccessibleText();
int index = accText.getIndexAtPoint(e.getPoint());
int linkIndex = accText.getLinkIndex(index);
// Check if the mouse is over a link; otherwise 
// set the tool tip to null
if (linkIndex == -1) {
        setToolTipText(null);
   return;
}
String linkDesc = accText.getLink(linkIndex)
               .getAccessibleActionDescription(0);
String toolTipText = "
Sumit Grover is a software engineer with Infosys Technologies Limited. His area of expertise is Java and related technologies. David Bismut is an IT engineer from the Ecole des Mines de Nantes (EMN), a French engineering school. He specializes in information management technologies. He was part of InStep, Infosys's global internship program in 2004 Swaminathan Natarajan is a technical architect with Infosys Technologies. His area of expertise is Java, repository technologies, and metadata management. Krishnakumar Pooloth is a senior technical architect with Infosys Technologies. His areas of expertise include object design, component technology, Java, and expert systems. He holds a bachelor's degree in electronics and communication from Calicut University, India.

Learn more about this topic

Join the discussion
Be the first to comment on this article. Our Commenting Policies
See more