DataTableColumn.java

package sk.iway.iwcm.system.datatable.json;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;

import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import lombok.Getter;
import lombok.Setter;
import sk.iway.iwcm.*;
import sk.iway.iwcm.i18n.Prop;
import sk.iway.iwcm.system.datatable.DataTableColumnType;
import sk.iway.iwcm.system.datatable.DataTableColumnsFactory;

import javax.persistence.Transient;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;

/**
 * Trieda pre generovanie JSONu pre DataTable {@see https://datatables.net/} z
 * anotacie {@link sk.iway.iwcm.system.datatable.annotations.DataTableColumn}
 * nad poliami objektu. Trieda je priamo mapovatelna pomocou
 * {@link com.fasterxml.jackson.databind.ObjectMapper}
 */
@JsonInclude(JsonInclude.Include.NON_NULL)
@Getter
@Setter
public class DataTableColumn {
    private String data;
    private String name;
    private String title;
    @JsonIgnore
    private String titleKeyOriginal; //originalny prekladovy kluc title

    private String defaultContent;

    private String className;
    private String renderFormat;
    private String renderFormatLinkTemplate;
    private String renderFormatPrefix;

    private DataTableColumnEditor editor;
    private Boolean visible;
    private Boolean hidden;
    private Boolean hiddenEditor;
    private Boolean filter;

    private String sortAfter;
    private String perms;

    private Boolean array;

    private Boolean orderable;

    @SuppressWarnings("rawtypes")
    public DataTableColumn(Class controller, Field field, String fieldPrefix) {
        String fieldPrefixNotNull = fieldPrefix;
        if (fieldPrefixNotNull == null)
            fieldPrefixNotNull = "";
        name = fieldPrefixNotNull + field.getName();
        data = fieldPrefixNotNull + field.getName();

        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (requestAttributes==null) return;

        HttpServletRequest request = requestAttributes.getRequest();
        Prop prop = Prop.getInstance(request);

        setPropertiesFromFieldType(field);
        setPropertiesFromAnnotation(controller, field, prop);
        setEditorPropertiesFromField(field);

        setFinalProperties(field);
        setCellNotEditable(field);
    }

    private void setPropertiesFromFieldType(Field field) {
        // DATE
        if (field.getType().isAssignableFrom(Date.class) || field.getType().isAssignableFrom(java.sql.Date.class)) {
            renderFormat = "dt-format-date-time";
            addClassName("dt-style-date");
        }
        if (field.getType().isArray()) array = Boolean.TRUE;
        else array = Boolean.FALSE;
    }

    /**
     * Metoda nastavi JSON podla anotacie
     * {@link sk.iway.iwcm.system.datatable.annotations.DataTableColumn} na danom
     * poli. Anotacie maju rovnaky nazov a format ako generovany JSON.
     *
     * @param field - reflection field, ktore ma nastavenu anotaciu
     *              {@link sk.iway.iwcm.system.datatable.annotations.DataTableColumn}
     */
    @SuppressWarnings("rawtypes")
    private void setPropertiesFromAnnotation(Class controller, Field field, Prop prop) {
        sk.iway.iwcm.system.datatable.annotations.DataTableColumn annotation = field.getAnnotation(sk.iway.iwcm.system.datatable.annotations.DataTableColumn.class);
        if (annotation == null) {
            return;
        }

        DataTableColumnType[] inputType = annotation.inputType();
        if (inputType.length > 0) {
            for (DataTableColumnType type : inputType) {
                setPropertiesFromType(type, annotation, prop);
            }
        }

        if (Tools.isNotEmpty(annotation.data())) {
            data = annotation.data();
        }

        if (Tools.isNotEmpty(annotation.name())) {
            name = annotation.name();
        }

        if (Tools.isNotEmpty(annotation.title())) {
            String[] titleArr = annotation.title().split(";");

            titleKeyOriginal = titleArr[0];

            if(titleArr.length > 1)
                title = prop.getTextWithParams(titleArr[0], Arrays.copyOfRange(titleArr, 1, titleArr.length));
            else {
                if (inputType!=null && inputType.length==1 && inputType[0].equals(DataTableColumnType.BOOLEAN_TEXT)) {
                    //title will be as option for checkbox
                    title = " ";
                } else {
                    title = prop.getText(annotation.title());
                }
            }


            // skus implementovat zapis z pug suboru
            title = DataTableColumnsFactory.translate(title);

            if (Tools.isEmpty(title) || " ".equals(title)) addClassName("empty-title");
        } else {
            String titleKey = "components." + toLowerUnderscore(controller.getSimpleName()) + "." + toLowerUnderscore(field.getName());
            title = prop.getText(titleKey);
            if (titleKey.equals(title)) title = Tools.replace(toLowerUnderscore(field.getName()), "_", " ");
        }

        if (inputType.length > 0 && inputType[0] == DataTableColumnType.STATIC_TEXT) {
            if (Tools.isNotEmpty(title)) {
                editor.addAttr("data-value", title);
            }
            title = " ";
        }

        if (hidden == null && (Tools.isEmpty(title) || " ".equals(title))) {
            // ak nie je nastaveny titulok, tak stlpec nezobraz v DB, asi sa jedna o nejaky
            // boolean atribut pre editor
            hidden = Boolean.TRUE;
        }

        String[] strings = annotation.defaultContent();
        if (strings != null && strings.length > 0) {
            defaultContent = strings[0];
        }

        String annotationClassName = annotation.className();
        if (Tools.isNotEmpty(annotationClassName)) {
            if (annotationClassName.startsWith("!")) className = annotationClassName;
            else addClassName(annotationClassName);
        }

        if (Tools.isNotEmpty(annotation.renderFormat())) {
            renderFormat = annotation.renderFormat();
        }

        if (Tools.isNotEmpty(annotation.renderFormatLinkTemplate())) {
            renderFormatLinkTemplate = annotation.renderFormatLinkTemplate();
        }

        if (Tools.isNotEmpty(annotation.renderFormatPrefix())) {
            renderFormatPrefix = annotation.renderFormatPrefix();
        }

        boolean[] _visible = annotation.visible();
        if (_visible.length > 0) {
            this.visible = _visible[0];
        }

        boolean[] _hidden = annotation.hidden();
        if (_hidden.length > 0) {
            this.hidden = _hidden[0];
        }

        boolean[] _hiddenEditor = annotation.hiddenEditor();
        if (_hiddenEditor.length > 0) {
            this.hiddenEditor = _hiddenEditor[0];
        }

        boolean[] _filter = annotation.filter();
        if (_filter.length > 0) {
            this.filter = _filter[0];
        } else if (field.getAnnotation(Transient.class)!=null) {
            if ("userFullName".equals(field.getName())) {
                //userFullName vieme standardne vyhladavat v DatatableRestControllerV2.addSpecSearchUserFullName
            } else {
                //ak je entita @Transient asi nie je v DB, filter pre istotu vypneme, ak treba musi sa zapnut manualne nastavenim filter: true
                this.filter = Boolean.FALSE;
            }
        } else if (className!=null && className.contains("todo")) {
            //ak ma className to do tiez nezobraz filter, na 99% nefunguje
            this.filter = Boolean.FALSE;
        }

        String tab = annotation.tab();
        if (Tools.isNotEmpty(tab)) {
            if (editor == null) {
                editor = new DataTableColumnEditor();
            }
            editor.setTab(tab);
        }

        sortAfter = annotation.sortAfter();
        perms = annotation.perms();

        String defaultValue = annotation.defaultValue();
        if (Tools.isNotEmpty(defaultValue)) {
            if (editor == null) {
                editor = new DataTableColumnEditor();
            }
            if ("{currentDomain}".equals(defaultValue)) {
                if (Constants.getBoolean("multiDomainEnabled")) {
                    RequestBean rb = SetCharacterEncodingFilter.getCurrentRequestBean();
                    defaultValue = rb.getDomain();
                } else {
                    defaultValue = "";
                }
            } else if ("{currentDate}".equals(defaultValue)) {
                defaultValue = Tools.formatDate(Tools.getNow());
            } else if ("{currentDateTimeSeconds}".equals(defaultValue)) {
                defaultValue = Tools.formatDateTimeSeconds(Tools.getNow());
            } else if ("{currentTime}".equals(defaultValue)) {
                defaultValue = Tools.formatTime(Tools.getNow());
            }
            editor.setDef(defaultValue);
        }

        boolean[] _orderable = annotation.orderable();
        if (_orderable.length > 0) {
            this.orderable = _orderable[0];
        } else if (Boolean.FALSE.equals(this.filter)) {
            //ak je vypnuty filter a nenastavim orderable, tak predpokladam, ze nema byt ani orderable
            this.orderable = false;
        }
    }

    private void setEditorPropertiesFromField(Field field) {
        if (editor == null) {
            editor = new DataTableColumnEditor();
        }

        editor.setPropertiesFromField(field);

        if (editor.isRequired()) addClassName("required");

        if (Tools.isEmpty(editor.getMessage()) && Tools.isNotEmpty(titleKeyOriginal) && "&nbsp".equals(titleKeyOriginal)==false && " ".equals(titleKeyOriginal)==false) {
            String key = titleKeyOriginal+".tooltip";
            if (key.startsWith("[[#{")) {
                key = Tools.replace(key, "[[#{", "");
                key = Tools.replace(key, "}]]", "");
            }
            String translated = DataTableColumnsFactory.translate(key);
            if (Tools.isNotEmpty(translated) && key.equals(translated)==false) {
                editor.setMessage(translated);
            }
        }

        if (editor.isEmpty()) {
            this.editor = null;
        }
    }

    /**
     * Metoda nastavi do JSONu atributy, podla toho, ako je natavena anotacia
     * inputType {@link DataTableColumnType}
     *
     * @param dataTableColumnType - {@link DataTableColumnType} - nastaveny v
     *                            anotacii inputType
     */
    private void setPropertiesFromType(DataTableColumnType dataTableColumnType, sk.iway.iwcm.system.datatable.annotations.DataTableColumn annotation, Prop prop) {
        if (dataTableColumnType == DataTableColumnType.ID) {
            if (Tools.isEmpty(data)) {
                data = "id";
            }
            defaultContent = "";
            addClassName("dt-select-td");
            renderFormat = "dt-format-selector";
            if (editor == null) {
                editor = new DataTableColumnEditor();
            }
            editor.setType("hidden");
        }

        if (dataTableColumnType == DataTableColumnType.TEXT) {
            renderFormat = "dt-format-text";
            if (editor == null) {
                editor = new DataTableColumnEditor();
            }
            editor.setType("text");
        }

        if (dataTableColumnType == DataTableColumnType.TEXT_NUMBER || dataTableColumnType == DataTableColumnType.NUMBER) {
            renderFormat = "dt-format-number";
            if (dataTableColumnType == DataTableColumnType.TEXT_NUMBER) renderFormat+="--text";
            addClassName("dt-style-number");

            if (editor == null) {
                editor = new DataTableColumnEditor();
            }
            editor.setType("text");
            HashMap<String, String> attrs = new HashMap<>();
            attrs.put("type", "number");
            editor.setAttr(attrs);
        }

        if (dataTableColumnType == DataTableColumnType.TEXT_NUMBER_INVISIBLE) {
            renderFormat = "dt-format-none";
            visible = false;

            if (editor == null) {
                editor = new DataTableColumnEditor();
            }
            editor.setType("text");
            HashMap<String, String> attrs = new HashMap<>();
            attrs.put("type", "number");
            editor.setAttr(attrs);
        }

        if (dataTableColumnType == DataTableColumnType.QUILL) {
            renderFormat = "dt-format-text";
            if (editor == null) {
                editor = new DataTableColumnEditor();
            }
            editor.setType("quill");
        }

        if (dataTableColumnType == DataTableColumnType.TEXTAREA) {
            renderFormat = "dt-format-text-wrap";
            addClassName("dt-style-text-wrap");

            if (editor == null) {
                editor = new DataTableColumnEditor();
            }
            editor.setType("textarea");

            if (annotation.className()!=null && annotation.className().contains("show-html")) editor.addAttr("entityDecode", "false");
        }

        if (dataTableColumnType == DataTableColumnType.DATE) {
            renderFormat = "dt-format-date";
            addClassName("dt-style-date");

            if (editor == null) {
                editor = new DataTableColumnEditor();
            }
            editor.setType("date");
        }

        if (dataTableColumnType == DataTableColumnType.DATETIME) {
            renderFormat = "dt-format-date-time";
            addClassName("dt-style-date");
            addClassName("dt-style-datetime");

            if (editor == null) {
                editor = new DataTableColumnEditor();
            }
            editor.setType("datetime");
        }

        if (dataTableColumnType == DataTableColumnType.TIME_HM) {
            renderFormat = "dt-format-time-hm";
            addClassName("dt-style-date");
            addClassName("dt-style-time");

            if (editor == null) {
                editor = new DataTableColumnEditor();
            }
            editor.setType("timehm");
        }

        if (dataTableColumnType == DataTableColumnType.TIME_HMS) {
            renderFormat = "dt-format-time-hms";
            addClassName("dt-style-date");
            addClassName("dt-style-time");

            if (editor == null) {
                editor = new DataTableColumnEditor();
            }
            editor.setType("timehms");
        }

        if (dataTableColumnType == DataTableColumnType.OPEN_EDITOR) {
            addClassName("dt-row-edit");
            renderFormat = "dt-format-text";
            renderFormatLinkTemplate = "javascript:;";
            renderFormatPrefix = "<i class=\"ti ti-pencil\"></i> ";

            if (editor == null) {
                editor = new DataTableColumnEditor();
            }
            editor.setType("text");
        }

        if (dataTableColumnType == DataTableColumnType.DISABLED) {
            if (editor == null) {
                editor = new DataTableColumnEditor();
            }

            editor.addAttr("disabled", "disabled");
        }

        if (dataTableColumnType == DataTableColumnType.GALLERY_IMAGE) {
            title = "";
            hidden = Boolean.FALSE;
        }

        if (dataTableColumnType == DataTableColumnType.SELECT || dataTableColumnType == DataTableColumnType.MULTISELECT) {
            renderFormat = "dt-format-select";
            if (editor == null) {
                editor = new DataTableColumnEditor();
            }
            editor.setType("select");

            if (dataTableColumnType == DataTableColumnType.MULTISELECT) {
                editor.setMultiple(Boolean.TRUE);
                //editor.addAttr("multiple", "multiple");
            }

            if (editor.getOptions() == null) {
                editor.setOptions(new ArrayList<>());
            }
        }

        if (dataTableColumnType == DataTableColumnType.BOOLEAN) {
            renderFormat = "dt-format-boolean-true";
            if (editor == null) {
                editor = new DataTableColumnEditor();
            }
            editor.setType("checkbox");
        }

        if (dataTableColumnType == DataTableColumnType.BOOLEAN_TEXT) {
            renderFormat = "dt-format-boolean-true";
            if (editor == null) {
                editor = new DataTableColumnEditor();
            }
            editor.setType("checkbox");
            if (editor.getOptions() == null) {
                List<LabelValue> options = new ArrayList<>();
                options.add(new LabelValue(prop.getText(annotation.title()), "true"));
                editor.setOptions(options);
            }
        }

        if (dataTableColumnType == DataTableColumnType.CHECKBOX) {
            renderFormat = "dt-format-checkbox";
            if (editor == null) {
                editor = new DataTableColumnEditor();
            }
            editor.setType("checkbox");
        }

        if (dataTableColumnType == DataTableColumnType.JSON) {
            renderFormat = "dt-format-json";
            addClassName("dt-style-json");

            if (editor == null) {
                editor = new DataTableColumnEditor();
            }
            editor.setType("json");
        }

        if (dataTableColumnType == DataTableColumnType.DATATABLE) {
            renderFormat = "dt-format-datatable";

            if (editor == null) {
                editor = new DataTableColumnEditor();
            }
            editor.setType("datatable");
        }

        if (dataTableColumnType == DataTableColumnType.ELFINDER) {
            renderFormat = "dt-format-elfinder";

            if (editor == null) {
                editor = new DataTableColumnEditor();
            }
            editor.setType("elfinder");
        }

        if (dataTableColumnType == DataTableColumnType.WYSIWYG) {
            renderFormat = "dt-format-wysiwyg";
            addClassName("dt-style-wysiwyg");
            addClassName("cell-not-editable");

            if (editor == null) {
                editor = new DataTableColumnEditor();
            }
            editor.setType("wysiwyg");
        }

        if (dataTableColumnType == DataTableColumnType.JSTREE) {
            renderFormat = "dt-format-jstree";
            addClassName("dt-style-jstree");

            if (editor == null) {
                editor = new DataTableColumnEditor();
            }
            editor.setType("jsTree");
        }

        if (dataTableColumnType == DataTableColumnType.HIDDEN) {
            renderFormat = "dt-format-hidden";
            hidden = Boolean.TRUE;
            if (editor == null) {
                editor = new DataTableColumnEditor();
            }
            editor.setType("hidden");
        }

        if (dataTableColumnType == DataTableColumnType.RADIO) {
            renderFormat = "dt-format-radio";
            if (editor == null) {
                editor = new DataTableColumnEditor();
            }
            editor.setType("radio");
        }

        if (dataTableColumnType == DataTableColumnType.PASSWORD) {
            renderFormat = "dt-format-text";
            if (editor == null) {
                editor = new DataTableColumnEditor();
            }
            editor.setType("password");
            editor.addAttr("autocomplete", "off");
        }

        if (dataTableColumnType == DataTableColumnType.ATTRS) {
            renderFormat = "dt-format-attrs";
            addClassName("dt-style-attrs");

            if (editor == null) {
                editor = new DataTableColumnEditor();
            }
            editor.setType("attrs");
        }

        if (dataTableColumnType == DataTableColumnType.COLOR) {
            renderFormat = "dt-format-color";
            if (editor == null) {
                editor = new DataTableColumnEditor();
            }
            editor.setType("color");
        }

        if (dataTableColumnType == DataTableColumnType.IFRAME) {
            renderFormat = "dt-format-iframe";

            if (editor == null) {
                editor = new DataTableColumnEditor();
            }
            editor.setType("iframe");
        }

        if (dataTableColumnType == DataTableColumnType.BASE64) {
            renderFormat = "dt-format-base64";
            if (editor == null) {
                editor = new DataTableColumnEditor();
            }
            editor.setType("base64");
        }

        if (dataTableColumnType == DataTableColumnType.STATIC_TEXT) {
            renderFormat = "dt-format-static-text";
            if (editor == null) {
                editor = new DataTableColumnEditor();
            }
            editor.setType("staticText");
        }

        if (dataTableColumnType == DataTableColumnType.IMAGE_RADIO) {
            renderFormat = "dt-format-image-radio";
            if (editor == null) {
                editor = new DataTableColumnEditor();
            }
            editor.setType("imageRadio");
        }
    }

    private void setFinalProperties(Field field) {
        sk.iway.iwcm.system.datatable.annotations.DataTableColumn annotation = field.getAnnotation(sk.iway.iwcm.system.datatable.annotations.DataTableColumn.class);
        if (annotation == null) {
            return;
        }
        DataTableColumnType[] inputType = annotation.inputType();
        if (inputType.length > 0) {
            DataTableColumnType dataTableColumnType = inputType[0];
            if (dataTableColumnType == DataTableColumnType.DATATABLE) {
                try {
                    String attrName = "data-dt-field-dt-columns";
                    String classNameAttr = editor.getAttr().get(attrName);
                    String json = new DataTableColumnsFactory(classNameAttr).getColumnsJson();
                    editor.addAttr(attrName, json);
                } catch (Exception e) {
                    Logger.error(DataTableColumn.class, e);
                }
            }
        }

    }

    /**
     * Nastavi className na cell-not-editable pre needitovatelne bunky (pre editaciu danej bunky)
     * @param field
     */
    private void setCellNotEditable(Field field) {
        boolean notEditable = false;
        String columnType = "";
        if (editor != null && editor.getType()!=null) columnType = editor.getType();
        if (hiddenEditor!=null && hiddenEditor.booleanValue()==true) notEditable = true;
        else if ("hidden".equals(columnType)) {
            notEditable = true;
        }
        else if (editor != null && editor.getAttr()!=null && ("disabled".equals(editor.getAttr().get("disabled")) || "true".equals(editor.getAttr().get("disabled")))) notEditable = true;

        if (notEditable) {
            addClassName("cell-not-editable");
        }
    }

    private String toLowerUnderscore(String str) {
        String underscored = str.replaceAll("([^_A-Z])([A-Z])", "$1_$2").toLowerCase();
        if (underscored.endsWith("_dto")) underscored = underscored.substring(0, underscored.length()-4);
        if (underscored.endsWith("_bean")) underscored = underscored.substring(0, underscored.length()-5);
        if (underscored.endsWith("_entity")) underscored = underscored.substring(0, underscored.length()-7);

        return underscored;
    }

    private void addClassName(String addClassName) {
        if (Tools.isEmpty(addClassName)) return;

        //add multiweb class name only in multiweb
        if ("multiweb-noteditable".equals(addClassName) && InitServlet.isTypeCloud()==false) return;

        if (Tools.isEmpty(className)) className = addClassName.trim();
        else className += " "+addClassName.trim();
    }

}