package jnpf.flowable.util; import jnpf.util.JsonUtil; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Locale; import java.util.Map; /** Renders the selected audit fields of the current form for a flow notification. */ final class FlowAuditTitleUtil { interface ValueResolver { String resolve(Map field, Map config, Object value, Map row); } private static final int MAX_SUFFIX_LENGTH = 200; private FlowAuditTitleUtil() { } static String append(String title, String suffix) { if (title == null || title.trim().isEmpty() || suffix == null || suffix.trim().isEmpty() || title.endsWith("/" + suffix)) { return title; } return title + "/" + suffix; } static String suffix(String formDataJson, Map data) { return suffix(formDataJson, data, (field, config, value, row) -> display(value)); } static String suffix(String formDataJson, Map data, ValueResolver resolver) { if (formDataJson == null || data == null || data.isEmpty()) { return null; } try { Map form = JsonUtil.stringToMap(formDataJson); if (form == null) { return null; } List values = new ArrayList<>(); collect(list(form.get("fields")), data, values, resolver); if (values.isEmpty()) { return null; } String suffix = String.join("/", values); return suffix.length() > MAX_SUFFIX_LENGTH ? suffix.substring(0, MAX_SUFFIX_LENGTH) : suffix; } catch (RuntimeException ignored) { return null; } } @SuppressWarnings("unchecked") private static void collect(List fields, Map row, List values, ValueResolver resolver) { for (Object raw : fields) { if (!(raw instanceof Map)) { continue; } Map field = (Map) raw; Object rawConfig = field.containsKey("config") ? field.get("config") : field.get("__config__"); if (!(rawConfig instanceof Map)) { continue; } Map config = (Map) rawConfig; List children = list(config.get("children")); if ("table".equals(config.get("jnpfKey"))) { Object tableData = value(row, model(field)); List rows = list(tableData); // A configured child table contributes values from its actual first row only. if (!rows.isEmpty() && rows.get(0) instanceof Map) { collect(children, (Map) rows.get(0), values, resolver); } continue; } String model = model(field); if (model != null && Boolean.parseBoolean(String.valueOf(config.get("biz_log_enabled"))) && !sensitive(model, config)) { String display; try { display = resolver.resolve(field, config, value(row, model), row); } catch (RuntimeException ignored) { display = null; } if (display != null) { values.add(display); } } if (!children.isEmpty()) { collect(children, row, values, resolver); } } } private static String model(Map field) { Object raw = field.containsKey("vModel") ? field.get("vModel") : field.get("__vModel__"); return raw == null || String.valueOf(raw).trim().isEmpty() ? null : String.valueOf(raw).trim(); } private static Object value(Map row, String model) { if (model == null) { return null; } Object direct = row.get(model); if (direct != null) { return direct; } for (Map.Entry entry : row.entrySet()) { if (entry.getKey() != null && model.equalsIgnoreCase(entry.getKey())) { return entry.getValue(); } } return null; } static String display(Object raw) { if (raw == null || raw instanceof Map) { return null; } if (raw instanceof List) { List parts = new ArrayList<>(); for (Object item : (List) raw) { if (item != null && !(item instanceof Map) && !String.valueOf(item).trim().isEmpty()) { parts.add(String.valueOf(item).trim()); } } return parts.isEmpty() ? null : String.join(",", parts); } String value = String.valueOf(raw).trim(); return value.isEmpty() || "***".equals(value) ? null : value; } private static boolean sensitive(String model, Map config) { String name = model.toLowerCase(Locale.ROOT); String key = String.valueOf(config.get("jnpfKey")).toLowerCase(Locale.ROOT); return name.equals("biz_sign") || name.equals("bizsign") || name.equals("sign_id") || name.equals("signid") || name.endsWith("sign") || name.endsWith("signid") || name.contains("password") || name.contains("mima") || key.contains("password") || key.contains("signature"); } @SuppressWarnings("unchecked") private static List list(Object raw) { if (raw instanceof List) { return (List) raw; } if (raw instanceof String && !((String) raw).trim().isEmpty()) { List parsed = JsonUtil.getJsonToList((String) raw, Map.class); return parsed == null ? Collections.emptyList() : parsed; } return Collections.emptyList(); } }