Cleaner way to 'sanitize' JTextField input?
-
01-06-2021 - |
Question
So I'm writing the TextValueChanged handler for a JTextField that takes a long as it's input. It needs to only allow the user to type in valid values between 0 and Long.MAX. If the user types an invalid character, it should strip it, so that the value in the JTextField is always a valid long.
My current code looks like this, but it seems ugly. With no external libs (including Apache Commons), is there a cleaner/simpler way to do this?
public void textValueChanged(TextEvent e) {
if (e.getSource() instanceof JTextField) {
String text = ((JTextField) e.getSource()).getText();
try {
// Try to parse cleanly
long longNum = Long.parseLong(text);
// Check for < 1
if (longNum < 1) throw new NumberFormatException();
// If we pass, set the value and return
setOption("FIELDKEY", longNum);
} catch (NumberFormatException e1) {
// We failed, so there's either a non-numeric or it's too large.
String s = ((JTextField) e.getSource()).getText();
// Strip non-numeric characters
s = s.replaceAll("[^\\d]", "");
long longNum = -1;
if (s.length() != 0) {
/* Really ugly workaround for the fact that a
* TextValueChanged event can capture more than one
* keystroke at a time, if it's typed fast enough,
* so we might have to strip more than one
* character. */
Exception e3;
do {
e3 = null;
try {
// Try and parse again
longNum = Long.parseLong(s);
} catch (NumberFormatException e2) {
// We failed, so it's too large.
e3 = e2;
// Strip the last character and try again.
s = s.substring(0, s.length() - 1);
}
// Repeat
} while (e3 != null);
}
// We parsed, so add it (or blank it if it's < 1) and return.
setOption("FIELDKEY", (longNum < 1 ? 0 : longNum));
}
}
}
The field is constantly repopulated from a getOption(key) call, so to 'store' the value, just pass it to that call.
La solution
DocumentFilter is a good solution for your case.
The Definitive Guide to Java Swing by John Zukowski has an excellent example on pages 542-546 showing a custom document filter for limiting an integer range.
Providing custom DocumentFilter should easily accommodate the requirement to strip off the unwanted characters upon copy/pase as you have specified in one of the comments.
Autres conseils
Also consider InputVerifier
, discussed in How to Use the Focus Subsystem: Validating Input.
I have this for the float you can use for the long
class DoubleTextDocument extends PlainDocument {
public void insertString(int offs, String str, AttributeSet a)
throws BadLocationException {
if (str == null)
return;
String oldString = getText(0, getLength());
String newString = oldString.substring(0, offs) + str
+ oldString.substring(offs);
try {
Float.parseFloat(newString + "0");
super.insertString(offs, str, a);
} catch (NumberFormatException e) {
}
}
}
and in your extended JTextField
you can use
@Override
protected Document createDefaultModel() {
return new DoubleTextDocument();
}