|
|
@@ -1,527 +1,529 @@
|
|
|
-package com.uas.kanban.support;
|
|
|
-
|
|
|
-import com.uas.kanban.annotation.NotEmpty;
|
|
|
-import com.uas.kanban.model.GlobalParameter;
|
|
|
-import com.uas.kanban.model.Type;
|
|
|
-import com.uas.kanban.util.ArrayUtils;
|
|
|
-import com.uas.kanban.util.CollectionUtils;
|
|
|
-import com.uas.kanban.util.StringUtils;
|
|
|
-import me.chyxion.jdbc.NewbieJdbcSupport;
|
|
|
-import org.dom4j.Attribute;
|
|
|
-import org.dom4j.Document;
|
|
|
-import org.dom4j.DocumentException;
|
|
|
-import org.dom4j.Element;
|
|
|
-import org.dom4j.io.SAXReader;
|
|
|
-import org.dom4j.tree.DefaultElement;
|
|
|
-import org.slf4j.Logger;
|
|
|
-import org.slf4j.LoggerFactory;
|
|
|
-import org.springframework.stereotype.Component;
|
|
|
-
|
|
|
-import javax.xml.transform.TransformerException;
|
|
|
-import java.io.IOException;
|
|
|
-import java.io.InputStream;
|
|
|
-import java.io.StringReader;
|
|
|
-import java.sql.Connection;
|
|
|
-import java.sql.PreparedStatement;
|
|
|
-import java.sql.ResultSet;
|
|
|
-import java.sql.SQLException;
|
|
|
-import java.util.*;
|
|
|
-import java.util.Map.Entry;
|
|
|
-import java.util.regex.Matcher;
|
|
|
-import java.util.regex.Pattern;
|
|
|
-
|
|
|
-/**
|
|
|
- * @author sunyj
|
|
|
- * @since 2017年9月7日 下午2:52:04
|
|
|
- */
|
|
|
-@Component
|
|
|
-public class TemplateParser {
|
|
|
-
|
|
|
- /**
|
|
|
- * 查询条件的结果数目限制
|
|
|
- */
|
|
|
- private static final int MAX_RECORD_SIZE = 10000;
|
|
|
-
|
|
|
- private Logger logger = LoggerFactory.getLogger(getClass());
|
|
|
-
|
|
|
- /**
|
|
|
- * 替换模版中的参数为实际值
|
|
|
- *
|
|
|
- * @param content 模版内容
|
|
|
- * @param globalParameters 公共参数
|
|
|
- * @param needSingleQuote 参数值是否以单引号括起来(用于 sql)
|
|
|
- * @param jdbc NewbieJdbcSupport 对象
|
|
|
- * @return 替换后的模版内容
|
|
|
- */
|
|
|
- private String replaceParameters(@NotEmpty("content") String content, List<GlobalParameter> globalParameters,
|
|
|
- @NotEmpty("needSingleQuote") Boolean needSingleQuote, @NotEmpty("jdbc") NewbieJdbcSupport jdbc) throws SQLException {
|
|
|
- if (CollectionUtils.isEmpty(globalParameters)) {
|
|
|
- return content;
|
|
|
- }
|
|
|
- Map<String, GlobalParameter> map = new HashMap<>();
|
|
|
- for (GlobalParameter globalParameter : globalParameters) {
|
|
|
- map.put(globalParameter.getName(), globalParameter);
|
|
|
- }
|
|
|
- String regex = "\\{([\\s\\S]+?)\\}";
|
|
|
- Pattern pattern = Pattern.compile(regex);
|
|
|
- Matcher matcher = pattern.matcher(content);
|
|
|
- while (matcher.find()) {
|
|
|
- int start = matcher.start();
|
|
|
- int end = matcher.end();
|
|
|
- String globalParameterName = matcher.group(1);
|
|
|
- GlobalParameter globalParameter = map.get(globalParameterName);
|
|
|
- if (globalParameter == null) {
|
|
|
- throw new IllegalStateException("未启用公共参数:" + globalParameterName);
|
|
|
- }
|
|
|
- Object value = globalParameter.getValue();
|
|
|
- if (value == null) {
|
|
|
- throw new IllegalStateException("公共参数值为null:" + globalParameterName);
|
|
|
- }
|
|
|
-
|
|
|
- // 如果公共参数值为 {@link Type#SQL} ,需要取得查询结果
|
|
|
- if (globalParameter.getType() == Type.SQL) {
|
|
|
- String stringValue = (String) value;
|
|
|
- // 不递归替换公共参数
|
|
|
- // stringValue=replaceParameters(stringValue,globalParameters,true,jdbc);
|
|
|
- checkCount(jdbc.getDataSource().getConnection(), stringValue);
|
|
|
- Map<String, Object> data = jdbc.findMap(stringValue);
|
|
|
- if (CollectionUtils.isEmpty(data)) {
|
|
|
- throw new IllegalStateException("公共参数通过 SQL 取得结果为空:" + globalParameterName);
|
|
|
- } else if (data.size() > 1) {
|
|
|
- throw new IllegalStateException("公共参数通过 SQL 取得结果不止一列:" + globalParameterName);
|
|
|
- }
|
|
|
- value = data.values().iterator().next();
|
|
|
- }
|
|
|
- // TODO value 为时间
|
|
|
- if (needSingleQuote != null && needSingleQuote) {
|
|
|
- content = content.substring(0, start) + "'" + value + "'" + content.substring(end);
|
|
|
- } else {
|
|
|
- content = content.substring(0, start) + value + content.substring(end);
|
|
|
- }
|
|
|
- matcher = pattern.matcher(content);
|
|
|
- }
|
|
|
- return content;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 解析单个 xml 模版,转为 json 格式的数据
|
|
|
- *
|
|
|
- * @param content 模版内容
|
|
|
- * @param title 标题
|
|
|
- * @param globalParameters 公共参数
|
|
|
- * @param jdbc NewbieJdbcSupport 对象
|
|
|
- * @return 解析后的 json 数据
|
|
|
- */
|
|
|
- public String parseXml(@NotEmpty("content") String content, String title, List<GlobalParameter> globalParameters, @NotEmpty("jdbc") NewbieJdbcSupport jdbc)
|
|
|
- throws DocumentException, TransformerException, IOException, IllegalStateException, SQLException {
|
|
|
- content = processSql(content);
|
|
|
- // 替换标题中的公共参数
|
|
|
- if (!com.uas.kanban.util.StringUtils.isEmpty(title)) {
|
|
|
- title = replaceParameters(title, globalParameters, false, jdbc);
|
|
|
- }
|
|
|
- content = processForm(content, jdbc, globalParameters);
|
|
|
- content = processGrid(content, jdbc, globalParameters);
|
|
|
- content = processBarAndLine(content, jdbc, globalParameters);
|
|
|
- content = processPie(content, jdbc, globalParameters);
|
|
|
- content = finalProcess(content, title);
|
|
|
- return TranslateHelper.xmlToJson(content);
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 处理 sql ,包括替换特殊字符等
|
|
|
- *
|
|
|
- * @param content 模版内容
|
|
|
- * @return 处理后的模版内容
|
|
|
- */
|
|
|
- private String processSql(@NotEmpty("content") String content) {
|
|
|
- String regex = "sql=\"([^\"]+?[<>]+?[^\"]+?)\"";
|
|
|
- Pattern pattern = Pattern.compile(regex);
|
|
|
- Matcher matcher = pattern.matcher(content);
|
|
|
- while (matcher.find()) {
|
|
|
- String sql = matcher.group(1);
|
|
|
- int start = matcher.start(1);
|
|
|
- int end = matcher.end(1);
|
|
|
- sql = sql.replaceAll(">", ">").replaceAll("<", "<");
|
|
|
- content = content.substring(0, start) + sql + content.substring(end);
|
|
|
- matcher = pattern.matcher(content);
|
|
|
- }
|
|
|
- return content;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 处理模版中的 form 组件
|
|
|
- *
|
|
|
- * @param content 模版内容
|
|
|
- * @param jdbc NewbieJdbcSupport 对象
|
|
|
- * @param globalParameters 公共参数
|
|
|
- * @return 解析后的模版内容
|
|
|
- */
|
|
|
- @SuppressWarnings("unchecked")
|
|
|
- private String processForm(@NotEmpty("content") String content, @NotEmpty("jdbc") NewbieJdbcSupport jdbc, List<GlobalParameter> globalParameters)
|
|
|
- throws DocumentException, IllegalStateException, SQLException {
|
|
|
- Document document = getDocument(content);
|
|
|
- // 获取 form 组件
|
|
|
- List<Element> elements = document.selectNodes("//form");
|
|
|
- for (Element element : elements) {
|
|
|
- // 执行 sql ,获取数据
|
|
|
- String sql = element.attribute("sql").getText();
|
|
|
- if (StringUtils.isEmpty(sql)) {
|
|
|
- continue;
|
|
|
- }
|
|
|
- sql = replaceParameters(sql, globalParameters, true, jdbc);
|
|
|
- // 获取 field 节点
|
|
|
- List<Element> fieldElements = element.elements("field");
|
|
|
- if (CollectionUtils.isEmpty(fieldElements)) {
|
|
|
- continue;
|
|
|
- }
|
|
|
- checkCount(jdbc.getDataSource().getConnection(), sql);
|
|
|
- Map<String, Object> map = jdbc.findMap(sql);
|
|
|
- if (CollectionUtils.isEmpty(map)) {
|
|
|
- continue;
|
|
|
- }
|
|
|
- for (Element fieldElement : fieldElements) {
|
|
|
- // 设置 field 的 value 属性
|
|
|
- Attribute valueAttribute = fieldElement.attribute("value");
|
|
|
- String fieldName = valueAttribute.getText();
|
|
|
- valueAttribute.setText(toString(map.get(fieldName.toUpperCase())));
|
|
|
- }
|
|
|
- }
|
|
|
- return document.asXML();
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 处理模版中的 grid 组件
|
|
|
- *
|
|
|
- * @param content 模版内容
|
|
|
- * @param jdbc NewbieJdbcSupport 对象
|
|
|
- * @param globalParameters 公共参数
|
|
|
- * @return 解析后的模版内容
|
|
|
- */
|
|
|
- @SuppressWarnings("unchecked")
|
|
|
- private String processGrid(@NotEmpty("content") String content, @NotEmpty("jdbc") NewbieJdbcSupport jdbc, List<GlobalParameter> globalParameters)
|
|
|
- throws DocumentException, IllegalStateException, SQLException {
|
|
|
- Document document = getDocument(content);
|
|
|
- // 获取 form 组件
|
|
|
- List<Element> elements = document.selectNodes("//grid");
|
|
|
- for (Element element : elements) {
|
|
|
- // 执行 sql ,获取数据
|
|
|
- String sql = element.attribute("sql").getText();
|
|
|
- if (StringUtils.isEmpty(sql)) {
|
|
|
- continue;
|
|
|
- }
|
|
|
- sql = replaceParameters(sql, globalParameters, true, jdbc);
|
|
|
- // 获取 field 节点
|
|
|
- List<Element> fieldElements = element.elements("field");
|
|
|
- if (CollectionUtils.isEmpty(fieldElements)) {
|
|
|
- continue;
|
|
|
- }
|
|
|
- // 提取 field 名称
|
|
|
- List<String> fieldNames = new ArrayList<>();
|
|
|
- for (Element fieldElement : fieldElements) {
|
|
|
- // 设置 field 的 name
|
|
|
- String fieldName = fieldElement.attribute("dataindex").getText();
|
|
|
- if (!StringUtils.isEmpty(fieldName)) {
|
|
|
- fieldNames.add(fieldName);
|
|
|
- }
|
|
|
- }
|
|
|
- checkCount(jdbc.getDataSource().getConnection(), sql);
|
|
|
- List<Map<String, Object>> listMap = jdbc.listMap(sql);
|
|
|
- if (CollectionUtils.isEmpty(listMap)) {
|
|
|
- continue;
|
|
|
- }
|
|
|
- for (Map<String, Object> map : listMap) {
|
|
|
- // 每一行数据添加一个 data标签
|
|
|
- Element dataElement = new DefaultElement("data");
|
|
|
- element.add(dataElement);
|
|
|
- for (String fieldName : fieldNames) {
|
|
|
- // 每一列在 data 下添加一个标签
|
|
|
- Element fieldElement = new DefaultElement(fieldName);
|
|
|
- fieldElement.setText(toString(map.get(fieldName.toUpperCase())));
|
|
|
- dataElement.add(fieldElement);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- return document.asXML();
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 处理模版中的 bar 和 line 组件
|
|
|
- *
|
|
|
- * @param content 模版内容
|
|
|
- * @param jdbc NewbieJdbcSupport 对象
|
|
|
- * @param globalParameters 公共参数
|
|
|
- * @return 解析后的模版内容
|
|
|
- */
|
|
|
- @SuppressWarnings("unchecked")
|
|
|
- private String processBarAndLine(@NotEmpty("content") String content, @NotEmpty("jdbc") NewbieJdbcSupport jdbc, List<GlobalParameter> globalParameters)
|
|
|
- throws DocumentException, IllegalStateException, SQLException {
|
|
|
- Document document = getDocument(content);
|
|
|
- // 获取 bar 和 line 组件
|
|
|
- List<Element> elements = document.selectNodes("//bar | //line");
|
|
|
- for (Element element : elements) {
|
|
|
- // 执行 sql ,获取数据
|
|
|
- String sql = element.attribute("sql").getText();
|
|
|
- if (StringUtils.isEmpty(sql)) {
|
|
|
- continue;
|
|
|
- }
|
|
|
- sql = replaceParameters(sql, globalParameters, true, jdbc);
|
|
|
- checkCount(jdbc.getDataSource().getConnection(), sql);
|
|
|
- Map<String, List<Object>> map = convert(jdbc.listMap(sql));
|
|
|
- if (CollectionUtils.isEmpty(map)) {
|
|
|
- continue;
|
|
|
- }
|
|
|
-
|
|
|
- // xvalue 字段名
|
|
|
- String xvalue = element.attribute("xvalue").getText();
|
|
|
- if (xvalue != null) {
|
|
|
- // 设置 xvalue
|
|
|
- element.attribute("xfields").setText(toString(map.remove(xvalue.toUpperCase())));
|
|
|
- }
|
|
|
-
|
|
|
- // fields 名称
|
|
|
- String[] fields = element.attribute("fields").getText().split(",[\\s]*");
|
|
|
- if (ArrayUtils.isEmpty(fields)) {
|
|
|
- continue;
|
|
|
- }
|
|
|
- // 添加 series 节点,用于存储 fields 的值
|
|
|
- for (String field : fields) {
|
|
|
- Element seriesElement = new DefaultElement("series");
|
|
|
- seriesElement.addAttribute("name", field);
|
|
|
- List<Object> value = map.remove(field.toUpperCase());
|
|
|
- // 对于某一列,其每一行的数据创建一个 data 标签
|
|
|
- if (CollectionUtils.isEmpty(value)) {
|
|
|
- Element dataElement = new DefaultElement("data");
|
|
|
- seriesElement.add(dataElement);
|
|
|
- } else {
|
|
|
- for (Object object : value) {
|
|
|
- Element dataElement = new DefaultElement("data");
|
|
|
- dataElement.setText(toString(object));
|
|
|
- seriesElement.add(dataElement);
|
|
|
- }
|
|
|
- }
|
|
|
- element.add(seriesElement);
|
|
|
- }
|
|
|
- }
|
|
|
- return document.asXML();
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 处理模版中的pie组件
|
|
|
- *
|
|
|
- * @param content 模版内容
|
|
|
- * @param jdbc NewbieJdbcSupport 对象
|
|
|
- * @param globalParameters 公共参数
|
|
|
- * @return 解析后的模版内容
|
|
|
- */
|
|
|
- @SuppressWarnings("unchecked")
|
|
|
- private String processPie(@NotEmpty("content") String content, @NotEmpty("jdbc") NewbieJdbcSupport jdbc, List<GlobalParameter> globalParameters)
|
|
|
- throws DocumentException, IllegalStateException, SQLException {
|
|
|
- Document document = getDocument(content);
|
|
|
- // 获取 bar 和 line 组件
|
|
|
- List<Element> elements = document.selectNodes("//pie");
|
|
|
- for (Element element : elements) {
|
|
|
- // 执行 sql ,获取数据
|
|
|
- String sql = element.attribute("sql").getText();
|
|
|
- if (StringUtils.isEmpty(sql)) {
|
|
|
- continue;
|
|
|
- }
|
|
|
- sql = replaceParameters(sql, globalParameters, true, jdbc);
|
|
|
- checkCount(jdbc.getDataSource().getConnection(), sql);
|
|
|
- List<Map<String, Object>> listMap = jdbc.listMap(sql);
|
|
|
- if (CollectionUtils.isEmpty(listMap)) {
|
|
|
- continue;
|
|
|
- }
|
|
|
-
|
|
|
- // field 和 value
|
|
|
- String field = element.attribute("field").getText().toUpperCase();
|
|
|
- String value = element.attribute("value").getText().toUpperCase();
|
|
|
- for (Map<String, Object> map : listMap) {
|
|
|
- Element seriesElement = new DefaultElement("series");
|
|
|
- seriesElement.addAttribute("name", toString(map.get(field)));
|
|
|
- seriesElement.addAttribute("data", toString(map.get(value)));
|
|
|
- element.add(seriesElement);
|
|
|
- }
|
|
|
- }
|
|
|
- return document.asXML();
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 读取 xml 为 Document 对象
|
|
|
- *
|
|
|
- * @param xml xml 字符串
|
|
|
- * @return Document 对象
|
|
|
- */
|
|
|
- private Document getDocument(@NotEmpty("xml") String xml) throws DocumentException {
|
|
|
- return new SAXReader().read(new StringReader(xml));
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 检查当前条件下的结果数目是否超出限制
|
|
|
- *
|
|
|
- * @param connection 数据库连接
|
|
|
- * @param sql 查询语句
|
|
|
- */
|
|
|
- private void checkCount(@NotEmpty("connection") Connection connection, @NotEmpty("sql") String sql)
|
|
|
- throws SQLException, IllegalStateException {
|
|
|
- int count = getCount(connection, sql);
|
|
|
- if (count > MAX_RECORD_SIZE) {
|
|
|
- String message = "查询条件的结果数目超出限制:" + count;
|
|
|
- throw new IllegalStateException(message, new IllegalStateException("sql : " + sql));
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 获取当前查询语句的结果数目
|
|
|
- *
|
|
|
- * @param connection 数据库连接
|
|
|
- * @param sql 查询语句
|
|
|
- * @return 结果数目
|
|
|
- */
|
|
|
- private int getCount(@NotEmpty("connection") Connection connection, @NotEmpty("sql") String sql)
|
|
|
- throws SQLException {
|
|
|
- PreparedStatement preparedStatement = null;
|
|
|
- ResultSet resultSet = null;
|
|
|
- try {
|
|
|
- // 如果直接在 sql 外用 count(1) 统计数目,当关联表多时,可能会出现错误
|
|
|
- // ORA-01792: 表或视图中的最大列数为 1000
|
|
|
- // 报错主要发生在 select * 的情况下,但是不能这样判断,因为可能存在 select tt.*, pi_id from
|
|
|
- // purchase t left join 这样的情况,很难区分
|
|
|
- // 因此 1. 对于普通 sql ,将 select 后的字段改为 count(1)
|
|
|
- // 2. 而最外层含有 group by 的 sql ,直接改为 count(1) 可能得到不止一行,结果也并非实际行数。再加上
|
|
|
- // group
|
|
|
- // by 的结果列数一般很小,所以可以在外面使用 count(1) ,一般不会超出 1000 行
|
|
|
- String lowerSql = sql.toLowerCase();
|
|
|
- if (!lowerSql.matches("[\\s\\S]+?group[\\s]+?by[\\s]+?[^)]+?")) {
|
|
|
- String regex = "([\\s\\S]+?from)[\\s]+?[^,]+?";
|
|
|
- Pattern pattern = Pattern.compile(regex);
|
|
|
- Matcher matcher = pattern.matcher(lowerSql);
|
|
|
- if (matcher.find()) {
|
|
|
- int start = matcher.start(1);
|
|
|
- int end = matcher.end(1);
|
|
|
- sql = sql.substring(0, start) + "select count(1) from" + sql.substring(end);
|
|
|
- } else {
|
|
|
- throw new IllegalStateException("sql 解析错误:未发现第一个 from:" + sql);
|
|
|
- }
|
|
|
- } else {
|
|
|
- sql = "select count(1) from (" + sql + ")";
|
|
|
- }
|
|
|
- preparedStatement = connection.prepareStatement(sql);
|
|
|
- resultSet = preparedStatement.executeQuery();
|
|
|
- resultSet.next();
|
|
|
- return resultSet.getInt(1);
|
|
|
- } finally {
|
|
|
- if (resultSet != null) {
|
|
|
- try {
|
|
|
- resultSet.close();
|
|
|
- } catch (SQLException e) {
|
|
|
- logger.error("", e);
|
|
|
- }
|
|
|
- }
|
|
|
- if (preparedStatement != null) {
|
|
|
- try {
|
|
|
- preparedStatement.close();
|
|
|
- } catch (SQLException e) {
|
|
|
- logger.error("", e);
|
|
|
- }
|
|
|
- }
|
|
|
- connection.close();
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 将 List<Map<String, Object>> 转为 Map<String, List<Object>>
|
|
|
- *
|
|
|
- * @param listMap 将转换的对象,每个 Map 的键都相同,否则会得到非预期的结果<br/>
|
|
|
- * 如下所示:
|
|
|
- * <p/>
|
|
|
- * <table border=1 cellpadding=5 cellspacing=0 summary=
|
|
|
- * "Key and value">
|
|
|
- * <tr>
|
|
|
- * <th>输入</th>
|
|
|
- * <th>输出</th>
|
|
|
- * <th>结果</th>
|
|
|
- * </tr>
|
|
|
- * <tr>
|
|
|
- * <td>[{age=12, name=Joe}, {age=31, name=Lee}]</td>
|
|
|
- * <td>{name=[Joe, Lee], age=[12, 31]}</td>
|
|
|
- * <td>预期</td>
|
|
|
- * </tr>
|
|
|
- * <tr>
|
|
|
- * <td>[{age=12, name=Joe}, {name=Lee, agee=31}]</td>
|
|
|
- * <td>{name=[Joe, Lee], age=[12], agee=[31]}</td>
|
|
|
- * <td>非预期</td>
|
|
|
- * </tr>
|
|
|
- * </table>
|
|
|
- * @return 转换的结果
|
|
|
- */
|
|
|
- private Map<String, List<Object>> convert(List<Map<String, Object>> listMap) {
|
|
|
- Map<String, List<Object>> result = new HashMap<>();
|
|
|
- if (CollectionUtils.isEmpty(listMap)) {
|
|
|
- return result;
|
|
|
- }
|
|
|
- for (Map<String, Object> map : listMap) {
|
|
|
- Set<Entry<String, Object>> entrySet = map.entrySet();
|
|
|
- for (Entry<String, Object> entry : entrySet) {
|
|
|
- String key = entry.getKey();
|
|
|
- Object value = entry.getValue();
|
|
|
- List<Object> list = result.get(key);
|
|
|
- if (list == null) {
|
|
|
- list = new ArrayList<>();
|
|
|
- result.put(key, list);
|
|
|
- }
|
|
|
- list.add(value);
|
|
|
- }
|
|
|
- }
|
|
|
- return result;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 将对象转为字符串
|
|
|
- *
|
|
|
- * @param object 对象
|
|
|
- * @return 转换的字符串
|
|
|
- */
|
|
|
- private String toString(Object object) {
|
|
|
- if (object == null) {
|
|
|
- return "";
|
|
|
- }
|
|
|
- if (object instanceof Date) {
|
|
|
- return String.valueOf(((Date) object).getTime());
|
|
|
- }
|
|
|
- return String.valueOf(object);
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 对模版内容的最终处理,包括映射、添加title等
|
|
|
- *
|
|
|
- * @param content 模版内容
|
|
|
- * @param title 标题
|
|
|
- * @return 处理后的模版内容
|
|
|
- */
|
|
|
- private String finalProcess(@NotEmpty("content") String content, String title)
|
|
|
- throws TransformerException, IOException, DocumentException {
|
|
|
- InputStream mapRuleStream = FileHelper.readStream("map-rule.xsl");
|
|
|
- content = TranslateHelper.map(content, mapRuleStream);
|
|
|
- if (!StringUtils.isEmpty(title)) {
|
|
|
- content = addTitle(content, title);
|
|
|
- }
|
|
|
- return content;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 为模版添加 title
|
|
|
- *
|
|
|
- * @param content 模版内容
|
|
|
- * @param title 标题
|
|
|
- * @return 处理后的模版内容
|
|
|
- */
|
|
|
- private String addTitle(@NotEmpty("content") String content, @NotEmpty("title") String title)
|
|
|
- throws DocumentException {
|
|
|
- Document document = getDocument(content);
|
|
|
- Element rootElement = document.getRootElement();
|
|
|
- Element titleElement = new DefaultElement("title");
|
|
|
- titleElement.setText(title);
|
|
|
- rootElement.add(titleElement);
|
|
|
- return document.asXML();
|
|
|
- }
|
|
|
-}
|
|
|
+package com.uas.kanban.support;
|
|
|
+
|
|
|
+import com.uas.kanban.annotation.NotEmpty;
|
|
|
+import com.uas.kanban.model.Parameter;
|
|
|
+import com.uas.kanban.model.Type;
|
|
|
+import com.uas.kanban.util.ArrayUtils;
|
|
|
+import com.uas.kanban.util.CollectionUtils;
|
|
|
+import com.uas.kanban.util.StringUtils;
|
|
|
+import me.chyxion.jdbc.NewbieJdbcSupport;
|
|
|
+import org.dom4j.Attribute;
|
|
|
+import org.dom4j.Document;
|
|
|
+import org.dom4j.DocumentException;
|
|
|
+import org.dom4j.Element;
|
|
|
+import org.dom4j.io.SAXReader;
|
|
|
+import org.dom4j.tree.DefaultElement;
|
|
|
+import org.slf4j.Logger;
|
|
|
+import org.slf4j.LoggerFactory;
|
|
|
+import org.springframework.stereotype.Component;
|
|
|
+
|
|
|
+import javax.xml.transform.TransformerException;
|
|
|
+import java.io.IOException;
|
|
|
+import java.io.InputStream;
|
|
|
+import java.io.StringReader;
|
|
|
+import java.sql.Connection;
|
|
|
+import java.sql.PreparedStatement;
|
|
|
+import java.sql.ResultSet;
|
|
|
+import java.sql.SQLException;
|
|
|
+import java.util.*;
|
|
|
+import java.util.Map.Entry;
|
|
|
+import java.util.regex.Matcher;
|
|
|
+import java.util.regex.Pattern;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 看板解析
|
|
|
+ *
|
|
|
+ * @author sunyj
|
|
|
+ * @since 2017年9月7日 下午2:52:04
|
|
|
+ */
|
|
|
+@Component
|
|
|
+public class KanbanParser {
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 查询条件的结果数目限制
|
|
|
+ */
|
|
|
+ private static final int MAX_RECORD_SIZE = 10000;
|
|
|
+
|
|
|
+ private Logger logger = LoggerFactory.getLogger(getClass());
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 替换看板中的参数为实际值
|
|
|
+ *
|
|
|
+ * @param content 看板内容
|
|
|
+ * @param parameters 参数
|
|
|
+ * @param needSingleQuote 参数值是否以单引号括起来(用于 sql)
|
|
|
+ * @param jdbc NewbieJdbcSupport 对象
|
|
|
+ * @return 替换后的看板内容
|
|
|
+ */
|
|
|
+ private String replaceParameters(@NotEmpty("content") String content, List<Parameter> parameters,
|
|
|
+ @NotEmpty("needSingleQuote") Boolean needSingleQuote, @NotEmpty("jdbc") NewbieJdbcSupport jdbc) throws SQLException {
|
|
|
+ if (CollectionUtils.isEmpty(parameters)) {
|
|
|
+ return content;
|
|
|
+ }
|
|
|
+ Map<String, Parameter> map = new HashMap<>();
|
|
|
+ for (Parameter parameter : parameters) {
|
|
|
+ map.put(parameter.getName(), parameter);
|
|
|
+ }
|
|
|
+ String regex = "\\{([\\s\\S]+?)\\}";
|
|
|
+ Pattern pattern = Pattern.compile(regex);
|
|
|
+ Matcher matcher = pattern.matcher(content);
|
|
|
+ while (matcher.find()) {
|
|
|
+ int start = matcher.start();
|
|
|
+ int end = matcher.end();
|
|
|
+ String parameterName = matcher.group(1);
|
|
|
+ Parameter parameter = map.get(parameterName);
|
|
|
+ if (parameter == null) {
|
|
|
+ throw new IllegalStateException("未启用参数:" + parameterName);
|
|
|
+ }
|
|
|
+ Object value = parameter.getValue();
|
|
|
+ if (value == null) {
|
|
|
+ throw new IllegalStateException("参数值为null:" + parameterName);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果参数值为 {@link Type#SQL} ,需要取得查询结果
|
|
|
+ if (parameter.getType() == Type.SQL) {
|
|
|
+ String stringValue = (String) value;
|
|
|
+ // 不递归替换参数
|
|
|
+ // stringValue=replaceParameters(stringValue,parameters,true,jdbc);
|
|
|
+ checkCount(jdbc.getDataSource().getConnection(), stringValue);
|
|
|
+ Map<String, Object> data = jdbc.findMap(stringValue);
|
|
|
+ if (CollectionUtils.isEmpty(data)) {
|
|
|
+ throw new IllegalStateException("参数通过 SQL 取得结果为空:" + parameterName);
|
|
|
+ } else if (data.size() > 1) {
|
|
|
+ throw new IllegalStateException("参数通过 SQL 取得结果不止一列:" + parameterName);
|
|
|
+ }
|
|
|
+ value = data.values().iterator().next();
|
|
|
+ }
|
|
|
+ // TODO value 为时间
|
|
|
+ if (needSingleQuote != null && needSingleQuote) {
|
|
|
+ content = content.substring(0, start) + "'" + value + "'" + content.substring(end);
|
|
|
+ } else {
|
|
|
+ content = content.substring(0, start) + value + content.substring(end);
|
|
|
+ }
|
|
|
+ matcher = pattern.matcher(content);
|
|
|
+ }
|
|
|
+ return content;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 解析单个 xml 看板,转为 json 格式的数据
|
|
|
+ *
|
|
|
+ * @param content 看板内容
|
|
|
+ * @param title 标题
|
|
|
+ * @param parameters 参数
|
|
|
+ * @param jdbc NewbieJdbcSupport 对象
|
|
|
+ * @return 解析后的 json 数据
|
|
|
+ */
|
|
|
+ public String parseXml(@NotEmpty("content") String content, String title, List<Parameter> parameters, @NotEmpty("jdbc") NewbieJdbcSupport jdbc)
|
|
|
+ throws DocumentException, TransformerException, IOException, IllegalStateException, SQLException {
|
|
|
+ content = processSql(content);
|
|
|
+ // 替换标题中的参数
|
|
|
+ if (!com.uas.kanban.util.StringUtils.isEmpty(title)) {
|
|
|
+ title = replaceParameters(title, parameters, false, jdbc);
|
|
|
+ }
|
|
|
+ content = processForm(content, jdbc, parameters);
|
|
|
+ content = processGrid(content, jdbc, parameters);
|
|
|
+ content = processBarAndLine(content, jdbc, parameters);
|
|
|
+ content = processPie(content, jdbc, parameters);
|
|
|
+ content = finalProcess(content, title);
|
|
|
+ return TranslateHelper.xmlToJson(content);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 处理 sql ,包括替换特殊字符等
|
|
|
+ *
|
|
|
+ * @param content 看板内容
|
|
|
+ * @return 处理后的看板内容
|
|
|
+ */
|
|
|
+ private String processSql(@NotEmpty("content") String content) {
|
|
|
+ String regex = "sql=\"([^\"]+?[<>]+?[^\"]+?)\"";
|
|
|
+ Pattern pattern = Pattern.compile(regex);
|
|
|
+ Matcher matcher = pattern.matcher(content);
|
|
|
+ while (matcher.find()) {
|
|
|
+ String sql = matcher.group(1);
|
|
|
+ int start = matcher.start(1);
|
|
|
+ int end = matcher.end(1);
|
|
|
+ sql = sql.replaceAll(">", ">").replaceAll("<", "<");
|
|
|
+ content = content.substring(0, start) + sql + content.substring(end);
|
|
|
+ matcher = pattern.matcher(content);
|
|
|
+ }
|
|
|
+ return content;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 处理看板中的 form 组件
|
|
|
+ *
|
|
|
+ * @param content 看板内容
|
|
|
+ * @param jdbc NewbieJdbcSupport 对象
|
|
|
+ * @param parameters 参数
|
|
|
+ * @return 解析后的看板内容
|
|
|
+ */
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
+ private String processForm(@NotEmpty("content") String content, @NotEmpty("jdbc") NewbieJdbcSupport jdbc, List<Parameter> parameters)
|
|
|
+ throws DocumentException, IllegalStateException, SQLException {
|
|
|
+ Document document = getDocument(content);
|
|
|
+ // 获取 form 组件
|
|
|
+ List<Element> elements = document.selectNodes("//form");
|
|
|
+ for (Element element : elements) {
|
|
|
+ // 执行 sql ,获取数据
|
|
|
+ String sql = element.attribute("sql").getText();
|
|
|
+ if (StringUtils.isEmpty(sql)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ sql = replaceParameters(sql, parameters, true, jdbc);
|
|
|
+ // 获取 field 节点
|
|
|
+ List<Element> fieldElements = element.elements("field");
|
|
|
+ if (CollectionUtils.isEmpty(fieldElements)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ checkCount(jdbc.getDataSource().getConnection(), sql);
|
|
|
+ Map<String, Object> map = jdbc.findMap(sql);
|
|
|
+ if (CollectionUtils.isEmpty(map)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ for (Element fieldElement : fieldElements) {
|
|
|
+ // 设置 field 的 value 属性
|
|
|
+ Attribute valueAttribute = fieldElement.attribute("value");
|
|
|
+ String fieldName = valueAttribute.getText();
|
|
|
+ valueAttribute.setText(toString(map.get(fieldName.toUpperCase())));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return document.asXML();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 处理看板中的 grid 组件
|
|
|
+ *
|
|
|
+ * @param content 看板内容
|
|
|
+ * @param jdbc NewbieJdbcSupport 对象
|
|
|
+ * @param parameters 参数
|
|
|
+ * @return 解析后的看板内容
|
|
|
+ */
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
+ private String processGrid(@NotEmpty("content") String content, @NotEmpty("jdbc") NewbieJdbcSupport jdbc, List<Parameter> parameters)
|
|
|
+ throws DocumentException, IllegalStateException, SQLException {
|
|
|
+ Document document = getDocument(content);
|
|
|
+ // 获取 form 组件
|
|
|
+ List<Element> elements = document.selectNodes("//grid");
|
|
|
+ for (Element element : elements) {
|
|
|
+ // 执行 sql ,获取数据
|
|
|
+ String sql = element.attribute("sql").getText();
|
|
|
+ if (StringUtils.isEmpty(sql)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ sql = replaceParameters(sql, parameters, true, jdbc);
|
|
|
+ // 获取 field 节点
|
|
|
+ List<Element> fieldElements = element.elements("field");
|
|
|
+ if (CollectionUtils.isEmpty(fieldElements)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ // 提取 field 名称
|
|
|
+ List<String> fieldNames = new ArrayList<>();
|
|
|
+ for (Element fieldElement : fieldElements) {
|
|
|
+ // 设置 field 的 name
|
|
|
+ String fieldName = fieldElement.attribute("dataindex").getText();
|
|
|
+ if (!StringUtils.isEmpty(fieldName)) {
|
|
|
+ fieldNames.add(fieldName);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ checkCount(jdbc.getDataSource().getConnection(), sql);
|
|
|
+ List<Map<String, Object>> listMap = jdbc.listMap(sql);
|
|
|
+ if (CollectionUtils.isEmpty(listMap)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ for (Map<String, Object> map : listMap) {
|
|
|
+ // 每一行数据添加一个 data标签
|
|
|
+ Element dataElement = new DefaultElement("data");
|
|
|
+ element.add(dataElement);
|
|
|
+ for (String fieldName : fieldNames) {
|
|
|
+ // 每一列在 data 下添加一个标签
|
|
|
+ Element fieldElement = new DefaultElement(fieldName);
|
|
|
+ fieldElement.setText(toString(map.get(fieldName.toUpperCase())));
|
|
|
+ dataElement.add(fieldElement);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return document.asXML();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 处理看板中的 bar 和 line 组件
|
|
|
+ *
|
|
|
+ * @param content 看板内容
|
|
|
+ * @param jdbc NewbieJdbcSupport 对象
|
|
|
+ * @param parameters 参数
|
|
|
+ * @return 解析后的看板内容
|
|
|
+ */
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
+ private String processBarAndLine(@NotEmpty("content") String content, @NotEmpty("jdbc") NewbieJdbcSupport jdbc, List<Parameter> parameters)
|
|
|
+ throws DocumentException, IllegalStateException, SQLException {
|
|
|
+ Document document = getDocument(content);
|
|
|
+ // 获取 bar 和 line 组件
|
|
|
+ List<Element> elements = document.selectNodes("//bar | //line");
|
|
|
+ for (Element element : elements) {
|
|
|
+ // 执行 sql ,获取数据
|
|
|
+ String sql = element.attribute("sql").getText();
|
|
|
+ if (StringUtils.isEmpty(sql)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ sql = replaceParameters(sql, parameters, true, jdbc);
|
|
|
+ checkCount(jdbc.getDataSource().getConnection(), sql);
|
|
|
+ Map<String, List<Object>> map = convert(jdbc.listMap(sql));
|
|
|
+ if (CollectionUtils.isEmpty(map)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ // xvalue 字段名
|
|
|
+ String xvalue = element.attribute("xvalue").getText();
|
|
|
+ if (xvalue != null) {
|
|
|
+ // 设置 xvalue
|
|
|
+ element.attribute("xfields").setText(toString(map.remove(xvalue.toUpperCase())));
|
|
|
+ }
|
|
|
+
|
|
|
+ // fields 名称
|
|
|
+ String[] fields = element.attribute("fields").getText().split(",[\\s]*");
|
|
|
+ if (ArrayUtils.isEmpty(fields)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ // 添加 series 节点,用于存储 fields 的值
|
|
|
+ for (String field : fields) {
|
|
|
+ Element seriesElement = new DefaultElement("series");
|
|
|
+ seriesElement.addAttribute("name", field);
|
|
|
+ List<Object> value = map.remove(field.toUpperCase());
|
|
|
+ // 对于某一列,其每一行的数据创建一个 data 标签
|
|
|
+ if (CollectionUtils.isEmpty(value)) {
|
|
|
+ Element dataElement = new DefaultElement("data");
|
|
|
+ seriesElement.add(dataElement);
|
|
|
+ } else {
|
|
|
+ for (Object object : value) {
|
|
|
+ Element dataElement = new DefaultElement("data");
|
|
|
+ dataElement.setText(toString(object));
|
|
|
+ seriesElement.add(dataElement);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ element.add(seriesElement);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return document.asXML();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 处理看板中的pie组件
|
|
|
+ *
|
|
|
+ * @param content 看板内容
|
|
|
+ * @param jdbc NewbieJdbcSupport 对象
|
|
|
+ * @param parameters 参数
|
|
|
+ * @return 解析后的看板内容
|
|
|
+ */
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
+ private String processPie(@NotEmpty("content") String content, @NotEmpty("jdbc") NewbieJdbcSupport jdbc, List<Parameter> parameters)
|
|
|
+ throws DocumentException, IllegalStateException, SQLException {
|
|
|
+ Document document = getDocument(content);
|
|
|
+ // 获取 bar 和 line 组件
|
|
|
+ List<Element> elements = document.selectNodes("//pie");
|
|
|
+ for (Element element : elements) {
|
|
|
+ // 执行 sql ,获取数据
|
|
|
+ String sql = element.attribute("sql").getText();
|
|
|
+ if (StringUtils.isEmpty(sql)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ sql = replaceParameters(sql, parameters, true, jdbc);
|
|
|
+ checkCount(jdbc.getDataSource().getConnection(), sql);
|
|
|
+ List<Map<String, Object>> listMap = jdbc.listMap(sql);
|
|
|
+ if (CollectionUtils.isEmpty(listMap)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ // field 和 value
|
|
|
+ String field = element.attribute("field").getText().toUpperCase();
|
|
|
+ String value = element.attribute("value").getText().toUpperCase();
|
|
|
+ for (Map<String, Object> map : listMap) {
|
|
|
+ Element seriesElement = new DefaultElement("series");
|
|
|
+ seriesElement.addAttribute("name", toString(map.get(field)));
|
|
|
+ seriesElement.addAttribute("data", toString(map.get(value)));
|
|
|
+ element.add(seriesElement);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return document.asXML();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 读取 xml 为 Document 对象
|
|
|
+ *
|
|
|
+ * @param xml xml 字符串
|
|
|
+ * @return Document 对象
|
|
|
+ */
|
|
|
+ private Document getDocument(@NotEmpty("xml") String xml) throws DocumentException {
|
|
|
+ return new SAXReader().read(new StringReader(xml));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 检查当前条件下的结果数目是否超出限制
|
|
|
+ *
|
|
|
+ * @param connection 数据库连接
|
|
|
+ * @param sql 查询语句
|
|
|
+ */
|
|
|
+ private void checkCount(@NotEmpty("connection") Connection connection, @NotEmpty("sql") String sql)
|
|
|
+ throws SQLException, IllegalStateException {
|
|
|
+ int count = getCount(connection, sql);
|
|
|
+ if (count > MAX_RECORD_SIZE) {
|
|
|
+ String message = "查询条件的结果数目超出限制:" + count;
|
|
|
+ throw new IllegalStateException(message, new IllegalStateException("sql : " + sql));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取当前查询语句的结果数目
|
|
|
+ *
|
|
|
+ * @param connection 数据库连接
|
|
|
+ * @param sql 查询语句
|
|
|
+ * @return 结果数目
|
|
|
+ */
|
|
|
+ private int getCount(@NotEmpty("connection") Connection connection, @NotEmpty("sql") String sql)
|
|
|
+ throws SQLException {
|
|
|
+ PreparedStatement preparedStatement = null;
|
|
|
+ ResultSet resultSet = null;
|
|
|
+ try {
|
|
|
+ // 如果直接在 sql 外用 count(1) 统计数目,当关联表多时,可能会出现错误
|
|
|
+ // ORA-01792: 表或视图中的最大列数为 1000
|
|
|
+ // 报错主要发生在 select * 的情况下,但是不能这样判断,因为可能存在 select tt.*, pi_id from
|
|
|
+ // purchase t left join 这样的情况,很难区分
|
|
|
+ // 因此 1. 对于普通 sql ,将 select 后的字段改为 count(1)
|
|
|
+ // 2. 而最外层含有 group by 的 sql ,直接改为 count(1) 可能得到不止一行,结果也并非实际行数。再加上
|
|
|
+ // group
|
|
|
+ // by 的结果列数一般很小,所以可以在外面使用 count(1) ,一般不会超出 1000 行
|
|
|
+ String lowerSql = sql.toLowerCase();
|
|
|
+ if (!lowerSql.matches("[\\s\\S]+?group[\\s]+?by[\\s]+?[^)]+?")) {
|
|
|
+ String regex = "([\\s\\S]+?from)[\\s]+?[^,]+?";
|
|
|
+ Pattern pattern = Pattern.compile(regex);
|
|
|
+ Matcher matcher = pattern.matcher(lowerSql);
|
|
|
+ if (matcher.find()) {
|
|
|
+ int start = matcher.start(1);
|
|
|
+ int end = matcher.end(1);
|
|
|
+ sql = sql.substring(0, start) + "select count(1) from" + sql.substring(end);
|
|
|
+ } else {
|
|
|
+ throw new IllegalStateException("sql 解析错误:未发现第一个 from:" + sql);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ sql = "select count(1) from (" + sql + ")";
|
|
|
+ }
|
|
|
+ preparedStatement = connection.prepareStatement(sql);
|
|
|
+ resultSet = preparedStatement.executeQuery();
|
|
|
+ resultSet.next();
|
|
|
+ return resultSet.getInt(1);
|
|
|
+ } finally {
|
|
|
+ if (resultSet != null) {
|
|
|
+ try {
|
|
|
+ resultSet.close();
|
|
|
+ } catch (SQLException e) {
|
|
|
+ logger.error("", e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (preparedStatement != null) {
|
|
|
+ try {
|
|
|
+ preparedStatement.close();
|
|
|
+ } catch (SQLException e) {
|
|
|
+ logger.error("", e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ connection.close();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 将 List<Map<String, Object>> 转为 Map<String, List<Object>>
|
|
|
+ *
|
|
|
+ * @param listMap 将转换的对象,每个 Map 的键都相同,否则会得到非预期的结果<br/>
|
|
|
+ * 如下所示:
|
|
|
+ * <p/>
|
|
|
+ * <table border=1 cellpadding=5 cellspacing=0 summary=
|
|
|
+ * "Key and value">
|
|
|
+ * <tr>
|
|
|
+ * <th>输入</th>
|
|
|
+ * <th>输出</th>
|
|
|
+ * <th>结果</th>
|
|
|
+ * </tr>
|
|
|
+ * <tr>
|
|
|
+ * <td>[{age=12, name=Joe}, {age=31, name=Lee}]</td>
|
|
|
+ * <td>{name=[Joe, Lee], age=[12, 31]}</td>
|
|
|
+ * <td>预期</td>
|
|
|
+ * </tr>
|
|
|
+ * <tr>
|
|
|
+ * <td>[{age=12, name=Joe}, {name=Lee, agee=31}]</td>
|
|
|
+ * <td>{name=[Joe, Lee], age=[12], agee=[31]}</td>
|
|
|
+ * <td>非预期</td>
|
|
|
+ * </tr>
|
|
|
+ * </table>
|
|
|
+ * @return 转换的结果
|
|
|
+ */
|
|
|
+ private Map<String, List<Object>> convert(List<Map<String, Object>> listMap) {
|
|
|
+ Map<String, List<Object>> result = new HashMap<>();
|
|
|
+ if (CollectionUtils.isEmpty(listMap)) {
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+ for (Map<String, Object> map : listMap) {
|
|
|
+ Set<Entry<String, Object>> entrySet = map.entrySet();
|
|
|
+ for (Entry<String, Object> entry : entrySet) {
|
|
|
+ String key = entry.getKey();
|
|
|
+ Object value = entry.getValue();
|
|
|
+ List<Object> list = result.get(key);
|
|
|
+ if (list == null) {
|
|
|
+ list = new ArrayList<>();
|
|
|
+ result.put(key, list);
|
|
|
+ }
|
|
|
+ list.add(value);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 将对象转为字符串
|
|
|
+ *
|
|
|
+ * @param object 对象
|
|
|
+ * @return 转换的字符串
|
|
|
+ */
|
|
|
+ private String toString(Object object) {
|
|
|
+ if (object == null) {
|
|
|
+ return "";
|
|
|
+ }
|
|
|
+ if (object instanceof Date) {
|
|
|
+ return String.valueOf(((Date) object).getTime());
|
|
|
+ }
|
|
|
+ return String.valueOf(object);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 对看板内容的最终处理,包括映射、添加title等
|
|
|
+ *
|
|
|
+ * @param content 看板内容
|
|
|
+ * @param title 标题
|
|
|
+ * @return 处理后的看板内容
|
|
|
+ */
|
|
|
+ private String finalProcess(@NotEmpty("content") String content, String title)
|
|
|
+ throws TransformerException, IOException, DocumentException {
|
|
|
+ InputStream mapRuleStream = FileHelper.readStream("map-rule.xsl");
|
|
|
+ content = TranslateHelper.map(content, mapRuleStream);
|
|
|
+ if (!StringUtils.isEmpty(title)) {
|
|
|
+ content = addTitle(content, title);
|
|
|
+ }
|
|
|
+ return content;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 为看板添加 title
|
|
|
+ *
|
|
|
+ * @param content 看板内容
|
|
|
+ * @param title 标题
|
|
|
+ * @return 处理后的看板内容
|
|
|
+ */
|
|
|
+ private String addTitle(@NotEmpty("content") String content, @NotEmpty("title") String title)
|
|
|
+ throws DocumentException {
|
|
|
+ Document document = getDocument(content);
|
|
|
+ Element rootElement = document.getRootElement();
|
|
|
+ Element titleElement = new DefaultElement("title");
|
|
|
+ titleElement.setText(title);
|
|
|
+ rootElement.add(titleElement);
|
|
|
+ return document.asXML();
|
|
|
+ }
|
|
|
+}
|