ambroafb.general.mapeditor.MapEditor.java Source code

Java tutorial

Introduction

Here is the source code for ambroafb.general.mapeditor.MapEditor.java

Source

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package ambroafb.general.mapeditor;

import com.sun.javafx.scene.control.skin.ComboBoxListViewSkin;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Consumer;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.value.ObservableValue;
import javafx.fxml.FXML;
import javafx.scene.control.ComboBox;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.util.StringConverter;
import org.apache.commons.lang3.StringUtils;

/**
 *
 * @author dato
 */
public class MapEditor extends ComboBox<MapEditorElement> {

    private Map<String, MapEditorElement> itemsMap;
    private String delimiter;
    private Consumer<MapEditorElement> removeElement, editElement;
    private String keyPattern, valuePattern, initializeClass, keySpecChars, valueSpecChars;
    private Class<?> initialize;
    private BooleanProperty incorrectElem = new SimpleBooleanProperty(false);
    private String viewableCSSFile = "/styles/css/countcomboboxviewable.css";
    private boolean isViewableState = false;

    public MapEditor() {
        this.setEditable(true);
        itemsMap = new HashMap<>();
        delimiter = " : "; // default value of delimiter
        keyPattern = ""; // (?<![\\d-])\\d+
        valuePattern = ""; // [0-9]{1,13}(\\.[0-9]*)?
        keySpecChars = "";
        valueSpecChars = "";

        this.setCellFactory((ListView<MapEditorElement> param) -> new CustomCell());

        removeElement = (MapEditorElement elem) -> {
            if (itemsMap.containsKey(elem.getKey())) {
                itemsMap.remove(elem.getKey());
                if (getValue() != null && getValue().compare(elem) == 0) {
                    getEditor().setText(delimiter);
                }
                getItems().remove(elem);
            }
        };

        editElement = (MapEditorElement elem) -> {
            getSelectionModel().select(-1);
            getEditor().setText(elem.getKey() + delimiter + elem.getValue());
            itemsMap.remove(elem.getKey());
            getItems().remove(elem);
        };

        // Never hide comboBox items listView:
        this.setSkin(new ComboBoxListViewSkin(this) {
            @Override
            protected boolean isHideOnClickEnabled() {
                return false;
            }
        });

        // Control textField input.
        TextField editor = getEditor();
        editor.setText(delimiter);
        editor.textProperty()
                .addListener((ObservableValue<? extends String> observable, String oldValue, String newValue) -> {
                    if (newValue == null || newValue.isEmpty() || newValue.equals(delimiter)) {
                        editor.setText(delimiter);
                    } else if (!newValue.contains(delimiter)) {
                        editor.setText(oldValue);
                    } else {
                        String keyInput = StringUtils.substringBefore(newValue, delimiter).trim();
                        String valueInput = StringUtils.substringAfter(newValue, delimiter).trim();

                        if (!keyInput.isEmpty() && !Pattern.matches(keyPattern, keyInput)) {
                            keyInput = StringUtils.substringBefore(oldValue, delimiter).trim();
                        }
                        if (!valueInput.isEmpty() && !Pattern.matches(valuePattern, valueInput)) {
                            valueInput = StringUtils.substringAfter(oldValue, delimiter).trim();
                        }

                        editor.setText(keyInput + delimiter + valueInput);
                    }
                });

        this.setConverter(new StringConverter<MapEditorElement>() {
            @Override
            public String toString(MapEditorElement object) {
                if (object == null) {
                    return delimiter;
                }
                return object.getKey() + delimiter + object.getValue();
            }

            @Override
            public MapEditorElement fromString(String input) {
                MapEditorElement result = null;
                if (input != null && input.contains(delimiter)) {
                    result = getNewInstance();
                    if (result == null)
                        return null;
                    String keyInput = StringUtils.substringBefore(input, delimiter).trim();
                    String valueInput = StringUtils.substringAfter(input, delimiter).trim();
                    if (!keyInput.isEmpty()) {
                        result.setKey(keyInput);
                    }
                    if (!valueInput.isEmpty()) {
                        result.setValue(valueInput);
                    }
                    boolean keyOutOfSpec = keySpecChars.isEmpty()
                            || !StringUtils.containsOnly(result.getKey(), keySpecChars);
                    boolean valueOutOfSpec = valueSpecChars.isEmpty()
                            || !StringUtils.containsOnly(result.getValue(), valueSpecChars);
                    if (!keyInput.isEmpty() && !valueInput.isEmpty() && !itemsMap.containsKey(keyInput)
                            && (keyOutOfSpec && valueOutOfSpec)) {
                        itemsMap.put(keyInput, result);
                        getItems().add(result);
                        return null;
                    }
                }
                return result;
            }
        });

        // Control caret position in textField.
        editor.addEventFilter(KeyEvent.KEY_PRESSED, (KeyEvent event) -> {
            int caretOldPos = editor.getCaretPosition();
            int delimiterIndex = editor.getText().indexOf(delimiter);
            if (event.getCode().equals(KeyCode.RIGHT)) {
                if (caretOldPos + 1 > delimiterIndex && caretOldPos + 1 <= delimiterIndex + delimiter.length()) {
                    editor.positionCaret(delimiterIndex + delimiter.length());
                    event.consume();
                }
            } else if (event.getCode().equals(KeyCode.LEFT)) {
                if (caretOldPos - 1 >= delimiterIndex && caretOldPos - 1 < delimiterIndex + delimiter.length()) {
                    editor.positionCaret(delimiterIndex);
                    event.consume();
                }
            }
        });
    }

    private MapEditorElement getNewInstance() {
        MapEditorElement result = null;
        try {
            result = (MapEditorElement) initialize.getConstructor().newInstance();
        } catch (InstantiationException | IllegalAccessException | IllegalArgumentException
                | InvocationTargetException | NoSuchMethodException | SecurityException ex) {
            Logger.getLogger(MapEditor.class.getName()).log(Level.SEVERE, null, ex);
        }
        return result;
    }

    private class CustomCell extends ListCell<MapEditorElement> {

        public CustomCell() {
            super();
        }

        @Override
        public void updateItem(MapEditorElement item, boolean empty) {
            super.updateItem(item, empty);
            if (item == null || empty) {
                setGraphic(null);
            } else {
                MapEditorItem mapEditorItem = new MapEditorItem(item, delimiter, removeElement, editElement);
                mapEditorItem.setViewableState(isViewableState);
                itemsMap.put(item.getKey(), item);
                setGraphic(mapEditorItem);
            }
        }
    }

    public void changeState(boolean isViewable) {
        isViewableState = isViewable;
        setEditable(!isViewable);
        if (isViewable) {
            getStylesheets().add(viewableCSSFile);
        } else {
            getStylesheets().remove(viewableCSSFile);
        }
    }

    public BooleanProperty incorrectElemProperty() {
        return incorrectElem;
    }

    @FXML
    public void setKeyPattern(String regex) {
        keyPattern = regex;
    }

    @FXML
    public String getKeyPattern() {
        return keyPattern;
    }

    @FXML
    public void setKeySpecChars(String keySpec) {
        keySpecChars = keySpec;
    }

    @FXML
    public String getKeySpecChars() {
        return keySpecChars;
    }

    @FXML
    public void setValuePattern(String regex) {
        valuePattern = regex;
    }

    @FXML
    public String getValuePattern() {
        return valuePattern;
    }

    @FXML
    public void setValueSpecChars(String valueSpec) {
        valueSpecChars = valueSpec;
    }

    @FXML
    public String getValueSpecChars() {
        return valueSpecChars;
    }

    @FXML
    public void setDelimiter(String delimiter) {
        this.delimiter = delimiter;
        getEditor().setText(delimiter);
    }

    @FXML
    public String getDelimiter() {
        return delimiter;
    }

    @FXML
    public void setInitialize(String classStr) {
        initializeClass = classStr;
        try {
            initialize = Class.forName(classStr);
        } catch (ClassNotFoundException ex) {
            Logger.getLogger(MapEditor.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    @FXML
    public String getInitialize() {
        return initializeClass;
    }
}