How to create input masks in a TextField?

6

A few weeks ago I've been studying JavaFX as an alternative to Swing, but I've noticed that it does not have any kind of TextField that lets you put in input masks, just like we do for example in the% / p>

I would like to know who develops in JavaFX, how did you make up for the lack of this component? Have you used any third-party libraries?

    
asked by anonymous 24.07.2015 / 23:16

3 answers

2

Actually with JFX you only have the option to hand or get lib with this. Below is a good starting point to start developing your own component.

public abstract class MaskFieldUtil {

private static List<KeyCode> ignoreKeyCodes;

static {
    ignoreKeyCodes = new ArrayList<>();
    Collections.addAll(ignoreKeyCodes, new KeyCode[]{F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12});
}

public static void ignoreKeys(final TextField textField) {
    textField.addEventFilter(KeyEvent.KEY_PRESSED, new EventHandler<KeyEvent>() {
        @Override
        public void handle(KeyEvent keyEvent) {
            if (ignoreKeyCodes.contains(keyEvent.getCode())) {
                keyEvent.consume();
            }
        }
    });
}

/**
 * Monta a mascara para Data (dd/MM/yyyy).
 *
 * @param textField TextField
 */
public static void dateField(final TextField textField) {
    maxField(textField, 10);

    textField.lengthProperty().addListener(new ChangeListener<Number>() {
        @Override
        public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
            if (newValue.intValue() < 11) {
                String value = textField.getText();
                value = value.replaceAll("[^0-9]", "");
                value = value.replaceFirst("(\d{2})(\d)", "$1/$2");
                value = value.replaceFirst("(\d{2})\/(\d{2})(\d)", "$1/$2/$3");
                textField.setText(value);
                positionCaret(textField);
            }
        }
    });
}

/**
 * Campo que aceita somente numericos.
 *
 * @param textField TextField
 */
public static void numericField(final TextField textField) {
    textField.lengthProperty().addListener(new ChangeListener<Number>() {
        @Override
        public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
            if (newValue.intValue() > oldValue.intValue()) {
                char ch = textField.getText().charAt(oldValue.intValue());
                if (!(ch >= '0' && ch <= '9')) {
                    textField.setText(textField.getText().substring(0, textField.getText().length() - 1));
                }
            }
        }
    });
}

/**
 * Monta a mascara para Moeda.
 *
 * @param textField TextField
 */
public static void monetaryField(final TextField textField) {
    textField.setAlignment(Pos.CENTER_RIGHT);
    textField.lengthProperty().addListener(new ChangeListener<Number>() {
        @Override
        public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
            String value = textField.getText();
            value = value.replaceAll("[^0-9]", "");
            value = value.replaceAll("([0-9]{1})([0-9]{14})$", "$1.$2");
            value = value.replaceAll("([0-9]{1})([0-9]{11})$", "$1.$2");
            value = value.replaceAll("([0-9]{1})([0-9]{8})$", "$1.$2");
            value = value.replaceAll("([0-9]{1})([0-9]{5})$", "$1.$2");
            value = value.replaceAll("([0-9]{1})([0-9]{2})$", "$1,$2");
            textField.setText(value);
            positionCaret(textField);

            textField.textProperty().addListener(new ChangeListener<String>() {
                @Override
                public void changed(ObservableValue<? extends String> observableValue, String oldValue, String newValue) {
                    if (newValue.length() > 17)
                        textField.setText(oldValue);
                }
            });
        }
    });

    textField.focusedProperty().addListener(new ChangeListener<Boolean>() {
        @Override
        public void changed(ObservableValue<? extends Boolean> observableValue, Boolean aBoolean, Boolean fieldChange) {
            if (!fieldChange) {
                final int length = textField.getText().length();
                if (length > 0 && length < 3) {
                    textField.setText(textField.getText() + "00");
                }
            }
        }
    });
}

/**
 * Monta as mascara para CPF/CNPJ. A mascara eh exibida somente apos o campo perder o foco.
 *
 * @param textField TextField
 */
public static void cpfCnpjField(final TextField textField) {

    textField.focusedProperty().addListener(new ChangeListener<Boolean>() {

        @Override
        public void changed(ObservableValue<? extends Boolean> observableValue, Boolean aBoolean, Boolean fieldChange) {
            String value = textField.getText();
            if (!fieldChange) {
                if (textField.getText().length() == 11) {
                    value = value.replaceAll("[^0-9]", "");
                    value = value.replaceFirst("([0-9]{3})([0-9]{3})([0-9]{3})([0-9]{2})$", "$1.$2.$3-$4");
                }
                if (textField.getText().length() == 14) {
                    value = value.replaceAll("[^0-9]", "");
                    value = value.replaceFirst("([0-9]{2})([0-9]{3})([0-9]{3})([0-9]{4})([0-9]{2})$", "$1.$2.$3/$4-$5");
                }
            }
            textField.setText(value);
            if (textField.getText() != value) {
                textField.setText("");
                textField.insertText(0, value);
            }

        }
    });

    maxField(textField, 18);
}

/**
 * Monta a mascara para os campos CNPJ.
 *
 * @param textField TextField
 */
public static void cnpjField(final TextField textField) {
    maxField(textField, 18);

    textField.lengthProperty().addListener(new ChangeListener<Number>() {
        @Override
        public void changed(ObservableValue<? extends Number> observableValue, Number number, Number number2) {
            String value = textField.getText();
            value = value.replaceAll("[^0-9]", "");
            value = value.replaceFirst("(\d{2})(\d)", "$1.$2");
            value = value.replaceFirst("(\d{2})\.(\d{3})(\d)", "$1.$2.$3");
            value = value.replaceFirst("\.(\d{3})(\d)", ".$1/$2");
            value = value.replaceFirst("(\d{4})(\d)", "$1-$2");
            textField.setText(value);
            positionCaret(textField);
        }
    });

}

/**
 * Devido ao incremento dos caracteres das mascaras eh necessario que o cursor sempre se posicione no final da string.
 *
 * @param textField TextField
 */
private static void positionCaret(final TextField textField) {
    Platform.runLater(new Runnable() {
        @Override
        public void run() {
            // Posiciona o cursor sempre a direita.
            textField.positionCaret(textField.getText().length());
        }
    });
}

/**
 * @param textField TextField.
 * @param length    Tamanho do campo.
 */
private static void maxField(final TextField textField, final Integer length) {
    textField.textProperty().addListener(new ChangeListener<String>() {
        @Override
        public void changed(ObservableValue<? extends String> observableValue, String oldValue, String newValue) {
            if (newValue.length() > length)
                textField.setText(oldValue);
        }
    });
}
}

Source: Java user group

    
25.07.2015 / 00:56
4

To whom it may concern, I have also developed a solution. I created a class with functions that generate the most common masks:

import javafx.beans.value.ObservableValue;
import javafx.scene.control.DatePicker;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyEvent;

/**
 * Funções para se aplicar máscaras aos controles do JavaFX
 * 
 * @author Paulo Henrique Luvisoto - [email protected]
 */
public class MascarasFX {

    public static void mascaraNumeroInteiro(TextField textField){

        textField.textProperty().addListener((ObservableValue<? extends String> observable, String oldValue, String newValue) -> {
            if (!newValue.matches("\d*")) {
                textField.setText(newValue.replaceAll("[^\d]", ""));
            }
        });

    }

    public static void mascaraNumero(TextField textField){

        textField.textProperty().addListener((ObservableValue<? extends String> observable, String oldValue, String newValue) -> {
            newValue = newValue.replaceAll(",",".");
            if(newValue.length()>0){
                try{
                    Double.parseDouble(newValue);
                    textField.setText(newValue.replaceAll(",","."));
                }catch(Exception e){
                    textField.setText(oldValue);
                }
            }
        });

    } 

    public static void mascaraCEP(TextField textField){

        String val = "";

        textField.setOnKeyTyped((KeyEvent event) -> {
            if("0123456789".contains(event.getCharacter())==false){
                event.consume();
            }

            if(event.getCharacter().trim().length()==0){ // apagando

                if(textField.getText().length()==6){
                    textField.setText(textField.getText().substring(0,5));
                    textField.positionCaret(textField.getText().length());
                }

            }else{ // escrevendo

                if(textField.getText().length()==9) event.consume();

                if(textField.getText().length()==5){
                    textField.setText(textField.getText()+"-");
                    textField.positionCaret(textField.getText().length());
                }


            }
        });

        textField.setOnKeyReleased((KeyEvent evt) -> {

            if(!textField.getText().matches("\d-*")){
                textField.setText(textField.getText().replaceAll("[^\d-]", ""));
                textField.positionCaret(textField.getText().length());
            }
        });

    }

    public static void mascaraData(TextField textField){

        textField.setOnKeyTyped((KeyEvent event) -> {
            if("0123456789".contains(event.getCharacter())==false){
                event.consume();
            }

            if(event.getCharacter().trim().length()==0){ // apagando

                if(textField.getText().length()==3){
                    textField.setText(textField.getText().substring(0,2));
                    textField.positionCaret(textField.getText().length());
                }
                if(textField.getText().length()==6){
                    textField.setText(textField.getText().substring(0,5));
                    textField.positionCaret(textField.getText().length());
                }

            }else{ // escrevendo

                if(textField.getText().length()==10) event.consume();

                if(textField.getText().length()==2){
                    textField.setText(textField.getText()+"/");
                    textField.positionCaret(textField.getText().length());
                }
                if(textField.getText().length()==5){
                    textField.setText(textField.getText()+"/");
                    textField.positionCaret(textField.getText().length());
                }

            }
        });

        textField.setOnKeyReleased((KeyEvent evt) -> {

            if(!textField.getText().matches("\d/*")){
                textField.setText(textField.getText().replaceAll("[^\d/]", ""));
                textField.positionCaret(textField.getText().length());
            }
        });

    }

    public static void mascaraData(DatePicker datePicker){

        datePicker.getEditor().setOnKeyTyped((KeyEvent event) -> {
            if("0123456789".contains(event.getCharacter())==false){
                event.consume();
            }

            if(event.getCharacter().trim().length()==0){ // apagando
                if(datePicker.getEditor().getText().length()==3){
                    datePicker.getEditor().setText(datePicker.getEditor().getText().substring(0,2));
                    datePicker.getEditor().positionCaret(datePicker.getEditor().getText().length());
                }
                if(datePicker.getEditor().getText().length()==6){
                    datePicker.getEditor().setText(datePicker.getEditor().getText().substring(0,5));
                    datePicker.getEditor().positionCaret(datePicker.getEditor().getText().length());
                }

            }else{ // escrevendo

                if(datePicker.getEditor().getText().length()==10) event.consume();

                if(datePicker.getEditor().getText().length()==2){
                    datePicker.getEditor().setText(datePicker.getEditor().getText()+"/");
                    datePicker.getEditor().positionCaret(datePicker.getEditor().getText().length());
                }
                if(datePicker.getEditor().getText().length()==5){
                    datePicker.getEditor().setText(datePicker.getEditor().getText()+"/");
                    datePicker.getEditor().positionCaret(datePicker.getEditor().getText().length());
                }

            }
        });

        datePicker.getEditor().setOnKeyReleased((KeyEvent evt) -> {

            if(!datePicker.getEditor().getText().matches("\d/*")){
                datePicker.getEditor().setText(datePicker.getEditor().getText().replaceAll("[^\d/]", ""));
                datePicker.getEditor().positionCaret(datePicker.getEditor().getText().length());
            }
        });

    }

    public static void mascaraCPF(TextField textField){

        textField.setOnKeyTyped((KeyEvent event) -> {
            if("0123456789".contains(event.getCharacter())==false){
                event.consume();
            }

            if(event.getCharacter().trim().length()==0){ // apagando

                if(textField.getText().length()==4){
                    textField.setText(textField.getText().substring(0,3));
                    textField.positionCaret(textField.getText().length());
                }
                if(textField.getText().length()==8){
                    textField.setText(textField.getText().substring(0,7));
                    textField.positionCaret(textField.getText().length());
                }
                if(textField.getText().length()==12){
                    textField.setText(textField.getText().substring(0,11));
                    textField.positionCaret(textField.getText().length());
                }

            }else{ // escrevendo

                if(textField.getText().length()==14) event.consume();

                if(textField.getText().length()==3){
                    textField.setText(textField.getText()+".");
                    textField.positionCaret(textField.getText().length());
                }
                if(textField.getText().length()==7){
                    textField.setText(textField.getText()+".");
                    textField.positionCaret(textField.getText().length());
                }
                if(textField.getText().length()==11){
                    textField.setText(textField.getText()+"-");
                    textField.positionCaret(textField.getText().length());
                }

            }
        });

        textField.setOnKeyReleased((KeyEvent evt) -> {

            if(!textField.getText().matches("\d.-*")){
                textField.setText(textField.getText().replaceAll("[^\d.-]", ""));
                textField.positionCaret(textField.getText().length());
            }
        });

    }

    public static void mascaraCNPJ(TextField textField){

        textField.setOnKeyTyped((KeyEvent event) -> {
            if("0123456789".contains(event.getCharacter())==false){
                event.consume();
            }

            if(event.getCharacter().trim().length()==0){ // apagando

                if(textField.getText().length()==3){
                    textField.setText(textField.getText().substring(0,2));
                    textField.positionCaret(textField.getText().length());
                }
                if(textField.getText().length()==7){
                    textField.setText(textField.getText().substring(0,6));
                    textField.positionCaret(textField.getText().length());
                }
                if(textField.getText().length()==11){
                    textField.setText(textField.getText().substring(0,10));
                    textField.positionCaret(textField.getText().length());
                }
                if(textField.getText().length()==16){
                    textField.setText(textField.getText().substring(0,15));
                    textField.positionCaret(textField.getText().length());
                }

            }else{ // escrevendo

                if(textField.getText().length()==18) event.consume();

                if(textField.getText().length()==2){
                    textField.setText(textField.getText()+".");
                    textField.positionCaret(textField.getText().length());
                }
                if(textField.getText().length()==6){
                    textField.setText(textField.getText()+".");
                    textField.positionCaret(textField.getText().length());
                }
                if(textField.getText().length()==10){
                    textField.setText(textField.getText()+"/");
                    textField.positionCaret(textField.getText().length());
                }
                if(textField.getText().length()==15){
                    textField.setText(textField.getText()+"-");
                    textField.positionCaret(textField.getText().length());
                }

            }
        });

        textField.setOnKeyReleased((KeyEvent evt) -> {

            if(!textField.getText().matches("\d./-*")){
                textField.setText(textField.getText().replaceAll("[^\d./-]", ""));
                textField.positionCaret(textField.getText().length());
            }
        });

    }

    public static void mascaraEmail(TextField textField){

        textField.setOnKeyTyped((KeyEvent event) -> {
            if("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz._-@".contains(event.getCharacter())==false){
                event.consume();
            }

            if("@".equals(event.getCharacter())&&textField.getText().contains("@")){
                event.consume();
            }

            if("@".equals(event.getCharacter())&&textField.getText().length()==0){
                event.consume();
            }
        });

    }

    public static void mascaraTelefone(TextField textField){

        textField.setOnKeyTyped((KeyEvent event) -> {
            if("0123456789".contains(event.getCharacter())==false){
                event.consume();
            }

            if(event.getCharacter().trim().length()==0){ // apagando

                if(textField.getText().length()==10&&textField.getText().substring(9,10).equals("-")){
                    textField.setText(textField.getText().substring(0,9));
                    textField.positionCaret(textField.getText().length());
                }
                if(textField.getText().length()==9&&textField.getText().substring(8,9).equals("-")){
                    textField.setText(textField.getText().substring(0,8));
                    textField.positionCaret(textField.getText().length());
                }
                if(textField.getText().length()==4){
                    textField.setText(textField.getText().substring(0,3));
                    textField.positionCaret(textField.getText().length());
                }
                if(textField.getText().length()==1){
                    textField.setText("");
                }

            }else{ //escrevendo

                if(textField.getText().length()==14) event.consume();

                if(textField.getText().length()==0){
                    textField.setText("("+event.getCharacter());
                    textField.positionCaret(textField.getText().length());
                    event.consume();
                }
                if(textField.getText().length()==3){
                    textField.setText(textField.getText()+")"+event.getCharacter());
                    textField.positionCaret(textField.getText().length());
                    event.consume();
                }
                if(textField.getText().length()==8){
                    textField.setText(textField.getText()+"-"+event.getCharacter());
                    textField.positionCaret(textField.getText().length());
                    event.consume();
                }
                if(textField.getText().length()==9&&textField.getText().substring(8,9)!="-"){
                    textField.setText(textField.getText()+"-"+event.getCharacter());
                    textField.positionCaret(textField.getText().length());
                    event.consume();
                }
                if(textField.getText().length()==13&&textField.getText().substring(8,9).equals("-")){
                    textField.setText(textField.getText().substring(0,8)+textField.getText().substring(9,10)+"-"+textField.getText().substring(10,13)+event.getCharacter());
                    textField.positionCaret(textField.getText().length());
                    event.consume();
                }

            }

        });

        textField.setOnKeyReleased((KeyEvent evt) -> {

            if(!textField.getText().matches("\d()-*")){
                textField.setText(textField.getText().replaceAll("[^\d()-]", ""));
                textField.positionCaret(textField.getText().length());
            }
        });

    }

}

The code is here: link

Any criticism or suggestion will be welcome.

    
25.07.2015 / 20:46
0

I've also developed a solution that works almost the same as JformmatedTextField.

The following characters can be used:

  • #: Any valid number (Character.isDigit).
  • ': Escape character, used to escape any of the special formatting characters.
  • U: Any character (Character.isLetter). All lowercase letters are mapped to uppercase.
  • L: Any character (Character.isLetter). All uppercase letters are mapped to lowercase.
  • A: Any character or number (Character.isLetter or Character.isDigit).
  • ? : Any character (Character.isLetter).
  • *: Anything.
  • H: Any hex character (0-9, a-f or A-F).

Using without FXML:

// Criando uma máscara para telefone com o placeholder padrão
MaskedTextField text = new MaskedTextField("(###)#####-####"); // Only Number

// Mudando o placeholder para espaço
text.setPlaceHolder(" ");

// Muda a máscara (Preservando o texto de acordo com a nova máscara)
text.setMask("((####))");

Using with FXML:

<?import packagepath.MaskedTextField?>

<MaskedTextField mask="(###)#####-####" placeholder=" " plainText="5555"/>

This solution is based on the vas7n code, while the system of insertion in the mask has been completely reformulated. This code includes readability and performance improvements, bug fixes, and inclusion of all JFormmatedTextField masks.

/*
 * Copyright (C) 2017 Gustavo Fragoso
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.fxutils.maskedtextfield;

import java.util.ArrayList;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import javafx.application.Platform;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ObservableValue;
import javafx.scene.control.IndexRange;
import javafx.scene.control.TextField;

/**
 * This approach has been inspired on https://github.com/vas7n/VAMaskField solution.
 * We added new masks, fixed bugs and improved performance.
 * Now this component works much closer to JFormattedTextfield.
 * @author gbfragoso
 */
public class MaskedTextField extends TextField{

    // Available properties
    private StringProperty mask;
    private StringProperty placeholder;
    private StringProperty plainText;

    // Available masks
    private static final char MASK_ESCAPE = '\'';
    private static final char MASK_NUMBER = '#';
    private static final char MASK_CHARACTER = '?';
    private static final char MASK_HEXADECIMAL = 'H';
    private static final char MASK_UPPER_CHARACTER = 'U';
    private static final char MASK_LOWER_CHARACTER = 'L';
    private static final char MASK_CHAR_OR_NUM = 'A';
    private static final char MASK_ANYTHING = '*';

    private ArrayList<Mask> semanticMask = new ArrayList<>();
    private int maskLength;
    private int semanticMaskLength;

    public MaskedTextField(){
        this.mask = new SimpleStringProperty(this, "mask", "");
        this.placeholder = new SimpleStringProperty(this,"placeholder","_");
        this.plainText = new SimpleStringProperty(this,"plaintext","");
        start();
    }

    public MaskedTextField(String mask){
        this.mask = new SimpleStringProperty(this, "mask", mask);
        this.placeholder = new SimpleStringProperty(this,"placeholder","_");
        this.plainText = new SimpleStringProperty(this,"plaintext","");
        start();
    }

    public MaskedTextField(String mask, char placeHolder){
        this.mask = new SimpleStringProperty(this, "mask", mask);
        this.placeholder = new SimpleStringProperty(this,"placeholder",String.valueOf(placeHolder));
        this.plainText = new SimpleStringProperty(this,"plaintext","");
        start();
    }

    // *******************************************************
    // Property's value getters
    // *******************************************************
    public final String getMask(){
        return mask.get();
    }

    public final String getPlaceholder(){
        return placeholder.get();
    }

    public final String getPlainText(){
        return plainText.get();
    }

    // *******************************************************
    // Property's value setters
    // *******************************************************
    public final void setMask(String m){
        mask.set(m);
        maskLength = m.length();
        buildSemanticMask();
        updateSemanticMask();
    }

    public final void setPlaceholder(String holder){
        placeholder.set(holder);
        resetSemanticMask();
        updateSemanticMask();
    }

    public final void setPlainText(String text){
        plainText.set(text);
        updateSemanticMask();
    }

    // *******************************************************
    // Property getters
    // *******************************************************
    public StringProperty maskProperty(){
        return mask;
    }

    public StringProperty placeholderProperty(){
        return placeholder;
    }

    public StringProperty plainTextProperty(){
        return plainText;
    }

    // *******************************************************
    // Methods
    // *******************************************************

    /**
     * Configuration method
     */
    private void start(){
        maskLength = getMask().length();
        buildSemanticMask();
        setText(allSemanticValuesToString());

        // When MaskedTextField gains focus caret goes to first placeholder position
        focusedProperty().addListener((ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) -> {
            Platform.runLater(() -> {
                int pos = firstPlaceholderPosition();
                selectRange(pos, pos);
                positionCaret(pos);
            });
        });
    }

    /**
     * Take user mask and convert it into a Semantic Mask, when each value receives
     * the state of literal or non-literal.
     */
    private void buildSemanticMask(){
        String newMask = getMask();
        for (int i = 0; i < maskLength; i++){
            char c = newMask.charAt(i);

            // If the actual char is MASK_ESCAPE look the next char as literal
            if(c == MASK_ESCAPE){
                semanticMask.add(new Mask(newMask.charAt(i+1), newMask.charAt(i+1), true, false));
                i++;
            }else{
                if(isLiteral(c)){
                    semanticMask.add(new Mask(c, c, true, false));
                }else{
                    semanticMask.add(new Mask(getPlaceholder().charAt(0), c, false, true));
                }
            }
        }
        semanticMaskLength = semanticMask.size();
    }

    /**
     * Returns to the initial semanticMask's state, when all non-literal values 
     * are equals to placeholder char and isPlaceholder is true.
     */
    private void resetSemanticMask(){
        char p = getPlaceholder().charAt(0);
        semanticMask.stream()
            .filter(e -> !e.isLiteral())
            .forEach(e -> {
                e.setValue(p);
                e.setPlaceholder(true);
            });
    }

    /**
     * Updates all semanticMask's values according to plainText and set the
     * editor text.
     */
    public void updateSemanticMask(){
        resetSemanticMask();

        String newPlainText = getPlainText();

        int plainCharCounter = 0;
        int plainTextSize = newPlainText.length();

        char[] textPlain = newPlainText.toCharArray();

        for(int i = 0; i < semanticMaskLength; i++){
            Mask m = semanticMask.get(i);

            // If we has free slots and plainText to add
            if(m.isPlaceholder() && plainTextSize > plainCharCounter){
                // Verifing if character entered are valid
                if(isCorrect(m.getMask(), textPlain[plainCharCounter])){

                    // Change value of mask and turn off isPlaceholder
                    m.setValue(textPlain[plainCharCounter]);
                    m.setPlaceholder(false);

                    // When MASK_LOWER|UPPER_CHARACTER is set apply lower|upper to the char
                    if(m.getMask() == MASK_LOWER_CHARACTER){
                        m.setValue(Character.toLowerCase(textPlain[plainCharCounter]));
                    }else if(m.getMask() == MASK_UPPER_CHARACTER){
                        m.setValue(Character.toUpperCase(textPlain[plainCharCounter]));
                    }

                }else{
                    // Remove wrong character from textplain
                    newPlainText = newPlainText.substring(0, plainCharCounter) + newPlainText.substring(plainCharCounter+1);
                }
                plainCharCounter++;
            }
        }

        // Setting new text with mask
        setText(allSemanticValuesToString());

        // Limiting plain text to number of free slots
        if (plainTextSize > plainCharCounter)
            newPlainText = newPlainText.substring(0, plainCharCounter);

        // If the statement above comes "true" or wrong characteres are entered, ajust the plain text
        if (!newPlainText.equals(getPlainText())){
            setPlainText(newPlainText);
        }    
    }

    /**
     * Get all the semanticMask's values and convert it into an string.
     * @return String Concatenation of all values of semanticMask
     */
    private String allSemanticValuesToString(){
        return semanticMask.stream().map(e -> String.valueOf(e.getValue())).collect(Collectors.joining());
    }

    /**
     * If a given char is literal, that is isn't a mask, return true
     * else false.
     * @param c character for verification
     * @return boolean
     */
    private boolean isLiteral(char c){
        return (c != MASK_ANYTHING &&
                c != MASK_CHARACTER &&
                c != MASK_ESCAPE &&
                c != MASK_NUMBER && 
                c != MASK_CHAR_OR_NUM &&
                c != MASK_HEXADECIMAL &&
                c != MASK_LOWER_CHARACTER &&
                c != MASK_UPPER_CHARACTER);
    }

    /**
     * For an given char and mask, returns if char is according with the mask
     * @param m An valid mask value
     * @param value Any char
     * @return boolean Return true if the given char is according with the mask
     */
    public boolean isCorrect(char m, char value){
        switch(m){
            case MASK_ANYTHING: return true;
            case MASK_CHARACTER: return Character.isLetter(value);
            case MASK_NUMBER: return Character.isDigit(value);
            case MASK_CHAR_OR_NUM: return Character.isLetterOrDigit(value);
            case MASK_HEXADECIMAL: return Pattern.matches("[0-9a-fA-F]", String.valueOf(value));
            case MASK_LOWER_CHARACTER: return Character.isLetter(value);
            case MASK_UPPER_CHARACTER: return Character.isLetter(value);
        }
        return false;
    }

    /**
     * Browse semanticMask and give the position of first mask with isPlaceholder = true.
     * Even if plainText has a placeholder on it.
     * @return int first placeholder on mask
     */
    public int firstPlaceholderPosition(){
        for(int i = 0; i < semanticMaskLength; i++){
            if(semanticMask.get(i).isPlaceholder()){
                return i;
            }
        }
        return -1;
    }

    /**
     * Given a position in mask convert it into plainText position
     * @param pos Position in mask
     * @return converted position
     */
    private int maskPositionToPlaintextPosition(int pos){
        int count = 0;

        for (int i = 0; i < semanticMaskLength && i < pos; i++){
            Mask m = semanticMask.get(i);
            if (!(m.isPlaceholder() || m.isLiteral())){
                count++;
            }
        }

        return count;
    }

    /**
     * Given a plain text position return the maskPosition
     * @param pos 
     */
    private int plaintextPositionToMaskPosition(int pos){
        int countLiterals = 0, countNonLiterals = 0;

        for (int i = 0; i < semanticMaskLength && countNonLiterals < pos; i++){
            Mask m = semanticMask.get(i);
            if (m.isLiteral()){
                countLiterals++;
            }else {
                countNonLiterals++;
            }
        }

        return countLiterals + countNonLiterals;
    }

    // *******************************************************
    // Overrides
    // *******************************************************

    /**
     * Main method to insert text on mask.
     * The left side of newString only exist if user insert text on middle, is empty on most cases
     * @param start Edition's start range
     * @param end Edition's end
     * @param newText Text to be inserted/replaced
     */
    @Override
    public void replaceText(int start, int end, String newText){

        int plainStart = maskPositionToPlaintextPosition(start);

        String oldPlainText = getPlainText();
        String newString = oldPlainText.substring(0, plainStart) + newText + oldPlainText.substring(plainStart, oldPlainText.length());
        setPlainText(newString);

        int newPos = plaintextPositionToMaskPosition(newString.lastIndexOf(newText) + newText.length());
        selectRange(newPos, newPos);
    }

    /**
     * Enables the delete/insert text at selected position
     * @param string 
     */
    @Override
    public void replaceSelection(String string){
        IndexRange range = getSelection();
        if(string.equals("")){
            deleteText(range.getStart(), range.getEnd());
        }else{
            replaceText(range.getStart(), range.getEnd(), string);
        }
    }

    /**
     * Delete text char by char left to right (backspace) and right to left (delete)
     * @param start Delete start
     * @param end Delete end
     */
    @Override
    public void deleteText(int start, int end){

        int plainStart = maskPositionToPlaintextPosition(start);
        int plainEnd = maskPositionToPlaintextPosition(end);

        StringBuilder newString = new StringBuilder(getPlainText());
        newString.delete(plainStart, plainEnd);
        setPlainText(newString.toString());

        selectRange(start, start);
    }

    @Override
    public void clear(){
        setPlainText("");
    }
}

The full code is a bit large to post here, but you can find it here .

See working:

    
02.08.2017 / 04:21