I would like to make sure the user enter a valid IPv4 address in a JavaFX TextField (0.0.0.0 to 255.255.255.255) but i can't seems to find any valid solutions.
I tried with that code :
private final TextField serverURI = new TextField();
final UnaryOperator<TextFormatter.Change> urlFilter = new UnaryOperator<TextFormatter.Change>() {
@Override
public TextFormatter.Change apply(TextFormatter.Change change) {
final String text = change.getText();
return (text.isEmpty() || text.matches("^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$")) ? change : null;
}
};
final TextFormatter<String> urlFormatter = new TextFormatter(urlFilter);
serverURI.setTextFormatter(urlFormatter);
But I can't even type one digit... It seems to work with only one input
These are tricky.
The first note is that you are using change.getText()
, which gives the text being added or removed; you want to test the resulting text (i.e. the text after adding or deleting). For this, use change.getControlNewText()
.
For example, if the current text in the text field is "255.2"
, and the user types a "5"
, then change.getText()
will return "5"
, whereas change.getControlNewText()
will return "255.25"
.
This still leaves the main issue, which is that the filter is being applied to every individual change to the text, and you are testing for a complete ip address. So, for example, to type "255.255.255.255", the user would first type "2". You test (even with change.getControlNewText()
) if "2" matches your regular expression, it fails (because it's not a complete IP address), and so the change is vetoed. On the next key typed, change.getControlNewText()
will be "25"
, and so on. So you want the entire sequence "2
", "25"
, "255"
, "255."
, "255.2"
, etc, etc to pass your filter. You also need the filter to accept the user deleting characters, copying and pasting, and so on.
So you really need a regex that tests for partial entries, instead of complete entries, which is of course a little trickier to get right. The following is an example, not intended to be bullet-proof, but should get you on the right track (at least). Note that you probably want additional validation when the user commits the value, to check you have a complete, valid ip address.
import java.util.function.UnaryOperator;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.scene.control.TextFormatter;
import javafx.scene.control.TextFormatter.Change;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class IPTextFieldTest extends Application {
@Override
public void start(Stage primaryStage) {
TextField ipTextField = new TextField();
String regex = makePartialIPRegex();
final UnaryOperator<Change> ipAddressFilter = c -> {
String text = c.getControlNewText();
if (text.matches(regex)) {
return c ;
} else {
return null ;
}
};
ipTextField.setTextFormatter(new TextFormatter<>(ipAddressFilter));
StackPane root = new StackPane(ipTextField);
Scene scene = new Scene(root, 350, 120);
primaryStage.setScene(scene);
primaryStage.show();
}
private String makePartialIPRegex() {
String partialBlock = "(([01]?[0-9]{0,2})|(2[0-4][0-9])|(25[0-5]))" ;
String subsequentPartialBlock = "(\\."+partialBlock+")" ;
String ipAddress = partialBlock+"?"+subsequentPartialBlock+"{0,3}";
return "^"+ipAddress ;
}
public static void main(String[] args) {
launch(args);
}
}