DataTableColumnsFactory.java

package sk.iway.iwcm.system.datatable;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

import org.apache.struts.util.ResponseUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import sk.iway.iwcm.Logger;
import sk.iway.iwcm.Tools;
import sk.iway.iwcm.i18n.Prop;
import sk.iway.iwcm.system.adminlog.AuditEntityListener;
import sk.iway.iwcm.system.datatable.annotations.DataTableColumnNested;
import sk.iway.iwcm.system.datatable.annotations.DataTableTabs;
import sk.iway.iwcm.system.datatable.json.DataTableColumn;
import sk.iway.iwcm.system.datatable.json.DataTableTab;

import javax.servlet.http.HttpServletRequest;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

public class DataTableColumnsFactory {
    Class<?> dto;
    public DataTableColumnsFactory(String clazz) {
        try {
            dto = Class.forName(clazz);
        } catch (ClassNotFoundException e) {
            Logger.error(DataTableColumnsFactory.class, e);
        }
    }

    public List<DataTableColumn> getColumns(String fieldPrefix) {
        List<DataTableColumn> columns = new ArrayList<>();

        if (dto != null) {
            Field[] declaredFields = AuditEntityListener.getDeclaredFieldsTwoLevels(dto);

            //Get from WebjetAppStore annotation commonSettings attribute (true - we want commmon settings tab and fields, false - we don't want common settings tab nor fields)
            boolean includeCommonSettings = true;
            if(dto.isAnnotationPresent(sk.iway.iwcm.system.annotations.WebjetAppStore.class)) {
                includeCommonSettings = dto.getAnnotation(sk.iway.iwcm.system.annotations.WebjetAppStore.class).commonSettings();
            }

            for (Field declaredField : declaredFields) {

                if (declaredField.isAnnotationPresent(sk.iway.iwcm.system.datatable.annotations.DataTableColumnNested.class)) {
                    //rekurzia
                    DataTableColumnsFactory dtcf = new DataTableColumnsFactory(declaredField.getGenericType().getTypeName());

                    //ziskaj prefix premennej (aby vzniklo editorFields.allowChangeUrl), defaultne podla mena premennej, alebo z anotacie
                    DataTableColumnNested annotation = declaredField.getAnnotation(DataTableColumnNested.class);
                    String nestedFieldPrefix;
                    if ("auto".equals(annotation.prefix())) nestedFieldPrefix = declaredField.getName()+".";
                    else nestedFieldPrefix = annotation.prefix();
                    if (Tools.isNotEmpty(fieldPrefix)) nestedFieldPrefix = fieldPrefix + nestedFieldPrefix;

                    List<DataTableColumn> columnsNested = dtcf.getColumns(nestedFieldPrefix);

                    columns.addAll(columnsNested);
                }

                if (!declaredField.isAnnotationPresent(sk.iway.iwcm.system.datatable.annotations.DataTableColumn.class)) {
                    continue;
                }

                //If we dont want common settings, skip all fields with tab commonSettings
                if(includeCommonSettings==false) {
                    if("commonSettings".equals( declaredField.getAnnotation(sk.iway.iwcm.system.datatable.annotations.DataTableColumn.class).tab()) ) continue;
                }

                columns.add(new DataTableColumn(dto, declaredField, fieldPrefix));
            }
        }

        return columns;
    }

    public String getColumnsJson() throws JsonProcessingException {
        ObjectMapper mapper = new ObjectMapper();
        List<DataTableColumn> columns = getColumns(null);
        List<DataTableColumn> columnsSorted = sortColumns(columns);
        return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(columnsSorted);
    }

    public static List<DataTableColumn> sortColumns(List<DataTableColumn> columns) {
        List<DataTableColumn> columnsSorted = new ArrayList<>();
        List<DataTableColumn> columnsToSort = new ArrayList<>();

        //najskor nakopirujeme columny kde nie je nastaveny sort atribut, tie predpokladame, ze su v korektnom poradi
        for (DataTableColumn c : columns) {
            if ("FIRST".equals(c.getSortAfter())) {
                columnsSorted.add(0, c);
            }
            else if (Tools.isEmpty(c.getSortAfter())) {
                columnsSorted.add(c);
            } else {
                columnsToSort.add(c);
            }
        }

        int failsafe = 0;
        while (columnsToSort.size()>0 && failsafe++ < 1000) {
            DataTableColumn c = columnsToSort.get(0);

            //prehladaj sorted a pridaj ho
            int i;
            for (i=0; i<columnsSorted.size(); i++) {
                DataTableColumn cs = columnsSorted.get(i);
                if (cs.getData()!= null && cs.getData().equals(c.getSortAfter())) {
                    if (i == columnsSorted.size()-1) columnsSorted.add(c);
                    else columnsSorted.add(i+1, c);
                    c = null;
                    break;
                }
            }

            //bolo pridane do sorted, removneme ho
            columnsToSort.remove(0);
            //inak ho pridame na koniec, asi je zavisle na nejakom poli v toSort pred nim
            if (c != null) {
                if (i>500) Logger.debug(DataTableColumnsFactory.class, "sortColumns("+failsafe+") POZOR SKONTROLUJTE sortAfter v COLUMNS definicii, prilis vysoke i sortovania - nenaslo sa, c="+c.getName());
                columnsToSort.add(c);
            }
        }

        if (columnsToSort.size()>0) {
            //nieco je zle zosortovane, pre istotu pridame na koniec
            columnsSorted.addAll(columnsToSort);
        }

        return columnsSorted;
    }

    public static String translate(String str) {

        String result = str;

        if ("[[#{}]]".equals(str)) return "";

        Prop prop = null;

        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (requestAttributes != null) {
            HttpServletRequest request = requestAttributes.getRequest();
            prop = Prop.getInstance(request);
        } else {
            //use RequestBean lng instead of request
            prop = Prop.getInstance();
        }
        if (prop != null) {
            int failsafe = 0;
            int startIndex = result.indexOf("[[#{");
            boolean hasDot = result.contains(".");
            if (hasDot) {


                if (startIndex == -1) {
                    result = escapeSpecialChars(prop.getText(result));
                } else {
                    while (startIndex!=-1 && failsafe++<20) {
                        hasDot = result.contains(".");
                        if (startIndex<0 && hasDot == false) return result;

                        int endIndex = result.indexOf("}]]", startIndex);
                        if (endIndex>startIndex) {
                            String key = result.substring(startIndex+4, endIndex);
                            String translated = escapeSpecialChars(prop.getText(key));
                            result = Tools.replace(result, result.substring(startIndex, endIndex+3), translated);
                        }
                        startIndex = result.indexOf("[[#{");
                    }
                }
            }
        }
        return result;
    }

    private static String escapeSpecialChars(String translated) {
        return ResponseUtils.filter(translated);
    }

    /**
     * Metoda pre ziskanie vsetkych moznych tabov z anotacii DataTableColumn pre danu triedu
     * @return List<DataTableTab>
     */
    public List<DataTableTab> getTabs() {
        List<DataTableTab> result = new ArrayList<>();

        if (dto == null) {
            return result;
        }

        //Get from WebjetAppStore annotation commonSettings attribute (true - we want commmon settings tab and fields, false - we don't want common settings tab nor fields)
        boolean includeCommonSettings = true;
        if(dto.isAnnotationPresent(sk.iway.iwcm.system.annotations.WebjetAppStore.class)) {
            includeCommonSettings = dto.getAnnotation(sk.iway.iwcm.system.annotations.WebjetAppStore.class).commonSettings();
        }

        if (dto.isAnnotationPresent(DataTableTabs.class)) {
            DataTableTabs annotation = dto.getAnnotation(DataTableTabs.class);
            for (sk.iway.iwcm.system.datatable.annotations.DataTableTab tab : annotation.tabs()) {
                result.add(new DataTableTab(tab));
            }
            if (includeCommonSettings) {
                result.add(new DataTableTab("commonSettings", "commonSettings", false));
            }
        } else {
            Field[] declaredFields = AuditEntityListener.getDeclaredFieldsTwoLevels(dto);

            for (Field declaredField : declaredFields) {
                if (!declaredField.isAnnotationPresent(sk.iway.iwcm.system.datatable.annotations.DataTableColumn.class)) {
                    continue;
                }

                sk.iway.iwcm.system.datatable.annotations.DataTableColumn annotation = declaredField.getAnnotation(sk.iway.iwcm.system.datatable.annotations.DataTableColumn.class);
                String tab = annotation.tab();

                //If we dont want common settings, skip all fields with tab commonSettings
                if(includeCommonSettings==false && "commonSettings".equals(tab)) continue;

                //If tab is not empty and tab is not already in result, add it
                if (Tools.isNotEmpty(tab) && result.stream().noneMatch(r -> r.getId().equals(tab))) {
                    result.add(new DataTableTab(annotation, result.isEmpty()));
                }
            }
        }

        return result;
    }

    /**
     * Returns JSON object from DataTableTabs annotation
     * @return
     * @throws JsonProcessingException
     */
    public String getTabsJson() throws JsonProcessingException {
        ObjectMapper mapper = new ObjectMapper();
        List<DataTableTab> tabs = getTabs();
        return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(tabs);
    }

    /**
     * Returns translated app title from nameKey attribute of WebjetAppStore annotation
     * @return
     */
    public String getTitle() {
        if (dto == null) {
            return null;
        }
        if(dto.isAnnotationPresent(sk.iway.iwcm.system.annotations.WebjetAppStore.class)) {
            String key = dto.getAnnotation(sk.iway.iwcm.system.annotations.WebjetAppStore.class).nameKey();
            return translate(key);
        }
        return null;
    }
}