1

I have a JTextField that I have overridden the Document for, so that I can prevent the user from entering some characters. The way this works is my extension of Document receives a Regex in its constructor, then checks anything the user types against the Regex:

public class ValidDocument extends PlainDocument
{
    private String regex;

    public ValidDocument(String regex)
    {
        this.regex = regex;
    }

    @Override
    public void insertString(int offs, String str, AttributeSet a)
    {
        if(str == null) return;

        try
        {
            if((this.getText(0, getLength()) + str).matches(regex))
                super.insertString(offs, str, a);
        }
        catch(BadLocationException ble)
        {
            System.out.println("Came across a BadLocationException");
        }
    }
}

I had an issue however, where a JTextField that I wanted to display only valid float/double numbers in wasn't displaying its initial value. The code I was using is below:

float value = 25.0f;
JTextField textField = new JTextField("" + value);
textField.setDocument(new ValidDocument("^(\\-)?\\d*(\\.)?\\d*$"));

So the JTextField displayed, but there was no initial value. I tried to enter 25.0 into the field and it accepted it I had originally expected it would. After a bit of stuffing around, I tried adding:

textField.setText("" + value);

And it displayed the value. It occurred to me that it might accept anything I tried to put in setText(), so I added alphabetic characters to it:

textField.setText("as" + value);

As I suspected, it included the alphabetic characters, even though the Regex should have prevented that. So my thoughts are that the Document is bypassed when using this function.

Can anyone shed some light on why applying the ValidDocument to my JTextField is removing the text I placed in the constructor of the text field? I have other JTextFields with less restrictive Regex's that still display the value I set in the constructor. And why would it be overriding the value passed into the constructor, but not the one passed into setText()?

mKorbel
  • 109,525
  • 20
  • 134
  • 319
Stevo
  • 397
  • 6
  • 26
  • prevent the user from entering some characters. == DocumentFilter – mKorbel Mar 01 '15 at 06:35
  • where a JTextField that I wanted to display only valid float/double numbers in wasn't displaying its initial value. == and then to parse users input to Float/Double, question is why bothering with plain JTextField, isn't JFormattedTextField better than ... (for standard input) – mKorbel Mar 01 '15 at 06:38
  • Hmm... I'll look at the JFormattedTextField now. Thanks for the direction – Stevo Mar 01 '15 at 06:41
  • Ok so the JFormattedTextField seems reasonable to achieve what I would like, but I'm still confused as to why the constructor value was being cleared but the setValueAt() was not? – Stevo Mar 01 '15 at 06:51
  • because ice_bear :-) [again is reasonable for simple users input](http://stackoverflow.com/questions/7378821/jformattedtextfield-issues) – mKorbel Mar 01 '15 at 06:53
  • I just tried reading through that thread, that was well above my current knowledge. But that is why I ask these questions, hopefully each time I will pick up something new! – Stevo Mar 01 '15 at 07:03

1 Answers1

1

Don't use PlainDocument, use a DocumentFilter, it's what it is deisnged for. The method you're attempting to use has been out of date for more than 10 years, for example...

public class PatternFilter extends DocumentFilter {

    // Useful for every kind of input validation !
    // this is the insert pattern
    // The pattern must contain all subpatterns so we can enter characters into a text component !
    private Pattern pattern;

    public PatternFilter(String pat) {
        pattern = Pattern.compile(pat);
    }

    public void insertString(FilterBypass fb, int offset, String string, AttributeSet attr)
            throws BadLocationException {

        String newStr = fb.getDocument().getText(0, fb.getDocument().getLength()) + string;
        Matcher m = pattern.matcher(newStr);
        if (m.matches()) {
            super.insertString(fb, offset, string, attr);
        } else {
        }
    }

    public void replace(FilterBypass fb, int offset,
                        int length, String string, AttributeSet attr) throws
            BadLocationException {

        if (length > 0) fb.remove(offset, length);
        insertString(fb, offset, string, attr);
    }
}

Which comes from this DocumentFilter Examples

Take a look at Implementing a Document Filter for more details

If you don't need real time filtering, another option would be to use a JFormattedTextField or JSpinner. See How to Use Formatted Text Fields and How to Use Spinners for more details

MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • Hahaha, I've only been using Swing properly in the last year, yet I still manage to pick up a habit that is completely out of date! The real time filtering is ideal, hence why ended up going with this method. I actually don't recall why I decided that the way I was using was the way to go, or how I actually came to that conclusion, but it's nice to know there are simpler ways of doing things. – Stevo Mar 01 '15 at 06:59
  • @Stevo I just ran a test using the filter from above with your regexp and it seems to work correctly for me... – MadProgrammer Mar 01 '15 at 07:14