Make bad users behave by using JFC documents — revisited

A reader asks for clarity on JFC documents; Java Q&A expert Tony Sintes responds

Q: I just read your previous Java Q&A ” Make Bad Users Behave by Using JFC Documents .” Upon reading the insertString() method I was a little surprised to see that it checked only the offset at which characters were being inserted, not the length of the document. I did a quick cut and paste and compiled the code. Sure enough: If you place the insertion point anywhere but at the end of the text you can insert more than limit characters up until the insertion point reaches limit .

Also note that the main() method shown is missing an f.pack().

A: Fixing the code

Let’s look at the original code:

import javax.swing.text.*;
public class LimitDocument extends PlainDocument 
{
    private int limit;
    public LimitDocument(int limit) 
    {
        super();
      setLimit(limit); // store the limit 
    }
    public final int getLimit() 
    {
      return limit;
    }
    public void insertString(int offset, String s, AttributeSet attributeSet) 
        throws BadLocationException 
    {
        if(offset < limit) // if we haven't reached the limit, insert the string
      {
            super.insertString(offset,s,attributeSet);
      } // otherwise, just lose the string
    }
    public final void setLimit(int newValue) 
    {
        this.limit = newValue;
    }
}
public static void main(String args[]) 
{
    JFrame f = new JFrame("Limit Test");
    JTextField text = new JTextField();
    text.setDocument(new LimitDocument(10));
    f.getContentPane().add(BorderLayout.NORTH,text);
    f.show();
}

Sure enough: no f.pack()! And yes, I’m using the offset and not the string length.

First, let’s fix the code:

import javax.swing.text.*;
public class LimitDocument extends PlainDocument 
{
    private int limit;
    public LimitDocument(int limit) 
    {
        super();
        setLimit(limit); // store the limit 
    }
    public final int getLimit() 
    {
        return limit;
    }
    public void insertString(int offset, String s, AttributeSet attributeSet) 
    throws BadLocationException 
    {
    int end = getEndPosition().getOffset() - 1; // get the position of where the string ends
    StringBuffer sb = new StringBuffer(getText(0,end)); 
    sb = sb.insert(offset,s);
    String new_s = sb.toString();
    if(new_s.length() >= limit)
    {
        new_s = new_s.substring(0,limit);
    }
    super.remove(0,end);
    super.insertString(0,new_s,attributeSet);
    }
    public final void setLimit(int newValue) 
    {
        this.limit = newValue;
    }
}
public static void main(String args[]) 
{
    JFrame f = new JFrame("Limit Test");
    JTextField text = new JTextField();
    text.setDocument(new LimitDocument(10));
    f.getContentPane().add(BorderLayout.NORTH,text);
    f.pack();
    f.show();
}

Wow, quite a change from the old code. So what am I doing?

Each time we try to enter new text, I grab the preexisting text and throw it in a StringBuffer. Using the offset, I then insert the new text into the old buffer. Finally, I grab the new string, truncate it if necessary, clear the old text, and write the new string.

As more requirements are added, the code becomes a bit more complex. The question in the previous column simply asked how to limit what is typed in, not what is pasted in. Pasting makes the answer much more complicated.

Let’s say we have the string “0123456789” in the text box. Furthermore, the user has the string “JavaWorld is neat-o” in the clipboard.

Inserting at index 0 is simple. We simply create “JavaWorld is neat-o0123456789” and truncate it to “JavaWorld “.

Inserting at 3 makes our job harder. The difficulty is also why I decided to take a buffered approach.

If we insert at 3, we will need to move all the old characters forward n positions (where n is the number of new characters). To input the new characters we create the following string “012JavaWor”, clear the old one, and insert it.

There is a drawback to this method. If we insert a single character, the cursor will jump to the end of the string. Oh well, I guess we can’t have it all! I invite any reader to send me a better solution. I’m sure there is a better one.

Addressing the missing f.pack()

Yes, I did miss a pack() call. But please be aware that I meant the main to be a quick, “let’s see it run” piece of code. It is not production quality and I did not mean it to be. Instead, it is a simple wrapper to test the code and see it run. Next time I’ll be sure to make that point clear.

I think that I may need to have a disclaimer at the end of each of my answers. Normally, I look at my answers as a way to get the reader started. At the end of the day, it is up to the reader to go out and learn more about the subject matter. I do not look at my solutions as the complete answer. Because of the lack of detail in most questions (missing requirements and so on), it is impossible for me to provide a full answer and head off every input/situation the coder may face. You cannot, and should not, cut and paste one of my answers right into a project (unless I say it is good enough). I’m sorry if I haven’t made this fact clearer.

Source: www.infoworld.com