Dear JOOQ Community,

after a lot of work i've created a JavaFX tableView dynamically showing a 
JOOQ Result / Record including the posibillity to edit and save the entries 
- i want to share it with you maybe it'll help some others too. There's an 
option to show multiple entries by a JOOQ Result (like a normal tableView) 
and also the possibility to show a single Record (inverted).
It's coded quick and dirty so there'll be a lot of potential to improve the 
code - any recommendations welcome. 

My Question: how to get the name of the table out of the record? during 
Debugging it's possible to see the table name but is there a function to 
get it?

<https://lh3.googleusercontent.com/-RZ5bD3T4Pbk/WR_sWppS6VI/AAAAAAAAW3Y/5-Tt5oceOlM8FE9rCA4Muj5FQZab4d9XQCLcB/s1600/Unbenannt.JPG>



---
The "Main" class:
public JooqRecordTableView(Table table, Result result)
= creating a TableView with multiple entries

public JooqRecordTableView(Table table, Record bRecord)
= creating a TableView with a single Record entry - inverted View - adding 
also a comment column

<https://lh3.googleusercontent.com/-HxsJvQ3BhMY/WR_vwaNLa1I/AAAAAAAAW3k/hZCmQ5cJSFY9J0K_glMGUWz5DuS2q8fggCLcB/s1600/single-inverted.JPG>




import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.control.Alert;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import org.jooq.*;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;

import static java.util.stream.Collectors.toList;

public class JooqRecordTableView<B extends Record> extends TableView {

    private static PropertiesHandle propertiesHandle;
    private Table<?> table;
    private ObservableList<B> records = FXCollections.observableArrayList();
    private String propertyDirectory = "./resources/tables/";
    private B templateRecord = null;

    /* Constructor for standard tableView with JOOQ Result */
    public JooqRecordTableView(Table table, Result result) {
        this.table = table;
        tableRecords(result);
        loadPropertiesForTable();
        tableColumns();
        this.setEditable(true);
    }
    /* generate Table Columns */
    private <R extends Record, O extends Object> void tableColumns() {

        List<TableColumn<R, ?>> tableColumns = new ArrayList<>();

        if (templateRecord != null) {
            tableColumns.addAll(Stream.of(templateRecord.fields())
                    .map((Field<?> f) -> {
                        TableColumn<R, ?> column = new 
TableColumn<>(f.getName());
                        column.setVisible((boolean) 
propertiesHandle.getProperty(f.getName() + "-visible", boolean.class));
                        column.setEditable((boolean) 
propertiesHandle.getProperty(f.getName() + "-editable", boolean.class));
                        
column.setText(propertiesHandle.getProperty(f.getName(), 
String.class).toString());
                        column.setCellValueFactory(d -> new 
SimpleObjectProperty(d.getValue().get(f)));
                        List<?> preloadValues = 
propertiesHandle.getPropertyValueList(f.getName(), String.class);
                        column.setCellFactory(param -> new 
CellUniversalEditor(f, preloadValues));
                        column.setOnEditCommit(
                                t -> {
                                    Field columnField = f;
                                    Object newValue = t.getNewValue();

                                    if (f.getDataType().hasLength()) {
                                        String s = (String) newValue;
                                        if (f.getDataType().length() > 
s.length()) {
                                            
records.get(t.getTablePosition().getRow()).set(columnField, newValue);
                                            ((UpdatableRecord) 
records.get(t.getTablePosition().getRow())).update();
                                            System.out.println("Updating: " 
+ f.getName() + "/" + newValue);
                                        } else {
                                            Alert alert = new 
Alert(Alert.AlertType.ERROR);
                                            alert.setHeaderText("Error: too 
much characters");
                                            alert.setContentText("Max.: " + 
f.getDataType().length());
                                            alert.showAndWait();
                                        }
                                    } else {
                                        
records.get(t.getTablePosition().getRow()).set(columnField, newValue);
                                        ((UpdatableRecord) 
records.get(t.getTablePosition().getRow())).update();
                                        System.out.println("Updating: " + 
f.getName() + "/" + newValue);
                                    }
                                    this.refresh();
                                }
                        );
                        return column;
                    })
                    .collect(toList()));

            for (TableColumn tableColumn : tableColumns) {
                this.getColumns().add(tableColumn);
            }
        }
    }
    private void tableRecords(Result result) {
        for (Object o : result) {
            B record = (B) o;
            records.add(record);
        }
        if (records.size() > 0) {
            templateRecord = records.get(0);
        }
        this.setItems(records);
        this.refresh();
    }

    /* Constructor for inverted >single< Record tableView with JOOQ Record 
*/
    public JooqRecordTableView(Table table, Record bRecord) {
        this.setEditable(true);
        this.table = table;
        loadPropertiesForTable();
        Record dataRecord = bRecord;

        ObservableList<ColumnInformation> data = 
FXCollections.observableArrayList();

        //prepare ColumnInformation for each Field
        for (Field field : bRecord.fields()) {
            String comment = propertiesHandle.getProperty(field.getName() + 
"-comment", String.class).toString();
            String tooltip = propertiesHandle.getProperty(field.getName() + 
"-tooltip", String.class).toString();
            Boolean visible = (Boolean) 
propertiesHandle.getProperty(field.getName() + "-visible", Boolean.class);
            Boolean editable = (Boolean) 
propertiesHandle.getProperty(field.getName() + "-editable", Boolean.class);
            if (comment.equals(field.getName() + "-comment")) {
                comment = "";
            }
            if (tooltip.equals(field.getName() + "-tooltip")) {
                tooltip = "";
            }
            List<?> preloadObjects = 
propertiesHandle.getPropertyValueList(field.getName(), String.class);

            if (visible) {
                data.add(new 
ColumnInformation(propertiesHandle.getProperty(field.getName(), 
String.class).toString(), field.getValue(bRecord), comment, field, tooltip,
                        editable, visible, preloadObjects));
            }
        }
        getItems().addAll(data);

        // table definition
        TableColumn<ColumnInformation, String> nameColumn = new 
TableColumn<>("Parameter");
        nameColumn.setCellValueFactory(d -> new 
SimpleObjectProperty(d.getValue().getName()));
        nameColumn.setEditable(false);

        TableColumn<ColumnInformation, Object> valueColumn = new 
TableColumn<>("Wert");
        valueColumn.setEditable(true);
        valueColumn.setCellValueFactory(d -> new 
SimpleObjectProperty(d.getValue().getValue()));
        CellUniversalEditor cellUniversalEditor = new CellUniversalEditor();
        valueColumn.setCellFactory(param -> new CellUniversalEditor());
        valueColumn.setOnEditCommit(
                t -> {
                    ColumnInformation cellData = 
data.get(t.getTablePosition().getRow());
                    Field columnField = cellData.getField();
                    Object newValue = t.getNewValue();

                    if (columnField.getDataType().hasLength()) {
                        String s = (String) newValue;
                        if (columnField.getDataType().length() > 
s.length()) {
                            
data.get(t.getTablePosition().getRow()).setValue(newValue);
                            dataRecord.set(columnField, newValue);
                            ((UpdatableRecord) dataRecord).update();
                            System.out.println("Updating: " + 
columnField.getName() + "/" + newValue);
                        } else {
                            Alert alert = new Alert(Alert.AlertType.ERROR);
                            alert.setHeaderText("Error: too much 
characters");
                            alert.setContentText("Max.: " + 
f.getDataType().length());
                            alert.showAndWait();
                        }
                    } else {
                        
data.get(t.getTablePosition().getRow()).setValue(newValue);
                        dataRecord.set(columnField, newValue);
                        ((UpdatableRecord) dataRecord).update();
                        System.out.println("Updating: " + 
columnField.getName() + "/" + newValue);
                    }
                    this.refresh();
                }
        );

        TableColumn<ColumnInformation, Object> commentColumn = new 
TableColumn<>("Kommentar");
        commentColumn.setCellValueFactory(d -> new 
SimpleObjectProperty(d.getValue().getComment()));
        commentColumn.setEditable(false);
        valueColumn.setSortable(false);
        getColumns().setAll(nameColumn, valueColumn, commentColumn);
    }

    /* Load Properties File for Table*/
    private void loadPropertiesForTable() {
        String propertyPath = propertyDirectory + table.getName();
        propertiesHandle = new PropertiesHandle(propertyPath);
    }

}

class ColumnInformation {
    private String name;
    private Object value;
    private String comment;
    private Field<?> field;
    private String tooltipText;
    private List<?> preloadValues;

    private Boolean editable;
    private Boolean visible;

    ColumnInformation(String name, Object value, String comment, Field<?> 
field, String tooltipText, Boolean editable, Boolean visible, List<?> 
preloadValues) {
        this.setName(name);
        this.setValue(value);
        this.setComment(comment);
        this.setField(field);
        this.setTooltipText(tooltipText);
        this.setVisible(visible);
        this.setEditable(editable);
        this.setPreloadValues(preloadValues);
    }

    public String getTooltipText() {
        return tooltipText;
    }

    public void setTooltipText(String tooltipText) {
        this.tooltipText = tooltipText;
    }

    public Field<?> getField() {
        return field;
    }

    public void setField(Field<?> field) {
        this.field = field;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Object getValue() {
        return value;
    }

    public void setValue(Object value) {
        this.value = value;
    }

    public String getComment() {
        return comment;
    }

    public void setComment(String comment) {
        this.comment = comment;
    }

    public Boolean getEditable() {
        return editable;
    }

    public void setEditable(Boolean editable) {
        this.editable = editable;
    }

    public Boolean getVisible() {
        return visible;
    }

    public void setVisible(Boolean visible) {
        this.visible = visible;
    }

    public List<?> getPreloadValues() {
        return preloadValues;
    }

    public void setPreloadValues(List<?> preloadValues) {
        this.preloadValues = preloadValues;
    }
}


The "universall cell" class representing a TableCell and dynamically adapts 
to record column Datatype

import javafx.scene.Node;
import javafx.scene.control.*;
import javafx.scene.input.KeyCode;
import javafx.util.StringConverter;
import org.jooq.Field;
import org.jooq.Record;
import org.jooq.Table;
import tornadofx.control.DateTimePicker;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

class CellUniversalEditor<R extends Record> extends TableCell<Table<R>, 
Object> {

    private TextField textField;
    private CheckBox checkBox;
    private DateTimePicker dateTimePicker;
    private DatePicker datePicker;
    private Label valueLabel = new Label();
    private Spinner<Integer> integerSpinner;
    private ComboBox comboBox;
    private Field field = null;

    private Object graphicElement;

    private List<?> preloadValues = new ArrayList<>();
    private Boolean cellEditable = false;

    public CellUniversalEditor() {
    }

    /*
    Constructor with preloadValues
     */
    public CellUniversalEditor(Field field, List<?> preloadValues) {
        this.field = field;
        this.preloadValues = preloadValues;
    }

    public Boolean getCellEditable() {
        return cellEditable;
    }

    public void setCellEditable(Boolean cellEditable) {
        this.cellEditable = cellEditable;
    }

    /*
    Override Functions for TableCell
     */
    @Override
    public void startEdit() {
        String dataType = null;
        Object value = null;
        ColumnInformation columnInformation = null;
        value = getValue();

        if (field == null) {
            columnInformation = (ColumnInformation) getTableRow().getItem();
            dataType = 
columnInformation.getField().getDataType().getType().getSimpleName();
            preloadValues = columnInformation.getPreloadValues();
            cellEditable = columnInformation.getEditable();
        } else {
            dataType = field.getDataType().getType().getSimpleName();
            cellEditable = getTableColumn().isEditable();
        }

        if (cellEditable) {
            super.startEdit();
            switch (dataType) {
                case "Boolean":
                    checkBox = new CheckBox();
                    Boolean aBoolean = value == null ? false : (boolean) 
value;
                    checkBox.setSelected(aBoolean);
                    checkBox.setOnAction(event -> 
commitEdit(checkBox.isSelected()));
                    graphicElement = checkBox;
                    break;

                case "LocalDateTime":
                    dateTimePicker = new DateTimePicker();
                    dateTimePicker.setDateTimeValue((LocalDateTime) 
getValue());
                    dateTimePicker.setOnAction(event -> 
commitEdit(dateTimePicker.getDateTimeValue()));
                    graphicElement = dateTimePicker;
                    break;

                case "LocalDate":
                    datePicker = new DateTimePicker();
                    datePicker.setValue((LocalDate) getValue());
                    datePicker.setOnAction(event -> 
commitEdit(datePicker.getValue()));
                    graphicElement = datePicker;
                    break;

                case "Integer":
                    integerSpinner = new Spinner<>();
                    Integer integer = value == null ? 0 : (int) value;
                    SpinnerValueFactory<Integer> valueFactory = new 
SpinnerValueFactory.IntegerSpinnerValueFactory(0, Integer.MAX_VALUE, 
integer);
                    integerSpinner.setValueFactory(valueFactory);
                    integerSpinner.setEditable(true);
                    integerSpinner.getEditor().setOnMouseClicked(event -> 
integerSpinner.getEditor().selectAll());
                    integerSpinner.getEditor().setOnKeyPressed(t -> {
                        if (t.getCode() == KeyCode.ENTER) {
                            String integerString = 
integerSpinner.getEditor().getText();
                            if (valueFactory != null) {
                                StringConverter<Integer> converter = 
valueFactory.getConverter();
                                if (converter != null) {
                                    try {
                                        
valueFactory.setValue(converter.fromString(integerString));
                                        
commitEdit(integerSpinner.valueProperty().getValue());
                                    } catch (NumberFormatException nfe) {
                                        
integerSpinner.getEditor().setText(converter.toString(valueFactory.getValue()));
                                    }
                                }
                            }
                        } else if (t.getCode() == KeyCode.ESCAPE) {
                            cancelEdit();
                        }
                    });
                    integerSpinner.getEditor().selectAll();
                    graphicElement = integerSpinner;
                    break;

                case "String":
                    if (preloadValues.size() > 0) {
                        comboBox = new ComboBox<>();
                        comboBox.getItems().add("");
                        comboBox.getItems().addAll(preloadValues);
                        if (getValue() != null) {
                            comboBox.getSelectionModel().select(getValue());
                        }
                        comboBox.setOnAction(event -> 
commitEdit(comboBox.getValue()));
                        graphicElement = comboBox;
                    } else {
                        String s = value == null ? "" : (String) value;
                        textField = new TextField(s);
                        textField.setMinWidth(this.getWidth() - 
this.getGraphicTextGap() * 2);
                        textField.setOnKeyPressed(t -> {
                            if (t.getCode() == KeyCode.ENTER) {
                                commitEdit(textField.getText());
                            } else if (t.getCode() == KeyCode.ESCAPE) {
                                cancelEdit();
                            }
                        });
                        graphicElement = textField;
                    }
                    break;

                default:
                    Alert alert = new Alert(Alert.AlertType.ERROR);
                    alert.setHeaderText("Form Error");
                    alert.setContentText("Datatype not supported!");
                    alert.showAndWait();
            }
            setGraphic((Node) graphicElement);
            setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
        }
    }

    @Override
    public void cancelEdit() {
        super.cancelEdit();
        setGraphic(valueLabel);
    }

    @Override
    public void updateItem(Object item, boolean empty) {
        super.updateItem(item, empty);
        setGraphic(valueLabel);
        setContentDisplay(ContentDisplay.GRAPHIC_ONLY);

        if (empty || item == null) {
            setText("");
            valueLabel.setText("");
        } else {
            if (isEditing()) {
            } else {
                setText(getValue().toString());
                switch (item.getClass().getSimpleName()) {
                    case "Boolean":
                        if ((Boolean) item == true) {
                            valueLabel.setText("Yes");
                        } else {
                            valueLabel.setText("No");
                        }
                        break;
                    case "LocalDate":
                        valueLabel.setText(date2GermanFormat((LocalDate) 
item));
                        break;
                    case "LocalDateTime":
                        
valueLabel.setText(datetime2GermanFormat((LocalDateTime) item));
                        break;
                    default:
                        valueLabel.setText(getValue().toString());
                }
            }
        }
    }

    private Object getValue() {
        return getItem() == null ? null : getItem();
    }
}

The Properties File - handling the entries for each Table / Column and 
creating - adding new Information if needed

import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;

public class PropertiesHandle {

    private SortedProperties properties;
    private String propertyPath;

    public PropertiesHandle(String propertyPath) {
        this.propertyPath = propertyPath + ".properties";

        Path path = Paths.get(this.propertyPath);
        if (Files.exists(path)) {
            properties = new SortedProperties();
            try {
                properties.load(new FileInputStream(this.propertyPath));
            } catch (IOException e) {
                e.printStackTrace();
            }
        } else {
            properties = new SortedProperties();
            writeProperties2File();
        }
    }

    private void writeProperties2File() {
        File file = new File(propertyPath);
        try {
            FileOutputStream fileOut = FileUtils.openOutputStream(file);
            properties.store(fileOut, propertyPath);
            fileOut.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public Object getProperty(String key, Class aClass) {
        Object o = null;

        String value = properties.getProperty(key);
        //if key exists
        if (value != null) {
            //if key is empty
            if (!value.equals("")) {
                switch (aClass.getSimpleName()) {
                    case "boolean":
                        o = Boolean.valueOf(value);
                        break;

                    case "Boolean":
                        o = Boolean.valueOf(value);
                        break;

                    default:
                        o = aClass.cast(value);
                }
            } else {
                switch (aClass.getSimpleName()) {
                    case "boolean":
                        o = new Boolean(false);
                        break;
                    case "Boolean":
                        o = new Boolean(false);
                        break;
                    default:
                        o = key;
                }

            }
            //if key not exists
        } else {
            properties.setProperty(key, "");
            writeProperties2File();
            switch (aClass.toString()) {
                case "boolean":
                    o = new Boolean(false);
                    break;

                default:
                    o = key;
            }
        }

        return o;
    }

    public List getPropertyValueList(String key, Class aClass) {
        List objects = new ArrayList<>();
        String sValue = (String) getProperty(key + "-values", String.class);
        if (!sValue.equals(key + "-values")) {
            List<String> strings = Arrays.asList(sValue.split(","));
            for (String s : strings){
                objects.add(s.trim());
            }
        }
        return objects;
    }

}

class SortedProperties extends Properties {
    public Enumeration keys() {
        Enumeration keysEnum = super.keys();
        Vector<String> keyList = new Vector<String>();
        while (keysEnum.hasMoreElements()) {
            keyList.add((String) keysEnum.nextElement());
        }
        Collections.sort(keyList);
        return keyList.elements();
    }
}



-- 
You received this message because you are subscribed to the Google Groups "jOOQ 
User Group" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/d/optout.

Reply via email to