Merge remote-tracking branch 'origin/dev-1.12.0'

This commit is contained in:
datagear 2020-09-15 10:03:14 +08:00
commit 104c81a3f4
121 changed files with 13380 additions and 2746 deletions

View File

@ -8,16 +8,16 @@ DataGear是一款数据可视化分析平台使用Java语言开发采用
## 系统特点
- 可管理数据库驱动
<br>管理员可通过驱动程序管理功能添加数据库驱动程序,无需重启,即可支持连接新数据库。
<br>可通过驱动程序管理功能添加数据库驱动程序,无需重启,即可支持连接新数据库。
- 参数化数据集
<br>可编写动态SQL语句数据集为其添加参数构建可交互式图表
- 多种格式的数据集
<br>支持SQL、CSV、Excel、HTTP接口、JSON等多种格式的数据集
- 多数据聚合图表
<br>一个图表可添加多个不同数据源的数据集,将它们聚合展示于同一图表
- 多数据聚合图表
<br>一个图表可添加多个不同格式的数据集,将它们聚合展示
- 插件式图表类型
<br>每一种类型的图表都以图表插件形式提供支持,并内置了大量图表插件,管理员也可上传自定义图表插件,丰富系统图表类型。
<br>每一种类型的图表都以图表插件形式提供,并内置了大量图表插件,管理员也可上传自定义图表插件,丰富系统图表类型。
- 可自由编辑的HTML看板模板
<br>看板使用原生的HTML网页作为模板可自由编辑、绑定、异步加载图表并支持将任意HTML网页导入为看板。

View File

@ -1,6 +1,6 @@
下一主版本:
扩展数据集功能支持Excel、HTTP API数据集
下一修订版本:
@ -8,6 +8,7 @@
待定:
数据分析添加项目概念,用于分组管理数据集、图表、看板;
组合数据集新建组合数据集CombineDataSet选定多个其他数据集通过SQL语句连接、选取它们生成新的数据
全局看板资源管理功能;
图表参数;

View File

@ -7,7 +7,7 @@
<parent>
<groupId>org.datagear</groupId>
<artifactId>datagear</artifactId>
<version>1.11.1</version>
<version>1.12.0</version>
</parent>
<artifactId>datagear-analysis</artifactId>
@ -44,6 +44,31 @@
<artifactId>freemarker</artifactId>
<version>${freemarker.version}</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-csv</artifactId>
<version>${commons-csv.version}</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>${poi.version}</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>${poi-ooxml.version}</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
<version>5.0.1</version>
</dependency>
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<version>2.4.0</version>
</dependency>
</dependencies>
<build>

View File

@ -72,6 +72,9 @@ public interface DataSet extends Identifiable
/**
* 获取{@linkplain DataSetResult}
* <p>
* 返回结果中的数据项应已转换为与{@linkplain #getProperties()}{@linkplain DataSetProperty#getType()}类型一致
* </p>
*
* @param paramValues
* {@linkplain #getParams()}所描述的参数值映射表其关键字是{@linkplain DataSetParam#getName()}

View File

@ -11,14 +11,8 @@ package org.datagear.analysis;
import java.io.Serializable;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.Date;
import java.util.List;
import java.util.Map;
import org.datagear.util.StringUtil;
/**
* 数据集属性信息
* <p>
@ -67,53 +61,11 @@ public class DataSetProperty extends AbstractDataNameType implements Serializabl
}
/**
* 连接给定列表的{@linkplain #getLabel()}
* <p>
* 如果{@code dataSetProperties}{@code null}将返回空字符串
* </p>
* {@linkplain DataSetProperty#getType()}类型枚举
*
* @param dataSetProperties
* @param splitter
* @author datagear@163.com
*
*/
public static String concatLabels(List<DataSetProperty> dataSetProperties, String splitter)
{
if (dataSetProperties == null)
return "";
StringBuilder sb = new StringBuilder();
for (int i = 0; i < dataSetProperties.size(); i++)
{
DataSetProperty dataSetProperty = dataSetProperties.get(i);
String label = dataSetProperty.getLabel();
if (!StringUtil.isEmpty(label))
{
if (sb.length() > 0)
sb.append(splitter);
sb.append(label);
}
}
return sb.toString();
}
/**
* 拆分由{@linkplain #concatLabels(List, String)}连接的字符串
*
* @param labelText
* @param splitter
* @return
*/
public static String[] splitLabels(String labelText, String splitter)
{
if (labelText == null)
return new String[0];
return StringUtil.split(labelText, splitter, true);
}
public static class DataType
{
/** 字符串 */
@ -143,270 +95,6 @@ public class DataSetProperty extends AbstractDataNameType implements Serializabl
/** 未知类型 */
public static final String UNKNOWN = "UNKNOWN";
/**
* 是否是{@linkplain #STRING}
*
* @param dataType
* @return
*/
public static boolean isString(String dataType)
{
return STRING.equals(dataType);
}
/**
* 是否是{@linkplain #BOOLEAN}
*
* @param dataType
* @return
*/
public static boolean isBoolean(String dataType)
{
return BOOLEAN.equals(dataType);
}
/**
* 是否是{@linkplain #NUMBER}
*
* @param dataType
* @return
*/
public static boolean isNumber(String dataType)
{
return NUMBER.equals(dataType);
}
/**
* 是否是{@linkplain #INTEGER}
*
* @param dataType
* @return
*/
public static boolean isInteger(String dataType)
{
return INTEGER.equals(dataType);
}
/**
* 是否是{@linkplain #DECIMAL}
*
* @param dataType
* @return
*/
public static boolean isDecimal(String dataType)
{
return DECIMAL.equals(dataType);
}
/**
* 是否是数值型的
*
* @param dataType
* @return
*/
public static boolean isNumberic(String dataType)
{
return (isNumber(dataType) || isInteger(dataType) || isDecimal(dataType));
}
/**
* 是否是{@linkplain #DATE}
*
* @param dataType
* @return
*/
public static boolean isDate(String dataType)
{
return DATE.equals(dataType);
}
/**
* 是否是{@linkplain #TIME}
*
* @param dataType
* @return
*/
public static boolean isTime(String dataType)
{
return TIME.equals(dataType);
}
/**
* 是否是{@linkplain #TIMESTAMP}
*
* @param dataType
* @return
*/
public static boolean isTimestamp(String dataType)
{
return TIMESTAMP.equals(dataType);
}
/**
* 是否是{@linkplain #UNKNOWN}
*
* @param dataType
* @return
*/
public static boolean isUnknown(String dataType)
{
return UNKNOWN.equals(dataType);
}
/**
* {@linkplain #STRING}类型的值转换为{@linkplain String}
* <p>
* {@code value}参数必须是{@linkplain String}类型
* </p>
*
* @param value
* @return
*/
public static String castString(Object value)
{
return (String) value;
}
/**
* {@linkplain #BOOLEAN}类型的值转换为{@linkplain Boolean}
* <p>
* {@code value}参数必须是{@linkplain Boolean}类型
* </p>
*
* @param value
* @return
*/
public static Boolean castBoolean(Object value)
{
return (Boolean) value;
}
/**
* {@linkplain #INTEGER}类型的值转换为{@linkplain Number}
* <p>
* {@code value}参数必须是{@linkplain Number}或其子类型
* </p>
*
* @param value
* @return
*/
public static Number castInteger(Object value)
{
return (Number) value;
}
/**
* {@linkplain #INTEGER}类型的值转换为{@linkplain BigInteger}
* <p>
* {@code value}参数必须是{@linkplain Number}或其子类型
* </p>
*
* @param value
* @return
*/
public static BigInteger castBigInteger(Object value)
{
Number number = castInteger(value);
if (number == null)
return null;
else if (number instanceof BigInteger)
return (BigInteger) number;
else
return BigInteger.valueOf(number.longValue());
}
/**
* {@linkplain #DECIMAL}类型的值转换为{@linkplain Number}
* <p>
* {@code value}参数必须是{@linkplain Number}或其子类型
* </p>
*
* @param value
* @return
*/
public static Number castDecimal(Object value)
{
return (Number) value;
}
/**
* {@linkplain #DECIMAL}类型的值转换为{@linkplain BigDecimal}
* <p>
* {@code value}参数必须是{@linkplain Number}或其子类型
* </p>
*
* @param value
* @return
*/
public static BigDecimal castBigDecimal(Object value)
{
Number number = castInteger(value);
if (number == null)
return null;
else if (number instanceof BigDecimal)
return (BigDecimal) number;
else
return BigDecimal.valueOf(number.doubleValue());
}
/**
* {@linkplain #DATE}类型的值转换为{@linkplain Date}
* <p>
* {@code value}参数必须是{@linkplain Date}或其子类型
* </p>
*
* @param value
* @return
*/
public static Date castDate(Object value)
{
return (Date) value;
}
/**
* {@linkplain #TIME}类型的值转换为{@linkplain Time}
* <p>
* {@code value}参数必须是{@linkplain Date}或其子类型
* </p>
*
* @param value
* @return
*/
public static Time castTime(Object value)
{
Date date = castDate(value);
if (date == null)
return null;
else if (date instanceof Time)
return (Time) value;
else
return new Time(date.getTime());
}
/**
* {@linkplain #TIME}类型的值转换为{@linkplain Timestamp}
* <p>
* {@code value}参数必须是{@linkplain Date}或其子类型
* </p>
*
* @param value
* @return
*/
public static Timestamp castTimestamp(Object value)
{
Date date = castDate(value);
if (date == null)
return null;
else if (date instanceof Timestamp)
return (Timestamp) value;
else
return new Timestamp(date.getTime());
}
/**
* 解析对象的数据类型
*

View File

@ -0,0 +1,387 @@
/*
* Copyright (c) 2018 datagear.tech. All Rights Reserved.
*/
/**
*
*/
package org.datagear.analysis.support;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVParser;
import org.apache.commons.csv.CSVRecord;
import org.datagear.analysis.DataSetException;
import org.datagear.analysis.DataSetProperty;
import org.datagear.analysis.DataSetResult;
import org.datagear.analysis.ResolvableDataSet;
import org.datagear.analysis.ResolvedDataSetResult;
import org.datagear.util.IOUtil;
/**
* 抽象CSV数据集
*
* @author datagear@163.com
*
*/
public abstract class AbstractCsvDataSet extends AbstractResolvableDataSet implements ResolvableDataSet
{
/** 作为名称行的行号 */
private int nameRow = -1;
public AbstractCsvDataSet()
{
super();
}
public AbstractCsvDataSet(String id, String name)
{
super(id, name);
}
public AbstractCsvDataSet(String id, String name, List<DataSetProperty> properties)
{
super(id, name, properties);
}
/**
* 是否有名称行
*
* @return
*/
public boolean hasNameRow()
{
return (this.nameRow > 0);
}
/**
* 获取作为名称行的行号
*
* @return
*/
public int getNameRow()
{
return nameRow;
}
/**
* 设置作为名称行的行号
*
* @param nameRow
* 行号小于{@code 1}则表示无名称行
*/
public void setNameRow(int nameRow)
{
this.nameRow = nameRow;
}
/**
* 解析结果
* <p>
* 如果{@linkplain #getCsvReader(Map)}返回的{@linkplain TemplateResolvedSource#hasResolvedTemplate()}
* 此方法将返回{@linkplain TemplateResolvedDataSetResult}
* </p>
*/
@Override
protected ResolvedDataSetResult resolveResult(Map<String, ?> paramValues, List<DataSetProperty> properties)
throws DataSetException
{
TemplateResolvedSource<Reader> reader = null;
try
{
reader = getCsvReader(paramValues);
ResolvedDataSetResult result = resolveResult(reader.getSource(), properties);
if (reader.hasResolvedTemplate())
result = new TemplateResolvedDataSetResult(result.getResult(), result.getProperties(),
reader.getResolvedTemplate());
return result;
}
catch (DataSetException e)
{
throw e;
}
catch (Throwable t)
{
throw new DataSetSourceParseException(t);
}
finally
{
if (reader != null)
IOUtil.close(reader.getSource());
}
}
/**
* 获取CSV输入流
* <p>
* 实现方法应该返回实例级不变的输入流
* </p>
*
* @param paramValues
* @return
* @throws Throwable
*/
protected abstract TemplateResolvedSource<Reader> getCsvReader(Map<String, ?> paramValues) throws Throwable;
@SuppressWarnings({ "unchecked", "rawtypes" })
protected ResolvedDataSetResult resolveResult(Reader csvReader, List<DataSetProperty> properties) throws Throwable
{
boolean resolveProperties = (properties == null || properties.isEmpty());
CSVParser csvParser = buildCSVParser(csvReader);
List<String> propertyNames = null;
List<List<Object>> data = new ArrayList<>();
DataSetPropertyValueConverter converter = createDataSetPropertyValueConverter();
int rowIdx = 0;
int dataRowIdx = 0;
for (CSVRecord csvRecord : csvParser)
{
if (isNameRow(rowIdx))
{
if (resolveProperties)
propertyNames = resolveDataSetPropertyNames(csvRecord, false);
}
else
{
if (resolveProperties && dataRowIdx == 0 && propertyNames == null)
propertyNames = resolveDataSetPropertyNames(csvRecord, true);
if (resolveProperties)
data.add(resolveCSVRecordValues(csvRecord, null, converter));
else
data.add(resolveCSVRecordValues(csvRecord, properties, converter));
dataRowIdx++;
}
rowIdx++;
}
if (resolveProperties)
return resolveResult(propertyNames, ((List) data));
else
{
DataSetResult result = new DataSetResult(listRowsToMapRows(data, properties));
return new ResolvedDataSetResult(result, properties);
}
}
/**
* 由原始的字符串CSV数据解析{@linkplain ResolvedDataSetResult}
*
* @param propertyNames
* 允许为{@code null}
* @param data
* 允许为{@code null}
* @return
* @throws Throwable
*/
protected ResolvedDataSetResult resolveResult(List<String> propertyNames, List<List<String>> data) throws Throwable
{
List<DataSetProperty> properties = new ArrayList<>((propertyNames == null ? 0 : propertyNames.size()));
if (propertyNames != null)
{
for (String name : propertyNames)
properties.add(new DataSetProperty(name, DataSetProperty.DataType.STRING));
}
@SuppressWarnings("unchecked")
List<Map<String, Object>> resultData = Collections.EMPTY_LIST;
// 根据数据格式修订可能的数值类型只有某一列的所有字符串都是数值格式才认为是数值类型
if (data != null)
{
int plen = properties.size();
// 指定索引的字符串是否都是数值内容
Map<Integer, Boolean> asNumberMap = new HashMap<>();
for (List<String> ele : data)
{
for (int i = 0; i < Math.min(plen, ele.size()); i++)
{
Boolean asNumber = asNumberMap.get(i);
if (Boolean.FALSE.equals(asNumber))
continue;
String val = ele.get(i);
asNumberMap.put(i, isNumberString(val));
}
}
for (Map.Entry<Integer, Boolean> entry : asNumberMap.entrySet())
{
if (Boolean.TRUE.equals(entry.getValue()))
properties.get(entry.getKey()).setType(DataSetProperty.DataType.NUMBER);
}
List<List<Object>> newData = new ArrayList<>(data.size());
for (List<String> ele : data)
{
int size = Math.min(plen, ele.size());
List<Object> newEle = new ArrayList<>(size);
for (int i = 0; i < size; i++)
{
String val = ele.get(i);
if (Boolean.TRUE.equals(asNumberMap.get(i)))
{
Number num = parseNumberString(val);
newEle.add(num);
}
else
newEle.add(val);
}
newData.add(newEle);
}
resultData = listRowsToMapRows(newData, properties);
}
DataSetResult result = new DataSetResult(resultData);
return new ResolvedDataSetResult(result, properties);
}
/**
* 指定的CSV值是否可被当做数值类型
*
* @param value
* @return
*/
protected boolean isNumberString(String value)
{
if (value == null || value.isEmpty())
return false;
try
{
parseNumberString(value);
return true;
}
catch (Throwable t)
{
return false;
}
}
/**
* 解析数值字符串{@linkplain #isNumberString(String)}应为{@code true}
*
* @param s
* @return
* @throws Throwable
*/
protected Number parseNumberString(String s) throws Throwable
{
return Double.parseDouble(s);
}
/**
*
* @param csvRecord
* @param forceIndex
* 是否强制使用索引号将返回{@code ["1""2"....]}
* @return
* @throws Throwable
*/
protected List<String> resolveDataSetPropertyNames(CSVRecord csvRecord, boolean forceIndex) throws Throwable
{
int size = csvRecord.size();
List<String> list = new ArrayList<>(size);
for (int i = 0; i < size; i++)
{
if (forceIndex)
list.add(Integer.toString(i + 1));
else
list.add(csvRecord.get(i));
}
return list;
}
/**
* 解析数据列表
* <p>
* 如果{@code properties}{@code null}或者对应元素为{@code null}则返回列表对应元素将是{@linkplain String}类型
* </p>
*
* @param csvRecord
* @param properties
* 允许为{@code null}元素为{@code null}
* @param converter
* @return
* @throws Throwable
*/
protected List<Object> resolveCSVRecordValues(CSVRecord csvRecord, List<DataSetProperty> properties,
DataSetPropertyValueConverter converter) throws Throwable
{
int size = csvRecord.size();
List<Object> list = new ArrayList<>(size);
int propertySize = (properties == null ? 0 : properties.size());
for (int i = 0; i < size; i++)
{
DataSetProperty property = (i < propertySize ? properties.get(i) : null);
String rawValue = csvRecord.get(i);
if (property == null)
list.add(rawValue);
else
{
Object value = convertToPropertyDataType(converter, rawValue, property);
list.add(value);
}
}
return list;
}
/**
* 是否名称行
*
* @param rowIndex
* 行索引{@code 0}计数
* @return
*/
protected boolean isNameRow(int rowIndex)
{
return ((rowIndex + 1) == this.nameRow);
}
/**
* 构建{@linkplain CSVParser}
*
* @param reader
* @return
* @throws Throwable
*/
protected CSVParser buildCSVParser(Reader reader) throws Throwable
{
return CSVFormat.DEFAULT.withIgnoreSurroundingSpaces().parse(reader);
}
}

View File

@ -0,0 +1,75 @@
/*
* Copyright (c) 2018 datagear.tech. All Rights Reserved.
*/
/**
*
*/
package org.datagear.analysis.support;
import java.io.File;
import java.io.Reader;
import java.util.List;
import java.util.Map;
import org.datagear.analysis.DataSetProperty;
import org.datagear.util.IOUtil;
/**
* 抽象CSV文件数据集
* <p>
* 注意此类不支持<code>Freemarker</code>模板语言
* </p>
*
* @author datagear@163.com
*
*/
public abstract class AbstractCsvFileDataSet extends AbstractCsvDataSet
{
/** 文件编码 */
private String encoding = IOUtil.CHARSET_UTF_8;
public AbstractCsvFileDataSet()
{
super();
}
public AbstractCsvFileDataSet(String id, String name)
{
super(id, name);
}
public AbstractCsvFileDataSet(String id, String name, List<DataSetProperty> properties)
{
super(id, name, properties);
}
public String getEncoding()
{
return encoding;
}
public void setEncoding(String encoding)
{
this.encoding = encoding;
}
@Override
protected TemplateResolvedSource<Reader> getCsvReader(Map<String, ?> paramValues) throws Throwable
{
File file = getCsvFile(paramValues);
return new TemplateResolvedSource<>(IOUtil.getReader(file, this.encoding));
}
/**
* 获取CSV文件
* <p>
* 实现方法应该返回实例级不变的文件
* </p>
*
* @param paramValues
* @return
* @throws Throwable
*/
protected abstract File getCsvFile(Map<String, ?> paramValues) throws Throwable;
}

View File

@ -7,7 +7,9 @@
*/
package org.datagear.analysis.support;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -25,6 +27,9 @@ import org.datagear.analysis.DataSetProperty;
*/
public abstract class AbstractDataSet extends AbstractIdentifiable implements DataSet
{
/** 默认Freemarker模板解析器 */
public static final DataSetFmkTemplateResolver FMK_TEMPLATE_RESOLVER = new DataSetFmkTemplateResolver();
private String name;
private List<DataSetProperty> properties;
@ -32,9 +37,13 @@ public abstract class AbstractDataSet extends AbstractIdentifiable implements Da
@SuppressWarnings("unchecked")
private List<DataSetParam> params = Collections.EMPTY_LIST;
/** 属性数据转换格式 */
private DataFormat propertyDataFormat = null;
public AbstractDataSet()
{
super();
setPropertyDataFormat(new DataFormat());
}
public AbstractDataSet(String id, String name, List<DataSetProperty> properties)
@ -42,6 +51,7 @@ public abstract class AbstractDataSet extends AbstractIdentifiable implements Da
super(id);
this.name = name;
this.properties = properties;
setPropertyDataFormat(new DataFormat());
}
@Override
@ -94,6 +104,24 @@ public abstract class AbstractDataSet extends AbstractIdentifiable implements Da
return getDataNameTypeByName(this.params, name);
}
public DataFormat getPropertyDataFormat()
{
return propertyDataFormat;
}
/**
* 设置属性数据转换格式
* <p>
* {@linkplain DataSetProperty#getType()}不是结果数据的原始类型而需要进行类型转换时需要使用数据转换格式进行转换
* </p>
*
* @param propertyDataFormat
*/
public void setPropertyDataFormat(DataFormat propertyDataFormat)
{
this.propertyDataFormat = propertyDataFormat;
}
@Override
public boolean isReady(Map<String, ?> paramValues)
{
@ -111,6 +139,76 @@ public abstract class AbstractDataSet extends AbstractIdentifiable implements Da
return true;
}
/**
* 将源对象转换为指定{@linkplain DataSetProperty.DataType}类型的对象
* <p>
* 如果{@code property}{@code null}则什么也不做直接返回
* </p>
*
* @param converter
* @param source
* @param property
* 允许为{@code null}
* @return
*/
protected Object convertToPropertyDataType(DataSetPropertyValueConverter converter, Object source,
DataSetProperty property)
{
if (property == null)
return source;
return convertToPropertyDataType(converter, source, property.getType());
}
/**
* 将源对象转换为指定{@linkplain DataSetProperty.DataType}类型的对象
* <p>
* 如果{@code propertyType}{@code null}则什么也不做直接返回
* </p>
*
* @param converter
* @param source
* @param propertyType
* 允许为{@code null}
* @return
*/
protected Object convertToPropertyDataType(DataSetPropertyValueConverter converter, Object source,
String propertyType)
{
if (propertyType == null || DataSetProperty.DataType.UNKNOWN.equals(propertyType))
return source;
return converter.convert(source, propertyType);
}
/**
* 创建一个{@linkplain DataSetPropertyValueConverter}实例
* <p>
* 由于{@linkplain DataSetPropertyValueConverter}不是线程安全的所以每次使用时要手动创建
* </p>
*
* @return
*/
protected DataSetPropertyValueConverter createDataSetPropertyValueConverter()
{
DataFormat dataFormat = this.propertyDataFormat;
if (dataFormat == null)
dataFormat = new DataFormat();
return new DataSetPropertyValueConverter(dataFormat);
}
/**
* 解析{@linkplain DataSetProperty.DataType}类型
*
* @param value
* @return
*/
protected String resolvePropertyDataType(Object value)
{
return DataSetProperty.DataType.resolveDataType(value);
}
/**
* 获取指定名称的{@linkplain DataNameType}对象没找到则返回{@code null}
*
@ -133,4 +231,60 @@ public abstract class AbstractDataSet extends AbstractIdentifiable implements Da
return null;
}
@SuppressWarnings("unchecked")
protected List<Map<String, Object>> listRowsToMapRows(List<List<Object>> data, List<DataSetProperty> properties)
{
if (data == null)
return Collections.EMPTY_LIST;
int plen = properties.size();
List<Map<String, Object>> maps = new ArrayList<>(data.size());
for (List<Object> row : data)
{
Map<String, Object> map = new HashMap<>();
for (int i = 0; i < Math.min(plen, row.size()); i++)
{
String name = properties.get(i).getName();
map.put(name, row.get(i));
}
maps.add(map);
}
return maps;
}
/**
* 只有当{@linkplain #hasParam()}{@code true}时才将指定文本作为Freemarker模板解析
*
* @param text
* @param paramValues
* @return
*/
protected String resolveAsFmkTemplateIfHasParam(String text, Map<String, ?> paramValues)
{
if (!hasParam())
return text;
return resolveAsFmkTemplate(text, paramValues);
}
/**
* 将指定文本作为Freemarker模板解析
*
* @param text
* @param paramValues
* @return
*/
protected String resolveAsFmkTemplate(String text, Map<String, ?> paramValues)
{
if (text == null)
return null;
return FMK_TEMPLATE_RESOLVER.resolve(text, paramValues);
}
}

View File

@ -0,0 +1,684 @@
/*
* Copyright (c) 2018 datagear.tech. All Rights Reserved.
*/
/**
*
*/
package org.datagear.analysis.support;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hssf.util.CellReference;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.openxml4j.opc.PackageAccess;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.DateUtil;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.datagear.analysis.DataSetException;
import org.datagear.analysis.DataSetProperty;
import org.datagear.analysis.DataSetResult;
import org.datagear.analysis.ResolvableDataSet;
import org.datagear.analysis.ResolvedDataSetResult;
import org.datagear.analysis.support.RangeExpResolver.IndexRange;
import org.datagear.analysis.support.RangeExpResolver.Range;
import org.datagear.util.FileUtil;
import org.datagear.util.IOUtil;
import org.datagear.util.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 抽象Excel数据集
* <p>
* 此类仅支持从Excel的单个sheet读取数据具体参考{@linkplain #setSheetIndex(int)}
* </p>
* <p>
* 通过{@linkplain #setDataRowExp(String)}{@linkplain #setDataColumnExp(String)}来设置读取行列范围
* </p>
* <p>
* 通过{@linkplain #setNameRow(int)}可设置名称行
* </p>
* <p>
* 注意此类不支持<code>Freemarker</code>模板语言
* </p>
*
* @author datagear@163.com
*
*/
public abstract class AbstractExcelDataSet extends AbstractResolvableDataSet implements ResolvableDataSet
{
protected static final Logger LOGGER = LoggerFactory.getLogger(AbstractExcelDataSet.class);
public static final String EXTENSION_XLSX = "xlsx";
public static final String EXTENSION_XLS = "xls";
protected static final RangeExpResolver RANGE_EXP_RESOLVER = RangeExpResolver
.valueOf(RangeExpResolver.RANGE_SPLITTER_CHAR, RangeExpResolver.RANGE_GROUP_SPLITTER_CHAR);
/** 此数据集所处的sheet索引号以1计数 */
private int sheetIndex = 1;
/** 作为名称行的行号 */
private int nameRow = -1;
/** 数据行范围表达式 */
private String dataRowExp = "";
/** 数据列范围表达式 */
private String dataColumnExp = "";
/** 是否强制作为xls文件处理 */
private boolean forceXls = false;
private transient List<IndexRange> _dataRowRanges = null;
private transient List<IndexRange> _dataColumnRanges = null;
public AbstractExcelDataSet()
{
super();
}
public AbstractExcelDataSet(String id, String name)
{
super(id, name);
}
public AbstractExcelDataSet(String id, String name, List<DataSetProperty> properties)
{
super(id, name, properties);
}
public int getSheetIndex()
{
return sheetIndex;
}
/**
* 设置此数据集所处的sheet号
*
* @param sheetIndex
* sheet号{@code 1}计数
*/
public void setSheetIndex(int sheetIndex)
{
this.sheetIndex = sheetIndex;
}
/**
* 是否有名称行
*
* @return
*/
public boolean hasNameRow()
{
return (this.nameRow > 0);
}
/**
* 获取作为名称行的行号
*
* @return
*/
public int getNameRow()
{
return nameRow;
}
/**
* 设置作为名称行的行号
*
* @param nameRow
* 行号小于{@code 1}则表示无名称行
*/
public void setNameRow(int nameRow)
{
this.nameRow = nameRow;
}
public String getDataRowExp()
{
return dataRowExp;
}
/**
* 设置数据行范围表达式
* <p>
* 表达式格式示例为
* </p>
* <p>
* {@code "6"} 第6行 <br>
* {@code "3-15"} 第3至15行 <br>
* {@code "1,4,8-15"}第148至15行
* </p>
* <p>
* 标题行{@linkplain #getNameRow()}将自动被排除
* </p>
* <p>
* 注意行号以{@code 1}开始计数
* </p>
*
* @param dataRowExp
* 表达式{@code null}{@code ""}则不限定
*/
public void setDataRowExp(String dataRowExp)
{
this.dataRowExp = dataRowExp;
this._dataRowRanges = getRangeExpResolver().resolveIndex(this.dataRowExp);
}
public String getDataColumnExp()
{
return dataColumnExp;
}
/**
* 设置数据列范围表达式
* <p>
* 表达式格式为
* </p>
* <p>
* {@code "A"}第A列 <br>
* {@code "C-E"}第C至E列 <br>
* {@code "A,C,E-H"}第ACE至H列
* </p>
*
* @param dataColumnExp
* 表达式{@code null}{@code ""}则不限定
*/
public void setDataColumnExp(String dataColumnExp)
{
this.dataColumnExp = dataColumnExp;
this._dataColumnRanges = resolveDataColumnRanges(dataColumnExp);
}
/**
* 是否强制作为xls文件处理
*
* @return
*/
public boolean isForceXls()
{
return forceXls;
}
/**
* 设置是否强制作为xls文件处理如果为{@code false}则根据文件扩展名判断
*
* @param forceXls
*/
public void setForceXls(boolean forceXls)
{
this.forceXls = forceXls;
}
@Override
protected ResolvedDataSetResult resolveResult(Map<String, ?> paramValues, List<DataSetProperty> properties)
throws DataSetException
{
File file = getExcelFile(paramValues);
ResolvedDataSetResult result = null;
if (isXls(file))
result = resolveResultForXls(paramValues, file, properties);
else
result = resolveResultForXlsx(paramValues, file, properties);
return result;
}
/**
* 解析{@code xls}结果
*
* @param paramValues
* @param file
* @param properties
* 允许为{@code null}此时会自动解析
* @return
* @throws DataSetException
*/
protected ResolvedDataSetResult resolveResultForXls(Map<String, ?> paramValues, File file,
List<DataSetProperty> properties) throws DataSetException
{
POIFSFileSystem poifs = null;
HSSFWorkbook wb = null;
try
{
poifs = new POIFSFileSystem(file, true);
wb = new HSSFWorkbook(poifs.getRoot(), true);
Sheet sheet = wb.getSheetAt(getSheetIndex() - 1);
return resolveResultForSheet(paramValues, sheet, properties);
}
catch (DataSetException e)
{
throw e;
}
catch (Throwable t)
{
throw new DataSetSourceParseException(t);
}
finally
{
IOUtil.close(wb);
IOUtil.close(poifs);
}
}
/**
* 解析{@code xlsx}结果
*
* @param paramValues
* @param file
* @param properties
* 允许为{@code null}此时会自动解析
* @return
* @throws DataSetException
*/
protected ResolvedDataSetResult resolveResultForXlsx(Map<String, ?> paramValues, File file,
List<DataSetProperty> properties) throws DataSetException
{
OPCPackage pkg = null;
XSSFWorkbook wb = null;
try
{
pkg = OPCPackage.open(file, PackageAccess.READ);
wb = new XSSFWorkbook(pkg);
Sheet sheet = wb.getSheetAt(getSheetIndex() - 1);
return resolveResultForSheet(paramValues, sheet, properties);
}
catch (DataSetException e)
{
throw e;
}
catch (Throwable t)
{
throw new DataSetSourceParseException(t);
}
finally
{
IOUtil.close(wb);
IOUtil.close(pkg);
}
}
/**
* 解析sheet结果
*
* @param paramValues
* @param sheet
* @param properties
* 允许为{@code null}此时会自动解析
* @return
* @throws DataSetException
*/
protected ResolvedDataSetResult resolveResultForSheet(Map<String, ?> paramValues, Sheet sheet,
List<DataSetProperty> properties) throws DataSetException
{
boolean resolveProperties = (properties == null || properties.isEmpty());
if (resolveProperties)
properties = new ArrayList<>();
List<List<Object>> data = new ArrayList<>();
List<String> propertyNames = null;
DataSetPropertyValueConverter converter = createDataSetPropertyValueConverter();
try
{
int rowIdx = 0;
int dataRowIdx = 0;
for (Row row : sheet)
{
if (isNameRow(rowIdx))
{
if (resolveProperties)
propertyNames = resolveDataSetPropertyNames(row, false);
}
else if (isDataRow(rowIdx))
{
if (resolveProperties && dataRowIdx == 0 && propertyNames == null)
propertyNames = resolveDataSetPropertyNames(row, true);
// 名称行不一定在数据行之前此时可能还无法确定属性名所以暂时采用列表存储
List<Object> rowObj = new ArrayList<>();
int colIdx = 0;
int dataColIdx = 0;
for (Cell cell : row)
{
if (isDataColumn(colIdx))
{
DataSetProperty property = null;
if (!resolveProperties)
{
if (dataColIdx >= properties.size())
throw new DataSetSourceParseException(
"No property defined for column index " + dataColIdx);
property = properties.get(dataColIdx);
}
Object value = resolvePropertyValue(cell, property, converter);
if (resolveProperties)
{
property = resolveDataSetProperty(row, rowIdx, dataRowIdx, cell, colIdx, dataColIdx,
value, properties);
}
rowObj.add(value);
dataColIdx++;
}
colIdx++;
}
data.add(rowObj);
dataRowIdx++;
}
rowIdx++;
}
}
catch (DataSetException e)
{
throw e;
}
catch (Throwable t)
{
throw new DataSetSourceParseException(t);
}
if (resolveProperties)
inflateDataSetProperties(properties, propertyNames);
DataSetResult result = new DataSetResult(listRowsToMapRows(data, properties));
return new ResolvedDataSetResult(result, properties);
}
protected void inflateDataSetProperties(List<DataSetProperty> properties, List<String> propertyNames)
{
for (int i = 0; i < properties.size(); i++)
{
DataSetProperty property = properties.get(i);
property.setName(propertyNames.get(i));
if (StringUtil.isEmpty(property.getType()))
property.setType(DataSetProperty.DataType.UNKNOWN);
}
}
/**
* 解析{@linkplain DataSetProperty}并写入{@code properties}
*
* @param row
* @param rowIdx
* @param dataRowIdx
* @param cell
* @param colIdx
* @param dataColIdx
* @param cellValue
* @param properties
* @return
*/
protected DataSetProperty resolveDataSetProperty(Row row, int rowIdx, int dataRowIdx, Cell cell, int colIdx,
int dataColIdx, Object cellValue, List<DataSetProperty> properties)
{
DataSetProperty property = null;
if (dataRowIdx == 0)
{
// 空单元格先不处理数据类型等待后续有非空单元格再判断
String dataType = (cellValue == null ? "" : resolvePropertyDataType(cellValue));
// 名称行不一定在数据行之前所以此时可能无法确定属性名
property = new DataSetProperty("should-be-set-later", dataType);
properties.add(property);
}
else
{
property = properties.get(dataColIdx);
if (StringUtil.isEmpty(property.getType()) && cellValue != null)
property.setType(resolvePropertyDataType(cellValue));
}
return property;
}
/**
* 解析属性名
*
* @param nameRow
* @return
*/
protected List<String> resolveDataSetPropertyNames(Row nameRow, boolean forceColumnString)
{
List<String> propertyNames = new ArrayList<>();
int colIdx = 0;
for (Cell cell : nameRow)
{
if (isDataColumn(colIdx))
{
String name = null;
if (forceColumnString)
name = CellReference.convertNumToColString(colIdx);
else
{
try
{
name = cell.getStringCellValue();
}
catch (Throwable t)
{
}
if (StringUtil.isEmpty(name))
name = CellReference.convertNumToColString(colIdx);
}
propertyNames.add(name);
}
colIdx++;
}
return propertyNames;
}
/**
* 解析单元格属性值
*
* @param cell
* @param property
* 允许为{@code null}
* @param converter
* @return
* @throws DataSetSourceParseException
* @throws DataSetException
*/
protected Object resolvePropertyValue(Cell cell, DataSetProperty property, DataSetPropertyValueConverter converter)
throws DataSetSourceParseException, DataSetException
{
CellType cellType = cell.getCellTypeEnum();
Object cellValue = null;
try
{
if (CellType.BLANK.equals(cellType))
{
cellValue = null;
}
else if (CellType.BOOLEAN.equals(cellType))
{
cellValue = cell.getBooleanCellValue();
}
else if (CellType.ERROR.equals(cellType))
{
cellValue = cell.getErrorCellValue();
}
else if (CellType.FORMULA.equals(cellType))
{
cellValue = cell.getCellFormula();
}
else if (CellType.NUMERIC.equals(cellType))
{
if (DateUtil.isCellDateFormatted(cell))
cellValue = cell.getDateCellValue();
else
cellValue = cell.getNumericCellValue();
}
else if (CellType.STRING.equals(cellType))
{
cellValue = cell.getStringCellValue();
}
}
catch (DataSetException e)
{
throw e;
}
catch (Throwable t)
{
throw new DataSetSourceParseException(t);
}
cellValue = convertToPropertyDataType(converter, cellValue, property);
return cellValue;
}
/**
* 是否名称行
*
* @param rowIndex
* 行索引{@code 0}计数
* @return
*/
protected boolean isNameRow(int rowIndex)
{
return ((rowIndex + 1) == this.nameRow);
}
/**
* 是否数据行
*
* @param rowIndex
* 行索引{@code 0}计数
* @return
*/
protected boolean isDataRow(int rowIndex)
{
if (isNameRow(rowIndex))
return false;
if (this._dataRowRanges == null || this._dataRowRanges.isEmpty())
return true;
return IndexRange.includes(this._dataRowRanges, rowIndex + 1);
}
/**
* 是否数据列
*
* @param columnIndex
* 列索引{@code 0}计数
* @return
*/
protected boolean isDataColumn(int columnIndex)
{
if (this._dataColumnRanges == null || this._dataColumnRanges.isEmpty())
return true;
return IndexRange.includes(this._dataColumnRanges, columnIndex);
}
@SuppressWarnings("unchecked")
protected List<IndexRange> resolveDataColumnRanges(String dataColumnExp) throws DataSetException
{
List<Range> ranges = getRangeExpResolver().resolve(dataColumnExp);
if (ranges == null || ranges.isEmpty())
return Collections.EMPTY_LIST;
List<IndexRange> indexRanges = new ArrayList<>(ranges.size());
for (Range range : ranges)
{
int from = 0;
int to = -1;
String fromStr = range.trimFrom();
String toStr = range.trimTo();
if (!StringUtil.isEmpty(fromStr))
from = CellReference.convertColStringToIndex(fromStr);
if (!StringUtil.isEmpty(toStr))
to = CellReference.convertColStringToIndex(toStr);
indexRanges.add(new IndexRange(from, to));
}
return indexRanges;
}
/**
* 给定Excel文件是否是老版本的{@code .xls}文件
*
* @param file
* @return
*/
protected boolean isXls(File file)
{
if (this.forceXls)
return true;
return FileUtil.isExtension(file, EXTENSION_XLS);
}
protected RangeExpResolver getRangeExpResolver()
{
return RANGE_EXP_RESOLVER;
}
/**
* 获取Excel文件
* <p>
* 实现方法应该返回实例级不变的文件
* </p>
*
* @param paramValues
* @return
* @throws DataSetException
*/
protected abstract File getExcelFile(Map<String, ?> paramValues) throws DataSetException;
}

View File

@ -1,43 +0,0 @@
/*
* Copyright (c) 2018 datagear.tech. All Rights Reserved.
*/
/**
*
*/
package org.datagear.analysis.support;
import java.util.List;
import java.util.Map;
import org.datagear.analysis.DataSet;
import org.datagear.analysis.DataSetProperty;
/**
* 抽象Freemarker模板{@linkplain DataSet}
*
* @author datagear@163.com
*
*/
public abstract class AbstractFmkTemplateDataSet extends AbstractDataSet
{
public static final DataSetFmkTemplateResolver TEMPLATE_RESOLVER = new DataSetFmkTemplateResolver();
public AbstractFmkTemplateDataSet()
{
super();
}
public AbstractFmkTemplateDataSet(String id, String name, List<DataSetProperty> properties)
{
super(id, name, properties);
}
protected String resolveTemplate(String template, Map<String, ?> paramValues)
{
if (template == null)
return null;
return TEMPLATE_RESOLVER.resolve(template, paramValues);
}
}

View File

@ -7,7 +7,10 @@
*/
package org.datagear.analysis.support;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -16,6 +19,18 @@ import org.datagear.analysis.DataSetProperty;
import org.datagear.analysis.DataSetResult;
import org.datagear.analysis.ResolvableDataSet;
import org.datagear.analysis.ResolvedDataSetResult;
import org.datagear.util.IOUtil;
import org.datagear.util.StringUtil;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.ValueNode;
import com.jayway.jsonpath.Configuration;
import com.jayway.jsonpath.JsonPath;
import com.jayway.jsonpath.spi.json.JacksonJsonProvider;
import com.jayway.jsonpath.spi.mapper.JacksonMappingProvider;
/**
* 抽象JSON数据集
@ -23,19 +38,23 @@ import org.datagear.analysis.ResolvedDataSetResult;
* @author datagear@163.com
*
*/
public abstract class AbstractJsonDataSet extends AbstractFmkTemplateDataSet implements ResolvableDataSet
public abstract class AbstractJsonDataSet extends AbstractResolvableDataSet implements ResolvableDataSet
{
public static final JsonDataSetSupport JSON_DATA_SET_SUPPORT = new JsonDataSetSupport();
/** 使用Jackson的{@code JSONPath}配置 */
protected static final Configuration JACKSON_JSON_PATH_CONFIGURATION = Configuration.builder()
.jsonProvider(new JacksonJsonProvider()).mappingProvider(new JacksonMappingProvider()).build();
/** 数据JSON路径 */
private String dataJsonPath = "";
public AbstractJsonDataSet()
{
super();
}
@SuppressWarnings("unchecked")
public AbstractJsonDataSet(String id, String name)
{
super(id, name, Collections.EMPTY_LIST);
super(id, name);
}
public AbstractJsonDataSet(String id, String name, List<DataSetProperty> properties)
@ -43,17 +62,350 @@ public abstract class AbstractJsonDataSet extends AbstractFmkTemplateDataSet imp
super(id, name, properties);
}
protected JsonDataSetSupport getJsonDataSetSupport()
public String getDataJsonPath()
{
return JSON_DATA_SET_SUPPORT;
return dataJsonPath;
}
@Override
public ResolvedDataSetResult resolve(Map<String, ?> paramValues) throws DataSetException
/**
* 设置数据JSON路径
* <p>
* 当希望返回的是原始JSON数据的指定JSON路径值时可以设置此项
* </p>
* <p>
* 例如"stores[0].books""[1].stores""$['store']['book'][0]"
* "$.store.book[*].author""$..book[2]"具体参考{@code JSONPath}相关文档
* </p>
* <p>
* 默认无数据路径将直接返回原始JSON数据
* </p>
*
* @param dataJsonPath
*/
public void setDataJsonPath(String dataJsonPath)
{
DataSetResult result = getResult(paramValues);
List<DataSetProperty> properties = getJsonDataSetSupport().resolveDataSetProperties(result.getData());
this.dataJsonPath = dataJsonPath;
}
/**
* 解析结果
* <p>
* 如果{@linkplain #getJsonReader(Map)}返回的{@linkplain TemplateResolvedSource#hasResolvedTemplate()}
* 此方法将返回{@linkplain TemplateResolvedDataSetResult}
* </p>
*/
@Override
protected ResolvedDataSetResult resolveResult(Map<String, ?> paramValues, List<DataSetProperty> properties)
throws DataSetException
{
TemplateResolvedSource<Reader> reader = null;
try
{
reader = getJsonReader(paramValues);
ResolvedDataSetResult result = resolveResult(reader.getSource(), properties);
if (reader.hasResolvedTemplate())
result = new TemplateResolvedDataSetResult(result.getResult(), result.getProperties(),
reader.getResolvedTemplate());
return result;
}
catch (DataSetException e)
{
throw e;
}
catch (Throwable t)
{
throw new DataSetSourceParseException(t);
}
finally
{
if (reader != null)
IOUtil.close(reader.getSource());
}
}
/**
* 获取JSON输入流
* <p>
* 实现方法应该返回实例级不变的输入流
* </p>
*
* @param paramValues
* @return
* @throws Throwable
*/
protected abstract TemplateResolvedSource<Reader> getJsonReader(Map<String, ?> paramValues) throws Throwable;
/**
* 解析结果
*
* @param jsonReader
* JSON输入流
* @param properties
* 允许为{@code null}此时会自动解析
* @return
* @throws Throwable
*/
protected ResolvedDataSetResult resolveResult(Reader jsonReader, List<DataSetProperty> properties) throws Throwable
{
boolean resolveProperties = (properties == null || properties.isEmpty());
JsonNode jsonNode = getObjectMapperNonStardand().readTree(jsonReader);
if (!isLegalResultDataJsonNode(jsonNode))
throw new UnsupportedJsonResultDataException("Result data must be JSON object or array");
Object data = null;
if (jsonNode != null)
data = readDataByJsonPath(jsonNode, getDataJsonPath());
if (resolveProperties)
properties = resolveDataSetProperties(data);
if (!resolveProperties)
data = convertJsonResultData(data, properties, createDataSetPropertyValueConverter());
DataSetResult result = new DataSetResult(data);
return new ResolvedDataSetResult(result, properties);
}
/**
* 读取指定JSON路径的数据
*
* @param jsonNode
* 允许为{@code null}
* @param dataJsonPath
* 允许为{@code null}
* @return
* @throws ReadJsonDataPathException
* @throws Throwable
*/
protected Object readDataByJsonPath(JsonNode jsonNode, String dataJsonPath)
throws ReadJsonDataPathException, Throwable
{
if (jsonNode == null)
return null;
Object data = getObjectMapperNonStardand().treeToValue(jsonNode, Object.class);
if (data == null)
return null;
if (StringUtil.isEmpty(dataJsonPath))
return data;
String stdDataJsonPath = dataJsonPath.trim();
if (StringUtil.isEmpty(stdDataJsonPath))
return data;
// 转换"stores[0].books""[1].stores"简化模式为规范的JSONPath
if (!stdDataJsonPath.startsWith("$"))
{
if (stdDataJsonPath.startsWith("["))
stdDataJsonPath = "$" + stdDataJsonPath;
else
stdDataJsonPath = "$." + stdDataJsonPath;
}
try
{
return JsonPath.compile(stdDataJsonPath).read(data, JACKSON_JSON_PATH_CONFIGURATION);
}
catch (Throwable t)
{
throw new ReadJsonDataPathException(dataJsonPath, t);
}
}
/**
*
* @param resultData
* 允许为{@code null}
* @param properties
* @param converter
* @return
* @throws Throwable
*/
protected Object convertJsonResultData(Object resultData, List<DataSetProperty> properties,
DataSetPropertyValueConverter converter) throws Throwable
{
Object re = null;
// JSON对象
if (resultData == null)
{
re = null;
}
else if (resultData instanceof Map<?, ?>)
{
Map<String, Object> reMap = new HashMap<>();
@SuppressWarnings("unchecked")
Map<String, Object> source = (Map<String, Object>) resultData;
for (Map.Entry<String, Object> entry : source.entrySet())
{
String name = entry.getKey();
Object value = entry.getValue();
DataSetProperty property = getDataNameTypeByName(properties, name);
value = convertToPropertyDataType(converter, value, property);
reMap.put(name, value);
}
re = reMap;
}
else if (resultData instanceof List<?>)
{
List<?> list = (List<?>) resultData;
List<Object> reList = new ArrayList<>(list.size());
for (Object ele : list)
reList.add(convertJsonResultData(ele, properties, converter));
re = reList;
}
else if (resultData instanceof Object[])
{
Object[] array = (Object[]) resultData;
Object[] reArray = new Object[array.length];
for (int i = 0; i < array.length; i++)
reArray[i] = convertJsonResultData(array[i], properties, converter);
re = reArray;
}
else
throw new UnsupportedJsonResultDataException("Result data must be object or object array/list");
return re;
}
/**
* 是否是合法的数据集结果数据{@linkplain JsonNode}
* <p>
* 参考{@linkplain DataSetResult#getData()}说明
* </p>
*
* @param jsonNode
* 允许为{@code null}
* @return
*/
protected boolean isLegalResultDataJsonNode(JsonNode jsonNode) throws Throwable
{
if (jsonNode == null || jsonNode.isNull())
return true;
if (jsonNode instanceof ValueNode)
return false;
if (jsonNode instanceof ArrayNode)
{
ArrayNode arrayNode = (ArrayNode) jsonNode;
for (int i = 0; i < arrayNode.size(); i++)
{
JsonNode eleNode = arrayNode.get(i);
if (eleNode == null || eleNode.isNull())
continue;
if (!(eleNode instanceof ObjectNode))
return false;
}
}
return true;
}
/**
* 解析JSON对象的{@linkplain DataSetProperty}
*
* @param resultData
* 允许为{@code null}JSON对象JSON对象数组JSON对象列表
* @return
* @throws Throwable
*/
@SuppressWarnings("unchecked")
protected List<DataSetProperty> resolveDataSetProperties(Object resultData) throws Throwable
{
if (resultData == null)
{
return Collections.EMPTY_LIST;
}
else if (resultData instanceof Map<?, ?>)
{
return resolveJsonObjDataSetProperties((Map<String, ?>) resultData);
}
else if (resultData instanceof List<?>)
{
List<?> list = (List<?>) resultData;
if (list.size() == 0)
return Collections.EMPTY_LIST;
else
return resolveJsonObjDataSetProperties((Map<String, ?>) list.get(0));
}
else if (resultData instanceof Object[])
{
Object[] array = (Object[]) resultData;
if (array.length == 0)
return Collections.EMPTY_LIST;
else
return resolveJsonObjDataSetProperties((Map<String, ?>) array[0]);
}
else
throw new UnsupportedJsonResultDataException("Result data must be object or object array/list");
}
/**
* 解析JSON对象的{@linkplain DataSetProperty}
*
* @param jsonObj
* @return
* @throws Throwable
*/
protected List<DataSetProperty> resolveJsonObjDataSetProperties(Map<String, ?> jsonObj) throws Throwable
{
List<DataSetProperty> properties = new ArrayList<>();
if (jsonObj == null)
{
}
else
{
for (Map.Entry<String, ?> entry : jsonObj.entrySet())
{
Object value = entry.getValue();
String type = DataSetProperty.DataType.resolveDataType(value);
DataSetProperty property = new DataSetProperty(entry.getKey(), type);
// JSON数值只有NUMBER类型
if (DataSetProperty.DataType.INTEGER.equals(property.getType())
|| DataSetProperty.DataType.DECIMAL.equals(property.getType()))
property.setType(DataSetProperty.DataType.NUMBER);
properties.add(property);
}
}
return properties;
}
protected ObjectMapper getObjectMapperNonStardand()
{
return JsonSupport.getObjectMapperNonStardand();
}
}

View File

@ -8,25 +8,27 @@
package org.datagear.analysis.support;
import java.io.File;
import java.io.Reader;
import java.util.List;
import java.util.Map;
import org.datagear.analysis.DataSetException;
import org.datagear.analysis.DataSetProperty;
import org.datagear.analysis.DataSetResult;
import org.datagear.util.IOUtil;
/**
* 抽象JSON文件数据集
* <p>
* 注意此类不支持<code>Freemarker</code>模板语言
* </p>
*
* @author datagear@163.com
*
*/
public abstract class AbstractJsonFileDataSet extends AbstractJsonSourceDataSet
public abstract class AbstractJsonFileDataSet extends AbstractJsonDataSet
{
public static final String DEFAULT_ENCODING = "UTF-8";
/** 文件编码 */
private String encoding = DEFAULT_ENCODING;
private String encoding = IOUtil.CHARSET_UTF_8;
public AbstractJsonFileDataSet()
{
@ -54,15 +56,17 @@ public abstract class AbstractJsonFileDataSet extends AbstractJsonSourceDataSet
}
@Override
protected DataSetResult getOrginalResult(Map<String, ?> paramValues) throws DataSetException
protected TemplateResolvedSource<Reader> getJsonReader(Map<String, ?> paramValues) throws Throwable
{
File jsonFile = getJsonFile(paramValues);
Object data = getJsonDataSetSupport().resolveResultData(jsonFile, getEncoding());
return new DataSetResult(data);
File file = getJsonFile(paramValues);
return new TemplateResolvedSource<>(IOUtil.getReader(file, this.encoding));
}
/**
* 获取JSON文件
* <p>
* 实现方法应该返回实例级不变的文件
* </p>
*
* @param paramValues
* @return

View File

@ -1,79 +0,0 @@
/*
* Copyright (c) 2018 datagear.tech. All Rights Reserved.
*/
/**
*
*/
package org.datagear.analysis.support;
import java.util.List;
import java.util.Map;
import org.datagear.analysis.DataSetException;
import org.datagear.analysis.DataSetProperty;
import org.datagear.analysis.DataSetResult;
/**
* 抽象JSON源数据集
* <p>
* JSON源数据集的一个特点是源数据是无法编辑的因此需要定义{@linkplain DataSetResultTransformer}逻辑
* </p>
*
* @author datagear@163.com
*
*/
public abstract class AbstractJsonSourceDataSet extends AbstractJsonDataSet
{
private DataSetResultTransformer dataSetResultTransformer;
public AbstractJsonSourceDataSet()
{
super();
}
public AbstractJsonSourceDataSet(String id, String name)
{
super(id, name);
}
public AbstractJsonSourceDataSet(String id, String name, List<DataSetProperty> properties)
{
super(id, name, properties);
}
/**
* 获取{@linkplain DataSetResultTransformer}
*
* @return 可能为{@code null}
*/
public DataSetResultTransformer getDataSetResultTransformer()
{
return dataSetResultTransformer;
}
public void setDataSetResultTransformer(DataSetResultTransformer dataSetResultTransformer)
{
this.dataSetResultTransformer = dataSetResultTransformer;
}
@Override
public DataSetResult getResult(Map<String, ?> paramValues) throws DataSetException
{
DataSetResult result = getOrginalResult(paramValues);
if (this.dataSetResultTransformer == null)
return result;
return this.dataSetResultTransformer.transform(result);
}
/**
* 获取原始的{@linkplain DataSetResult}
*
* @param paramValues
* @return
* @throws DataSetException
*/
protected abstract DataSetResult getOrginalResult(Map<String, ?> paramValues) throws DataSetException;
}

View File

@ -0,0 +1,76 @@
/*
* Copyright (c) 2018 datagear.tech. All Rights Reserved.
*/
/**
*
*/
package org.datagear.analysis.support;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.datagear.analysis.DataSetException;
import org.datagear.analysis.DataSetProperty;
import org.datagear.analysis.DataSetResult;
import org.datagear.analysis.ResolvableDataSet;
import org.datagear.analysis.ResolvedDataSetResult;
/**
* 抽象{@linkplain ResolvableDataSet}
*
* @author datagear@163.com
*
*/
public abstract class AbstractResolvableDataSet extends AbstractDataSet implements ResolvableDataSet
{
public AbstractResolvableDataSet()
{
super();
}
@SuppressWarnings("unchecked")
public AbstractResolvableDataSet(String id, String name)
{
super(id, name, Collections.EMPTY_LIST);
}
public AbstractResolvableDataSet(String id, String name, List<DataSetProperty> properties)
{
super(id, name, properties);
}
@Override
public DataSetResult getResult(Map<String, ?> paramValues) throws DataSetException
{
List<DataSetProperty> properties = getProperties();
if (properties == null || properties.isEmpty())
throw new DataSetException("[getProperties()] must not be empty");
ResolvedDataSetResult result = resolveResult(paramValues, properties);
return result.getResult();
}
@Override
public ResolvedDataSetResult resolve(Map<String, ?> paramValues) throws DataSetException
{
return resolveResult(paramValues, null);
}
/**
* 解析结果
*
* @param paramValues
* @param properties
* 允许为{@code null}/此时应自动解析并设置返回结果的{@linkplain ResolvedDataSetResult#setProperties(List)}
* 如果不为{@code null}/直接将{@code properties}作为解析数据依据 使用它处理结果数据
* 并设置为返回结果的{@linkplain ResolvedDataSetResult#setProperties(List)}
* @return
* @throws DataSetException
*/
protected abstract ResolvedDataSetResult resolveResult(Map<String, ?> paramValues, List<DataSetProperty> properties)
throws DataSetException;
}

View File

@ -0,0 +1,81 @@
/*
* Copyright (c) 2018 datagear.tech. All Rights Reserved.
*/
/**
*
*/
package org.datagear.analysis.support;
import java.io.File;
import java.util.List;
import java.util.Map;
import org.datagear.analysis.DataSet;
import org.datagear.analysis.DataSetProperty;
import org.datagear.util.FileUtil;
/**
* 目录内CSV文件{@linkplain DataSet}
* <p>
* 注意此类不支持<code>Freemarker</code>模板语言
* </p>
*
* @author datagear@163.com
*
*/
public class CsvDirectoryFileDataSet extends AbstractCsvFileDataSet
{
/** JSON文件所在的目录 */
private File directory;
/** JSON文件名 */
private String fileName;
public CsvDirectoryFileDataSet()
{
super();
}
public CsvDirectoryFileDataSet(String id, String name, File directory, String fileName)
{
super(id, name);
this.directory = directory;
this.fileName = fileName;
}
public CsvDirectoryFileDataSet(String id, String name, List<DataSetProperty> properties, File directory,
String fileName)
{
super(id, name, properties);
this.directory = directory;
this.fileName = fileName;
}
public File getDirectory()
{
return directory;
}
public void setDirectory(File directory)
{
this.directory = directory;
}
public String getFileName()
{
return fileName;
}
public void setFileName(String fileName)
{
this.fileName = fileName;
}
@Override
protected File getCsvFile(Map<String, ?> paramValues) throws Throwable
{
File jsonFile = FileUtil.getFile(this.directory, this.fileName);
return jsonFile;
}
}

View File

@ -0,0 +1,79 @@
/*
* Copyright 2018 datagear.tech. All Rights Reserved.
*/
package org.datagear.analysis.support;
import java.io.Reader;
import java.util.List;
import java.util.Map;
import org.datagear.analysis.DataSetException;
import org.datagear.analysis.DataSetProperty;
import org.datagear.util.IOUtil;
/**
* CSV值数据集
* <p>
* 此类的{@linkplain #getValue()}支持<code>Freemarker</code>模板语言
* </p>
*
* @author datagear@163.com
*
*/
public class CsvValueDataSet extends AbstractCsvDataSet
{
/** CSV字符串 */
private String value;
public CsvValueDataSet()
{
super();
}
public CsvValueDataSet(String id, String name, String value)
{
super(id, name);
this.value = value;
}
public CsvValueDataSet(String id, String name, List<DataSetProperty> properties, String value)
{
super(id, name, properties);
this.value = value;
}
public String getValue()
{
return value;
}
/**
* 设置CSV字符串值格式为
*
* <pre>
* name, value
* aaa, 1
* bbb, 2
* </pre>
*
* @param value
*/
public void setValue(String value)
{
this.value = value;
}
@Override
public TemplateResolvedDataSetResult resolve(Map<String, ?> paramValues) throws DataSetException
{
return (TemplateResolvedDataSetResult) resolveResult(paramValues, null);
}
@Override
protected TemplateResolvedSource<Reader> getCsvReader(Map<String, ?> paramValues) throws Throwable
{
String csv = resolveAsFmkTemplateIfHasParam(this.value, paramValues);
return new TemplateResolvedSource<>(IOUtil.getReader(csv), csv);
}
}

View File

@ -0,0 +1,144 @@
/*
* Copyright 2018 datagear.tech. All Rights Reserved.
*/
package org.datagear.analysis.support;
import java.io.Serializable;
/**
* 数据格式
*
* @author datagear@163.com
*
*/
public class DataFormat implements Serializable
{
private static final long serialVersionUID = 1L;
public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
public static final String DEFAULT_TIME_FORMAT = "hh:mm:ss";
public static final String DEFAULT_TIMESTAMP_FORMAT = "yyyy-MM-dd hh:mm:ss";
public static final String DEFAULT_NUMBER_FORMAT = "#.##########";
/** 日期格式 */
private String dateFormat = DEFAULT_DATE_FORMAT;
/** 时间格式 */
private String timeFormat = DEFAULT_TIME_FORMAT;
/** 时间戳格式 */
private String timestampFormat = DEFAULT_TIMESTAMP_FORMAT;
/** 数值格式 */
private String numberFormat = DEFAULT_NUMBER_FORMAT;
public DataFormat()
{
super();
}
public String getDateFormat()
{
return dateFormat;
}
public void setDateFormat(String dateFormat)
{
this.dateFormat = dateFormat;
}
public String getTimeFormat()
{
return timeFormat;
}
public void setTimeFormat(String timeFormat)
{
this.timeFormat = timeFormat;
}
public String getTimestampFormat()
{
return timestampFormat;
}
public void setTimestampFormat(String timestampFormat)
{
this.timestampFormat = timestampFormat;
}
public String getNumberFormat()
{
return numberFormat;
}
public void setNumberFormat(String numberFormat)
{
this.numberFormat = numberFormat;
}
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + ((dateFormat == null) ? 0 : dateFormat.hashCode());
result = prime * result + ((numberFormat == null) ? 0 : numberFormat.hashCode());
result = prime * result + ((timeFormat == null) ? 0 : timeFormat.hashCode());
result = prime * result + ((timestampFormat == null) ? 0 : timestampFormat.hashCode());
return result;
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
DataFormat other = (DataFormat) obj;
if (dateFormat == null)
{
if (other.dateFormat != null)
return false;
}
else if (!dateFormat.equals(other.dateFormat))
return false;
if (numberFormat == null)
{
if (other.numberFormat != null)
return false;
}
else if (!numberFormat.equals(other.numberFormat))
return false;
if (timeFormat == null)
{
if (other.timeFormat != null)
return false;
}
else if (!timeFormat.equals(other.timeFormat))
return false;
if (timestampFormat == null)
{
if (other.timestampFormat != null)
return false;
}
else if (!timestampFormat.equals(other.timestampFormat))
return false;
return true;
}
@Override
public String toString()
{
return getClass().getSimpleName() + " [dateFormat=" + dateFormat + ", timeFormat=" + timeFormat
+ ", timestampFormat="
+ timestampFormat + ", numberFormat=" + numberFormat + "]";
}
}

View File

@ -0,0 +1,255 @@
/*
* Copyright 2018 datagear.tech. All Rights Reserved.
*/
package org.datagear.analysis.support;
import java.sql.Date;
import java.sql.Time;
import java.sql.Timestamp;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import org.datagear.analysis.DataSetProperty;
import org.datagear.analysis.DataSetProperty.DataType;
/**
* {@linkplain DataSetProperty}值转换器
* <p>
* 它支持将对象转换为{@linkplain DataSetProperty.DataType}类型的值
* </p>
* <p>
* 此类的{@linkplain #convert(java.util.Map, java.util.Collection)}{@linkplain #convert(Object, String)}不是线程安全的
* </p>
*
* @author datagear@163.com
*
*/
public class DataSetPropertyValueConverter extends DataValueConverter
{
private DataFormat dataFormat;
private SimpleDateFormat _dateFormat = null;
private SimpleDateFormat _timeFormat = null;
private SimpleDateFormat _timestampFormat = null;
private DecimalFormat _numberFormat = null;
public DataSetPropertyValueConverter()
{
super();
setDataFormat(new DataFormat());
}
public DataSetPropertyValueConverter(DataFormat dataFormat)
{
super();
setDataFormat(new DataFormat());
}
public DataFormat getDataFormat()
{
return dataFormat;
}
public void setDataFormat(DataFormat dataFormat)
{
this.dataFormat = dataFormat;
this._dateFormat = new SimpleDateFormat(dataFormat.getDateFormat());
this._timeFormat = new SimpleDateFormat(dataFormat.getTimeFormat());
this._timestampFormat = new SimpleDateFormat(dataFormat.getTimestampFormat());
this._numberFormat = new DecimalFormat(dataFormat.getNumberFormat());
}
@Override
protected Object convertValue(Object value, String type) throws DataValueConvertionException
{
if (value == null)
return null;
if (type == null)
return value;
try
{
if (value instanceof String)
return convertStringValue((String) value, type);
else if (value instanceof Boolean)
return convertBooleanValue((Boolean) value, type);
else if (value instanceof Number)
return convertNumberValue((Number) value, type);
else if (value instanceof Time)
return convertTimeValue((Time) value, type);
else if (value instanceof Timestamp)
return convertTimestampValue((Timestamp) value, type);
else if (value instanceof java.util.Date)
return convertDateValue((java.util.Date) value, type);
else
{
if (DataType.UNKNOWN.equals(type))
return value;
else
throw new DataValueConvertionException(value, type);
}
}
catch (DataValueConvertionException e)
{
throw e;
}
catch (Throwable t)
{
throw new DataValueConvertionException(value, type);
}
}
protected Object convertStringValue(String value, String type) throws Throwable
{
if (DataType.STRING.equals(type) || DataType.UNKNOWN.equals(type))
return value;
if (value == null || value.isEmpty())
return null;
if (DataType.BOOLEAN.equals(type))
return "true".equalsIgnoreCase(value) || "1".equals(value);
else if (DataType.NUMBER.equals(type))
return this._numberFormat.parse(value);
else if (DataType.INTEGER.equals(type))
return this._numberFormat.parse(value).intValue();
else if (DataType.DECIMAL.equals(type))
return this._numberFormat.parse(value).doubleValue();
else if (DataType.DATE.equals(type))
{
java.util.Date date = this._dateFormat.parse(value);
return new Date(date.getTime());
}
else if (DataType.TIME.equals(type))
{
java.util.Date date = this._timeFormat.parse(value);
return new Time(date.getTime());
}
else if (DataType.TIMESTAMP.equals(type))
{
java.util.Date date = this._timestampFormat.parse(value);
return new Timestamp(date.getTime());
}
else
throw new DataValueConvertionException(value, type);
}
protected Object convertBooleanValue(Boolean value, String type) throws Throwable
{
if (DataType.BOOLEAN.equals(type) || DataType.UNKNOWN.equals(type))
return value;
if (value == null)
return null;
if (DataType.STRING.equals(type))
return value.toString();
else if (DataType.NUMBER.equals(type) || DataType.INTEGER.equals(type) || DataType.DECIMAL.equals(type))
return (Boolean.TRUE.equals(value) ? 1 : 0);
else
throw new DataValueConvertionException(value, type);
}
protected Object convertNumberValue(Number value, String type) throws Throwable
{
if (DataType.NUMBER.equals(type) || DataType.UNKNOWN.equals(type))
return value;
if (value == null)
return null;
if (DataType.STRING.equals(type))
return this._numberFormat.format(value);
else if (DataType.BOOLEAN.equals(type))
return (value.intValue() > 0);
else if (DataType.INTEGER.equals(type))
return value.longValue();
else if (DataType.DECIMAL.equals(type))
return value.doubleValue();
else if (DataType.DATE.equals(type))
return new Date(value.longValue());
else if (DataType.TIME.equals(type))
return new Time(value.longValue());
else if (DataType.TIMESTAMP.equals(type))
return new Timestamp(value.longValue());
else
throw new DataValueConvertionException(value, type);
}
protected Object convertDateValue(java.util.Date value, String type) throws Throwable
{
if (DataType.UNKNOWN.equals(type))
return value;
if (value == null)
return null;
if (DataType.STRING.equals(type))
return this._dateFormat.format(value);
else if (DataType.NUMBER.equals(type))
return value.getTime();
else if (DataType.INTEGER.equals(type))
return value.getTime();
else if (DataType.DECIMAL.equals(type))
return value.getTime();
else if (DataType.DATE.equals(type))
return new Date(value.getTime());
else if (DataType.TIME.equals(type))
return new Time(value.getTime());
else if (DataType.TIMESTAMP.equals(type))
return new Timestamp(value.getTime());
else
throw new DataValueConvertionException(value, type);
}
protected Object convertTimeValue(Time value, String type) throws Throwable
{
if (DataType.TIME.equals(type) || DataType.UNKNOWN.equals(type))
return value;
if (value == null)
return null;
if (DataType.STRING.equals(type))
return this._timeFormat.format(value);
else if (DataType.NUMBER.equals(type))
return value.getTime();
else if (DataType.INTEGER.equals(type))
return value.getTime();
else if (DataType.DECIMAL.equals(type))
return value.getTime();
else if (DataType.DATE.equals(type))
return new Date(value.getTime());
else if (DataType.TIMESTAMP.equals(type))
return new Timestamp(value.getTime());
else
throw new DataValueConvertionException(value, type);
}
protected Object convertTimestampValue(Timestamp value, String type) throws Throwable
{
if (DataType.TIMESTAMP.equals(type) || DataType.UNKNOWN.equals(type))
return value;
if (value == null)
return null;
if (DataType.STRING.equals(type))
return this._timestampFormat.format(value);
else if (DataType.NUMBER.equals(type))
return value.getTime();
else if (DataType.INTEGER.equals(type))
return value.getTime();
else if (DataType.DECIMAL.equals(type))
return value.getTime();
else if (DataType.DATE.equals(type))
return new Date(value.getTime());
else if (DataType.TIME.equals(type))
return new Time(value.getTime());
else
throw new DataValueConvertionException(value, type);
}
}

View File

@ -1,41 +0,0 @@
/*
* Copyright (c) 2018 datagear.tech. All Rights Reserved.
*/
/**
*
*/
package org.datagear.analysis.support;
import org.datagear.analysis.DataSetException;
/**
* {@linkplain DataSetResultTransformer}转换异常
*
* @author datagear@163.com
*
*/
public class DataSetResultTransformException extends DataSetException
{
private static final long serialVersionUID = 1L;
public DataSetResultTransformException()
{
super();
}
public DataSetResultTransformException(String message)
{
super(message);
}
public DataSetResultTransformException(Throwable cause)
{
super(cause);
}
public DataSetResultTransformException(String message, Throwable cause)
{
super(message, cause);
}
}

View File

@ -1,33 +0,0 @@
/*
* Copyright (c) 2018 datagear.tech. All Rights Reserved.
*/
/**
*
*/
package org.datagear.analysis.support;
import org.datagear.analysis.DataSet;
import org.datagear.analysis.DataSetResult;
/**
* {@linkplain DataSetResult}转换器
* <p>
* 某些类型的{@linkplain DataSet}是从不可控制的数据源中读取数据的比如API调用JSON文件CSV文件
* 此类即为这些场景提供支持使{@linkplain DataSet}支持对数据源的数据进行转换
* </p>
*
* @author datagear@163.com
*
*/
public interface DataSetResultTransformer
{
/**
* 转换为新的{@linkplain DataSetResult}
*
* @param orginalResult
* @return 已转换的{@linkplain DataSetResult}
* @throws DataSetResultTransformException
*/
DataSetResult transform(DataSetResult orginalResult) throws DataSetResultTransformException;
}

View File

@ -23,7 +23,7 @@ public class DataValueConvertionException extends RuntimeException
public DataValueConvertionException(Object source, String type)
{
super();
super("Convert from [" + source + "] to [" + type + "] is not supported");
this.type = type;
this.source = source;
}

View File

@ -0,0 +1,87 @@
/*
* Copyright (c) 2018 datagear.tech. All Rights Reserved.
*/
/**
*
*/
package org.datagear.analysis.support;
import java.io.File;
import java.util.List;
import java.util.Map;
import org.datagear.analysis.DataSet;
import org.datagear.analysis.DataSetException;
import org.datagear.analysis.DataSetProperty;
import org.datagear.util.FileUtil;
/**
* 目录内Excel文件{@linkplain DataSet}
* <p>
* 注意此类不支持<code>Freemarker</code>模板语言
* </p>
*
* @author datagear@163.com
*
*/
public class ExcelDirectoryFileDataSet extends AbstractExcelDataSet
{
/** Excel文件所在的目录 */
private File directory;
/** Excel文件名 */
private String fileName;
public ExcelDirectoryFileDataSet()
{
super();
}
public ExcelDirectoryFileDataSet(String id, String name, File directory, String fileName)
{
super(id, name);
this.directory = directory;
this.fileName = fileName;
}
/**
* @param id
* @param name
* @param properties
*/
public ExcelDirectoryFileDataSet(String id, String name, List<DataSetProperty> properties, File directory,
String fileName)
{
super(id, name, properties);
this.directory = directory;
this.fileName = fileName;
}
public File getDirectory()
{
return directory;
}
public void setDirectory(File directory)
{
this.directory = directory;
}
public String getFileName()
{
return fileName;
}
public void setFileName(String fileName)
{
this.fileName = fileName;
}
@Override
protected File getExcelFile(Map<String, ?> paramValues) throws DataSetException
{
File excelFile = FileUtil.getFile(this.directory, this.fileName);
return excelFile;
}
}

View File

@ -0,0 +1,39 @@
/*
* Copyright (c) 2018 datagear.tech. All Rights Reserved.
*/
/**
*
*/
package org.datagear.analysis.support;
/**
* {@linkplain HttpDataSet#getHeaderContent()}不是名/值对象数组JSON异常
*
* @author datagear@163.com
*
*/
public class HeaderContentNotNameValueObjArrayJsonException extends NotNameValueObjArrayJsonException
{
private static final long serialVersionUID = 1L;
public HeaderContentNotNameValueObjArrayJsonException(String json)
{
super(json);
}
public HeaderContentNotNameValueObjArrayJsonException(String json, String message)
{
super(json, message);
}
public HeaderContentNotNameValueObjArrayJsonException(String json, Throwable cause)
{
super(json, cause);
}
public HeaderContentNotNameValueObjArrayJsonException(String json, String message, Throwable cause)
{
super(json, message, cause);
}
}

View File

@ -0,0 +1,622 @@
/*
* Copyright (c) 2018 datagear.tech. All Rights Reserved.
*/
/**
*
*/
package org.datagear.analysis.support;
import java.io.IOException;
import java.io.Reader;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.apache.hc.client5.http.HttpResponseException;
import org.apache.hc.client5.http.classic.HttpClient;
import org.apache.hc.client5.http.classic.methods.HttpDelete;
import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.classic.methods.HttpPatch;
import org.apache.hc.client5.http.classic.methods.HttpPost;
import org.apache.hc.client5.http.classic.methods.HttpPut;
import org.apache.hc.client5.http.entity.UrlEncodedFormEntity;
import org.apache.hc.core5.http.ClassicHttpRequest;
import org.apache.hc.core5.http.ClassicHttpResponse;
import org.apache.hc.core5.http.ContentType;
import org.apache.hc.core5.http.HttpEntity;
import org.apache.hc.core5.http.HttpException;
import org.apache.hc.core5.http.NameValuePair;
import org.apache.hc.core5.http.io.HttpClientResponseHandler;
import org.apache.hc.core5.http.io.entity.StringEntity;
import org.apache.hc.core5.http.message.BasicNameValuePair;
import org.datagear.analysis.DataSetException;
import org.datagear.analysis.DataSetProperty;
import org.datagear.analysis.DataSetResult;
import org.datagear.analysis.ResolvedDataSetResult;
import org.datagear.util.IOUtil;
import org.datagear.util.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* HTTP数据集
* <p>
* 此类的{@linkplain #getUri()}{@linkplain #getHeaderContent()}{@linkplain #getRequestContent()}支持<code>Freemarker</code>模板语言
* </p>
*
* @author datagear@163.com
*
*/
public class HttpDataSet extends AbstractResolvableDataSet
{
protected static final Logger LOGGER = LoggerFactory.getLogger(HttpDataSet.class);
public static final String REQUEST_METHOD_GET = "GET";
public static final String REQUEST_METHOD_POST = "POST";
public static final String REQUEST_METHOD_PUT = "PUT";
public static final String REQUEST_METHOD_PATCH = "PATCH";
public static final String REQUEST_METHOD_DELETE = "DELETE";
// 这些HTTP方法不适应于此
// public static final String REQUEST_METHOD_HEAD = "HEAD";
// public static final String REQUEST_METHOD_OPTIONS = "OPTIONS";
// public static final String REQUEST_METHOD_TRACE = "TRACE";
/**
* 请求内容类型表单式的参数名/值类型对应的HTTP请求类型为application/x-www-form-urlencoded
*/
public static final String REQUEST_CONTENT_TYPE_FORM_URLENCODED = "FORM_URLENCODED";
/**
* 请求内容类型JSON对应的HTTP请求类型为application/json
*/
public static final String REQUEST_CONTENT_TYPE_JSON = "JSON";
/**
* 响应内容类型JSON对应的HTTP响应类型为application/json
*/
public static final String RESPONSE_CONTENT_TYPE_JSON = "JSON";
protected static final List<NameValuePair> NOT_NAME_VALUE_PAIR_OBJ_ARRAY_JSON = new ArrayList<>(0);
/** HTTP客户端 */
private HttpClient httpClient;
/** HTTP请求地址 */
private String uri;
/** 请求头JSON文本 */
private String headerContent = "";
/** 请求方法 */
private String requestMethod = REQUEST_METHOD_GET;
/** 请求内容类型 */
private String requestContentType = REQUEST_CONTENT_TYPE_FORM_URLENCODED;
/** 请求内容编码 */
private String requestContentCharset = IOUtil.CHARSET_UTF_8;
/** 请求内容JSON文本 */
private String requestContent = "";
/** 响应类型 */
private String responseContentType = RESPONSE_CONTENT_TYPE_JSON;
/** 响应数据的JSON路径 */
private String responseDataJsonPath = "";
public HttpDataSet()
{
super();
}
public HttpDataSet(String id, String name, HttpClient httpClient, String uri)
{
super(id, name);
this.httpClient = httpClient;
this.uri = uri;
}
public HttpDataSet(String id, String name, List<DataSetProperty> properties, HttpClient httpClient, String uri)
{
super(id, name, properties);
this.httpClient = httpClient;
this.uri = uri;
}
public HttpClient getHttpClient()
{
return httpClient;
}
public void setHttpClient(HttpClient httpClient)
{
this.httpClient = httpClient;
}
public String getUri()
{
return uri;
}
/**
* 设置请求地址
* <p>
* 请求地址支持<code>Freemarker</code>模板语言
* </p>
*
* @param uri
*/
public void setUri(String uri)
{
this.uri = uri;
}
public String getHeaderContent()
{
return headerContent;
}
/**
* 设置请求头JSON文本格式应为
* <p>
* <code>
* <pre>
* [
* {name: "...", value: "..."},
* {name: "...", value: "..."},
* ...
* ]
* </pre>
* </code>
* </p>
* <p>
* 请求头JSON文本支持<code>Freemarker</code>模板语言
* </p>
*
* @param headerContent
*/
public void setHeaderContent(String headerContent)
{
this.headerContent = headerContent;
}
public String getRequestMethod()
{
return requestMethod;
}
/**
* 设置HTTP方法参考{@code REQUEST_METHOD_*}常量
*
* @param requestMethod
*/
public void setRequestMethod(String requestMethod)
{
this.requestMethod = requestMethod;
}
public String getRequestContentType()
{
return requestContentType;
}
/**
* 设置请求内容类型允许的值为
* <p>
* {@linkplain #REQUEST_CONTENT_TYPE_FORM_URLENCODED}{@linkplain #REQUEST_CONTENT_TYPE_JSON}
* </p>
*
* @param requestContentType
*/
public void setRequestContentType(String requestContentType)
{
this.requestContentType = requestContentType;
}
public String getRequestContentCharset()
{
return requestContentCharset;
}
/**
* 设置请求内容编码
* <p>
* 默认请求内容编码为{@code UTF-8}
* </p>
*
* @param requestContentCharset
*/
public void setRequestContentCharset(String requestContentCharset)
{
this.requestContentCharset = requestContentCharset;
}
public String getRequestContent()
{
return requestContent;
}
/**
* 设置请求内容JSON文本{@code null}{@code ""}表示无请求内容
* <p>
* {@linkplain #getRequestContentType()}{@linkplain #REQUEST_CONTENT_TYPE_FORM_URLENCODED}请求内容JSON文本格式应为
* </p>
* <code>
* <pre>
* [
* {name: "...", value: "..."},
* {name: "...", value: "..."},
* ...
* ]
* </pre>
* </code>
* <p>
* 其中{@code name}表示请求参数名{@code value}表示请求参数值
* </p>
* <p>
* {@linkplain #getRequestContentType()}{@linkplain #REQUEST_CONTENT_TYPE_JSON}请求内容JSON文本没有特殊格式要求
* </p>
* <p>
* 请求内容JSON文本支持<code>Freemarker</code>模板语言
* </p>
*
* @param requestContent
*/
public void setRequestContent(String requestContent)
{
this.requestContent = requestContent;
}
public String getResponseContentType()
{
return responseContentType;
}
/**
* 设置相应类型
* <p>
* 目前仅支持{@linkplain #RESPONSE_CONTENT_TYPE_JSON}且是默认值
* </p>
*
* @param responseContentType
*/
public void setResponseContentType(String responseContentType)
{
this.responseContentType = responseContentType;
}
public String getResponseDataJsonPath()
{
return responseDataJsonPath;
}
/**
* 设置响应数据的JSON路径
* <p>
* 当希望返回的是响应原始JSON数据的指定JSON路径值时可以设置此项
* </p>
* <p>
* 具体格式参考{@linkplain AbstractJsonDataSet#setDataJsonPath(String)}
* </p>
* <p>
* 默认无数据路径将直接返回响应原始JSON数据
* </p>
*
* @param responseDataJsonPath
*/
public void setResponseDataJsonPath(String responseDataJsonPath)
{
this.responseDataJsonPath = responseDataJsonPath;
}
@Override
public TemplateResolvedDataSetResult resolve(Map<String, ?> paramValues) throws DataSetException
{
return resolveResult(paramValues, null);
}
@Override
protected TemplateResolvedDataSetResult resolveResult(Map<String, ?> paramValues, List<DataSetProperty> properties)
throws DataSetException
{
try
{
String uri = resolveTemplateUri(paramValues);
String headerContent = resolveTemplateHeaderContent(paramValues);
String requestContent = resolveTemplateRequestContent(paramValues);
ClassicHttpRequest request = createHttpRequest(uri);
setHttpHeaders(request, headerContent);
setHttpEntity(request, requestContent);
JsonResponseHandler responseHandler = new JsonResponseHandler();
responseHandler.setProperties(properties);
responseHandler.setResponseDataJsonPath(getResponseDataJsonPath());
ResolvedDataSetResult result = this.httpClient.execute(request, responseHandler);
String templateResult = "URI:" + System.lineSeparator() + uri //
+ System.lineSeparator() + "-----------------------------------------" + System.lineSeparator() //
+ "Request headers:" + System.lineSeparator() + headerContent //
+ System.lineSeparator() + "-----------------------------------------" + System.lineSeparator() //
+ "Request content:" + System.lineSeparator() + requestContent;
return new TemplateResolvedDataSetResult(result.getResult(), result.getProperties(), templateResult);
}
catch (DataSetException e)
{
throw e;
}
catch (Throwable t)
{
throw new DataSetSourceParseException(t);
}
}
protected void setHttpHeaders(ClassicHttpRequest request, String headerContent) throws Throwable
{
if (StringUtil.isEmpty(headerContent))
return;
List<NameValuePair> headers = toNameValuePairs(headerContent);
if (headers == NOT_NAME_VALUE_PAIR_OBJ_ARRAY_JSON)
throw new HeaderContentNotNameValueObjArrayJsonException(headerContent);
for (NameValuePair header : headers)
request.setHeader(header.getName(), header.getValue());
}
protected void setHttpEntity(ClassicHttpRequest request, String requestContent) throws Throwable
{
if (REQUEST_CONTENT_TYPE_FORM_URLENCODED.equals(this.requestContentType))
{
List<NameValuePair> params = toNameValuePairs(requestContent);
if (params == NOT_NAME_VALUE_PAIR_OBJ_ARRAY_JSON)
throw new RequestContentNotNameValueObjArrayJsonException(requestContent);
request.setEntity(new UrlEncodedFormEntity(params, Charset.forName(this.requestContentCharset)));
}
else if (REQUEST_CONTENT_TYPE_JSON.equals(this.requestContentType))
{
ContentType contentType = ContentType.create(ContentType.APPLICATION_JSON.getMimeType(),
Charset.forName(this.requestContentCharset));
StringEntity entity = new StringEntity(requestContent, contentType);
request.setEntity(entity);
}
else
throw new DataSetException("Request content type [" + this.requestContentType + "] is not supported");
}
protected String resolveTemplateUri(Map<String, ?> paramValues) throws Throwable
{
return resolveAsFmkTemplateIfHasParam(this.uri, paramValues);
}
protected String resolveTemplateHeaderContent(Map<String, ?> paramValues) throws Throwable
{
return resolveAsFmkTemplateIfHasParam(this.headerContent, paramValues);
}
protected String resolveTemplateRequestContent(Map<String, ?> paramValues) throws Throwable
{
return resolveAsFmkTemplateIfHasParam(this.requestContent, paramValues);
}
protected ClassicHttpRequest createHttpRequest(String uri) throws Throwable
{
if (REQUEST_METHOD_GET.equals(this.requestMethod) || StringUtil.isEmpty(this.requestMethod))
return new HttpGet(uri);
else if (REQUEST_METHOD_POST.equals(this.requestMethod))
return new HttpPost(uri);
else if (REQUEST_METHOD_PUT.equals(this.requestMethod))
return new HttpPut(uri);
else if (REQUEST_METHOD_PATCH.equals(this.requestMethod))
return new HttpPatch(uri);
else if (REQUEST_METHOD_DELETE.equals(this.requestMethod))
return new HttpDelete(uri);
// else if (REQUEST_METHOD_HEAD.equals(this.httpMethod))
// return new HttpHead(uri);
// else if (REQUEST_METHOD_OPTIONS.equals(this.httpMethod))
// return new HttpOptions(uri);
// else if (REQUEST_METHOD_TRACE.equals(this.httpMethod))
// return new HttpTrace(uri);
else
throw new DataSetException("HTTP method [" + this.requestMethod + "] is not supported");
}
/**
* 将指定JSON字符串转换为名/值列表
*
* @param nameValueObjJsonArray
* 允许为{@code null}{@code ""}
* @return 空列表表示无名/返回{@code #NOT_NAME_VALUE_PAIR_OBJ_ARRAY_JSON}表示{@code nameValueObjJsonArray}格式不合法
* @throws Throwable
*/
@SuppressWarnings("unchecked")
protected List<NameValuePair> toNameValuePairs(String nameValueObjJsonArray) throws Throwable
{
if (StringUtil.isEmpty(nameValueObjJsonArray))
return Collections.EMPTY_LIST;
Object jsonObj = getObjectMapperNonStardand().readValue(nameValueObjJsonArray, Object.class);
if (jsonObj == null)
return Collections.EMPTY_LIST;
if (!(jsonObj instanceof Collection<?>))
return NOT_NAME_VALUE_PAIR_OBJ_ARRAY_JSON;
Collection<?> collection = (Collection<?>) jsonObj;
List<NameValuePair> nameValuePairs = new ArrayList<>(collection.size());
for (Object ele : collection)
{
String name = null;
String value = null;
if (ele instanceof Map<?, ?>)
{
Map<String, ?> eleMap = (Map<String, ?>) ele;
Object nameVal = eleMap.get("name");
Object valueVal = eleMap.get("value");
if (nameVal instanceof String)
{
name = (String) nameVal;
if (valueVal != null)
value = (valueVal instanceof String ? (String) valueVal : valueVal.toString());
}
}
if (name == null)
return NOT_NAME_VALUE_PAIR_OBJ_ARRAY_JSON;
nameValuePairs.add(new BasicNameValuePair(name, value));
}
return nameValuePairs;
}
protected ObjectMapper getObjectMapperNonStardand()
{
return JsonSupport.getObjectMapperNonStardand();
}
protected static class JsonResponseHandler implements HttpClientResponseHandler<ResolvedDataSetResult>
{
private List<DataSetProperty> properties;
private String responseDataJsonPath = "";
public JsonResponseHandler()
{
super();
}
public List<DataSetProperty> getProperties()
{
return properties;
}
/**
* 设置数据集属性
*
* @param properties
* 如果为{@code null}或空则执行解析
*/
public void setProperties(List<DataSetProperty> properties)
{
this.properties = properties;
}
public String getResponseDataJsonPath()
{
return responseDataJsonPath;
}
public void setResponseDataJsonPath(String responseDataJsonPath)
{
this.responseDataJsonPath = responseDataJsonPath;
}
@SuppressWarnings("unchecked")
@Override
public ResolvedDataSetResult handleResponse(ClassicHttpResponse response) throws HttpException, IOException
{
int code = response.getCode();
HttpEntity entity = response.getEntity();
if (code < 200 || code >= 300)
throw new HttpResponseException(code, response.getReasonPhrase());
Reader reader = null;
if (entity == null)
reader = IOUtil.getReader("");
else
{
Charset contentCharset = resolveCharset(entity, ContentType.APPLICATION_JSON.getCharset());
reader = IOUtil.getReader(entity.getContent(), contentCharset);
}
if (this.properties == null || this.properties.isEmpty())
{
HttpResponseJsonDataSet jsonDataSet = new HttpResponseJsonDataSet(reader);
jsonDataSet.setDataJsonPath(this.responseDataJsonPath);
return jsonDataSet.resolve(Collections.EMPTY_MAP);
}
else
{
HttpResponseJsonDataSet jsonDataSet = new HttpResponseJsonDataSet(this.properties, reader);
jsonDataSet.setDataJsonPath(this.responseDataJsonPath);
DataSetResult result = jsonDataSet.getResult(Collections.EMPTY_MAP);
return new ResolvedDataSetResult(result, this.properties);
}
}
protected Charset resolveCharset(HttpEntity entity, Charset defaultCharset)
{
Charset contentCharset = null;
String contentTypeStr = entity.getContentType();
if (!StringUtil.isEmpty(contentTypeStr))
{
try
{
ContentType contentType = ContentType.parse(contentTypeStr);
contentCharset = contentType.getCharset();
}
catch (Throwable t)
{
LOGGER.warn("Default charset [" + defaultCharset + "] will be used because parse error", t);
contentCharset = defaultCharset;
}
}
return (contentCharset != null ? contentCharset : defaultCharset);
}
}
protected static class HttpResponseJsonDataSet extends AbstractJsonDataSet
{
private Reader responseJsonReader;
public HttpResponseJsonDataSet(Reader responseJsonReader)
{
super(HttpResponseJsonDataSet.class.getName(), HttpResponseJsonDataSet.class.getName());
this.responseJsonReader = responseJsonReader;
}
public HttpResponseJsonDataSet(List<DataSetProperty> properties, Reader responseJsonReader)
{
super(HttpResponseJsonDataSet.class.getName(), HttpResponseJsonDataSet.class.getName(), properties);
this.responseJsonReader = responseJsonReader;
}
@Override
protected TemplateResolvedSource<Reader> getJsonReader(Map<String, ?> paramValues) throws Throwable
{
return new TemplateResolvedSource<>(this.responseJsonReader);
}
}
}

View File

@ -1,274 +0,0 @@
/*
* Copyright (c) 2018 datagear.tech. All Rights Reserved.
*/
package org.datagear.analysis.support;
import java.io.File;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.datagear.analysis.DataSet;
import org.datagear.analysis.DataSetException;
import org.datagear.analysis.DataSetProperty;
import org.datagear.analysis.DataSetResult;
import org.datagear.util.IOUtil;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.ValueNode;
/**
* JSON {@linkplain DataSet}支持类
*
* @author datagear@163.com
*
*/
public class JsonDataSetSupport extends JsonSupport
{
public JsonDataSetSupport()
{
super();
}
/**
* 解析JSON数据
*
* @param jsonValue
* @return
* @throws DataSetSourceParseException
* @throws DataSetException
*/
public Object resolveValue(String jsonValue) throws DataSetSourceParseException, DataSetException
{
StringReader reader = new StringReader(jsonValue);
return resolveValue(reader);
}
/**
* 解析JSON数据
*
* @param jsonReader
* @return
* @throws DataSetSourceParseException
* @throws DataSetException
*/
public Object resolveValue(Reader jsonReader) throws DataSetSourceParseException, DataSetException
{
try
{
return parseNonStardand(jsonReader, Object.class);
}
catch (Throwable t)
{
throw new DataSetException(t);
}
}
/**
* 解析数据集结果数据
*
* @param jsonValue
* @return
* @throws DataSetSourceParseException
* @throws UnsupportedJsonResultDataException
* @throws DataSetException
*/
public Object resolveResultData(String jsonValue)
throws DataSetSourceParseException, UnsupportedJsonResultDataException, DataSetException
{
StringReader reader = new StringReader(jsonValue);
return resolveResultData(reader);
}
/**
* 解析数据集结果数据
*
* @param file
* @param encoding
* @return
* @throws DataSetSourceParseException
* @throws UnsupportedJsonResultDataException
* @throws DataSetException
*/
public Object resolveResultData(File file, String encoding)
throws DataSetSourceParseException, UnsupportedJsonResultDataException, DataSetException
{
Reader reader = null;
try
{
reader = IOUtil.getReader(file, encoding);
return resolveResultData(reader);
}
catch (IOException e)
{
throw new DataSetException(e);
}
finally
{
IOUtil.close(reader);
}
}
/**
* 解析数据集结果数据
*
* @param reader
* @return
* @throws DataSetSourceParseException
* @throws UnsupportedJsonResultDataException
* @throws DataSetException
*/
public Object resolveResultData(Reader reader)
throws DataSetSourceParseException, UnsupportedJsonResultDataException, DataSetException
{
JsonNode jsonNode = null;
try
{
jsonNode = getObjectMapperNonStardand().readTree(reader);
}
catch (Throwable t)
{
throw new DataSetSourceParseException(t);
}
if (!isLegalResultDataJsonNode(jsonNode))
throw new UnsupportedJsonResultDataException("Result data must be object or object array/list");
if (jsonNode == null)
return null;
Object data = null;
try
{
data = getObjectMapperNonStardand().treeToValue(jsonNode, Object.class);
}
catch (Throwable t)
{
throw new DataSetException(t);
}
return data;
}
/**
* 是否是合法的数据集结果数据{@linkplain JsonNode}
* <p>
* 参考{@linkplain DataSetResult#getData()}说明
* </p>
*
* @param jsonNode
* @return
*/
public boolean isLegalResultDataJsonNode(JsonNode jsonNode)
{
if (jsonNode == null || jsonNode.isNull())
return true;
if (jsonNode instanceof ValueNode)
return false;
if (jsonNode instanceof ArrayNode)
{
ArrayNode arrayNode = (ArrayNode) jsonNode;
for (int i = 0; i < arrayNode.size(); i++)
{
JsonNode eleNode = arrayNode.get(i);
if (eleNode == null || eleNode.isNull())
continue;
if (!(eleNode instanceof ObjectNode))
return false;
}
}
return true;
}
/**
* 解析JSON对象的{@linkplain DataSetProperty}
*
* @param resultData
* JSON对象JSON对象数组JSON对象列表
* @return
* @throws UnsupportedJsonResultDataException
*/
@SuppressWarnings("unchecked")
public List<DataSetProperty> resolveDataSetProperties(Object resultData) throws UnsupportedJsonResultDataException
{
if (resultData == null)
{
return Collections.EMPTY_LIST;
}
else if (resultData instanceof Map<?, ?>)
{
return resolveJsonObjDataSetProperties((Map<String, ?>) resultData);
}
else if (resultData instanceof List<?>)
{
List<?> list = (List<?>) resultData;
if (list.size() == 0)
return Collections.EMPTY_LIST;
else
return resolveJsonObjDataSetProperties((Map<String, ?>) list.get(0));
}
else if (resultData instanceof Object[])
{
Object[] array = (Object[]) resultData;
if (array.length == 0)
return Collections.EMPTY_LIST;
else
return resolveJsonObjDataSetProperties((Map<String, ?>) array[0]);
}
else
throw new UnsupportedJsonResultDataException("Result data must be object or object array/list");
}
/**
* 解析JSON对象的{@linkplain DataSetProperty}
*
* @param jsonObj
* @return
*/
public List<DataSetProperty> resolveJsonObjDataSetProperties(Map<String, ?> jsonObj)
{
List<DataSetProperty> properties = new ArrayList<>();
if (jsonObj == null)
{
}
else
{
for (Map.Entry<String, ?> entry : jsonObj.entrySet())
{
Object value = entry.getValue();
String type = DataSetProperty.DataType.resolveDataType(value);
DataSetProperty property = new DataSetProperty(entry.getKey(), type);
// JSON数值只有NUMBER类型
if (DataSetProperty.DataType.isInteger(property.getType())
|| DataSetProperty.DataType.isDecimal(property.getType()))
property.setType(DataSetProperty.DataType.NUMBER);
properties.add(property);
}
}
return properties;
}
}

View File

@ -18,6 +18,9 @@ import org.datagear.util.FileUtil;
/**
* 目录内JSON文件{@linkplain DataSet}
* <p>
* 注意此类不支持<code>Freemarker</code>模板语言
* </p>
*
* @author datagear@163.com
*
@ -73,8 +76,7 @@ public class JsonDirectoryFileDataSet extends AbstractJsonFileDataSet
@Override
protected File getJsonFile(Map<String, ?> paramValues) throws DataSetException
{
String fileName = resolveTemplate(this.fileName, paramValues);
File jsonFile = FileUtil.getFile(directory, fileName);
File jsonFile = FileUtil.getFile(this.directory, this.fileName);
return jsonFile;
}
}

View File

@ -7,12 +7,13 @@
*/
package org.datagear.analysis.support;
import java.io.Reader;
import java.util.List;
import java.util.Map;
import org.datagear.analysis.DataSetException;
import org.datagear.analysis.DataSetProperty;
import org.datagear.analysis.DataSetResult;
import org.datagear.util.IOUtil;
/**
* JSON字符串值数据集
@ -55,25 +56,15 @@ public class JsonValueDataSet extends AbstractJsonDataSet
}
@Override
public DataSetResult getResult(Map<String, ?> paramValues) throws DataSetException
public TemplateResolvedDataSetResult resolve(Map<String, ?> paramValues) throws DataSetException
{
String json = resolveTemplate(this.value, paramValues);
Object data = getJsonDataSetSupport().resolveResultData(json);
return new DataSetResult(data);
return (TemplateResolvedDataSetResult) resolveResult(paramValues, null);
}
@Override
public TemplateResolvedDataSetResult resolve(Map<String, ?> paramValues) throws DataSetException
protected TemplateResolvedSource<Reader> getJsonReader(Map<String, ?> paramValues) throws Throwable
{
String json = resolveTemplate(this.value, paramValues);
Object data = getJsonDataSetSupport().resolveResultData(json);
DataSetResult result = new DataSetResult(data);
List<DataSetProperty> properties = getJsonDataSetSupport().resolveDataSetProperties(result.getData());
return new TemplateResolvedDataSetResult(result, properties, json);
String json = resolveAsFmkTemplateIfHasParam(this.value, paramValues);
return new TemplateResolvedSource<>(IOUtil.getReader(json), json);
}
}

View File

@ -0,0 +1,57 @@
/*
* Copyright (c) 2018 datagear.tech. All Rights Reserved.
*/
/**
*
*/
package org.datagear.analysis.support;
import org.datagear.analysis.DataSetException;
/**
* JSON字符串不是名/值数组格式异常
*
* @author datagear@163.com
*
*/
public class NotNameValueObjArrayJsonException extends DataSetException
{
private static final long serialVersionUID = 1L;
private String json;
public NotNameValueObjArrayJsonException(String json)
{
super("The json must be name/value object array");
this.json = json;
}
public NotNameValueObjArrayJsonException(String json, String message)
{
super(message);
this.json = json;
}
public NotNameValueObjArrayJsonException(String json, Throwable cause)
{
super(cause);
this.json = json;
}
public NotNameValueObjArrayJsonException(String json, String message, Throwable cause)
{
super(message, cause);
this.json = json;
}
public String getJson()
{
return json;
}
protected void setJson(String json)
{
this.json = json;
}
}

View File

@ -0,0 +1,465 @@
/*
* Copyright (c) 2018 datagear.tech. All Rights Reserved.
*/
/**
*
*/
package org.datagear.analysis.support;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.datagear.util.StringUtil;
/**
* 范围解析器
* <p>
* 此类用于解析诸如{@code "1, 2-3, 6-, -15}之类的范围表达式。
* </p>
*
* @author datagear@163.com
*
*/
public class RangeExpResolver
{
public static final char RANGE_SPLITTER_CHAR = '-';
public static final String RANGE_SPLITTER_STRING = "-";
public static final char RANGE_GROUP_SPLITTER_CHAR = ',';
public static final String RANGE_GROUP_SPLITTER_STRING = ",";
private char rangeSplitter = RANGE_SPLITTER_CHAR;
private char rangeGroupSplitter = RANGE_GROUP_SPLITTER_CHAR;
public RangeExpResolver()
{
super();
}
public char getRangeSplitter()
{
return rangeSplitter;
}
public void setRangeSplitter(char rangeSplitter)
{
this.rangeSplitter = rangeSplitter;
}
public char getRangeGroupSplitter()
{
return rangeGroupSplitter;
}
public void setRangeGroupSplitter(char rangeGroupSplitter)
{
this.rangeGroupSplitter = rangeGroupSplitter;
}
@SuppressWarnings("unchecked")
public List<IndexRange> resolveIndex(String exp) throws NumberFormatException
{
List<Range> ranges = resolve(exp);
if (ranges.isEmpty())
return Collections.EMPTY_LIST;
List<IndexRange> indexRanges = new ArrayList<>(ranges.size());
for (Range range : ranges)
indexRanges.add(new IndexRange(range));
return indexRanges;
}
/**
* 解析范围表达式组
* <p>
* 例如{@code "1, 2-5, 8-, -15"}
* </p>
*
* @param exp
* @return 如果{@code exp}{@code null}{@code ""}将返回空列表
*/
@SuppressWarnings("unchecked")
public List<Range> resolve(String exp)
{
if (exp != null)
exp = exp.trim();
if (exp == null || exp.isEmpty())
return Collections.EMPTY_LIST;
List<Range> ranges = new ArrayList<>();
String[] ss = StringUtil.split(exp, this.rangeGroupSplitter + "", false);
for (String s : ss)
{
Range range = resolveSingle(s);
if (range != null)
ranges.add(range);
}
return ranges;
}
public IndexRange resolveSingleIndex(String exp) throws NumberFormatException
{
Range range = resolveSingle(exp);
if (range == null)
return null;
return new IndexRange(range);
}
/**
* 解析单个范围表达式
* <p>
* 例如{@code "1"}{@code "4-5"}{@code "8-"}{@code "-15"}
* </p>
*
* @param exp
* @return 如果{@code exp}{@code null}{@code ""}将返回{@code null}
*/
public Range resolveSingle(String exp)
{
if (exp != null)
exp = exp.trim();
if (exp == null || exp.isEmpty())
return null;
String from = "";
String to = "";
int idx = exp.indexOf(this.rangeSplitter);
int len = exp.length();
// 单个值"1"
if (idx < 0)
{
from = exp;
to = from;
}
// 都未指定"-"
else if (idx == 0 && len == 1)
{
from = "";
to = "";
}
// 仅指定截至值"-4"
else if (idx == 0)
{
from = "";
to = exp.substring(1);
}
// 仅指定起始值"4-"
else if (idx == len - 1)
{
from = exp.substring(0, len - 1);
to = "";
}
// 都指定"3-5"
else
{
from = exp.substring(0, idx);
to = exp.substring(idx + 1);
}
return new Range(from, to);
}
public static RangeExpResolver valueOf(char rangeSplitter, char rangeGroupSplitter)
{
RangeExpResolver resolver = new RangeExpResolver();
resolver.setRangeSplitter(rangeSplitter);
resolver.setRangeGroupSplitter(rangeGroupSplitter);
return resolver;
}
/**
* 范围
*
* @author datagear@163.com
*
*/
public static class Range implements Serializable
{
private static final long serialVersionUID = 1L;
/** 起始 */
private String from = "";
/** 截至 */
private String to = "";
public Range()
{
super();
}
public Range(String from)
{
super();
this.from = from;
}
public Range(String from, String to)
{
super();
this.from = from;
this.to = to;
}
public boolean hasFrom()
{
return (this.from != null && !this.from.isEmpty());
}
public String trimFrom()
{
if (this.from == null)
return "";
return this.from.trim();
}
public String getFrom()
{
return from;
}
public void setFrom(String from)
{
this.from = from;
}
public boolean hasTo()
{
return (this.to != null && !this.to.isEmpty());
}
public String trimTo()
{
if (this.to == null)
return "";
return this.to.trim();
}
public String getTo()
{
return to;
}
public void setTo(String to)
{
this.to = to;
}
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + ((from == null) ? 0 : from.hashCode());
result = prime * result + ((to == null) ? 0 : to.hashCode());
return result;
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Range other = (Range) obj;
if (from == null)
{
if (other.from != null)
return false;
}
else if (!from.equals(other.from))
return false;
if (to == null)
{
if (other.to != null)
return false;
}
else if (!to.equals(other.to))
return false;
return true;
}
@Override
public String toString()
{
return getClass().getSimpleName() + " [from=" + from + ", to=" + to + "]";
}
}
/**
* 索引范围
* <p>
* 索引指大于或等于{@code 0}的整数值
* </p>
*
* @author datagear@163.com
*
*/
protected static class IndexRange implements Serializable
{
private static final long serialVersionUID = 1L;
/** 起始索引 */
private int from = 0;
/** 截至索引(包含) */
private int to = -1;
public IndexRange()
{
super();
this.from = 0;
this.to = -1;
}
public IndexRange(int from)
{
super();
this.from = from;
this.to = -1;
}
public IndexRange(int from, int to)
{
super();
this.from = from;
this.to = to;
}
public IndexRange(Range range) throws NumberFormatException
{
super();
int from = 0;
int to = -1;
String fromStr = range.trimFrom();
String toStr = range.trimTo();
if (!StringUtil.isEmpty(fromStr))
from = Integer.parseInt(fromStr);
if (!StringUtil.isEmpty(toStr))
to = Integer.parseInt(toStr);
this.from = from;
this.to = to;
}
public int getFrom()
{
return from;
}
/**
* 设置起始索引
*
* @param from
* 起始索引小于{@code 0}表示不限定
*/
public void setFrom(int from)
{
this.from = from;
}
public int getTo()
{
return to;
}
/**
* 设置截至索引包含
*
* @param to
* 截至索引小于{@code 0}表示不限定
*/
public void setTo(int to)
{
this.to = to;
}
/**
* 是否包含给定索引数值
*
* @param index
* @return
*/
public boolean includes(int index)
{
if (this.from > -1 && index < this.from)
return false;
if (this.to > -1 && index > this.to)
return false;
return true;
}
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + from;
result = prime * result + to;
return result;
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
IndexRange other = (IndexRange) obj;
if (from != other.from)
return false;
if (to != other.to)
return false;
return true;
}
@Override
public String toString()
{
return getClass().getSimpleName() + " [from=" + from + ", to=" + to + "]";
}
public static boolean includes(List<IndexRange> indexRanges, int index)
{
for (int i = 0; i < indexRanges.size(); i++)
if (indexRanges.get(i).includes(index))
return true;
return false;
}
}
}

View File

@ -0,0 +1,55 @@
/*
* Copyright (c) 2018 datagear.tech. All Rights Reserved.
*/
/**
*
*/
package org.datagear.analysis.support;
/**
* 读取指定JSON路径的数据异常
*
* @author datagear@163.com
*
*/
public class ReadJsonDataPathException extends DataSetSourceParseException
{
private static final long serialVersionUID = 1L;
private String dataPath;
public ReadJsonDataPathException(String dataPath)
{
super();
this.dataPath = dataPath;
}
public ReadJsonDataPathException(String dataPath, String message)
{
super(message);
this.dataPath = dataPath;
}
public ReadJsonDataPathException(String dataPath, Throwable cause)
{
super(cause);
this.dataPath = dataPath;
}
public ReadJsonDataPathException(String dataPath, String message, Throwable cause)
{
super(message, cause);
this.dataPath = dataPath;
}
public String getDataPath()
{
return dataPath;
}
protected void setDataPath(String dataPath)
{
this.dataPath = dataPath;
}
}

View File

@ -0,0 +1,39 @@
/*
* Copyright (c) 2018 datagear.tech. All Rights Reserved.
*/
/**
*
*/
package org.datagear.analysis.support;
/**
* {@linkplain HttpDataSet#getRequestContent()}不是名/值对象数组JSON异常
*
* @author datagear@163.com
*
*/
public class RequestContentNotNameValueObjArrayJsonException extends NotNameValueObjArrayJsonException
{
private static final long serialVersionUID = 1L;
public RequestContentNotNameValueObjArrayJsonException(String json)
{
super(json);
}
public RequestContentNotNameValueObjArrayJsonException(String json, String message)
{
super(json, message);
}
public RequestContentNotNameValueObjArrayJsonException(String json, Throwable cause)
{
super(json, cause);
}
public RequestContentNotNameValueObjArrayJsonException(String json, String message, Throwable cause)
{
super(json, message, cause);
}
}

View File

@ -0,0 +1,59 @@
/*
* Copyright 2018 datagear.tech. All Rights Reserved.
*/
package org.datagear.analysis.support;
import java.io.File;
import java.util.List;
import java.util.Map;
import org.datagear.analysis.DataSetProperty;
/**
* 简单CSV文件数据集
* <p>
* 注意此类不支持<code>Freemarker</code>模板语言
* </p>
*
* @author datagear@163.com
*
*/
public class SimpleCsvFileDataSet extends AbstractCsvFileDataSet
{
/** CSV文件 */
private File file;
public SimpleCsvFileDataSet()
{
super();
}
public SimpleCsvFileDataSet(String id, String name, File file)
{
super(id, name);
this.file = file;
}
public SimpleCsvFileDataSet(String id, String name, List<DataSetProperty> properties, File file)
{
super(id, name, properties);
this.file = file;
}
public File getFile()
{
return file;
}
public void setFile(File file)
{
this.file = file;
}
@Override
protected File getCsvFile(Map<String, ?> paramValues) throws Throwable
{
return this.file;
}
}

View File

@ -0,0 +1,53 @@
/*
* Copyright (c) 2018 datagear.tech. All Rights Reserved.
*/
/**
*
*/
package org.datagear.analysis.support;
import java.io.File;
import java.util.List;
import java.util.Map;
import org.datagear.analysis.DataSetException;
import org.datagear.analysis.DataSetProperty;
/**
* 简单Excel数据集
* <p>
* 注意此类不支持<code>Freemarker</code>模板语言
* </p>
*
* @author datagear@163.com
*
*/
public class SimpleExcelDataSet extends AbstractExcelDataSet
{
/** Excel文件 */
private File file;
public SimpleExcelDataSet()
{
super();
}
public SimpleExcelDataSet(String id, String name, File file)
{
super(id, name);
this.file = file;
}
public SimpleExcelDataSet(String id, String name, List<DataSetProperty> properties, File file)
{
super(id, name, properties);
this.file = file;
}
@Override
protected File getExcelFile(Map<String, ?> paramValues) throws DataSetException
{
return this.file;
}
}

View File

@ -13,6 +13,9 @@ import org.datagear.analysis.DataSetProperty;
/**
* 简单JSON文件数据集
* <p>
* 注意此类不支持<code>Freemarker</code>模板语言
* </p>
*
* @author datagear@163.com
*

View File

@ -9,20 +9,27 @@ package org.datagear.analysis.support;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.Collections;
import java.sql.Types;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.datagear.analysis.DataSet;
import org.datagear.analysis.DataSetException;
import org.datagear.analysis.DataSetProperty;
import org.datagear.analysis.DataSetProperty.DataType;
import org.datagear.analysis.DataSetResult;
import org.datagear.analysis.ResolvableDataSet;
import org.datagear.analysis.ResolvedDataSetResult;
import org.datagear.util.JDBCCompatiblity;
import org.datagear.util.JdbcSupport;
import org.datagear.util.JdbcUtil;
import org.datagear.util.QueryResultSet;
import org.datagear.util.Sql;
import org.datagear.util.SqlType;
import org.datagear.util.resource.ConnectionFactory;
/**
@ -34,9 +41,9 @@ import org.datagear.util.resource.ConnectionFactory;
* @author datagear@163.com
*
*/
public class SqlDataSet extends AbstractFmkTemplateDataSet implements ResolvableDataSet
public class SqlDataSet extends AbstractResolvableDataSet implements ResolvableDataSet
{
protected static final SqlDataSetSupport SQL_DATA_SET_SUPPORT = new SqlDataSetSupport();
protected static final JdbcSupport JDBC_SUPPORT = new JdbcSupport();
private ConnectionFactory connectionFactory;
@ -47,10 +54,9 @@ public class SqlDataSet extends AbstractFmkTemplateDataSet implements Resolvable
super();
}
@SuppressWarnings("unchecked")
public SqlDataSet(String id, String name, ConnectionFactory connectionFactory, String sql)
{
super(id, name, Collections.EMPTY_LIST);
super(id, name);
this.connectionFactory = connectionFactory;
this.sql = sql;
}
@ -84,35 +90,16 @@ public class SqlDataSet extends AbstractFmkTemplateDataSet implements Resolvable
}
@Override
public DataSetResult getResult(Map<String, ?> paramValues) throws DataSetException
public TemplateResolvedDataSetResult resolve(Map<String, ?> paramValues) throws DataSetException
{
List<DataSetProperty> properties = getProperties();
if (properties == null || properties.isEmpty())
throw new DataSetException("[getProperties()] must not be empty");
ResolvedDataSetResult result = getResolvedDataSetResult(paramValues, properties);
return result.getResult();
return resolveResult(paramValues, null);
}
@Override
public TemplateResolvedDataSetResult resolve(Map<String, ?> paramValues) throws DataSetException
protected TemplateResolvedDataSetResult resolveResult(Map<String, ?> paramValues, List<DataSetProperty> properties)
throws DataSetException
{
return getResolvedDataSetResult(paramValues, null);
}
/**
*
* @param paramValues
* @param properties
* 允许为{@code null}此时会自动解析
* @return
* @throws DataSetException
*/
protected TemplateResolvedDataSetResult getResolvedDataSetResult(Map<String, ?> paramValues,
List<DataSetProperty> properties) throws DataSetException
{
String sql = resolveTemplate(getSql(), paramValues);
String sql = resolveAsFmkTemplateIfHasParam(getSql(), paramValues);
Connection cn = null;
@ -120,32 +107,42 @@ public class SqlDataSet extends AbstractFmkTemplateDataSet implements Resolvable
{
cn = getConnectionFactory().get();
}
catch (Exception e)
catch (Throwable t)
{
JdbcUtil.closeConnection(cn);
throw new SqlDataSetConnectionException(e);
throw new SqlDataSetConnectionException(t);
}
Sql sqlObj = Sql.valueOf(sql);
JdbcSupport jdbcSupport = getJdbcSupport();
QueryResultSet qrs = null;
try
{
qrs = getSqlDataSetSupport().executeQuery(cn, sqlObj, ResultSet.TYPE_FORWARD_ONLY);
qrs = jdbcSupport.executeQuery(cn, sqlObj, ResultSet.TYPE_FORWARD_ONLY);
}
catch (Throwable t)
{
throw new SqlDataSetSqlExecutionException(sql, t);
}
try
{
ResultSet rs = qrs.getResultSet();
if (properties == null || properties.isEmpty())
properties = getSqlDataSetSupport().resolveDataSetProperties(cn, rs, null);
ResolvedDataSetResult result = resolveResult(cn, rs, properties);
List<Map<String, ?>> data = getSqlDataSetSupport().resolveResultData(cn, rs, properties);
DataSetResult result = new DataSetResult(data);
return new TemplateResolvedDataSetResult(result, properties, sql);
return new TemplateResolvedDataSetResult(result.getResult(), result.getProperties(), sql);
}
catch (SQLException e)
catch (DataSetException e)
{
throw new SqlDataSetSqlExecutionException(sql, e);
throw e;
}
catch (Throwable t)
{
throw new DataSetException(t);
}
finally
{
@ -167,46 +164,163 @@ public class SqlDataSet extends AbstractFmkTemplateDataSet implements Resolvable
}
/**
* 解析结果
*
* @param cn
* @param sql
* @param rs
* @param properties
* 允许为{@code null}此时会自动解析
* @return
* @throws DataSetException
* @throws Throwable
*/
protected ResolvedDataSetResult getResolvedDataSetResult(Connection cn, String sql,
List<DataSetProperty> properties) throws DataSetException
protected ResolvedDataSetResult resolveResult(Connection cn, ResultSet rs, List<DataSetProperty> properties)
throws Throwable
{
Sql sqlObj = Sql.valueOf(sql);
boolean resolveProperties = (properties == null || properties.isEmpty());
QueryResultSet qrs = null;
List<Map<String, ?>> datas = new ArrayList<>();
try
JdbcSupport jdbcSupport = getJdbcSupport();
DataSetPropertyValueConverter converter = createDataSetPropertyValueConverter();
ResultSetMetaData rsMeta = rs.getMetaData();
String[] colNames = jdbcSupport.getColumnNames(rsMeta);
SqlType[] sqlTypes = jdbcSupport.getColumnSqlTypes(rsMeta);
if (resolveProperties)
{
qrs = getSqlDataSetSupport().executeQuery(cn, sqlObj, ResultSet.TYPE_FORWARD_ONLY);
ResultSet rs = qrs.getResultSet();
if (properties == null || properties.isEmpty())
properties = getSqlDataSetSupport().resolveDataSetProperties(cn, rs, null);
List<Map<String, ?>> data = getSqlDataSetSupport().resolveResultData(cn, rs, properties);
DataSetResult result = new DataSetResult(data);
return new ResolvedDataSetResult(result, properties);
properties = new ArrayList<>(colNames.length);
for (int i = 0; i < colNames.length; i++)
properties.add(new DataSetProperty(colNames[i], toPropertyDataType(sqlTypes[i], colNames[i])));
}
catch (SQLException e)
int maxColumnSize = Math.min(colNames.length, properties.size());
int rowIdx = 0;
while (rs.next())
{
throw new SqlDataSetSqlExecutionException(sql, e);
}
finally
{
QueryResultSet.close(qrs);
Map<String, Object> row = new HashMap<>();
for (int i = 0; i < maxColumnSize; i++)
{
DataSetProperty property = properties.get(i);
Object value = jdbcSupport.getColumnValue(cn, rs, colNames[i], sqlTypes[i].getType());
if (resolveProperties && rowIdx == 0)
{
@JDBCCompatiblity("某些驱动程序可能存在一种情况列类型会被toPropertyDataType()解析为DataType.UNKNOWN但是实际值是允许的"
+ "比如PostgreSQL-42.2.5驱动对于[SELECT 'aaa' as NAME]语句结果的SQL类型是Types.OTHER但实际值是允许的字符串")
boolean resolveTypeByValue = (DataType.UNKNOWN.equals(property.getType()));
if (resolveTypeByValue)
property.setType(resolvePropertyDataType(value));
}
value = convertToPropertyDataType(converter, value, property);
row.put(property.getName(), value);
}
datas.add(row);
rowIdx++;
}
DataSetResult result = new DataSetResult(datas);
return new ResolvedDataSetResult(result, properties);
}
protected SqlDataSetSupport getSqlDataSetSupport()
/**
* 由SQL类型转换为{@linkplain DataSetProperty#getType()}
*
* @param sqlType
* @param columnName
* 允许为{@code null}列名称
* @return
* @throws SQLException
* @throws SqlDataSetUnsupportedSqlTypeException
*/
protected String toPropertyDataType(SqlType sqlType, String columnName)
throws SQLException, SqlDataSetUnsupportedSqlTypeException
{
return SQL_DATA_SET_SUPPORT;
String dataType = null;
int type = sqlType.getType();
switch (type)
{
// 确定不支持的类型
case Types.BINARY:
case Types.BLOB:
case Types.LONGVARBINARY:
case Types.VARBINARY:
throw new SqlDataSetUnsupportedSqlTypeException(sqlType, columnName);
case Types.CHAR:
case Types.NCHAR:
case Types.NVARCHAR:
case Types.VARCHAR:
{
dataType = DataType.STRING;
break;
}
case Types.BOOLEAN:
{
dataType = DataType.BOOLEAN;
break;
}
case Types.BIGINT:
case Types.BIT:
case Types.INTEGER:
case Types.SMALLINT:
case Types.TINYINT:
{
dataType = DataType.INTEGER;
break;
}
case Types.DECIMAL:
case Types.DOUBLE:
case Types.FLOAT:
case Types.NUMERIC:
case Types.REAL:
{
dataType = DataType.DECIMAL;
break;
}
case Types.DATE:
{
dataType = DataType.DATE;
break;
}
case Types.TIME:
{
dataType = DataType.TIME;
break;
}
case Types.TIMESTAMP:
{
dataType = DataType.TIMESTAMP;
break;
}
default:
dataType = DataType.UNKNOWN;
}
return dataType;
}
protected JdbcSupport getJdbcSupport()
{
return JDBC_SUPPORT;
}
}

View File

@ -1,433 +0,0 @@
/*
* Copyright (c) 2018 datagear.tech. All Rights Reserved.
*/
/**
*
*/
package org.datagear.analysis.support;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Types;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.datagear.analysis.DataSetException;
import org.datagear.analysis.DataSetProperty;
import org.datagear.analysis.DataSetProperty.DataType;
import org.datagear.util.JdbcSupport;
import org.datagear.util.SqlType;
/**
* SQL数据集支持类
*
* @author datagear@163.com
*
*/
public class SqlDataSetSupport extends JdbcSupport
{
public SqlDataSetSupport()
{
super();
}
/**
* 解析结果数据
*
* @param cn
* @param rs
* @param properties
* @return
* @throws SQLException
*/
public List<Map<String, ?>> resolveResultData(Connection cn, ResultSet rs, List<DataSetProperty> properties)
throws SQLException
{
List<Map<String, ?>> datas = new ArrayList<>();
ResultSetMetaData rsMeta = rs.getMetaData();
int[] rsColumns = resolveResultsetColumns(properties, rsMeta);
while (rs.next())
{
Map<String, Object> row = new HashMap<>();
for (int i = 0; i < rsColumns.length; i++)
{
DataSetProperty property = properties.get(i);
int rsColumn = rsColumns[i];
Object value = resolvePropertyDataValue(cn, rs, rsColumn, getColumnSqlType(rsMeta, rsColumn),
property.getType());
row.put(property.getName(), value);
}
datas.add(row);
}
return datas;
}
/**
* 解析结果集中对应{@linkplain DataSetProperty}的索引数组
*
* @param properties
* @param rsMeta
* @return
* @throws SQLException
* @throws DataSetException
*/
public int[] resolveResultsetColumns(List<DataSetProperty> properties, ResultSetMetaData rsMeta)
throws SQLException, DataSetException
{
int[] columns = new int[properties.size()];
int rsColumnCount = rsMeta.getColumnCount();
for (int i = 0; i < columns.length; i++)
{
String pname = properties.get(i).getName();
int myIndex = -1;
for (int j = 1; j <= rsColumnCount; j++)
{
if (pname.equalsIgnoreCase(getColumnName(rsMeta, j)))
{
myIndex = j;
break;
}
}
if (myIndex <= 0)
throw new DataSetException(
"Column named '" + pname + "' not found in the " + ResultSet.class.getSimpleName());
columns[i] = myIndex;
}
return columns;
}
/**
* 解析数据值
*
* @param cn
* @param rs
* @param column
* @param sqlType
* @param dataType
* @return
* @throws SQLException
*/
public Object resolvePropertyDataValue(Connection cn, ResultSet rs, int column, SqlType sqlType, String dataType)
throws SQLException
{
Object value = null;
int type = sqlType.getType();
if (DataType.isString(dataType))
{
switch (type)
{
case Types.CHAR:
case Types.NCHAR:
case Types.NVARCHAR:
case Types.VARCHAR:
{
value = rs.getString(column);
break;
}
default:
throw new SqlDataSetUnsupportedSqlTypeException(sqlType);
}
}
else if (DataType.isBoolean(dataType))
{
switch (type)
{
case Types.BIT:
case Types.BIGINT:
case Types.INTEGER:
case Types.SMALLINT:
case Types.TINYINT:
{
value = (rs.getInt(column) > 0);
break;
}
case Types.BOOLEAN:
{
value = rs.getBoolean(column);
break;
}
default:
throw new SqlDataSetUnsupportedSqlTypeException(sqlType);
}
}
else if (DataType.isInteger(dataType))
{
switch (type)
{
case Types.BIGINT:
{
value = rs.getLong(column);
break;
}
case Types.BIT:
case Types.INTEGER:
case Types.SMALLINT:
case Types.TINYINT:
{
value = rs.getInt(column);
break;
}
case Types.DECIMAL:
case Types.NUMERIC:
{
value = rs.getBigDecimal(column).toBigInteger();
break;
}
case Types.DOUBLE:
{
value = new Double(rs.getDouble(column)).longValue();
break;
}
case Types.FLOAT:
case Types.REAL:
{
value = new Float(rs.getFloat(column)).longValue();
break;
}
default:
throw new SqlDataSetUnsupportedSqlTypeException(sqlType);
}
}
else if (DataType.isDecimal(dataType))
{
switch (type)
{
case Types.BIGINT:
{
value = rs.getLong(column);
break;
}
case Types.BIT:
case Types.INTEGER:
case Types.SMALLINT:
case Types.TINYINT:
{
value = rs.getInt(column);
break;
}
case Types.DECIMAL:
case Types.NUMERIC:
{
value = rs.getBigDecimal(column);
break;
}
case Types.DOUBLE:
{
value = rs.getDouble(column);
break;
}
case Types.FLOAT:
case Types.REAL:
{
value = rs.getFloat(column);
break;
}
default:
throw new SqlDataSetUnsupportedSqlTypeException(sqlType);
}
}
else if (DataType.isDate(dataType))
{
switch (type)
{
case Types.DATE:
{
value = rs.getDate(column);
break;
}
default:
throw new SqlDataSetUnsupportedSqlTypeException(sqlType);
}
}
else if (DataType.isTime(dataType))
{
switch (type)
{
case Types.TIME:
{
value = rs.getTime(column);
break;
}
default:
throw new SqlDataSetUnsupportedSqlTypeException(sqlType);
}
}
else if (DataType.isTimestamp(dataType))
{
switch (type)
{
case Types.TIMESTAMP:
{
value = rs.getTimestamp(column);
break;
}
default:
throw new SqlDataSetUnsupportedSqlTypeException(sqlType);
}
}
else
throw new UnsupportedOperationException();
if (rs.wasNull())
value = null;
return value;
}
/**
* 解析{@linkplain DataSetProperty}列表
*
* @param cn
* @param rs
* @param labels
* {@linkplain DataSetProperty#getLabel()}数组允许为{@code null}或任意长度的数组
* @return
* @throws SQLException
*/
public List<DataSetProperty> resolveDataSetProperties(Connection cn, ResultSet rs, String[] labels)
throws SQLException
{
ResultSetMetaData metaData = rs.getMetaData();
int columnCount = metaData.getColumnCount();
List<DataSetProperty> properties = new ArrayList<>(columnCount);
for (int i = 1; i <= columnCount; i++)
{
String columnName = getColumnName(metaData, i);
SqlType sqlType = getColumnSqlType(metaData, i);
String dataType = toPropertyDataType(sqlType, columnName);
DataSetProperty property = createDataSetProperty();
property.setName(columnName);
property.setType(dataType);
if (labels != null && labels.length > i - 1)
property.setLabel(labels[i - 1]);
properties.add(property);
}
return properties;
}
/**
* 由SQL类型转换为{@linkplain DataSetProperty#getType()}
*
* @param sqlType
* @param columnName
* 允许为{@code null}列名称
* @return
* @throws SQLException
*/
public String toPropertyDataType(SqlType sqlType, String columnName) throws SQLException
{
String dataType = null;
int type = sqlType.getType();
switch (type)
{
case Types.CHAR:
case Types.NCHAR:
case Types.NVARCHAR:
case Types.VARCHAR:
{
dataType = DataType.STRING;
break;
}
case Types.BOOLEAN:
{
dataType = DataType.BOOLEAN;
break;
}
case Types.BIGINT:
case Types.BIT:
case Types.INTEGER:
case Types.SMALLINT:
case Types.TINYINT:
{
dataType = DataType.INTEGER;
break;
}
case Types.DECIMAL:
case Types.DOUBLE:
case Types.FLOAT:
case Types.NUMERIC:
case Types.REAL:
{
dataType = DataType.DECIMAL;
break;
}
case Types.DATE:
{
dataType = DataType.DATE;
break;
}
case Types.TIME:
{
dataType = DataType.TIME;
break;
}
case Types.TIMESTAMP:
{
dataType = DataType.TIMESTAMP;
break;
}
default:
throw new SqlDataSetUnsupportedSqlTypeException(sqlType, columnName);
}
return dataType;
}
protected DataSetProperty createDataSetProperty()
{
return new DataSetProperty();
}
}

View File

@ -0,0 +1,62 @@
/*
* Copyright 2018 datagear.tech. All Rights Reserved.
*/
package org.datagear.analysis.support;
/**
* 模板已解析的源
*
* @author datagear@163.com
*
*/
public class TemplateResolvedSource<T>
{
private T source;
/** 已解析的模板内容 */
private String resolvedTemplate = null;
public TemplateResolvedSource()
{
super();
}
public TemplateResolvedSource(T source)
{
super();
this.source = source;
}
public TemplateResolvedSource(T source, String resolvedTemplate)
{
super();
this.source = source;
this.resolvedTemplate = resolvedTemplate;
}
public T getSource()
{
return source;
}
public void setSource(T source)
{
this.source = source;
}
public boolean hasResolvedTemplate()
{
return (this.resolvedTemplate != null && !this.resolvedTemplate.isEmpty());
}
public String getResolvedTemplate()
{
return resolvedTemplate;
}
public void setResolvedTemplate(String resolvedTemplate)
{
this.resolvedTemplate = resolvedTemplate;
}
}

View File

@ -81,7 +81,7 @@ public class HtmlChartPluginLoader
private JsonChartPluginPropertiesResolver jsonChartPluginPropertiesResolver = new JsonChartPluginPropertiesResolver();
/** 文件编码 */
private String encoding = "UTF-8";
private String encoding = IOUtil.CHARSET_UTF_8;
public HtmlChartPluginLoader()
{

View File

@ -0,0 +1,78 @@
/*
* Copyright (c) 2018 datagear.tech. All Rights Reserved.
*/
/**
*
*/
package org.datagear.analysis.support;
import static org.junit.Assert.assertEquals;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.datagear.analysis.DataSetProperty;
import org.junit.Test;
/**
* {@linkplain AbstractJsonDataSet}单元测试用例
*
* @author datagear@163.com
*
*/
public class AbstractJsonDataSetTest
{
@Test
public void resolveTest_dataJsonPath()
{
String jsonString = "{ path0: { path1: [ { path2: [ { name:'aaa', value: 11, size: 12 } ] } ] } }";
JsonValueDataSet dataSet = new JsonValueDataSet(JsonValueDataSet.class.getSimpleName(),
JsonValueDataSet.class.getSimpleName(), jsonString);
dataSet.setDataJsonPath("path0.path1[0].path2");
TemplateResolvedDataSetResult result = dataSet.resolve(Collections.emptyMap());
List<DataSetProperty> properties = result.getProperties();
@SuppressWarnings("unchecked")
List<Map<String, Object>> data = (List<Map<String, Object>>) result.getResult().getData();
assertEquals(jsonString, result.getTemplateResult());
{
assertEquals(3, properties.size());
{
DataSetProperty property = properties.get(0);
assertEquals("name", property.getName());
assertEquals(DataSetProperty.DataType.STRING, property.getType());
}
{
DataSetProperty property = properties.get(1);
assertEquals("value", property.getName());
assertEquals(DataSetProperty.DataType.NUMBER, property.getType());
}
{
DataSetProperty property = properties.get(2);
assertEquals("size", property.getName());
assertEquals(DataSetProperty.DataType.NUMBER, property.getType());
}
}
{
assertEquals(1, data.size());
{
Map<String, Object> row = data.get(0);
assertEquals("aaa", row.get("name"));
assertEquals(11, ((Number) row.get("value")).intValue());
assertEquals(12, ((Number) row.get("size")).intValue());
}
}
}
}

View File

@ -0,0 +1,210 @@
/*
* Copyright 2018 datagear.tech. All Rights Reserved.
*/
package org.datagear.analysis.support;
import static org.junit.Assert.assertEquals;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.datagear.analysis.DataSetProperty;
import org.datagear.analysis.DataSetResult;
import org.datagear.analysis.ResolvedDataSetResult;
import org.junit.Test;
/**
* {@linkplain CsvDirectoryFileDataSet}单元测试类
*
* @author datagear@163.com
*
*/
public class CsvDirectoryFileDataSetTest
{
private static final File DIRECTORY = new File("src/test/resources/org/datagear/analysis/support/");
@Test
public void getResultTest()
{
List<DataSetProperty> properties = new ArrayList<>();
properties.add(new DataSetProperty("name", DataSetProperty.DataType.STRING));
properties.add(new DataSetProperty("value", DataSetProperty.DataType.NUMBER));
properties.add(new DataSetProperty("尺寸", DataSetProperty.DataType.NUMBER));
CsvDirectoryFileDataSet dataSet = new CsvDirectoryFileDataSet("a", "a", properties, DIRECTORY,
"CsvDirectoryFileDataSetTest-0.csv");
dataSet.setNameRow(1);
@SuppressWarnings("unchecked")
DataSetResult result = dataSet.getResult(Collections.EMPTY_MAP);
@SuppressWarnings("unchecked")
List<Map<String, Object>> data = (List<Map<String, Object>>) result.getData();
{
assertEquals(3, data.size());
{
Map<String, Object> row = data.get(0);
assertEquals("aaa", row.get("name"));
assertEquals(11, ((Number) row.get("value")).intValue());
assertEquals(12, ((Number) row.get("尺寸")).intValue());
}
{
Map<String, Object> row = data.get(1);
assertEquals("bbb", row.get("name"));
assertEquals(21, ((Number) row.get("value")).intValue());
assertEquals(22, ((Number) row.get("尺寸")).intValue());
}
{
Map<String, Object> row = data.get(2);
assertEquals("ccc", row.get("name"));
assertEquals(31, ((Number) row.get("value")).intValue());
assertEquals(32, ((Number) row.get("尺寸")).intValue());
}
}
}
@Test
public void resolveTest()
{
CsvDirectoryFileDataSet dataSet = new CsvDirectoryFileDataSet("a", "a", DIRECTORY,
"CsvDirectoryFileDataSetTest-0.csv");
dataSet.setNameRow(1);
@SuppressWarnings("unchecked")
ResolvedDataSetResult result = dataSet.resolve(Collections.EMPTY_MAP);
List<DataSetProperty> properties = result.getProperties();
@SuppressWarnings("unchecked")
List<Map<String, Object>> data = (List<Map<String, Object>>) result.getResult().getData();
{
assertEquals(3, properties.size());
{
DataSetProperty property = properties.get(0);
assertEquals("name", property.getName());
assertEquals(DataSetProperty.DataType.STRING, property.getType());
}
{
DataSetProperty property = properties.get(1);
assertEquals("value", property.getName());
assertEquals(DataSetProperty.DataType.NUMBER, property.getType());
}
{
DataSetProperty property = properties.get(2);
assertEquals("尺寸", property.getName());
assertEquals(DataSetProperty.DataType.NUMBER, property.getType());
}
}
{
assertEquals(3, data.size());
{
Map<String, Object> row = data.get(0);
assertEquals("aaa", row.get("name"));
assertEquals(11, ((Number) row.get("value")).intValue());
assertEquals(12, ((Number) row.get("尺寸")).intValue());
}
{
Map<String, Object> row = data.get(1);
assertEquals("bbb", row.get("name"));
assertEquals(21, ((Number) row.get("value")).intValue());
assertEquals(22, ((Number) row.get("尺寸")).intValue());
}
{
Map<String, Object> row = data.get(2);
assertEquals("ccc", row.get("name"));
assertEquals(31, ((Number) row.get("value")).intValue());
assertEquals(32, ((Number) row.get("尺寸")).intValue());
}
}
}
@Test
public void resolveTest_noNameRow()
{
CsvDirectoryFileDataSet dataSet = new CsvDirectoryFileDataSet("a", "a", DIRECTORY,
"CsvDirectoryFileDataSetTest-0.csv");
@SuppressWarnings("unchecked")
ResolvedDataSetResult result = dataSet.resolve(Collections.EMPTY_MAP);
List<DataSetProperty> properties = result.getProperties();
@SuppressWarnings("unchecked")
List<Map<String, Object>> data = (List<Map<String, Object>>) result.getResult().getData();
{
assertEquals(3, properties.size());
{
DataSetProperty property = properties.get(0);
assertEquals("1", property.getName());
assertEquals(DataSetProperty.DataType.STRING, property.getType());
}
{
DataSetProperty property = properties.get(1);
assertEquals("2", property.getName());
assertEquals(DataSetProperty.DataType.STRING, property.getType());
}
{
DataSetProperty property = properties.get(2);
assertEquals("3", property.getName());
assertEquals(DataSetProperty.DataType.STRING, property.getType());
}
}
{
assertEquals(4, data.size());
{
Map<String, Object> row = data.get(0);
assertEquals("name", row.get("1"));
assertEquals("value", row.get("2"));
assertEquals("尺寸", row.get("3"));
}
{
Map<String, Object> row = data.get(1);
assertEquals("aaa", row.get("1"));
assertEquals("11", row.get("2"));
assertEquals("12", row.get("3"));
}
{
Map<String, Object> row = data.get(2);
assertEquals("bbb", row.get("1"));
assertEquals("21", row.get("2"));
assertEquals("22", row.get("3"));
}
{
Map<String, Object> row = data.get(3);
assertEquals("ccc", row.get("1"));
assertEquals("31", row.get("2"));
assertEquals("32", row.get("3"));
}
}
}
}

View File

@ -0,0 +1,154 @@
/*
* Copyright 2018 datagear.tech. All Rights Reserved.
*/
package org.datagear.analysis.support;
import static org.junit.Assert.assertEquals;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.datagear.analysis.DataSetParam;
import org.datagear.analysis.DataSetProperty;
import org.datagear.analysis.DataSetResult;
import org.junit.Test;
/**
* {@linkplain CsvValueDataSet}单元测试类
*
* @author datagear@163.com
*
*/
public class CsvValueDataSetTest
{
@Test
public void getResultTest_hasParam()
{
List<DataSetProperty> properties = new ArrayList<>();
properties.add(new DataSetProperty("name", DataSetProperty.DataType.STRING));
properties.add(new DataSetProperty("value", DataSetProperty.DataType.NUMBER));
properties.add(new DataSetProperty("size", DataSetProperty.DataType.NUMBER));
List<DataSetParam> params = new ArrayList<DataSetParam>();
params.add(new DataSetParam("size", DataSetParam.DataType.NUMBER, true));
CsvValueDataSet dataSet = new CsvValueDataSet("a", "a", properties,
"name, value, size \n aaa, 11, ${size}");
dataSet.setParams(params);
dataSet.setNameRow(1);
Map<String, Object> paramValues = new HashMap<>();
paramValues.put("size", 12);
DataSetResult result = dataSet.getResult(paramValues);
@SuppressWarnings("unchecked")
List<Map<String, Object>> data = (List<Map<String, Object>>) result.getData();
{
assertEquals(1, data.size());
{
Map<String, Object> row = data.get(0);
assertEquals("aaa", row.get("name"));
assertEquals(11, ((Number) row.get("value")).intValue());
assertEquals(12, ((Number) row.get("size")).intValue());
}
}
}
@Test
public void getResultTest_hasParam_convertPropertyValue()
{
List<DataSetProperty> properties = new ArrayList<>();
properties.add(new DataSetProperty("name", DataSetProperty.DataType.STRING));
properties.add(new DataSetProperty("value", DataSetProperty.DataType.NUMBER));
properties.add(new DataSetProperty("size", DataSetProperty.DataType.STRING));
List<DataSetParam> params = new ArrayList<DataSetParam>();
params.add(new DataSetParam("size", DataSetParam.DataType.NUMBER, true));
CsvValueDataSet dataSet = new CsvValueDataSet("a", "a", properties,
"name, value, size \n aaa, 11, ${size}");
dataSet.setParams(params);
dataSet.setNameRow(1);
Map<String, Object> paramValues = new HashMap<>();
paramValues.put("size", 12);
DataSetResult result = dataSet.getResult(paramValues);
@SuppressWarnings("unchecked")
List<Map<String, Object>> data = (List<Map<String, Object>>) result.getData();
{
assertEquals(1, data.size());
{
Map<String, Object> row = data.get(0);
assertEquals("aaa", row.get("name"));
assertEquals(11, ((Number) row.get("value")).intValue());
assertEquals("12", row.get("size"));
}
}
}
@Test
public void resolveTest_hasParam()
{
List<DataSetParam> params = new ArrayList<DataSetParam>();
params.add(new DataSetParam("size", DataSetParam.DataType.NUMBER, true));
CsvValueDataSet dataSet = new CsvValueDataSet("a", "a",
"name, value, size \n aaa, 11, ${size}");
dataSet.setParams(params);
dataSet.setNameRow(1);
Map<String, Object> paramValues = new HashMap<>();
paramValues.put("size", 12);
TemplateResolvedDataSetResult result = dataSet.resolve(paramValues);
List<DataSetProperty> properties = result.getProperties();
@SuppressWarnings("unchecked")
List<Map<String, Object>> data = (List<Map<String, Object>>) result.getResult().getData();
assertEquals("name, value, size \n aaa, 11, 12", result.getTemplateResult());
{
assertEquals(3, properties.size());
{
DataSetProperty property = properties.get(0);
assertEquals("name", property.getName());
assertEquals(DataSetProperty.DataType.STRING, property.getType());
}
{
DataSetProperty property = properties.get(1);
assertEquals("value", property.getName());
assertEquals(DataSetProperty.DataType.NUMBER, property.getType());
}
{
DataSetProperty property = properties.get(2);
assertEquals("size", property.getName());
assertEquals(DataSetProperty.DataType.NUMBER, property.getType());
}
}
{
assertEquals(1, data.size());
{
Map<String, Object> row = data.get(0);
assertEquals("aaa", row.get("name"));
assertEquals(11, ((Number) row.get("value")).intValue());
assertEquals(12, ((Number) row.get("size")).intValue());
}
}
}
}

View File

@ -1,74 +0,0 @@
/*
* Copyright (c) 2018 datagear.org. All Rights Reserved.
*/
package org.datagear.analysis.support;
import java.io.File;
import java.io.FileReader;
import java.io.Reader;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;
/**
* 数据库测试支持类
*
* @author datagear@163.com
*
*/
public abstract class DBTestSupport
{
private static final Properties JDBC_PROPERTIES = new Properties();
static
{
File jdbcConfigFile = new File("test/config/jdbc.properties");
if (!jdbcConfigFile.exists())
jdbcConfigFile = new File("../test/config/jdbc.properties");
try
{
Reader reader = new FileReader(jdbcConfigFile);
JDBC_PROPERTIES.load(reader);
reader.close();
}
catch (Exception e)
{
if (e instanceof RuntimeException)
throw (RuntimeException) e;
else
throw new RuntimeException(e);
}
}
protected Connection getConnection() throws SQLException
{
return DriverManager.getConnection(JDBC_PROPERTIES.getProperty("jdbc.url"),
JDBC_PROPERTIES.getProperty("jdbc.user"), JDBC_PROPERTIES.getProperty("jdbc.password"));
}
protected Connection getConnection(Properties properties) throws Exception
{
properties.setProperty("user", JDBC_PROPERTIES.getProperty("jdbc.user"));
properties.setProperty("password", JDBC_PROPERTIES.getProperty("jdbc.password"));
return DriverManager.getConnection(JDBC_PROPERTIES.getProperty("jdbc.url"), properties);
}
protected void println()
{
System.out.println();
}
protected void println(Object o)
{
System.out.println((o == null ? "null" : o.toString()));
}
protected void print(Object o)
{
System.out.print((o == null ? "null" : o.toString()));
}
}

View File

@ -0,0 +1,355 @@
/*
* Copyright (c) 2018 datagear.tech. All Rights Reserved.
*/
/**
*
*/
package org.datagear.analysis.support;
import static org.junit.Assert.assertEquals;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.datagear.analysis.DataSetProperty;
import org.datagear.analysis.DataSetResult;
import org.datagear.analysis.ResolvedDataSetResult;
import org.junit.Test;
/**
* {@linkplain ExcelDirectoryFileDataSet}单元测试用例
*
* @author datagear@163.com
*
*/
public class ExcelDirectoryFileDataSetTest
{
private static final File DIRECTORY = new File("src/test/resources/org/datagear/analysis/support/");
@Test
public void getResultTest_xlsx()
{
List<DataSetProperty> properties = new ArrayList<>();
properties.add(new DataSetProperty("name", DataSetProperty.DataType.STRING));
properties.add(new DataSetProperty("value", DataSetProperty.DataType.NUMBER));
properties.add(new DataSetProperty("size", DataSetProperty.DataType.NUMBER));
properties.add(new DataSetProperty("date", DataSetProperty.DataType.DATE));
ExcelDirectoryFileDataSet dataSet = new ExcelDirectoryFileDataSet("a", "a", properties, DIRECTORY,
"ExcelDirectoryFileDataSetTest-0.xlsx");
dataSet.setNameRow(1);
@SuppressWarnings("unchecked")
DataSetResult result = dataSet.getResult(Collections.EMPTY_MAP);
@SuppressWarnings("unchecked")
List<Map<String, Object>> data = (List<Map<String, Object>>) result.getData();
{
assertEquals(3, data.size());
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
{
Map<String, Object> row = data.get(0);
assertEquals("aaa", row.get("name"));
assertEquals(15, ((Number) row.get("value")).intValue());
assertEquals(16, ((Number) row.get("size")).intValue());
assertEquals("2020-08-01", dateFormat.format((Date) row.get("date")));
}
{
Map<String, Object> row = data.get(1);
assertEquals("bbb", row.get("name"));
assertEquals(25, ((Number) row.get("value")).intValue());
assertEquals(26, ((Number) row.get("size")).intValue());
assertEquals("2020-08-02", dateFormat.format((Date) row.get("date")));
}
{
Map<String, Object> row = data.get(2);
assertEquals("ccc", row.get("name"));
assertEquals(35, ((Number) row.get("value")).intValue());
assertEquals(36, ((Number) row.get("size")).intValue());
assertEquals("2020-08-03", dateFormat.format((Date) row.get("date")));
}
}
}
@Test
public void getResultTest_xlsx_convertPropertyValue()
{
List<DataSetProperty> properties = new ArrayList<>();
properties.add(new DataSetProperty("name", DataSetProperty.DataType.STRING));
properties.add(new DataSetProperty("value", DataSetProperty.DataType.NUMBER));
properties.add(new DataSetProperty("size", DataSetProperty.DataType.STRING));
properties.add(new DataSetProperty("date", DataSetProperty.DataType.STRING));
ExcelDirectoryFileDataSet dataSet = new ExcelDirectoryFileDataSet("a", "a", properties, DIRECTORY,
"ExcelDirectoryFileDataSetTest-0.xlsx");
dataSet.setNameRow(1);
@SuppressWarnings("unchecked")
DataSetResult result = dataSet.getResult(Collections.EMPTY_MAP);
@SuppressWarnings("unchecked")
List<Map<String, Object>> data = (List<Map<String, Object>>) result.getData();
{
assertEquals(3, data.size());
{
Map<String, Object> row = data.get(0);
assertEquals("aaa", row.get("name"));
assertEquals(15, ((Number) row.get("value")).intValue());
assertEquals("16", row.get("size"));
assertEquals("2020-08-01", row.get("date"));
}
{
Map<String, Object> row = data.get(1);
assertEquals("bbb", row.get("name"));
assertEquals(25, ((Number) row.get("value")).intValue());
assertEquals("26", row.get("size"));
assertEquals("2020-08-02", row.get("date"));
}
{
Map<String, Object> row = data.get(2);
assertEquals("ccc", row.get("name"));
assertEquals(35, ((Number) row.get("value")).intValue());
assertEquals("36", row.get("size"));
assertEquals("2020-08-03", row.get("date"));
}
}
}
@Test
public void resolveTest_xlsx()
{
ExcelDirectoryFileDataSet dataSet = new ExcelDirectoryFileDataSet("a", "a", DIRECTORY,
"ExcelDirectoryFileDataSetTest-0.xlsx");
dataSet.setNameRow(1);
ResolvedDataSetResult resolvedResult = dataSet.resolve(new HashMap<>());
@SuppressWarnings("unchecked")
List<Map<String, Object>> data = (List<Map<String, Object>>) resolvedResult.getResult().getData();
List<DataSetProperty> properties = resolvedResult.getProperties();
{
assertEquals(4, properties.size());
{
DataSetProperty property = properties.get(0);
assertEquals("name", property.getName());
assertEquals(DataSetProperty.DataType.STRING, property.getType());
}
{
DataSetProperty property = properties.get(1);
assertEquals("value", property.getName());
assertEquals(DataSetProperty.DataType.DECIMAL, property.getType());
}
{
DataSetProperty property = properties.get(2);
assertEquals("size", property.getName());
assertEquals(DataSetProperty.DataType.DECIMAL, property.getType());
}
{
DataSetProperty property = properties.get(3);
assertEquals("date", property.getName());
assertEquals(DataSetProperty.DataType.DATE, property.getType());
}
}
{
assertEquals(3, data.size());
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
{
Map<String, Object> row = data.get(0);
assertEquals("aaa", row.get("name"));
assertEquals(15, ((Number) row.get("value")).intValue());
assertEquals(16, ((Number) row.get("size")).intValue());
assertEquals("2020-08-01", dateFormat.format((Date) row.get("date")));
}
{
Map<String, Object> row = data.get(1);
assertEquals("bbb", row.get("name"));
assertEquals(25, ((Number) row.get("value")).intValue());
assertEquals(26, ((Number) row.get("size")).intValue());
assertEquals("2020-08-02", dateFormat.format((Date) row.get("date")));
}
{
Map<String, Object> row = data.get(2);
assertEquals("ccc", row.get("name"));
assertEquals(35, ((Number) row.get("value")).intValue());
assertEquals(36, ((Number) row.get("size")).intValue());
assertEquals("2020-08-03", dateFormat.format((Date) row.get("date")));
}
}
}
@Test
public void resolveTest_xls()
{
ExcelDirectoryFileDataSet dataSet = new ExcelDirectoryFileDataSet("a", "a", DIRECTORY,
"ExcelDirectoryFileDataSetTest-1.xls");
dataSet.setNameRow(1);
ResolvedDataSetResult resolvedResult = dataSet.resolve(new HashMap<>());
@SuppressWarnings("unchecked")
List<Map<String, Object>> data = (List<Map<String, Object>>) resolvedResult.getResult().getData();
List<DataSetProperty> properties = resolvedResult.getProperties();
{
assertEquals(4, properties.size());
{
DataSetProperty property = properties.get(0);
assertEquals("name", property.getName());
assertEquals(DataSetProperty.DataType.STRING, property.getType());
}
{
DataSetProperty property = properties.get(1);
assertEquals("value", property.getName());
assertEquals(DataSetProperty.DataType.DECIMAL, property.getType());
}
{
DataSetProperty property = properties.get(2);
assertEquals("size", property.getName());
assertEquals(DataSetProperty.DataType.DECIMAL, property.getType());
}
{
DataSetProperty property = properties.get(3);
assertEquals("date", property.getName());
assertEquals(DataSetProperty.DataType.DATE, property.getType());
}
}
{
assertEquals(3, data.size());
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
{
Map<String, Object> row = data.get(0);
assertEquals("aaa", row.get("name"));
assertEquals(15, ((Number) row.get("value")).intValue());
assertEquals(16, ((Number) row.get("size")).intValue());
assertEquals("2020-08-01", dateFormat.format((Date) row.get("date")));
}
{
Map<String, Object> row = data.get(1);
assertEquals("bbb", row.get("name"));
assertEquals(25, ((Number) row.get("value")).intValue());
assertEquals(26, ((Number) row.get("size")).intValue());
assertEquals("2020-08-02", dateFormat.format((Date) row.get("date")));
}
{
Map<String, Object> row = data.get(2);
assertEquals("ccc", row.get("name"));
assertEquals(35, ((Number) row.get("value")).intValue());
assertEquals(36, ((Number) row.get("size")).intValue());
assertEquals("2020-08-03", dateFormat.format((Date) row.get("date")));
}
}
}
@Test
public void resolveTest_dataRowColumnExp()
{
ExcelDirectoryFileDataSet dataSet = new ExcelDirectoryFileDataSet("a", "a", DIRECTORY,
"ExcelDirectoryFileDataSetTest-0.xlsx");
dataSet.setNameRow(1);
dataSet.setDataRowExp("2,3-");
dataSet.setDataColumnExp("A,C-");
ResolvedDataSetResult resolvedResult = dataSet.resolve(new HashMap<>());
@SuppressWarnings("unchecked")
List<Map<String, Object>> data = (List<Map<String, Object>>) resolvedResult.getResult().getData();
List<DataSetProperty> properties = resolvedResult.getProperties();
{
assertEquals(3, properties.size());
{
DataSetProperty property = properties.get(0);
assertEquals("name", property.getName());
assertEquals(DataSetProperty.DataType.STRING, property.getType());
}
{
DataSetProperty property = properties.get(1);
assertEquals("size", property.getName());
assertEquals(DataSetProperty.DataType.DECIMAL, property.getType());
}
{
DataSetProperty property = properties.get(2);
assertEquals("date", property.getName());
assertEquals(DataSetProperty.DataType.DATE, property.getType());
}
}
{
assertEquals(3, data.size());
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
{
Map<String, Object> row = data.get(0);
assertEquals("aaa", row.get("name"));
assertEquals(16, ((Number) row.get("size")).intValue());
assertEquals("2020-08-01", dateFormat.format((Date) row.get("date")));
}
{
Map<String, Object> row = data.get(1);
assertEquals("bbb", row.get("name"));
assertEquals(26, ((Number) row.get("size")).intValue());
assertEquals("2020-08-02", dateFormat.format((Date) row.get("date")));
}
{
Map<String, Object> row = data.get(2);
assertEquals("ccc", row.get("name"));
assertEquals(36, ((Number) row.get("size")).intValue());
assertEquals("2020-08-03", dateFormat.format((Date) row.get("date")));
}
}
}
}

View File

@ -0,0 +1,496 @@
/*
* Copyright 2018 datagear.tech. All Rights Reserved.
*/
package org.datagear.analysis.support;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.URLDecoder;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.core5.http.ClassicHttpRequest;
import org.apache.hc.core5.http.ClassicHttpResponse;
import org.apache.hc.core5.http.ContentType;
import org.apache.hc.core5.http.Header;
import org.apache.hc.core5.http.HttpEntity;
import org.apache.hc.core5.http.HttpException;
import org.apache.hc.core5.http.impl.bootstrap.HttpServer;
import org.apache.hc.core5.http.impl.bootstrap.ServerBootstrap;
import org.apache.hc.core5.http.io.HttpRequestHandler;
import org.apache.hc.core5.http.io.entity.StringEntity;
import org.apache.hc.core5.http.protocol.HttpContext;
import org.datagear.analysis.DataSetParam;
import org.datagear.analysis.DataSetProperty;
import org.datagear.util.IOUtil;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
/**
* {@linkplain HttpDataSet}单元测试用例
*
* @author datagear@163.com
*
*/
public class HttpDataSetTest
{
protected static final int PORT = 50402;
protected static final String SERVER = "http://localhost:" + PORT;
protected static final String PARAM_NAME_0 = "param0";
protected static final String PARAM_NAME_1 = "param1";
protected static HttpServer server;
protected static CloseableHttpClient httpClient;
@BeforeClass
public static void initTestHttpServer() throws Throwable
{
server = ServerBootstrap.bootstrap().setListenerPort(PORT)
//
.register("/testSimple", new HttpRequestHandler()
{
@Override
public void handle(ClassicHttpRequest request, ClassicHttpResponse response, HttpContext context)
throws HttpException, IOException
{
StringEntity responseEntity = new StringEntity(
"[{name: 'aaa', value: 11}, {name: '名称b', value: 22}]", ContentType.APPLICATION_JSON);
response.setEntity(responseEntity);
}
})
//
.register("/testParam", new HttpRequestHandler()
{
@Override
public void handle(ClassicHttpRequest request, ClassicHttpResponse response, HttpContext context)
throws HttpException, IOException
{
Map<String, String> params = parseRequestParams(request);
String p0 = params.get(PARAM_NAME_0);
String p1 = params.get(PARAM_NAME_1);
StringEntity responseEntity = new StringEntity("[{name: '" + PARAM_NAME_0 + "', value: '" + p0
+ "'}, {name: '" + PARAM_NAME_1 + "', value: '" + p1 + "'}]",
ContentType.APPLICATION_JSON);
response.setEntity(responseEntity);
}
})
//
.register("/testJson", new HttpRequestHandler()
{
@Override
public void handle(ClassicHttpRequest request, ClassicHttpResponse response, HttpContext context)
throws HttpException, IOException
{
String reqJson = getRequestStringContent(request);
StringEntity responseEntity = new StringEntity(reqJson, ContentType.APPLICATION_JSON);
response.setEntity(responseEntity);
}
})
//
.register("/testHeader", new HttpRequestHandler()
{
@Override
public void handle(ClassicHttpRequest request, ClassicHttpResponse response, HttpContext context)
throws HttpException, IOException
{
Header h0 = request.getHeader(PARAM_NAME_0);
Header h1 = request.getHeader(PARAM_NAME_1);
StringEntity responseEntity = new StringEntity(
"[{name: '" + PARAM_NAME_0 + "', value: '" + h0.getValue() + "'}, {name: '"
+ PARAM_NAME_1 + "', value: '" + h1.getValue() + "'}]",
ContentType.APPLICATION_JSON);
response.setEntity(responseEntity);
}
})
//
.register("/testResponseJsonPath", new HttpRequestHandler()
{
@Override
public void handle(ClassicHttpRequest request, ClassicHttpResponse response, HttpContext context)
throws HttpException, IOException
{
StringEntity responseEntity = new StringEntity(
"{ path0: { path1: [ { path2: [{name: 'aaa', value: 11}, {name: '名称b', value: 22}] } ] } }",
ContentType.APPLICATION_JSON);
response.setEntity(responseEntity);
}
})
//
.create();
server.start();
httpClient = HttpClients.createDefault();
}
@AfterClass
public static void closeHttpServer() throws Throwable
{
server.close();
httpClient.close();
}
@Test
public void resolveTest_onlyUri() throws Throwable
{
List<DataSetParam> params = Arrays.asList(new DataSetParam("param", DataSetParam.DataType.NUMBER, true));
HttpDataSet dataSet = new HttpDataSet(HttpDataSet.class.getName(), HttpDataSet.class.getName(), httpClient,
SERVER + "/testSimple?param=${param}");
dataSet.setParams(params);
Map<String, Object> paramValues = new HashMap<>();
paramValues.put("param", "pv");
TemplateResolvedDataSetResult result = dataSet.resolve(paramValues);
List<DataSetProperty> properties = result.getProperties();
@SuppressWarnings("unchecked")
List<Map<String, Object>> data = (List<Map<String, Object>>) result.getResult().getData();
String templateResult = result.getTemplateResult();
{
assertEquals(2, properties.size());
assertTrue(templateResult.contains("param=pv"));
{
DataSetProperty property = properties.get(0);
assertEquals("name", property.getName());
assertEquals(DataSetProperty.DataType.STRING, property.getType());
}
{
DataSetProperty property = properties.get(1);
assertEquals("value", property.getName());
assertEquals(DataSetProperty.DataType.NUMBER, property.getType());
}
}
{
assertEquals(2, data.size());
{
Map<String, Object> row = data.get(0);
assertEquals("aaa", row.get("name"));
assertEquals(11, ((Number) row.get("value")).intValue());
}
{
Map<String, Object> row = data.get(1);
assertEquals("名称b", row.get("name"));
assertEquals(22, ((Number) row.get("value")).intValue());
}
}
}
@Test
public void resolveTest_setRequestMethod_GET() throws Throwable
{
HttpDataSet dataSet = new HttpDataSet(HttpDataSet.class.getName(), HttpDataSet.class.getName(), httpClient,
SERVER + "/testSimple");
dataSet.setRequestMethod(HttpDataSet.REQUEST_METHOD_GET);
TemplateResolvedDataSetResult result = dataSet.resolve(Collections.emptyMap());
List<DataSetProperty> properties = result.getProperties();
@SuppressWarnings("unchecked")
List<Map<String, Object>> data = (List<Map<String, Object>>) result.getResult().getData();
assertEquals(2, properties.size());
assertEquals(2, data.size());
}
@Test
public void resolveTest_setRequestMethod_POST() throws Throwable
{
HttpDataSet dataSet = new HttpDataSet(HttpDataSet.class.getName(), HttpDataSet.class.getName(), httpClient,
SERVER + "/testSimple");
dataSet.setRequestMethod(HttpDataSet.REQUEST_METHOD_POST);
TemplateResolvedDataSetResult result = dataSet.resolve(Collections.emptyMap());
List<DataSetProperty> properties = result.getProperties();
@SuppressWarnings("unchecked")
List<Map<String, Object>> data = (List<Map<String, Object>>) result.getResult().getData();
assertEquals(2, properties.size());
assertEquals(2, data.size());
}
@Test
public void resolveTest_REQUEST_CONTENT_TYPE_FORM_URLENCODED() throws Throwable
{
String pv0 = "p0";
String pv1 = "参数值1";
List<DataSetParam> params = Arrays.asList(new DataSetParam("param", DataSetParam.DataType.NUMBER, true));
HttpDataSet dataSet = new HttpDataSet(HttpDataSet.class.getName(), HttpDataSet.class.getName(), httpClient,
SERVER + "/testParam");
dataSet.setRequestContent("[ { name: '" + PARAM_NAME_0 + "', value: '" + pv0 + "' }, { name: '" + PARAM_NAME_1
+ "', value: '${param}' } ]");
dataSet.setParams(params);
Map<String, Object> paramValues = new HashMap<>();
paramValues.put("param", pv1);
TemplateResolvedDataSetResult result = dataSet.resolve(paramValues);
List<DataSetProperty> properties = result.getProperties();
@SuppressWarnings("unchecked")
List<Map<String, Object>> data = (List<Map<String, Object>>) result.getResult().getData();
String templateResult = result.getTemplateResult();
{
assertEquals(2, properties.size());
assertTrue(templateResult.contains("value: '" + pv1 + "'"));
{
DataSetProperty property = properties.get(0);
assertEquals("name", property.getName());
assertEquals(DataSetProperty.DataType.STRING, property.getType());
}
{
DataSetProperty property = properties.get(1);
assertEquals("value", property.getName());
assertEquals(DataSetProperty.DataType.STRING, property.getType());
}
}
{
assertEquals(2, data.size());
{
Map<String, Object> row = data.get(0);
assertEquals(PARAM_NAME_0, row.get("name"));
assertEquals(pv0, row.get("value"));
}
{
Map<String, Object> row = data.get(1);
assertEquals(PARAM_NAME_1, row.get("name"));
assertEquals(pv1, row.get("value"));
}
}
}
@Test
public void resolveTest_REQUEST_CONTENT_TYPE_JSON() throws Throwable
{
String pv0 = "p0";
String pv1 = "参数值1";
List<DataSetParam> params = Arrays.asList(new DataSetParam("param", DataSetParam.DataType.NUMBER, true));
HttpDataSet dataSet = new HttpDataSet(HttpDataSet.class.getName(), HttpDataSet.class.getName(), httpClient,
SERVER + "/testJson");
dataSet.setRequestContentType(HttpDataSet.REQUEST_CONTENT_TYPE_JSON);
dataSet.setRequestContent("[ { name: '" + PARAM_NAME_0 + "', value: '" + pv0 + "' }, { name: '" + PARAM_NAME_1
+ "', value: '${param}' } ]");
dataSet.setParams(params);
Map<String, Object> paramValues = new HashMap<>();
paramValues.put("param", pv1);
TemplateResolvedDataSetResult result = dataSet.resolve(paramValues);
List<DataSetProperty> properties = result.getProperties();
@SuppressWarnings("unchecked")
List<Map<String, Object>> data = (List<Map<String, Object>>) result.getResult().getData();
String templateResult = result.getTemplateResult();
{
assertEquals(2, properties.size());
assertTrue(templateResult.contains("value: '" + pv1 + "'"));
{
DataSetProperty property = properties.get(0);
assertEquals("name", property.getName());
assertEquals(DataSetProperty.DataType.STRING, property.getType());
}
{
DataSetProperty property = properties.get(1);
assertEquals("value", property.getName());
assertEquals(DataSetProperty.DataType.STRING, property.getType());
}
}
{
assertEquals(2, data.size());
{
Map<String, Object> row = data.get(0);
assertEquals(PARAM_NAME_0, row.get("name"));
assertEquals(pv0, row.get("value"));
}
{
Map<String, Object> row = data.get(1);
assertEquals(PARAM_NAME_1, row.get("name"));
assertEquals(pv1, row.get("value"));
}
}
}
@Test
public void resolveTest_setHeaderContent() throws Throwable
{
String pv0 = "p0";
String pv1 = "p1";
List<DataSetParam> params = Arrays.asList(new DataSetParam("param", DataSetParam.DataType.NUMBER, true));
HttpDataSet dataSet = new HttpDataSet(HttpDataSet.class.getName(), HttpDataSet.class.getName(), httpClient,
SERVER + "/testHeader");
dataSet.setHeaderContent("[ { name: '" + PARAM_NAME_0 + "', value: '" + pv0 + "' }, { name: '" + PARAM_NAME_1
+ "', value: '${param}' } ]");
dataSet.setParams(params);
Map<String, Object> paramValues = new HashMap<>();
paramValues.put("param", pv1);
TemplateResolvedDataSetResult result = dataSet.resolve(paramValues);
List<DataSetProperty> properties = result.getProperties();
@SuppressWarnings("unchecked")
List<Map<String, Object>> data = (List<Map<String, Object>>) result.getResult().getData();
String templateResult = result.getTemplateResult();
{
assertEquals(2, properties.size());
assertTrue(templateResult.contains("value: '" + pv1 + "'"));
{
DataSetProperty property = properties.get(0);
assertEquals("name", property.getName());
assertEquals(DataSetProperty.DataType.STRING, property.getType());
}
{
DataSetProperty property = properties.get(1);
assertEquals("value", property.getName());
assertEquals(DataSetProperty.DataType.STRING, property.getType());
}
}
{
assertEquals(2, data.size());
{
Map<String, Object> row = data.get(0);
assertEquals(PARAM_NAME_0, row.get("name"));
assertEquals(pv0, row.get("value"));
}
{
Map<String, Object> row = data.get(1);
assertEquals(PARAM_NAME_1, row.get("name"));
assertEquals(pv1, row.get("value"));
}
}
}
@Test
public void resolveTest_setResponseDataJsonPath() throws Throwable
{
HttpDataSet dataSet = new HttpDataSet(HttpDataSet.class.getName(), HttpDataSet.class.getName(), httpClient,
SERVER + "/testResponseJsonPath");
dataSet.setResponseDataJsonPath("path0.path1[0].path2");
TemplateResolvedDataSetResult result = dataSet.resolve(Collections.emptyMap());
List<DataSetProperty> properties = result.getProperties();
@SuppressWarnings("unchecked")
List<Map<String, Object>> data = (List<Map<String, Object>>) result.getResult().getData();
{
assertEquals(2, properties.size());
{
DataSetProperty property = properties.get(0);
assertEquals("name", property.getName());
assertEquals(DataSetProperty.DataType.STRING, property.getType());
}
{
DataSetProperty property = properties.get(1);
assertEquals("value", property.getName());
assertEquals(DataSetProperty.DataType.NUMBER, property.getType());
}
}
{
assertEquals(2, data.size());
{
Map<String, Object> row = data.get(0);
assertEquals("aaa", row.get("name"));
assertEquals(11, ((Number) row.get("value")).intValue());
}
{
Map<String, Object> row = data.get(1);
assertEquals("名称b", row.get("name"));
assertEquals(22, ((Number) row.get("value")).intValue());
}
}
}
protected static Map<String, String> parseRequestParams(ClassicHttpRequest request) throws IOException
{
Map<String, String> map = new HashMap<>();
String content = getRequestStringContent(request);
String[] strss = content.split("&");
for (String strs : strss)
{
String[] pv = strs.split("=");
map.put(pv[0], URLDecoder.decode(pv[1], IOUtil.CHARSET_UTF_8));
}
return map;
}
protected static String getRequestStringContent(ClassicHttpRequest request) throws IOException
{
HttpEntity entity = request.getEntity();
String contentTypeStr = entity.getContentType();
ContentType contentType = ContentType.parse(contentTypeStr);
Reader reader = new InputStreamReader(entity.getContent(), contentType.getCharset());
String content = IOUtil.readString(reader, false);
return content;
}
}

View File

@ -1,38 +0,0 @@
/*
* Copyright (c) 2018 datagear.tech. All Rights Reserved.
*/
/**
*
*/
package org.datagear.analysis.support;
import java.util.Map;
import org.junit.Assert;
import org.junit.Test;
/**
* {@linkplain JsonDataSetSupport}单元测试类
*
* @author datagear@163.com
*
*/
public class JsonDataSetSupportTest
{
private JsonDataSetSupport jsonDataSetSupport = new JsonDataSetSupport();
@Test
public void resolveResultDataTest_String()
{
Object data = jsonDataSetSupport.resolveResultData("{name:'a', value: 3}");
Assert.assertTrue(data instanceof Map);
@SuppressWarnings("unchecked")
Map<String, Object> map = (Map<String, Object>) data;
Assert.assertEquals("a", map.get("name"));
Assert.assertEquals(3, ((Number) map.get("value")).intValue());
}
}

View File

@ -0,0 +1,203 @@
/*
* Copyright 2018 datagear.tech. All Rights Reserved.
*/
package org.datagear.analysis.support;
import static org.junit.Assert.assertEquals;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import org.datagear.analysis.DataSetProperty;
import org.datagear.analysis.DataSetResult;
import org.datagear.analysis.ResolvedDataSetResult;
import org.junit.Test;
/**
* {@linkplain JsonDirectoryFileDataSet}单元测试类
*
* @author datagear@163.com
*
*/
public class JsonDirectoryFileDataSetTest
{
private static final File DIRECTORY = new File("src/test/resources/org/datagear/analysis/support/");
@Test
public void getResultTest()
{
List<DataSetProperty> properties = new ArrayList<>();
properties.add(new DataSetProperty("name", DataSetProperty.DataType.STRING));
properties.add(new DataSetProperty("value", DataSetProperty.DataType.NUMBER));
properties.add(new DataSetProperty("尺寸", DataSetProperty.DataType.NUMBER));
properties.add(new DataSetProperty("date", DataSetProperty.DataType.STRING));
JsonDirectoryFileDataSet dataSet = new JsonDirectoryFileDataSet("a", "a", properties, DIRECTORY,
"JsonDirectoryFileDataSetTest-0.json");
@SuppressWarnings("unchecked")
DataSetResult result = dataSet.getResult(Collections.EMPTY_MAP);
@SuppressWarnings("unchecked")
List<Map<String, Object>> data = (List<Map<String, Object>>) result.getData();
{
assertEquals(3, data.size());
{
Map<String, Object> row = data.get(0);
assertEquals("aaa", row.get("name"));
assertEquals(11, ((Number) row.get("value")).intValue());
assertEquals(12, ((Number) row.get("尺寸")).intValue());
assertEquals("2020-08-01", row.get("date"));
}
{
Map<String, Object> row = data.get(1);
assertEquals("bbb", row.get("name"));
assertEquals(21, ((Number) row.get("value")).intValue());
assertEquals(22, ((Number) row.get("尺寸")).intValue());
assertEquals("2020-08-02", row.get("date"));
}
{
Map<String, Object> row = data.get(2);
assertEquals("ccc", row.get("name"));
assertEquals(31, ((Number) row.get("value")).intValue());
assertEquals(32, ((Number) row.get("尺寸")).intValue());
assertEquals("2020-08-03", row.get("date"));
}
}
}
@Test
public void getResultTest_convertPropertyValue()
{
List<DataSetProperty> properties = new ArrayList<>();
properties.add(new DataSetProperty("name", DataSetProperty.DataType.STRING));
properties.add(new DataSetProperty("value", DataSetProperty.DataType.NUMBER));
properties.add(new DataSetProperty("尺寸", DataSetProperty.DataType.NUMBER));
properties.add(new DataSetProperty("date", DataSetProperty.DataType.DATE));
JsonDirectoryFileDataSet dataSet = new JsonDirectoryFileDataSet("a", "a", properties, DIRECTORY,
"JsonDirectoryFileDataSetTest-0.json");
@SuppressWarnings("unchecked")
DataSetResult result = dataSet.getResult(Collections.EMPTY_MAP);
@SuppressWarnings("unchecked")
List<Map<String, Object>> data = (List<Map<String, Object>>) result.getData();
{
assertEquals(3, data.size());
SimpleDateFormat dateFormat = new SimpleDateFormat(DataFormat.DEFAULT_DATE_FORMAT);
{
Map<String, Object> row = data.get(0);
assertEquals("aaa", row.get("name"));
assertEquals(11, ((Number) row.get("value")).intValue());
assertEquals(12, ((Number) row.get("尺寸")).intValue());
assertEquals("2020-08-01", dateFormat.format(((Date) row.get("date"))));
}
{
Map<String, Object> row = data.get(1);
assertEquals("bbb", row.get("name"));
assertEquals(21, ((Number) row.get("value")).intValue());
assertEquals(22, ((Number) row.get("尺寸")).intValue());
assertEquals("2020-08-02", dateFormat.format(((Date) row.get("date"))));
}
{
Map<String, Object> row = data.get(2);
assertEquals("ccc", row.get("name"));
assertEquals(31, ((Number) row.get("value")).intValue());
assertEquals(32, ((Number) row.get("尺寸")).intValue());
assertEquals("2020-08-03", dateFormat.format(((Date) row.get("date"))));
}
}
}
@Test
public void resolveTest()
{
JsonDirectoryFileDataSet dataSet = new JsonDirectoryFileDataSet("a", "a", DIRECTORY,
"JsonDirectoryFileDataSetTest-0.json");
@SuppressWarnings("unchecked")
ResolvedDataSetResult result = dataSet.resolve(Collections.EMPTY_MAP);
List<DataSetProperty> properties = result.getProperties();
@SuppressWarnings("unchecked")
List<Map<String, Object>> data = (List<Map<String, Object>>) result.getResult().getData();
{
assertEquals(4, properties.size());
{
DataSetProperty property = properties.get(0);
assertEquals("name", property.getName());
assertEquals(DataSetProperty.DataType.STRING, property.getType());
}
{
DataSetProperty property = properties.get(1);
assertEquals("value", property.getName());
assertEquals(DataSetProperty.DataType.NUMBER, property.getType());
}
{
DataSetProperty property = properties.get(2);
assertEquals("尺寸", property.getName());
assertEquals(DataSetProperty.DataType.NUMBER, property.getType());
}
{
DataSetProperty property = properties.get(3);
assertEquals("date", property.getName());
assertEquals(DataSetProperty.DataType.STRING, property.getType());
}
}
{
assertEquals(3, data.size());
{
Map<String, Object> row = data.get(0);
assertEquals("aaa", row.get("name"));
assertEquals(11, ((Number) row.get("value")).intValue());
assertEquals(12, ((Number) row.get("尺寸")).intValue());
assertEquals("2020-08-01", row.get("date"));
}
{
Map<String, Object> row = data.get(1);
assertEquals("bbb", row.get("name"));
assertEquals(21, ((Number) row.get("value")).intValue());
assertEquals(22, ((Number) row.get("尺寸")).intValue());
assertEquals("2020-08-02", row.get("date"));
}
{
Map<String, Object> row = data.get(2);
assertEquals("ccc", row.get("name"));
assertEquals(31, ((Number) row.get("value")).intValue());
assertEquals(32, ((Number) row.get("尺寸")).intValue());
assertEquals("2020-08-03", row.get("date"));
}
}
}
}

View File

@ -0,0 +1,151 @@
/*
* Copyright 2018 datagear.tech. All Rights Reserved.
*/
package org.datagear.analysis.support;
import static org.junit.Assert.assertEquals;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.datagear.analysis.DataSetParam;
import org.datagear.analysis.DataSetProperty;
import org.datagear.analysis.DataSetResult;
import org.junit.Test;
/**
* {@linkplain JsonValueDataSet}单元测试类
*
* @author datagear@163.com
*
*/
public class JsonValueDataSetTest
{
@Test
public void getResultTest_hasParam()
{
List<DataSetProperty> properties = new ArrayList<>();
properties.add(new DataSetProperty("name", DataSetProperty.DataType.STRING));
properties.add(new DataSetProperty("value", DataSetProperty.DataType.NUMBER));
properties.add(new DataSetProperty("size", DataSetProperty.DataType.NUMBER));
List<DataSetParam> params = new ArrayList<>();
params.add(new DataSetParam("size", DataSetParam.DataType.NUMBER, true));
JsonValueDataSet dataSet = new JsonValueDataSet(JsonValueDataSet.class.getSimpleName(),
JsonValueDataSet.class.getSimpleName(), properties, "[ { name:'aaa', value: 11, size: ${size} } ]");
dataSet.setParams(params);
Map<String, Object> paramValues = new HashMap<>();
paramValues.put("size", 12);
DataSetResult result = dataSet.getResult(paramValues);
@SuppressWarnings("unchecked")
List<Map<String, Object>> data = (List<Map<String, Object>>) result.getData();
{
assertEquals(1, data.size());
{
Map<String, Object> row = data.get(0);
assertEquals("aaa", row.get("name"));
assertEquals(11, ((Number) row.get("value")).intValue());
assertEquals(12, ((Number) row.get("size")).intValue());
}
}
}
@Test
public void getResultTest_hasParam_convertPropertyValue()
{
List<DataSetProperty> properties = new ArrayList<>();
properties.add(new DataSetProperty("name", DataSetProperty.DataType.STRING));
properties.add(new DataSetProperty("value", DataSetProperty.DataType.NUMBER));
properties.add(new DataSetProperty("size", DataSetProperty.DataType.STRING));
List<DataSetParam> params = new ArrayList<>();
params.add(new DataSetParam("size", DataSetParam.DataType.NUMBER, true));
JsonValueDataSet dataSet = new JsonValueDataSet(JsonValueDataSet.class.getSimpleName(),
JsonValueDataSet.class.getSimpleName(), properties, "[ { name:'aaa', value: 11, size: ${size} } ]");
dataSet.setParams(params);
Map<String, Object> paramValues = new HashMap<>();
paramValues.put("size", 12);
DataSetResult result = dataSet.getResult(paramValues);
@SuppressWarnings("unchecked")
List<Map<String, Object>> data = (List<Map<String, Object>>) result.getData();
{
assertEquals(1, data.size());
{
Map<String, Object> row = data.get(0);
assertEquals("aaa", row.get("name"));
assertEquals(11, ((Number) row.get("value")).intValue());
assertEquals("12", row.get("size"));
}
}
}
@Test
public void resolveTest_hasParam()
{
List<DataSetParam> params = new ArrayList<>();
params.add(new DataSetParam("size", DataSetParam.DataType.NUMBER, true));
JsonValueDataSet dataSet = new JsonValueDataSet(JsonValueDataSet.class.getSimpleName(),
JsonValueDataSet.class.getSimpleName(), "[ { name:'aaa', value: 11, size: ${size} } ]");
dataSet.setParams(params);
Map<String, Object> paramValues = new HashMap<>();
paramValues.put("size", 12);
TemplateResolvedDataSetResult result = dataSet.resolve(paramValues);
List<DataSetProperty> properties = result.getProperties();
@SuppressWarnings("unchecked")
List<Map<String, Object>> data = (List<Map<String, Object>>) result.getResult().getData();
assertEquals("[ { name:'aaa', value: 11, size: 12 } ]", result.getTemplateResult());
{
assertEquals(3, properties.size());
{
DataSetProperty property = properties.get(0);
assertEquals("name", property.getName());
assertEquals(DataSetProperty.DataType.STRING, property.getType());
}
{
DataSetProperty property = properties.get(1);
assertEquals("value", property.getName());
assertEquals(DataSetProperty.DataType.NUMBER, property.getType());
}
{
DataSetProperty property = properties.get(2);
assertEquals("size", property.getName());
assertEquals(DataSetProperty.DataType.NUMBER, property.getType());
}
}
{
assertEquals(1, data.size());
{
Map<String, Object> row = data.get(0);
assertEquals("aaa", row.get("name"));
assertEquals(11, ((Number) row.get("value")).intValue());
assertEquals(12, ((Number) row.get("size")).intValue());
}
}
}
}

View File

@ -16,10 +16,10 @@ import java.util.Map;
import org.datagear.analysis.DataSetParam;
import org.datagear.analysis.DataSetProperty;
import org.datagear.analysis.DataSetProperty.DataType;
import org.datagear.analysis.DataSetResult;
import org.datagear.util.JdbcUtil;
import org.datagear.util.resource.SimpleConnectionFactory;
import org.datagear.util.test.DBTestSupport;
import org.junit.Assert;
import org.junit.Test;
@ -61,10 +61,12 @@ public class SqlDataSetTest extends DBTestSupport
String sql = "SELECT ID, NAME FROM T_ACCOUNT <#if id??>WHERE ID = ${id} AND NAME != '${name}'</#if>";
List<DataSetProperty> dataSetProperties = Arrays.asList(new DataSetProperty("ID", DataType.INTEGER),
new DataSetProperty("NAME", DataType.STRING));
List<DataSetParam> dataSetParams = Arrays.asList(new DataSetParam("id", DataType.STRING, true),
new DataSetParam("name", DataType.STRING, true));
List<DataSetProperty> dataSetProperties = Arrays.asList(
new DataSetProperty("ID", DataSetProperty.DataType.INTEGER),
new DataSetProperty("NAME", DataSetProperty.DataType.STRING));
List<DataSetParam> dataSetParams = Arrays.asList(new DataSetParam("id", DataSetParam.DataType.STRING, true),
new DataSetParam("name", DataSetParam.DataType.STRING, true));
SqlDataSet sqlDataSet = new SqlDataSet("1", "1", dataSetProperties, connectionFactory, sql);
sqlDataSet.setParams(dataSetParams);

View File

@ -0,0 +1,4 @@
name, value, 尺寸
aaa, 11, 12
bbb, 21, 22
ccc, 31, 32
1 name value 尺寸
2 aaa 11 12
3 bbb 21 22
4 ccc 31 32

View File

@ -0,0 +1,20 @@
[
{
name: "aaa",
value: 11,
"尺寸": 12,
date: "2020-08-01"
},
{
name: "bbb",
value: 21,
"尺寸": 22,
date: "2020-08-02"
},
{
name: "ccc",
value: 31,
"尺寸": 32,
date: "2020-08-03"
}
]

View File

@ -6,7 +6,7 @@
<parent>
<groupId>org.datagear</groupId>
<artifactId>datagear</artifactId>
<version>1.11.1</version>
<version>1.12.0</version>
</parent>
<artifactId>datagear-connection</artifactId>

View File

@ -7,7 +7,7 @@
<parent>
<groupId>org.datagear</groupId>
<artifactId>datagear</artifactId>
<version>1.11.1</version>
<version>1.12.0</version>
</parent>
<artifactId>datagear-dataexchange</artifactId>
@ -27,17 +27,17 @@
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-csv</artifactId>
<version>1.4</version>
<version>${commons-csv.version}</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.17</version>
<version>${poi.version}</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.17</version>
<version>${poi-ooxml.version}</version>
</dependency>
<dependency>
<groupId>org.glassfish</groupId>

View File

@ -6,7 +6,7 @@
<parent>
<groupId>org.datagear</groupId>
<artifactId>datagear</artifactId>
<version>1.11.1</version>
<version>1.12.0</version>
</parent>
<artifactId>datagear-management</artifactId>

View File

@ -0,0 +1,114 @@
/*
* Copyright (c) 2018 datagear.tech. All Rights Reserved.
*/
/**
*
*/
package org.datagear.management.domain;
import java.io.File;
import java.util.Date;
import java.util.List;
import org.datagear.analysis.DataSetProperty;
import org.datagear.analysis.support.CsvDirectoryFileDataSet;
/**
* {@linkplain CsvDirectoryFileDataSet}实体
*
* @author datagear@163.com
*
*/
public class CsvFileDataSetEntity extends CsvDirectoryFileDataSet implements DirectoryFileDataSetEntity
{
private static final long serialVersionUID = 1L;
/** 展示名 */
private String displayName;
/** 创建用户 */
private User createUser;
/** 创建时间 */
private Date createTime;
/** 权限 */
private int dataPermission = PERMISSION_NOT_LOADED;
public CsvFileDataSetEntity()
{
super();
this.createTime = new Date();
}
public CsvFileDataSetEntity(String id, String name, List<DataSetProperty> properties, File directory,
String fileName, String displayName, User createUser)
{
super(id, name, properties, directory, fileName);
this.displayName = displayName;
this.createTime = new Date();
this.createUser = createUser;
}
@Override
public String getDisplayName()
{
return displayName;
}
@Override
public void setDisplayName(String displayName)
{
this.displayName = displayName;
}
@Override
public String getDataSetType()
{
return DataSetEntity.DATA_SET_TYPE_CsvFile;
}
@Override
public void setDataSetType(String dataSetType)
{
// XXX 什么也不做不采用抛出异常的方式便于统一底层SQL查询语句
// throw new UnsupportedOperationException();
}
@Override
public User getCreateUser()
{
return createUser;
}
@Override
public void setCreateUser(User createUser)
{
this.createUser = createUser;
}
@Override
public Date getCreateTime()
{
return createTime;
}
@Override
public void setCreateTime(Date createTime)
{
this.createTime = createTime;
}
@Override
public int getDataPermission()
{
return dataPermission;
}
@Override
public void setDataPermission(int dataPermission)
{
this.dataPermission = dataPermission;
}
}

View File

@ -0,0 +1,97 @@
/*
* Copyright (c) 2018 datagear.tech. All Rights Reserved.
*/
/**
*
*/
package org.datagear.management.domain;
import java.util.Date;
import java.util.List;
import org.datagear.analysis.DataSetProperty;
import org.datagear.analysis.support.CsvValueDataSet;
/**
* {@linkplain CsvValueDataSet}实体
*
* @author datagear@163.com
*
*/
public class CsvValueDataSetEntity extends CsvValueDataSet implements DataSetEntity
{
private static final long serialVersionUID = 1L;
/** 创建用户 */
private User createUser;
/** 创建时间 */
private Date createTime;
/** 权限 */
private int dataPermission = PERMISSION_NOT_LOADED;
public CsvValueDataSetEntity()
{
super();
this.createTime = new Date();
}
public CsvValueDataSetEntity(String id, String name, List<DataSetProperty> properties, String value,
User createUser)
{
super(id, name, properties, value);
this.createTime = new Date();
this.createUser = createUser;
}
@Override
public String getDataSetType()
{
return DataSetEntity.DATA_SET_TYPE_CsvValue;
}
@Override
public void setDataSetType(String dataSetType)
{
// XXX 什么也不做不采用抛出异常的方式便于统一底层SQL查询语句
// throw new UnsupportedOperationException();
}
@Override
public User getCreateUser()
{
return createUser;
}
@Override
public void setCreateUser(User createUser)
{
this.createUser = createUser;
}
@Override
public Date getCreateTime()
{
return createTime;
}
@Override
public void setCreateTime(Date createTime)
{
this.createTime = createTime;
}
@Override
public int getDataPermission()
{
return dataPermission;
}
@Override
public void setDataPermission(int dataPermission)
{
this.dataPermission = dataPermission;
}
}

View File

@ -31,6 +31,18 @@ public interface DataSetEntity extends DataSet, CreateUserEntity<String>, DataPe
/** 数据集类型JSON文件 */
String DATA_SET_TYPE_JsonFile = "JsonFile";
/** 数据集类型Excel */
String DATA_SET_TYPE_Excel = "Excel";
/** 数据集类型CSV值 */
String DATA_SET_TYPE_CsvValue = "CsvValue";
/** 数据集类型CSV文件 */
String DATA_SET_TYPE_CsvFile = "CsvFile";
/** 数据集类型HTTP接口 */
String DATA_SET_TYPE_Http = "Http";
/**
* 设置名称
*

View File

@ -0,0 +1,58 @@
/*
* Copyright 2018 datagear.tech. All Rights Reserved.
*/
package org.datagear.management.domain;
import java.io.File;
/**
* 目录内文件数据集实体
*
* @author datagear@163.com
*
*/
public interface DirectoryFileDataSetEntity extends DataSetEntity
{
/**
* 获取目录
*
* @return
*/
File getDirectory();
/**
* 设置目录
*
* @param directory
*/
void setDirectory(File directory);
/**
* 获取文件名
*
* @return
*/
String getFileName();
/**
* 设置文件名
*
* @param fileName
*/
void setFileName(String fileName);
/**
* 获取文件展示名
*
* @return
*/
String getDisplayName();
/**
* 设置文件展示名
*
* @param displayName
*/
void setDisplayName(String displayName);
}

View File

@ -0,0 +1,114 @@
/*
* Copyright (c) 2018 datagear.tech. All Rights Reserved.
*/
/**
*
*/
package org.datagear.management.domain;
import java.io.File;
import java.util.Date;
import java.util.List;
import org.datagear.analysis.DataSetProperty;
import org.datagear.analysis.support.ExcelDirectoryFileDataSet;
/**
* {@linkplain ExcelDirectoryFileDataSet}实体
*
* @author datagear@163.com
*
*/
public class ExcelDataSetEntity extends ExcelDirectoryFileDataSet implements DirectoryFileDataSetEntity
{
private static final long serialVersionUID = 1L;
/** 展示名 */
private String displayName;
/** 创建用户 */
private User createUser;
/** 创建时间 */
private Date createTime;
/** 权限 */
private int dataPermission = PERMISSION_NOT_LOADED;
public ExcelDataSetEntity()
{
super();
this.createTime = new Date();
}
public ExcelDataSetEntity(String id, String name, List<DataSetProperty> properties, File directory, String fileName,
String displayName, User createUser)
{
super(id, name, properties, directory, fileName);
this.displayName = displayName;
this.createTime = new Date();
this.createUser = createUser;
}
@Override
public String getDisplayName()
{
return displayName;
}
@Override
public void setDisplayName(String displayName)
{
this.displayName = displayName;
}
@Override
public String getDataSetType()
{
return DataSetEntity.DATA_SET_TYPE_Excel;
}
@Override
public void setDataSetType(String dataSetType)
{
// XXX 什么也不做不采用抛出异常的方式便于统一底层SQL查询语句
// throw new UnsupportedOperationException();
}
@Override
public User getCreateUser()
{
return createUser;
}
@Override
public void setCreateUser(User createUser)
{
this.createUser = createUser;
}
@Override
public Date getCreateTime()
{
return createTime;
}
@Override
public void setCreateTime(Date createTime)
{
this.createTime = createTime;
}
@Override
public int getDataPermission()
{
return dataPermission;
}
@Override
public void setDataPermission(int dataPermission)
{
this.dataPermission = dataPermission;
}
}

View File

@ -0,0 +1,105 @@
/*
* Copyright (c) 2018 datagear.tech. All Rights Reserved.
*/
/**
*
*/
package org.datagear.management.domain;
import java.util.Date;
import java.util.List;
import org.apache.hc.client5.http.classic.HttpClient;
import org.datagear.analysis.DataSetProperty;
import org.datagear.analysis.support.HttpDataSet;
/**
* {@linkplain HttpDataSet}实体
*
* @author datagear@163.com
*
*/
public class HttpDataSetEntity extends HttpDataSet implements DataSetEntity
{
private static final long serialVersionUID = 1L;
/** 创建用户 */
private User createUser;
/** 创建时间 */
private Date createTime;
/** 权限 */
private int dataPermission = PERMISSION_NOT_LOADED;
public HttpDataSetEntity()
{
super();
this.createTime = new Date();
}
public HttpDataSetEntity(String id, String name, HttpClient httpClient, String uri, User createUser)
{
super(id, name, httpClient, uri);
this.createTime = new Date();
this.createUser = createUser;
}
public HttpDataSetEntity(String id, String name, List<DataSetProperty> properties, HttpClient httpClient,
String uri, User createUser)
{
super(id, name, properties, httpClient, uri);
this.createTime = new Date();
this.createUser = createUser;
}
@Override
public String getDataSetType()
{
return DataSetEntity.DATA_SET_TYPE_Http;
}
@Override
public void setDataSetType(String dataSetType)
{
// XXX 什么也不做不采用抛出异常的方式便于统一底层SQL查询语句
// throw new UnsupportedOperationException();
}
@Override
public User getCreateUser()
{
return createUser;
}
@Override
public void setCreateUser(User createUser)
{
this.createUser = createUser;
}
@Override
public Date getCreateTime()
{
return createTime;
}
@Override
public void setCreateTime(Date createTime)
{
this.createTime = createTime;
}
@Override
public int getDataPermission()
{
return dataPermission;
}
@Override
public void setDataPermission(int dataPermission)
{
this.dataPermission = dataPermission;
}
}

View File

@ -20,7 +20,7 @@ import org.datagear.analysis.support.JsonDirectoryFileDataSet;
* @author datagear@163.com
*
*/
public class JsonFileDataSetEntity extends JsonDirectoryFileDataSet implements DataSetEntity
public class JsonFileDataSetEntity extends JsonDirectoryFileDataSet implements DirectoryFileDataSetEntity
{
private static final long serialVersionUID = 1L;
@ -51,11 +51,13 @@ public class JsonFileDataSetEntity extends JsonDirectoryFileDataSet implements D
this.createUser = createUser;
}
@Override
public String getDisplayName()
{
return displayName;
}
@Override
public void setDisplayName(String displayName)
{
this.displayName = displayName;

View File

@ -9,6 +9,7 @@ package org.datagear.management.service;
import java.io.File;
import org.apache.hc.client5.http.classic.HttpClient;
import org.datagear.analysis.DataSet;
import org.datagear.management.domain.DataSetEntity;
@ -36,4 +37,11 @@ public interface DataSetEntityService
* @return
*/
File getDataSetDirectory(String dataSetId);
/**
* 获取{@linkplain HttpClient}
*
* @return
*/
HttpClient getHttpClient();
}

View File

@ -12,12 +12,17 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.apache.hc.client5.http.classic.HttpClient;
import org.apache.ibatis.session.SqlSessionFactory;
import org.datagear.analysis.DataSet;
import org.datagear.analysis.DataSetParam;
import org.datagear.analysis.DataSetProperty;
import org.datagear.connection.ConnectionSource;
import org.datagear.management.domain.CsvFileDataSetEntity;
import org.datagear.management.domain.CsvValueDataSetEntity;
import org.datagear.management.domain.DataSetEntity;
import org.datagear.management.domain.ExcelDataSetEntity;
import org.datagear.management.domain.HttpDataSetEntity;
import org.datagear.management.domain.JsonFileDataSetEntity;
import org.datagear.management.domain.JsonValueDataSetEntity;
import org.datagear.management.domain.SchemaConnectionFactory;
@ -51,29 +56,35 @@ public class DataSetEntityServiceImpl extends AbstractMybatisDataPermissionEntit
/** 数据集文件存储根目录 */
private File dataSetRootDirectory;
private HttpClient httpClient;
public DataSetEntityServiceImpl()
{
super();
}
public DataSetEntityServiceImpl(SqlSessionFactory sqlSessionFactory, ConnectionSource connectionSource,
SchemaService schemaService, AuthorizationService authorizationService, File dataSetRootDirectory)
SchemaService schemaService, AuthorizationService authorizationService, File dataSetRootDirectory,
HttpClient httpClient)
{
super(sqlSessionFactory);
this.connectionSource = connectionSource;
this.schemaService = schemaService;
this.authorizationService = authorizationService;
setDataSetRootDirectory(dataSetRootDirectory);
this.httpClient = httpClient;
}
public DataSetEntityServiceImpl(SqlSessionTemplate sqlSessionTemplate, ConnectionSource connectionSource,
SchemaService schemaService, AuthorizationService authorizationService, File dataSetRootDirectory)
SchemaService schemaService, AuthorizationService authorizationService, File dataSetRootDirectory,
HttpClient httpClient)
{
super(sqlSessionTemplate);
this.connectionSource = connectionSource;
this.schemaService = schemaService;
this.authorizationService = authorizationService;
setDataSetRootDirectory(dataSetRootDirectory);
this.httpClient = httpClient;
}
public ConnectionSource getConnectionSource()
@ -116,6 +127,17 @@ public class DataSetEntityServiceImpl extends AbstractMybatisDataPermissionEntit
this.dataSetRootDirectory = dataSetRootDirectory;
}
@Override
public HttpClient getHttpClient()
{
return httpClient;
}
public void setHttpClient(HttpClient httpClient)
{
this.httpClient = httpClient;
}
@Override
public File getDataSetDirectory(String dataSetId)
{
@ -167,6 +189,14 @@ public class DataSetEntityServiceImpl extends AbstractMybatisDataPermissionEntit
success = addJsonValueDataSetEntity((JsonValueDataSetEntity) entity);
else if (entity instanceof JsonFileDataSetEntity)
success = addJsonFileDataSetEntity((JsonFileDataSetEntity) entity);
else if (entity instanceof ExcelDataSetEntity)
success = addExcelDataSetEntity((ExcelDataSetEntity) entity);
else if (entity instanceof CsvValueDataSetEntity)
success = addCsvValueDataSetEntity((CsvValueDataSetEntity) entity);
else if (entity instanceof CsvFileDataSetEntity)
success = addCsvFileDataSetEntity((CsvFileDataSetEntity) entity);
else if (entity instanceof HttpDataSetEntity)
success = addHttpDataSetEntity((HttpDataSetEntity) entity);
}
if (success)
@ -199,6 +229,38 @@ public class DataSetEntityServiceImpl extends AbstractMybatisDataPermissionEntit
return (updateMybatis("insertJsonFileDataSetEntity", params) > 0);
}
protected boolean addExcelDataSetEntity(ExcelDataSetEntity entity)
{
Map<String, Object> params = buildParamMapWithIdentifierQuoteParameter();
params.put("entity", entity);
return (updateMybatis("insertExcelDataSetEntity", params) > 0);
}
protected boolean addCsvValueDataSetEntity(CsvValueDataSetEntity entity)
{
Map<String, Object> params = buildParamMapWithIdentifierQuoteParameter();
params.put("entity", entity);
return (updateMybatis("insertCsvValueDataSetEntity", params) > 0);
}
protected boolean addCsvFileDataSetEntity(CsvFileDataSetEntity entity)
{
Map<String, Object> params = buildParamMapWithIdentifierQuoteParameter();
params.put("entity", entity);
return (updateMybatis("insertCsvFileDataSetEntity", params) > 0);
}
protected boolean addHttpDataSetEntity(HttpDataSetEntity entity)
{
Map<String, Object> params = buildParamMapWithIdentifierQuoteParameter();
params.put("entity", entity);
return (updateMybatis("insertHttpDataSetEntity", params) > 0);
}
@Override
protected boolean update(DataSetEntity entity, Map<String, Object> params)
{
@ -215,6 +277,14 @@ public class DataSetEntityServiceImpl extends AbstractMybatisDataPermissionEntit
success = updateJsonValueDataSetEntity((JsonValueDataSetEntity) entity);
else if (entity instanceof JsonFileDataSetEntity)
success = updateJsonFileDataSetEntity((JsonFileDataSetEntity) entity);
else if (entity instanceof ExcelDataSetEntity)
success = updateExcelDataSetEntity((ExcelDataSetEntity) entity);
else if (entity instanceof CsvValueDataSetEntity)
success = updateCsvValueDataSetEntity((CsvValueDataSetEntity) entity);
else if (entity instanceof CsvFileDataSetEntity)
success = updateCsvFileDataSetEntity((CsvFileDataSetEntity) entity);
else if (entity instanceof HttpDataSetEntity)
success = updateHttpDataSetEntity((HttpDataSetEntity) entity);
}
if (success)
@ -247,6 +317,38 @@ public class DataSetEntityServiceImpl extends AbstractMybatisDataPermissionEntit
return (updateMybatis("updateJsonFileDataSetEntity", params) > 0);
}
protected boolean updateExcelDataSetEntity(ExcelDataSetEntity entity)
{
Map<String, Object> params = buildParamMapWithIdentifierQuoteParameter();
params.put("entity", entity);
return (updateMybatis("updateExcelDataSetEntity", params) > 0);
}
protected boolean updateCsvValueDataSetEntity(CsvValueDataSetEntity entity)
{
Map<String, Object> params = buildParamMapWithIdentifierQuoteParameter();
params.put("entity", entity);
return (updateMybatis("updateCsvValueDataSetEntity", params) > 0);
}
protected boolean updateCsvFileDataSetEntity(CsvFileDataSetEntity entity)
{
Map<String, Object> params = buildParamMapWithIdentifierQuoteParameter();
params.put("entity", entity);
return (updateMybatis("updateCsvFileDataSetEntity", params) > 0);
}
protected boolean updateHttpDataSetEntity(HttpDataSetEntity entity)
{
Map<String, Object> params = buildParamMapWithIdentifierQuoteParameter();
params.put("entity", entity);
return (updateMybatis("updateHttpDataSetEntity", params) > 0);
}
@Override
public String getResourceType()
{
@ -291,6 +393,14 @@ public class DataSetEntityServiceImpl extends AbstractMybatisDataPermissionEntit
obj = getJsonValueDataSetEntityById(obj.getId());
else if (DataSetEntity.DATA_SET_TYPE_JsonFile.equals(obj.getDataSetType()))
obj = getJsonFileDataSetEntityById(obj.getId());
else if (DataSetEntity.DATA_SET_TYPE_Excel.equals(obj.getDataSetType()))
obj = getExcelDataSetEntityById(obj.getId());
else if (DataSetEntity.DATA_SET_TYPE_CsvValue.equals(obj.getDataSetType()))
obj = getCsvValueDataSetEntityById(obj.getId());
else if (DataSetEntity.DATA_SET_TYPE_CsvFile.equals(obj.getDataSetType()))
obj = getCsvFileDataSetEntityById(obj.getId());
else if (DataSetEntity.DATA_SET_TYPE_Http.equals(obj.getDataSetType()))
obj = getHttpDataSetEntityById(obj.getId());
if (obj == null)
return null;
@ -342,6 +452,55 @@ public class DataSetEntityServiceImpl extends AbstractMybatisDataPermissionEntit
return entity;
}
protected ExcelDataSetEntity getExcelDataSetEntityById(String id)
{
Map<String, Object> params = buildParamMapWithIdentifierQuoteParameter();
params.put("id", id);
ExcelDataSetEntity entity = selectOneMybatis("getExcelDataSetEntityById", params);
if (entity != null)
entity.setDirectory(getDataSetDirectory(id));
return entity;
}
protected CsvValueDataSetEntity getCsvValueDataSetEntityById(String id)
{
Map<String, Object> params = buildParamMapWithIdentifierQuoteParameter();
params.put("id", id);
CsvValueDataSetEntity entity = selectOneMybatis("getCsvValueDataSetEntityById", params);
return entity;
}
protected CsvFileDataSetEntity getCsvFileDataSetEntityById(String id)
{
Map<String, Object> params = buildParamMapWithIdentifierQuoteParameter();
params.put("id", id);
CsvFileDataSetEntity entity = selectOneMybatis("getCsvFileDataSetEntityById", params);
if (entity != null)
entity.setDirectory(getDataSetDirectory(id));
return entity;
}
protected HttpDataSetEntity getHttpDataSetEntityById(String id)
{
Map<String, Object> params = buildParamMapWithIdentifierQuoteParameter();
params.put("id", id);
HttpDataSetEntity entity = selectOneMybatis("getHttpDataSetEntityById", params);
if (entity != null)
entity.setHttpClient(this.httpClient);
return entity;
}
@Override
protected void addDataPermissionParameters(Map<String, Object> params, User user)
{

View File

@ -506,3 +506,76 @@ ALTER TABLE DATAGEAR_DATA_SET_JSON_FILE ADD FOREIGN KEY (DS_ID) REFERENCES DATAG
--version[1.11.1], DO NOT EDIT THIS LINE!
-----------------------------------------
-----------------------------------------
--version[1.12.0], DO NOT EDIT THIS LINE!
-----------------------------------------
--2020-08-28
--JSON文件数据集添加编码字段
ALTER TABLE DATAGEAR_DATA_SET_JSON_FILE ADD COLUMN DS_FILE_ENCODING VARCHAR(50);
--2020-08-28
--Excel文件数据集
CREATE TABLE DATAGEAR_DATA_SET_EXCEL
(
DS_ID VARCHAR(50) NOT NULL,
DS_FILE_NAME VARCHAR(100) NOT NULL,
DS_DISPLAY_NAME VARCHAR(100) NOT NULL,
DS_SHEET_INDEX INTEGER,
DS_NAME_ROW INTEGER,
DS_DATA_ROW_EXP VARCHAR(100),
DS_DATA_COLUMN_EXP VARCHAR(100),
DS_FORCE_XLS VARCHAR(10),
PRIMARY KEY (DS_ID)
);
ALTER TABLE DATAGEAR_DATA_SET_EXCEL ADD FOREIGN KEY (DS_ID) REFERENCES DATAGEAR_DATA_SET (DS_ID) ON DELETE CASCADE;
--2020-08-31
--CSV值数据集
CREATE TABLE DATAGEAR_DATA_SET_CSV_VALUE
(
DS_ID VARCHAR(50) NOT NULL,
DS_VALUE VARCHAR(10000) NOT NULL,
DS_NAME_ROW INTEGER,
PRIMARY KEY (DS_ID)
);
ALTER TABLE DATAGEAR_DATA_SET_CSV_VALUE ADD FOREIGN KEY (DS_ID) REFERENCES DATAGEAR_DATA_SET (DS_ID) ON DELETE CASCADE;
--2020-08-31
--CSV文件数据集
CREATE TABLE DATAGEAR_DATA_SET_CSV_FILE
(
DS_ID VARCHAR(50) NOT NULL,
DS_FILE_NAME VARCHAR(100) NOT NULL,
DS_DISPLAY_NAME VARCHAR(100) NOT NULL,
DS_FILE_ENCODING VARCHAR(50),
DS_NAME_ROW INTEGER,
PRIMARY KEY (DS_ID)
);
ALTER TABLE DATAGEAR_DATA_SET_CSV_FILE ADD FOREIGN KEY (DS_ID) REFERENCES DATAGEAR_DATA_SET (DS_ID) ON DELETE CASCADE;
--2020-09-03
--JSON文件数据集添加数据JSON路径字段
ALTER TABLE DATAGEAR_DATA_SET_JSON_FILE ADD COLUMN DS_DATA_JSON_PATH VARCHAR(200);
--2020-09-05
--HTTP数据集
CREATE TABLE DATAGEAR_DATA_SET_HTTP
(
DS_ID VARCHAR(50) NOT NULL,
DS_URI VARCHAR(1000) NOT NULL,
DS_HEADER_CONTENT VARCHAR(5000),
DS_RQT_METHOD VARCHAR(50),
DS_RQT_CONTENT_TYPE VARCHAR(100),
DS_RQT_CONTENT_CHARSET VARCHAR(100),
DS_RQT_CONTENT varchar(10000),
DS_RPS_CONTENT_TYPE VARCHAR(100),
DS_RPS_DATA_JSON_PATH VARCHAR(200),
PRIMARY KEY (DS_ID)
);
ALTER TABLE DATAGEAR_DATA_SET_HTTP ADD FOREIGN KEY (DS_ID) REFERENCES DATAGEAR_DATA_SET (DS_ID) ON DELETE CASCADE;

View File

@ -39,11 +39,59 @@
<insert id="insertJsonFileDataSetEntity">
INSERT INTO DATAGEAR_DATA_SET_JSON_FILE
(
DS_ID, DS_FILE_NAME, DS_DISPLAY_NAME
DS_ID, DS_FILE_NAME, DS_FILE_ENCODING, DS_DISPLAY_NAME, DS_DATA_JSON_PATH
)
VALUES
(
#{entity.id}, #{entity.fileName}, #{entity.displayName}
#{entity.id}, #{entity.fileName}, #{entity.encoding}, #{entity.displayName}, #{entity.dataJsonPath}
)
</insert>
<insert id="insertExcelDataSetEntity">
INSERT INTO DATAGEAR_DATA_SET_EXCEL
(
DS_ID, DS_FILE_NAME, DS_DISPLAY_NAME, DS_SHEET_INDEX, DS_NAME_ROW,
DS_DATA_ROW_EXP, DS_DATA_COLUMN_EXP, DS_FORCE_XLS
)
VALUES
(
#{entity.id}, #{entity.fileName}, #{entity.displayName}, #{entity.sheetIndex}, #{entity.nameRow},
#{entity.dataRowExp}, #{entity.dataColumnExp}, #{entity.forceXls}
)
</insert>
<insert id="insertCsvValueDataSetEntity">
INSERT INTO DATAGEAR_DATA_SET_CSV_VALUE
(
DS_ID, DS_VALUE, DS_NAME_ROW
)
VALUES
(
#{entity.id}, #{entity.value}, #{entity.nameRow}
)
</insert>
<insert id="insertCsvFileDataSetEntity">
INSERT INTO DATAGEAR_DATA_SET_CSV_FILE
(
DS_ID, DS_FILE_NAME, DS_FILE_ENCODING, DS_DISPLAY_NAME, DS_NAME_ROW
)
VALUES
(
#{entity.id}, #{entity.fileName}, #{entity.encoding}, #{entity.displayName}, #{entity.nameRow}
)
</insert>
<insert id="insertHttpDataSetEntity">
INSERT INTO DATAGEAR_DATA_SET_HTTP
(
DS_ID, DS_URI, DS_HEADER_CONTENT, DS_RQT_METHOD, DS_RQT_CONTENT_TYPE,
DS_RQT_CONTENT_CHARSET, DS_RQT_CONTENT, DS_RPS_CONTENT_TYPE, DS_RPS_DATA_JSON_PATH
)
VALUES
(
#{entity.id}, #{entity.uri}, #{entity.headerContent}, #{entity.requestMethod}, #{entity.requestContentType},
#{entity.requestContentCharset}, #{entity.requestContent}, #{entity.responseContentType}, #{entity.responseDataJsonPath}
)
</insert>
@ -103,7 +151,54 @@
<update id="updateJsonFileDataSetEntity">
UPDATE DATAGEAR_DATA_SET_JSON_FILE SET
DS_FILE_NAME = #{entity.fileName},
DS_DISPLAY_NAME = #{entity.displayName}
DS_FILE_ENCODING = #{entity.encoding},
DS_DISPLAY_NAME = #{entity.displayName},
DS_DATA_JSON_PATH = #{entity.dataJsonPath}
WHERE
DS_ID = #{entity.id}
</update>
<update id="updateExcelDataSetEntity">
UPDATE DATAGEAR_DATA_SET_EXCEL SET
DS_FILE_NAME = #{entity.fileName},
DS_DISPLAY_NAME = #{entity.displayName},
DS_SHEET_INDEX = #{entity.sheetIndex},
DS_NAME_ROW = #{entity.nameRow},
DS_DATA_ROW_EXP = #{entity.dataRowExp},
DS_DATA_COLUMN_EXP = #{entity.dataColumnExp},
DS_FORCE_XLS = #{entity.forceXls}
WHERE
DS_ID = #{entity.id}
</update>
<update id="updateCsvValueDataSetEntity">
UPDATE DATAGEAR_DATA_SET_CSV_VALUE SET
DS_VALUE = #{entity.value},
DS_NAME_ROW = #{entity.nameRow}
WHERE
DS_ID = #{entity.id}
</update>
<update id="updateCsvFileDataSetEntity">
UPDATE DATAGEAR_DATA_SET_CSV_FILE SET
DS_FILE_NAME = #{entity.fileName},
DS_FILE_ENCODING = #{entity.encoding},
DS_DISPLAY_NAME = #{entity.displayName},
DS_NAME_ROW = #{entity.nameRow}
WHERE
DS_ID = #{entity.id}
</update>
<update id="updateHttpDataSetEntity">
UPDATE DATAGEAR_DATA_SET_HTTP SET
DS_URI = #{entity.uri},
DS_HEADER_CONTENT = #{entity.headerContent},
DS_RQT_METHOD = #{entity.requestMethod},
DS_RQT_CONTENT_TYPE = #{entity.requestContentType},
DS_RQT_CONTENT_CHARSET = #{entity.requestContentCharset},
DS_RQT_CONTENT = #{entity.requestContent},
DS_RPS_CONTENT_TYPE = #{entity.responseContentType},
DS_RPS_DATA_JSON_PATH = #{entity.responseDataJsonPath}
WHERE
DS_ID = #{entity.id}
</update>
@ -169,7 +264,9 @@
SELECT
T1.*,
T2.DS_FILE_NAME AS ${_iq_}fileName${_iq_},
T2.DS_DISPLAY_NAME AS ${_iq_}displayName${_iq_}
T2.DS_FILE_ENCODING AS ${_iq_}encoding${_iq_},
T2.DS_DISPLAY_NAME AS ${_iq_}displayName${_iq_},
T2.DS_DATA_JSON_PATH AS ${_iq_}dataJsonPath${_iq_}
FROM
(SELECT * FROM (<include refid="queryView" />) T0 WHERE T0.${_iq_}id${_iq_} = #{id}) T1
INNER JOIN
@ -178,6 +275,72 @@
T1.${_iq_}id${_iq_} = T2.DS_ID
</select>
<select id="getExcelDataSetEntityById" resultType="org.datagear.management.domain.ExcelDataSetEntity">
SELECT
T1.*,
T2.DS_FILE_NAME AS ${_iq_}fileName${_iq_},
T2.DS_DISPLAY_NAME AS ${_iq_}displayName${_iq_},
T2.DS_SHEET_INDEX AS ${_iq_}sheetIndex${_iq_},
T2.DS_NAME_ROW AS ${_iq_}nameRow${_iq_},
T2.DS_DATA_ROW_EXP AS ${_iq_}dataRowExp${_iq_},
T2.DS_DATA_COLUMN_EXP AS ${_iq_}dataColumnExp${_iq_},
T2.DS_FORCE_XLS AS ${_iq_}forceXls${_iq_}
FROM
(SELECT * FROM (<include refid="queryView" />) T0 WHERE T0.${_iq_}id${_iq_} = #{id}) T1
INNER JOIN
DATAGEAR_DATA_SET_EXCEL T2
ON
T1.${_iq_}id${_iq_} = T2.DS_ID
</select>
<select id="getCsvValueDataSetEntityById" resultType="org.datagear.management.domain.CsvValueDataSetEntity">
SELECT
T1.*,
T2.DS_VALUE AS ${_iq_}value${_iq_},
T2.DS_NAME_ROW AS ${_iq_}nameRow${_iq_}
FROM
(SELECT * FROM (<include refid="queryView" />) T0 WHERE T0.${_iq_}id${_iq_} = #{id}) T1
INNER JOIN
DATAGEAR_DATA_SET_CSV_VALUE T2
ON
T1.${_iq_}id${_iq_} = T2.DS_ID
</select>
<select id="getCsvFileDataSetEntityById" resultType="org.datagear.management.domain.CsvFileDataSetEntity">
SELECT
T1.*,
T2.DS_FILE_NAME AS ${_iq_}fileName${_iq_},
T2.DS_FILE_ENCODING AS ${_iq_}encoding${_iq_},
T2.DS_DISPLAY_NAME AS ${_iq_}displayName${_iq_},
T2.DS_NAME_ROW AS ${_iq_}nameRow${_iq_}
FROM
(SELECT * FROM (<include refid="queryView" />) T0 WHERE T0.${_iq_}id${_iq_} = #{id}) T1
INNER JOIN
DATAGEAR_DATA_SET_CSV_FILE T2
ON
T1.${_iq_}id${_iq_} = T2.DS_ID
</select>
<select id="getHttpDataSetEntityById" resultType="org.datagear.management.domain.HttpDataSetEntity">
SELECT
T1.*,
T2.DS_URI AS ${_iq_}uri${_iq_},
T2.DS_HEADER_CONTENT AS ${_iq_}headerContent${_iq_},
T2.DS_RQT_METHOD AS ${_iq_}requestMethod${_iq_},
T2.DS_RQT_CONTENT_TYPE AS ${_iq_}requestContentType${_iq_},
T2.DS_RQT_CONTENT_CHARSET AS ${_iq_}requestContentCharset${_iq_},
T2.DS_RQT_CONTENT AS ${_iq_}requestContent${_iq_},
T2.DS_RPS_CONTENT_TYPE AS ${_iq_}responseContentType${_iq_},
T2.DS_RPS_DATA_JSON_PATH AS ${_iq_}responseDataJsonPath${_iq_}
FROM
(SELECT * FROM (<include refid="queryView" />) T0 WHERE T0.${_iq_}id${_iq_} = #{id}) T1
INNER JOIN
DATAGEAR_DATA_SET_HTTP T2
ON
T1.${_iq_}id${_iq_} = T2.DS_ID
</select>
<select id="getPropertyPOs" resultType="org.datagear.management.service.impl.DataSetEntityServiceImpl$DataSetPropertyPO">
SELECT
PROP_DS_ID AS ${_iq_}dataSetId${_iq_},

View File

@ -6,7 +6,7 @@
<parent>
<groupId>org.datagear</groupId>
<artifactId>datagear</artifactId>
<version>1.11.1</version>
<version>1.12.0</version>
</parent>
<artifactId>datagear-meta</artifactId>

View File

@ -6,7 +6,7 @@
<parent>
<groupId>org.datagear</groupId>
<artifactId>datagear</artifactId>
<version>1.11.1</version>
<version>1.12.0</version>
</parent>
<artifactId>datagear-persistence</artifactId>

View File

@ -7,7 +7,7 @@
<parent>
<groupId>org.datagear</groupId>
<artifactId>datagear</artifactId>
<version>1.11.1</version>
<version>1.12.0</version>
</parent>
<artifactId>datagear-util</artifactId>

View File

@ -21,7 +21,7 @@ public final class Global
}
/** 当前版本号 */
public static final String VERSION = "1.11.1";
public static final String VERSION = "1.12.0";
/** 中文产品名称 */
public static final String PRODUCT_NAME_ZH = "数据齿轮";

View File

@ -37,6 +37,10 @@ import java.util.zip.ZipOutputStream;
*/
public class IOUtil
{
public static final String CHARSET_UTF_8 = "UTF-8";
public static final String CHARSET_ISO_8859_1 = "ISO-8859-1";
private IOUtil()
{
throw new UnsupportedOperationException();
@ -337,24 +341,41 @@ public class IOUtil
*/
public static BufferedReader getReader(File file) throws FileNotFoundException, UnsupportedEncodingException
{
return getReader(getInputStream(file), null);
return getReader(getInputStream(file), (String) null);
}
/**
* 获取输入流
*
* @param in
* @param encoding
* @param charset
* 允许为{@code null}
* @return
* @throws UnsupportedEncodingException
*/
public static BufferedReader getReader(InputStream in, String encoding) throws UnsupportedEncodingException
public static BufferedReader getReader(InputStream in, String charset) throws UnsupportedEncodingException
{
if (StringUtil.isEmpty(encoding))
if (StringUtil.isEmpty(charset))
return new BufferedReader(new InputStreamReader(in));
else
return new BufferedReader(new InputStreamReader(in, encoding));
return new BufferedReader(new InputStreamReader(in, charset));
}
/**
* 获取输入流
*
* @param in
* @param charset
* 允许为{@code null}
* @return
* @throws UnsupportedEncodingException
*/
public static BufferedReader getReader(InputStream in, Charset charset) throws UnsupportedEncodingException
{
if (charset == null)
return new BufferedReader(new InputStreamReader(in));
else
return new BufferedReader(new InputStreamReader(in, charset));
}
/**

View File

@ -950,27 +950,16 @@ public class JdbcSupport
* @return
* @throws SQLException
*/
@JDBCCompatiblity("某些驱动程序可能不支持ResultSet.getObject方法所以这里没有使用")
public Object getColumnValue(Connection cn, ResultSet rs, String columnName, int sqlType) throws SQLException
{
Object value = null;
switch (sqlType)
{
case Types.TINYINT:
case Types.ARRAY:
{
value = rs.getByte(columnName);
break;
}
case Types.SMALLINT:
{
value = rs.getShort(columnName);
break;
}
case Types.INTEGER:
{
value = rs.getInt(columnName);
value = rs.getArray(columnName);
break;
}
@ -980,23 +969,9 @@ public class JdbcSupport
break;
}
case Types.REAL:
case Types.BINARY:
{
value = rs.getFloat(columnName);
break;
}
case Types.FLOAT:
case Types.DOUBLE:
{
value = rs.getDouble(columnName);
break;
}
case Types.DECIMAL:
case Types.NUMERIC:
{
value = rs.getBigDecimal(columnName);
value = rs.getBytes(columnName);
break;
}
@ -1006,6 +981,12 @@ public class JdbcSupport
break;
}
case Types.BLOB:
{
value = rs.getBlob(columnName);
break;
}
case Types.BOOLEAN:
{
value = rs.getBoolean(columnName);
@ -1013,22 +994,68 @@ public class JdbcSupport
}
case Types.CHAR:
case Types.VARCHAR:
{
value = rs.getString(columnName);
break;
}
case Types.LONGVARCHAR:
case Types.CLOB:
{
value = rs.getCharacterStream(columnName);
value = rs.getClob(columnName);
break;
}
case Types.BINARY:
case Types.VARBINARY:
case Types.DATALINK:
{
value = rs.getBytes(columnName);
value = rs.getObject(columnName);
break;
}
case Types.DATE:
{
value = rs.getDate(columnName);
break;
}
case Types.DECIMAL:
{
value = rs.getBigDecimal(columnName);
break;
}
case Types.DISTINCT:
{
value = rs.getObject(columnName);
break;
}
case Types.DOUBLE:
{
value = rs.getDouble(columnName);
break;
}
case Types.FLOAT:
{
value = rs.getFloat(columnName);
break;
}
case Types.INTEGER:
{
value = rs.getInt(columnName);
break;
}
case Types.JAVA_OBJECT:
{
value = rs.getObject(columnName);
break;
}
case Types.LONGNVARCHAR:
{
value = rs.getNCharacterStream(columnName);
break;
}
@ -1038,9 +1065,81 @@ public class JdbcSupport
break;
}
case Types.DATE:
case Types.LONGVARCHAR:
{
value = rs.getDate(columnName);
value = rs.getCharacterStream(columnName);
break;
}
case Types.NCHAR:
{
value = rs.getNString(columnName);
break;
}
case Types.NCLOB:
{
value = rs.getNClob(columnName);
break;
}
case Types.NUMERIC:
{
value = rs.getBigDecimal(columnName);
break;
}
case Types.NVARCHAR:
{
value = rs.getNString(columnName);
break;
}
case Types.OTHER:
{
value = rs.getObject(columnName);
break;
}
case Types.REAL:
{
value = rs.getFloat(columnName);
break;
}
case Types.REF:
{
value = rs.getRef(columnName);
break;
}
case Types.REF_CURSOR:
{
value = rs.getObject(columnName);
break;
}
case Types.ROWID:
{
value = rs.getRowId(columnName);
break;
}
case Types.SMALLINT:
{
value = rs.getShort(columnName);
break;
}
case Types.SQLXML:
{
value = rs.getSQLXML(columnName);
break;
}
case Types.STRUCT:
{
value = rs.getObject(columnName);
break;
}
@ -1058,46 +1157,29 @@ public class JdbcSupport
break;
}
case Types.CLOB:
case Types.TINYINT:
{
value = rs.getClob(columnName);
value = rs.getByte(columnName);
break;
}
case Types.BLOB:
case Types.VARBINARY:
{
value = rs.getBlob(columnName);
value = rs.getBytes(columnName);
break;
}
case Types.NCHAR:
case Types.NVARCHAR:
case Types.VARCHAR:
{
value = rs.getNString(columnName);
break;
}
case Types.LONGNVARCHAR:
{
value = rs.getNCharacterStream(columnName);
break;
}
case Types.NCLOB:
{
value = rs.getNClob(columnName);
break;
}
case Types.SQLXML:
{
value = rs.getSQLXML(columnName);
value = rs.getString(columnName);
break;
}
default:
{
value = getColumnValueExt(cn, rs, columnName, sqlType);
break;
}
}
if (rs.wasNull())
@ -1171,6 +1253,25 @@ public class JdbcSupport
return columnName;
}
/**
* 获取列名数组
*
* @param metaData
* @return
* @throws SQLException
*/
public String[] getColumnNames(ResultSetMetaData metaData) throws SQLException
{
int size = metaData.getColumnCount();
String[] names = new String[size];
for (int i = 0; i < names.length; i++)
names[i] = getColumnName(metaData, i + 1);
return names;
}
/**
* 获取列类型
*
@ -1188,6 +1289,25 @@ public class JdbcSupport
return new SqlType(type, typeName);
}
/**
* 获取列类型数组
*
* @param metaData
* @return
* @throws SQLException
*/
public SqlType[] getColumnSqlTypes(ResultSetMetaData metaData) throws SQLException
{
int size = metaData.getColumnCount();
SqlType[] sqlTypes = new SqlType[size];
for (int i = 0; i < sqlTypes.length; i++)
sqlTypes[i] = getColumnSqlType(metaData, i + 1);
return sqlTypes;
}
/**
* SQL插入操作的自动生成结果
*

View File

@ -6,7 +6,7 @@
<parent>
<groupId>org.datagear</groupId>
<artifactId>datagear</artifactId>
<version>1.11.1</version>
<version>1.12.0</version>
</parent>
<artifactId>datagear-web</artifactId>

View File

@ -17,6 +17,8 @@ import java.util.Map;
import javax.servlet.Filter;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.cometd.bayeux.server.BayeuxServer;
import org.cometd.bayeux.server.BayeuxServer.Extension;
import org.cometd.server.ext.AcknowledgedMessagesExtension;
@ -72,6 +74,7 @@ import org.datagear.persistence.PersistenceManager;
import org.datagear.persistence.support.DefaultDialectSource;
import org.datagear.persistence.support.DefaultPersistenceManager;
import org.datagear.persistence.support.SqlSelectManager;
import org.datagear.util.IOUtil;
import org.datagear.web.cometd.CustomJacksonJSONContextServer;
import org.datagear.web.cometd.dataexchange.DataExchangeCometdService;
import org.datagear.web.convert.CustomFormattingConversionServiceFactoryBean;
@ -193,6 +196,7 @@ public class CoreConfiguration implements InitializingBean
{
ResourceBundleMessageSource bean = new ResourceBundleMessageSource();
bean.setBasename("org.datagear.web.locales.datagear");
bean.setDefaultEncoding(IOUtil.CHARSET_UTF_8);
return bean;
}
@ -233,6 +237,12 @@ public class CoreConfiguration implements InitializingBean
return createDirectory(environment.getProperty("directory.dataSet"), true);
}
@Bean
public CloseableHttpClient httpClient()
{
return HttpClients.createDefault();
}
protected File createDirectory(String directoryName, boolean createIfInexistence) throws IOException
{
DirectoryFactory bean = new DirectoryFactory();
@ -390,8 +400,8 @@ public class CoreConfiguration implements InitializingBean
public DataSetEntityService dataSetEntityService() throws Exception
{
DataSetEntityServiceImpl bean = new DataSetEntityServiceImpl(this.sqlSessionFactory().getObject(),
this.connectionSource(), this.schemaService(), this.authorizationService(),
this.dataSetRootDirectory());
this.connectionSource(), this.schemaService(), this.authorizationService(), this.dataSetRootDirectory(),
this.httpClient());
return bean;
}

View File

@ -11,18 +11,24 @@ import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.datagear.analysis.Category;
import org.datagear.analysis.Chart;
import org.datagear.analysis.ChartDataSet;
import org.datagear.analysis.ChartDefinition;
import org.datagear.analysis.DashboardTheme;
import org.datagear.analysis.DataSet;
import org.datagear.analysis.DataSetException;
import org.datagear.analysis.DataSetResult;
import org.datagear.analysis.DataSign;
import org.datagear.analysis.Icon;
import org.datagear.analysis.RenderContext;
import org.datagear.analysis.RenderException;
import org.datagear.analysis.support.AbstractChartPlugin;
import org.datagear.analysis.support.AbstractDataSet;
import org.datagear.analysis.support.CategorizationResolver;
import org.datagear.analysis.support.CategorizationResolver.Categorization;
import org.datagear.analysis.support.html.DirectoryHtmlChartPluginManager;
@ -32,6 +38,8 @@ import org.datagear.web.util.KeywordMatcher;
import org.datagear.web.util.WebUtils;
import org.springframework.beans.factory.annotation.Autowired;
import com.fasterxml.jackson.annotation.JsonIgnore;
/**
* 抽象插件相关的控制器
*
@ -214,6 +222,19 @@ public class AbstractChartPluginAwareController extends AbstractDataAnalysisCont
return "/analysis/chartPlugin/icon/" + plugin.getId();
}
protected ChartDataSetViewObj[] toChartDataSetViewObjs(ChartDataSet[] chartDataSets)
{
if (chartDataSets == null)
return null;
ChartDataSetViewObj[] viewObjs = new ChartDataSetViewObj[chartDataSets.length];
for (int i = 0; i < chartDataSets.length; i++)
viewObjs[i] = new ChartDataSetViewObj(chartDataSets[i]);
return viewObjs;
}
/**
* {@linkplain HtmlChartPlugin}视图对象
*
@ -264,4 +285,70 @@ public class AbstractChartPluginAwareController extends AbstractDataAnalysisCont
throw new UnsupportedOperationException();
}
}
/**
* {@linkplain DataSet}视图对象
*
* @author datagear@163.com
*
*/
public static class DataSetViewObj extends AbstractDataSet
{
public DataSetViewObj()
{
super();
}
public DataSetViewObj(DataSet dataSet)
{
super();
setId(dataSet.getId());
setName(dataSet.getName());
setProperties(dataSet.getProperties());
setParams(dataSet.getParams());
}
@Override
public DataSetResult getResult(Map<String, ?> paramValues) throws DataSetException
{
throw new UnsupportedOperationException();
}
}
/**
* {@linkplain ChartDataSet}视图对象
*
* @author datagear@163.com
*
*/
public static class ChartDataSetViewObj extends ChartDataSet
{
public ChartDataSetViewObj()
{
super();
}
public ChartDataSetViewObj(ChartDataSet chartDataSet)
{
super();
setDataSet(new DataSetViewObj(chartDataSet.getDataSet()));
setPropertySigns(chartDataSet.getPropertySigns());
setAlias(chartDataSet.getAlias());
setParamValues(chartDataSet.getParamValues());
}
@JsonIgnore
@Override
public boolean isResultReady()
{
return false;
}
@JsonIgnore
@Override
public DataSetResult getResult()
{
return null;
}
}
}

View File

@ -113,8 +113,8 @@ public abstract class AbstractController
* 检查并补充{@linkplain DataFilterPagingQuery#getDataFilter()}
*
* @param request
* @param pagingQuery
* @return
* @param pagingQuery 允许为{@code null}
* @return 不会为{@code null}
*/
protected DataFilterPagingQuery inflateDataFilterPagingQuery(HttpServletRequest request,
DataFilterPagingQuery pagingQuery)
@ -126,14 +126,20 @@ public abstract class AbstractController
* 检查并补充{@linkplain DataFilterPagingQuery#getDataFilter()}
*
* @param request
* @param pagingQuery
* @param pagingQuery 允许为{@code null}
* @param cookiePaginationSize
* @return
* @return 不会为{@code null}
*/
protected DataFilterPagingQuery inflateDataFilterPagingQuery(HttpServletRequest request,
DataFilterPagingQuery pagingQuery, String cookiePaginationSize)
{
inflatePagingQuery(request, pagingQuery, cookiePaginationSize);
PagingQuery pq = inflatePagingQuery(request, pagingQuery, cookiePaginationSize);
if (pagingQuery == null)
{
pagingQuery = new DataFilterPagingQuery(pq.getPage(), pq.getPageSize(), pq.getKeyword(), pq.getCondition());
pagingQuery.setNotLike(pq.isNotLike());
}
String value = pagingQuery.getDataFilter();
@ -187,8 +193,8 @@ public abstract class AbstractController
* 检查并补充{@linkplain PagingQuery}
*
* @param request
* @param pagingQuery
* @return
* @param pagingQuery 允许为{@code null}
* @return 不会为{@code null}
*/
protected PagingQuery inflatePagingQuery(HttpServletRequest request, PagingQuery pagingQuery)
{
@ -199,18 +205,15 @@ public abstract class AbstractController
* 检查并补充{@linkplain PagingQuery}
*
* @param request
* @param pagingQuery
* @param cookiePaginationSize
* 允许为{@code null}
* @return
* @param pagingQuery 允许为{@code null}
* @param cookiePaginationSize 允许为{@code null}
* @return 不会为{@code null}
*/
protected PagingQuery inflatePagingQuery(HttpServletRequest request, PagingQuery pagingQuery,
String cookiePaginationSize)
{
if (pagingQuery != null)
return pagingQuery;
pagingQuery = new PagingQuery();
if (pagingQuery == null)
pagingQuery = new PagingQuery();
if (!isEmpty(cookiePaginationSize))
{

View File

@ -275,13 +275,30 @@ public abstract class AbstractDataAnalysisController extends AbstractController
}
@SuppressWarnings("unchecked")
protected void addHeartBeatValue(WebContext webContext)
protected void addHeartBeatValue(HttpServletRequest request, WebContext webContext)
{
String heartbeatURL = webContext.getContextPath() + "/analysis/dashboard/heartbeat";
heartbeatURL = addJsessionidParam(heartbeatURL, request.getSession().getId());
((Map<String, Object>) webContext.getExtraValues()).put(DASHBOARD_HEARTBEAT_URL_NAME, heartbeatURL);
}
/**
* 为指定URL添加会话ID参数
* <p>
* 图表看板展示页可能会以&lt;iframe&gt;的方式嵌入外部网页中当在跨域场景时某些浏览器会禁用&lt;iframe&gt;内的cookie导致会话无法保持
* 从而引起图表看板内的ajax请求失效此方法可以解决上述问题
* </p>
*
* @param url
* @param sessionId
* @return
*/
protected String addJsessionidParam(String url, String sessionId)
{
return WebUtils.addJsessionidParam(url, sessionId);
}
protected static class SessionHtmlTplDashboardManager implements Serializable
{
private static final long serialVersionUID = 1L;

View File

@ -14,6 +14,7 @@ import java.util.Map;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.datagear.analysis.ChartPluginManager;
import org.datagear.analysis.DataSetParam;
@ -153,7 +154,7 @@ public class ChartController extends AbstractChartPluginAwareController implemen
model.addAttribute("chart", chart);
model.addAttribute("chartPluginVO", toWriteJsonTemplateModel(chartPluginVO));
model.addAttribute("chartDataSets", toWriteJsonTemplateModel(chart.getChartDataSets()));
model.addAttribute("chartDataSets", toWriteJsonTemplateModel(toChartDataSetViewObjs(chart.getChartDataSets())));
model.addAttribute(KEY_TITLE_MESSAGE_KEY, "chart.editChart");
model.addAttribute(KEY_FORM_ACTION, "save");
@ -208,7 +209,7 @@ public class ChartController extends AbstractChartPluginAwareController implemen
model.addAttribute("chart", chart);
model.addAttribute("chartPluginVO", toWriteJsonTemplateModel(chartPluginVO));
model.addAttribute("chartDataSets", toWriteJsonTemplateModel(chart.getChartDataSets()));
model.addAttribute("chartDataSets", toWriteJsonTemplateModel(toChartDataSetViewObjs(chart.getChartDataSets())));
model.addAttribute(KEY_TITLE_MESSAGE_KEY, "chart.viewChart");
model.addAttribute(KEY_READONLY, true);
@ -397,12 +398,15 @@ public class ChartController extends AbstractChartPluginAwareController implemen
protected WebContext createWebContext(HttpServletRequest request)
{
HttpSession session = request.getSession();
String contextPath = getWebContextPath(request).get(request);
WebContext webContext = new WebContext(contextPath, contextPath + "/analysis/chart/showData",
contextPath + "/analysis/dashboard/loadChart");
WebContext webContext = new WebContext(contextPath,
addJsessionidParam(contextPath + "/analysis/chart/showData", session.getId()),
addJsessionidParam(contextPath + "/analysis/dashboard/loadChart", session.getId()));
webContext.setExtraValues(new HashMap<String, Object>());
addHeartBeatValue(webContext);
addHeartBeatValue(request, webContext);
return webContext;
}

View File

@ -9,7 +9,10 @@ import java.sql.SQLException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.datagear.analysis.DataSetException;
import org.datagear.analysis.support.DataSetSourceParseException;
import org.datagear.analysis.support.HeaderContentNotNameValueObjArrayJsonException;
import org.datagear.analysis.support.RequestContentNotNameValueObjArrayJsonException;
import org.datagear.analysis.support.SqlDataSetConnectionException;
import org.datagear.analysis.support.SqlDataSetSqlExecutionException;
import org.datagear.analysis.support.SqlDataSetUnsupportedSqlTypeException;
@ -449,8 +452,8 @@ public class ControllerAdvice extends AbstractController
@ExceptionHandler(SqlDataSetConnectionException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public String handleAnalysisSqlDataSetConnectionException(HttpServletRequest request,
HttpServletResponse response, SqlDataSetConnectionException exception)
public String handleAnalysisSqlDataSetConnectionException(HttpServletRequest request, HttpServletResponse response,
SqlDataSetConnectionException exception)
{
setOperationMessageForThrowable(request, buildMessageCode(SqlDataSetConnectionException.class), exception,
false, exception.getMessage());
@ -458,6 +461,39 @@ public class ControllerAdvice extends AbstractController
return getErrorView(request, response);
}
@ExceptionHandler(RequestContentNotNameValueObjArrayJsonException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public String handleAnalysisRequestContentNotNameValueObjArrayJsonException(HttpServletRequest request,
HttpServletResponse response, RequestContentNotNameValueObjArrayJsonException exception)
{
setOperationMessageForThrowable(request,
buildMessageCode(RequestContentNotNameValueObjArrayJsonException.class), exception, false);
return getErrorView(request, response);
}
@ExceptionHandler(HeaderContentNotNameValueObjArrayJsonException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public String handleAnalysisHeaderContentNotNameValueObjArrayJsonException(HttpServletRequest request,
HttpServletResponse response, HeaderContentNotNameValueObjArrayJsonException exception)
{
setOperationMessageForThrowable(request, buildMessageCode(HeaderContentNotNameValueObjArrayJsonException.class),
exception, false);
return getErrorView(request, response);
}
@ExceptionHandler(DataSetException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public String handleAnalysisDataSetException(HttpServletRequest request, HttpServletResponse response,
DataSetException exception)
{
setOperationMessageForThrowable(request, buildMessageCode(DataSetException.class), exception, true,
exception.getMessage());
return getErrorView(request, response);
}
@ExceptionHandler(SaveSchemaUrlPermissionDeniedException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public String handleServicePermissionDeniedException(HttpServletRequest request, HttpServletResponse response,

View File

@ -20,6 +20,7 @@ import java.util.zip.ZipInputStream;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.datagear.analysis.Chart;
import org.datagear.analysis.DataSetResult;
@ -898,12 +899,15 @@ public class DashboardController extends AbstractDataAnalysisController implemen
protected WebContext createWebContext(HttpServletRequest request)
{
HttpSession session = request.getSession();
String contextPath = getWebContextPath(request).get(request);
WebContext webContext = new WebContext(contextPath, contextPath + "/analysis/dashboard/showData",
contextPath + "/analysis/dashboard/loadChart");
WebContext webContext = new WebContext(contextPath,
addJsessionidParam(contextPath + "/analysis/dashboard/showData", session.getId()),
addJsessionidParam(contextPath + "/analysis/dashboard/loadChart", session.getId()));
webContext.setExtraValues(new HashMap<String, Object>());
addHeartBeatValue(webContext);
addHeartBeatValue(request, webContext);
return webContext;
}

View File

@ -7,6 +7,7 @@ package org.datagear.web.controller;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@ -20,15 +21,20 @@ import javax.servlet.http.HttpServletResponse;
import org.datagear.analysis.DataSet;
import org.datagear.analysis.DataSetParam;
import org.datagear.analysis.ResolvedDataSetResult;
import org.datagear.analysis.support.AbstractFmkTemplateDataSet;
import org.datagear.analysis.support.AbstractDataSet;
import org.datagear.analysis.support.CsvValueDataSet;
import org.datagear.analysis.support.DataSetFmkTemplateResolver;
import org.datagear.analysis.support.DataSetParamValueConverter;
import org.datagear.analysis.support.JsonDirectoryFileDataSet;
import org.datagear.analysis.support.JsonValueDataSet;
import org.datagear.analysis.support.SqlDataSet;
import org.datagear.analysis.support.TemplateContext;
import org.datagear.analysis.support.TemplateResolvedDataSetResult;
import org.datagear.management.domain.CsvFileDataSetEntity;
import org.datagear.management.domain.CsvValueDataSetEntity;
import org.datagear.management.domain.DataSetEntity;
import org.datagear.management.domain.DirectoryFileDataSetEntity;
import org.datagear.management.domain.ExcelDataSetEntity;
import org.datagear.management.domain.HttpDataSetEntity;
import org.datagear.management.domain.JsonFileDataSetEntity;
import org.datagear.management.domain.JsonValueDataSetEntity;
import org.datagear.management.domain.Schema;
@ -194,7 +200,130 @@ public class DataSetController extends AbstractSchemaConnController
checkSaveJsonFileDataSetEntity(dataSet);
this.dataSetEntityService.add(user, dataSet);
copyToJsonFileDataSetEntityDirectoryIf(dataSet, "");
copyToDirectoryFileDataSetEntityDirectoryIf(dataSet, "");
return buildOperationMessageSaveSuccessResponseEntity(request, dataSet);
}
@RequestMapping("/addForExcel")
public String addForExcel(HttpServletRequest request, org.springframework.ui.Model model)
{
ExcelDataSetEntity dataSet = new ExcelDataSetEntity();
dataSet.setNameRow(1);
model.addAttribute("dataSet", dataSet);
model.addAttribute(KEY_TITLE_MESSAGE_KEY, "dataSet.addDataSet");
model.addAttribute(KEY_FORM_ACTION, "saveAddForExcel");
return buildFormView(dataSet.getDataSetType());
}
@RequestMapping(value = "/saveAddForExcel", produces = CONTENT_TYPE_JSON)
@ResponseBody
public ResponseEntity<OperationMessage> saveAddForExcel(HttpServletRequest request, HttpServletResponse response,
@RequestBody ExcelDataSetEntity dataSet) throws Throwable
{
User user = WebUtils.getUser(request, response);
dataSet.setId(IDUtil.randomIdOnTime20());
dataSet.setCreateUser(User.copyWithoutPassword(user));
checkSaveExcelDataSetEntity(dataSet);
this.dataSetEntityService.add(user, dataSet);
copyToDirectoryFileDataSetEntityDirectoryIf(dataSet, "");
return buildOperationMessageSaveSuccessResponseEntity(request, dataSet);
}
@RequestMapping("/addForCsvValue")
public String addForCsvValue(HttpServletRequest request, org.springframework.ui.Model model)
{
CsvValueDataSetEntity dataSet = new CsvValueDataSetEntity();
dataSet.setNameRow(1);
model.addAttribute("dataSet", dataSet);
model.addAttribute(KEY_TITLE_MESSAGE_KEY, "dataSet.addDataSet");
model.addAttribute(KEY_FORM_ACTION, "saveAddForCsvValue");
return buildFormView(dataSet.getDataSetType());
}
@RequestMapping(value = "/saveAddForCsvValue", produces = CONTENT_TYPE_JSON)
@ResponseBody
public ResponseEntity<OperationMessage> saveAddForCsvValue(HttpServletRequest request, HttpServletResponse response,
@RequestBody CsvValueDataSetEntity dataSet)
{
User user = WebUtils.getUser(request, response);
dataSet.setId(IDUtil.randomIdOnTime20());
dataSet.setCreateUser(User.copyWithoutPassword(user));
checkSaveCsvValueDataSetEntity(dataSet);
this.dataSetEntityService.add(user, dataSet);
return buildOperationMessageSaveSuccessResponseEntity(request, dataSet);
}
@RequestMapping("/addForCsvFile")
public String addForCsvFile(HttpServletRequest request, org.springframework.ui.Model model)
{
CsvFileDataSetEntity dataSet = new CsvFileDataSetEntity();
dataSet.setNameRow(1);
model.addAttribute("dataSet", dataSet);
model.addAttribute("availableCharsetNames", getAvailableCharsetNames());
model.addAttribute(KEY_TITLE_MESSAGE_KEY, "dataSet.addDataSet");
model.addAttribute(KEY_FORM_ACTION, "saveAddForCsvFile");
return buildFormView(dataSet.getDataSetType());
}
@RequestMapping(value = "/saveAddForCsvFile", produces = CONTENT_TYPE_JSON)
@ResponseBody
public ResponseEntity<OperationMessage> saveAddForCsvFile(HttpServletRequest request, HttpServletResponse response,
@RequestBody CsvFileDataSetEntity dataSet) throws Throwable
{
User user = WebUtils.getUser(request, response);
dataSet.setId(IDUtil.randomIdOnTime20());
dataSet.setCreateUser(User.copyWithoutPassword(user));
checkSaveCsvFileDataSetEntity(dataSet);
this.dataSetEntityService.add(user, dataSet);
copyToDirectoryFileDataSetEntityDirectoryIf(dataSet, "");
return buildOperationMessageSaveSuccessResponseEntity(request, dataSet);
}
@RequestMapping("/addForHttp")
public String addForHttp(HttpServletRequest request, org.springframework.ui.Model model)
{
HttpDataSetEntity dataSet = new HttpDataSetEntity();
model.addAttribute("dataSet", dataSet);
model.addAttribute("availableCharsetNames", getAvailableCharsetNames());
model.addAttribute(KEY_TITLE_MESSAGE_KEY, "dataSet.addDataSet");
model.addAttribute(KEY_FORM_ACTION, "saveAddForHttp");
return buildFormView(dataSet.getDataSetType());
}
@RequestMapping(value = "/saveAddForHttp", produces = CONTENT_TYPE_JSON)
@ResponseBody
public ResponseEntity<OperationMessage> saveAddForHttp(HttpServletRequest request, HttpServletResponse response,
@RequestBody HttpDataSetEntity dataSet)
{
User user = WebUtils.getUser(request, response);
dataSet.setId(IDUtil.randomIdOnTime20());
dataSet.setCreateUser(User.copyWithoutPassword(user));
checkSaveHttpDataSetEntity(dataSet);
this.dataSetEntityService.add(user, dataSet);
return buildOperationMessageSaveSuccessResponseEntity(request, dataSet);
}
@ -216,7 +345,9 @@ public class DataSetController extends AbstractSchemaConnController
model.addAttribute(KEY_TITLE_MESSAGE_KEY, "dataSet.editDataSet");
model.addAttribute(KEY_FORM_ACTION, "saveEditFor" + dataSet.getDataSetType());
if (DataSetEntity.DATA_SET_TYPE_JsonFile.equals(dataSet.getDataSetType()))
if (DataSetEntity.DATA_SET_TYPE_JsonFile.equals(dataSet.getDataSetType())
|| DataSetEntity.DATA_SET_TYPE_CsvFile.equals(dataSet.getDataSetType())
|| DataSetEntity.DATA_SET_TYPE_Http.equals(dataSet.getDataSetType()))
model.addAttribute("availableCharsetNames", getAvailableCharsetNames());
return buildFormView(dataSet.getDataSetType());
@ -261,7 +392,67 @@ public class DataSetController extends AbstractSchemaConnController
checkSaveJsonFileDataSetEntity(dataSet);
this.dataSetEntityService.update(user, dataSet);
copyToJsonFileDataSetEntityDirectoryIf(dataSet, originalFileName);
copyToDirectoryFileDataSetEntityDirectoryIf(dataSet, originalFileName);
return buildOperationMessageSaveSuccessResponseEntity(request, dataSet);
}
@RequestMapping(value = "/saveEditFor" + DataSetEntity.DATA_SET_TYPE_Excel, produces = CONTENT_TYPE_JSON)
@ResponseBody
public ResponseEntity<OperationMessage> saveEditForExcel(HttpServletRequest request, HttpServletResponse response,
@RequestBody ExcelDataSetEntity dataSet, @RequestParam("originalFileName") String originalFileName)
throws Throwable
{
User user = WebUtils.getUser(request, response);
checkSaveExcelDataSetEntity(dataSet);
this.dataSetEntityService.update(user, dataSet);
copyToDirectoryFileDataSetEntityDirectoryIf(dataSet, originalFileName);
return buildOperationMessageSaveSuccessResponseEntity(request, dataSet);
}
@RequestMapping(value = "/saveEditFor" + DataSetEntity.DATA_SET_TYPE_CsvValue, produces = CONTENT_TYPE_JSON)
@ResponseBody
public ResponseEntity<OperationMessage> saveEditForCsvValue(HttpServletRequest request,
HttpServletResponse response, @RequestBody CsvValueDataSetEntity dataSet)
{
User user = WebUtils.getUser(request, response);
checkSaveCsvValueDataSetEntity(dataSet);
this.dataSetEntityService.update(user, dataSet);
return buildOperationMessageSaveSuccessResponseEntity(request, dataSet);
}
@RequestMapping(value = "/saveEditFor" + DataSetEntity.DATA_SET_TYPE_CsvFile, produces = CONTENT_TYPE_JSON)
@ResponseBody
public ResponseEntity<OperationMessage> saveEditForCsvFile(HttpServletRequest request, HttpServletResponse response,
@RequestBody CsvFileDataSetEntity dataSet, @RequestParam("originalFileName") String originalFileName)
throws Throwable
{
User user = WebUtils.getUser(request, response);
checkSaveCsvFileDataSetEntity(dataSet);
this.dataSetEntityService.update(user, dataSet);
copyToDirectoryFileDataSetEntityDirectoryIf(dataSet, originalFileName);
return buildOperationMessageSaveSuccessResponseEntity(request, dataSet);
}
@RequestMapping(value = "/saveEditFor" + DataSetEntity.DATA_SET_TYPE_Http, produces = CONTENT_TYPE_JSON)
@ResponseBody
public ResponseEntity<OperationMessage> saveEditForHttp(HttpServletRequest request, HttpServletResponse response,
@RequestBody HttpDataSetEntity dataSet)
{
User user = WebUtils.getUser(request, response);
checkSaveHttpDataSetEntity(dataSet);
this.dataSetEntityService.update(user, dataSet);
return buildOperationMessageSaveSuccessResponseEntity(request, dataSet);
}
@ -294,6 +485,35 @@ public class DataSetController extends AbstractSchemaConnController
return results;
}
@RequestMapping(value = "/downloadFile")
public void downloadFile(HttpServletRequest request, HttpServletResponse response, @RequestParam("id") String id)
throws Exception
{
User user = WebUtils.getUser(request, response);
DataSetEntity dataSet = this.dataSetEntityService.getById(user, id);
if (dataSet == null)
throw new RecordNotFoundException();
if (!(dataSet instanceof DirectoryFileDataSetEntity))
throw new IllegalInputException();
DirectoryFileDataSetEntity dataSetEntity = (DirectoryFileDataSetEntity) dataSet;
File dataSetDirectory = getDataSetEntityService().getDataSetDirectory(dataSetEntity.getId());
File entityFile = FileUtil.getFile(dataSetDirectory, dataSetEntity.getFileName());
String displayName = dataSetEntity.getDisplayName();
displayName = new String(displayName.getBytes("UTF-8"), "ISO-8859-1");
response.setCharacterEncoding("UTF-8");
response.setHeader("Content-Disposition", "attachment; filename=" + displayName + "");
OutputStream out = response.getOutputStream();
IOUtil.write(entityFile, out);
}
@RequestMapping("/view")
public String view(HttpServletRequest request, HttpServletResponse response, org.springframework.ui.Model model,
@RequestParam("id") String id)
@ -311,7 +531,9 @@ public class DataSetController extends AbstractSchemaConnController
model.addAttribute(KEY_TITLE_MESSAGE_KEY, "dataSet.viewDataSet");
model.addAttribute(KEY_READONLY, true);
if (DataSetEntity.DATA_SET_TYPE_JsonFile.equals(dataSet.getDataSetType()))
if (DataSetEntity.DATA_SET_TYPE_JsonFile.equals(dataSet.getDataSetType())
|| DataSetEntity.DATA_SET_TYPE_CsvFile.equals(dataSet.getDataSetType())
|| DataSetEntity.DATA_SET_TYPE_Http.equals(dataSet.getDataSetType()))
model.addAttribute("availableCharsetNames", getAvailableCharsetNames());
return buildFormView(dataSet.getDataSetType());
@ -463,8 +685,8 @@ public class DataSetController extends AbstractSchemaConnController
{
JsonValueDataSet dataSet = preview.getDataSet();
Map<String, Object> convertedParamValues = getDataSetParamValueConverter()
.convert(preview.getParamValues(), dataSet.getParams());
Map<String, Object> convertedParamValues = getDataSetParamValueConverter().convert(preview.getParamValues(),
dataSet.getParams());
TemplateResolvedDataSetResult result = dataSet.resolve(convertedParamValues);
@ -474,22 +696,85 @@ public class DataSetController extends AbstractSchemaConnController
@RequestMapping(value = "/previewJsonFile", produces = CONTENT_TYPE_JSON)
@ResponseBody
public ResolvedDataSetResult previewJsonFile(HttpServletRequest request, HttpServletResponse response,
org.springframework.ui.Model springModel, @RequestBody JsonDirectoryFileDataSetPreview preview)
org.springframework.ui.Model springModel, @RequestBody JsonFileDataSetEntityPreview preview)
throws Throwable
{
JsonDirectoryFileDataSet dataSet = preview.getDataSet();
setJsonDirectoryFileDataSetDirectory(dataSet, preview.getOriginalFileName());
JsonFileDataSetEntity dataSet = preview.getDataSet();
setDirectoryFileDataSetDirectory(dataSet, preview.getOriginalFileName());
Map<String, Object> convertedParamValues = getDataSetParamValueConverter()
.convert(preview.getParamValues(), dataSet.getParams());
Map<String, Object> convertedParamValues = getDataSetParamValueConverter().convert(preview.getParamValues(),
dataSet.getParams());
ResolvedDataSetResult result = dataSet.resolve(convertedParamValues);
return result;
}
protected boolean copyToJsonFileDataSetEntityDirectoryIf(JsonFileDataSetEntity entity, String originalFileName)
throws IOException
@RequestMapping(value = "/previewExcel", produces = CONTENT_TYPE_JSON)
@ResponseBody
public ResolvedDataSetResult previewExcel(HttpServletRequest request, HttpServletResponse response,
org.springframework.ui.Model springModel, @RequestBody ExcelDataSetEntityPreview preview) throws Throwable
{
ExcelDataSetEntity dataSet = preview.getDataSet();
setDirectoryFileDataSetDirectory(dataSet, preview.getOriginalFileName());
Map<String, Object> convertedParamValues = getDataSetParamValueConverter().convert(preview.getParamValues(),
dataSet.getParams());
ResolvedDataSetResult result = dataSet.resolve(convertedParamValues);
return result;
}
@RequestMapping(value = "/previewCsvValue", produces = CONTENT_TYPE_JSON)
@ResponseBody
public TemplateResolvedDataSetResult previewCsvValue(HttpServletRequest request, HttpServletResponse response,
org.springframework.ui.Model springModel, @RequestBody CsvValueDataSetPreview preview) throws Throwable
{
CsvValueDataSet dataSet = preview.getDataSet();
Map<String, Object> convertedParamValues = getDataSetParamValueConverter().convert(preview.getParamValues(),
dataSet.getParams());
TemplateResolvedDataSetResult result = dataSet.resolve(convertedParamValues);
return result;
}
@RequestMapping(value = "/previewCsvFile", produces = CONTENT_TYPE_JSON)
@ResponseBody
public ResolvedDataSetResult previewCsvFile(HttpServletRequest request, HttpServletResponse response,
org.springframework.ui.Model springModel, @RequestBody CsvFileDataSetEntityPreview preview) throws Throwable
{
CsvFileDataSetEntity dataSet = preview.getDataSet();
setDirectoryFileDataSetDirectory(dataSet, preview.getOriginalFileName());
Map<String, Object> convertedParamValues = getDataSetParamValueConverter().convert(preview.getParamValues(),
dataSet.getParams());
ResolvedDataSetResult result = dataSet.resolve(convertedParamValues);
return result;
}
@RequestMapping(value = "/previewHttp", produces = CONTENT_TYPE_JSON)
@ResponseBody
public TemplateResolvedDataSetResult previewHttp(HttpServletRequest request, HttpServletResponse response,
org.springframework.ui.Model springModel, @RequestBody HttpDataSetEntityPreview preview) throws Throwable
{
HttpDataSetEntity dataSet = preview.getDataSet();
dataSet.setHttpClient(getDataSetEntityService().getHttpClient());
Map<String, Object> convertedParamValues = getDataSetParamValueConverter().convert(preview.getParamValues(),
dataSet.getParams());
TemplateResolvedDataSetResult result = dataSet.resolve(convertedParamValues);
return result;
}
protected boolean copyToDirectoryFileDataSetEntityDirectoryIf(DirectoryFileDataSetEntity entity,
String originalFileName) throws IOException
{
String fileName = entity.getFileName();
@ -510,7 +795,7 @@ public class DataSetController extends AbstractSchemaConnController
return true;
}
protected void setJsonDirectoryFileDataSetDirectory(JsonDirectoryFileDataSet dataSet, String originalFileName)
protected void setDirectoryFileDataSetDirectory(DirectoryFileDataSetEntity dataSet, String originalFileName)
{
String fileName = dataSet.getFileName();
@ -538,7 +823,7 @@ public class DataSetController extends AbstractSchemaConnController
protected DataSetFmkTemplateResolver getDataSetFmkTemplateResolver()
{
return AbstractFmkTemplateDataSet.TEMPLATE_RESOLVER;
return AbstractDataSet.FMK_TEMPLATE_RESOLVER;
}
protected void checkSaveEntity(DataSetEntity dataSet)
@ -586,6 +871,44 @@ public class DataSetController extends AbstractSchemaConnController
throw new IllegalInputException();
}
protected void checkSaveExcelDataSetEntity(ExcelDataSetEntity dataSet)
{
checkSaveEntity(dataSet);
if (isEmpty(dataSet.getFileName()))
throw new IllegalInputException();
if (isEmpty(dataSet.getDisplayName()))
throw new IllegalInputException();
}
protected void checkSaveCsvValueDataSetEntity(CsvValueDataSetEntity dataSet)
{
checkSaveEntity(dataSet);
if (isEmpty(dataSet.getValue()))
throw new IllegalInputException();
}
protected void checkSaveCsvFileDataSetEntity(CsvFileDataSetEntity dataSet)
{
checkSaveEntity(dataSet);
if (isEmpty(dataSet.getFileName()))
throw new IllegalInputException();
if (isEmpty(dataSet.getDisplayName()))
throw new IllegalInputException();
}
protected void checkSaveHttpDataSetEntity(HttpDataSetEntity dataSet)
{
checkSaveEntity(dataSet);
if (isEmpty(dataSet.getUri()))
throw new IllegalInputException();
}
public static class AbstractDataSetPreview<T extends DataSet>
{
private T dataSet;
@ -641,35 +964,17 @@ public class DataSetController extends AbstractSchemaConnController
public static class JsonValueDataSetPreview extends AbstractDataSetPreview<JsonValueDataSet>
{
private String value;
public JsonValueDataSetPreview()
{
super();
}
public JsonValueDataSetPreview(String value)
{
super();
this.value = value;
}
public String getValue()
{
return value;
}
public void setValue(String value)
{
this.value = value;
}
}
public static class JsonDirectoryFileDataSetPreview extends AbstractDataSetPreview<JsonDirectoryFileDataSet>
public static class JsonFileDataSetEntityPreview extends AbstractDataSetPreview<JsonFileDataSetEntity>
{
private String originalFileName;
public JsonDirectoryFileDataSetPreview()
public JsonFileDataSetEntityPreview()
{
super();
}
@ -685,6 +990,62 @@ public class DataSetController extends AbstractSchemaConnController
}
}
public static class ExcelDataSetEntityPreview extends AbstractDataSetPreview<ExcelDataSetEntity>
{
private String originalFileName;
public ExcelDataSetEntityPreview()
{
super();
}
public String getOriginalFileName()
{
return originalFileName;
}
public void setOriginalFileName(String originalFileName)
{
this.originalFileName = originalFileName;
}
}
public static class CsvValueDataSetPreview extends AbstractDataSetPreview<CsvValueDataSet>
{
public CsvValueDataSetPreview()
{
super();
}
}
public static class CsvFileDataSetEntityPreview extends AbstractDataSetPreview<CsvFileDataSetEntity>
{
private String originalFileName;
public CsvFileDataSetEntityPreview()
{
super();
}
public String getOriginalFileName()
{
return originalFileName;
}
public void setOriginalFileName(String originalFileName)
{
this.originalFileName = originalFileName;
}
}
public static class HttpDataSetEntityPreview extends AbstractDataSetPreview<HttpDataSetEntity>
{
public HttpDataSetEntityPreview()
{
super();
}
}
public static class ResolveSqlParam
{
private String sql;

View File

@ -37,6 +37,11 @@ public class WebUtils
public static final String COOKIE_PAGINATION_SIZE = "PAGINATION_PAGE_SIZE";
/**
* Servlet规范中参数会话ID名称当客户端不支持cookie时则应使用此参数传递会话ID格式为/aa/bb;jsessionid=[id]
*/
public static final String PARAM_JSESSIONID = "jsessionid";
/** Servlet环境中存储操作消息的关键字 */
public static final String KEY_OPERATION_MESSAGE = "operationMessage";
@ -189,7 +194,8 @@ public class WebUtils
* @param name
* @param value
* @param age
* @param path {@code null}时则设置为应用根路径
* @param path
* {@code null}时则设置为应用根路径
*/
public static void setCookie(HttpServletRequest request, HttpServletResponse response, String name, String value,
int age, String path)
@ -503,4 +509,19 @@ public class WebUtils
return sb.toString();
}
/**
* 为指定URL添加{@linkplain #PARAM_JSESSIONID}参数
* <p>
* 当要保持会话而客户端不支持cookie时应使用此方法为URL添加会话ID参数
* </p>
*
* @param url
* @param sessionId
* @return
*/
public static String addJsessionidParam(String url, String sessionId)
{
return url + ";" + PARAM_JSESSIONID + "=" + sessionId;
}
}

View File

@ -249,3 +249,26 @@ DataGear版本更新日志
改进:匿名用户创建的数据集、图表、看板在登录后将被迁移至登录用户;
改进SQL数据集预览SQL出错时给出友好提示
改进:导入看板模板文件不存在时给出友好提示;
-----------------------------------------
--v1.12.0
-----------------------------------------
新增新增CSV文本、CSV文件数据集
新增新增Excel数据集
新增新增HTTP接口数据集
修复修复地图图表在有缩放比例或拖动时刷新地图会出现空白的BUG
修复修复图表、看板在嵌入外部网页的iframe时对于禁用跨域cookie的浏览器无法正常展示的BUG
修复修复数据集参数设置表单的年份输入框在焦点移开后无法保存新设置年份的BUG
修复修复数据集预览、表格图表展示时没有处理XSS的问题
修复修复保存JSON文件数据集时编码未保存的BUG
改进:雷达图添加列式数据结构支持,可简化雷达图定义;
改进:图表联动设置支持数据属性路径,例如:{target: '...', data:{'column[0]': 0}}
改进:数据集参数设置表单的年份格式输入框改为专门的年份选择器;
改进改进SQL数据集的数据类型兼容性仅禁止查询二进制数据列
改进JSON文件数据集新增JSON数据路径设置项用于读取文件中指定JSON路径的数据而非整个文件
改进:文件类数据集表单页面新增下载文件功能;
改进:数据集预览时参数化语句解析结果改为通过按钮点击后显示浮动面板,避免影响页面布局;
改进:图表、数据集页面日期类的参数输入框禁用浏览器自动补全功能,避免遮挡日期选择器;
改进:看板编辑页编辑区最大化按钮移至底部;

View File

@ -31,6 +31,7 @@
<value>org.datagear.web.locales.datagear</value>
</list>
</property>
<property name="defaultEncoding" value="UTF-8" />
</bean>
<bean id="driverEntityManagerRootDirectoryFactory" class="org.datagear.web.util.DirectoryFactory" init-method="init">
@ -225,12 +226,15 @@
</property>
</bean>
<bean id="httpClient" class="org.apache.hc.client5.http.impl.classic.HttpClients" factory-method="createDefault" />
<bean id="dataSetEntityService" class="org.datagear.management.service.impl.DataSetEntityServiceImpl">
<property name="sqlSessionFactory" ref="sqlSessionFactory" />
<property name="connectionSource" ref="connectionSource" />
<property name="schemaService" ref="schemaService" />
<property name="authorizationService" ref="authorizationService" />
<property name="dataSetRootDirectory" ref="dataSetRootDirectory" />
<property name="httpClient" ref="httpClient" />
</bean>
<bean id="directoryHtmlChartPluginManager" class="org.datagear.analysis.support.html.DirectoryHtmlChartPluginManager">

View File

@ -1,7 +1,7 @@
#--UTF-8 file--
#系统版本号
version=1.11.1
version=1.12.0
#构建日期
buildDate=${buildtimestamp}

View File

@ -269,6 +269,17 @@
-webkit-transform:rotate(90deg);
-o-transform:rotate(90deg);
}
.xdsoft_datetimepicker .xdsoft_save_selected.xdsoft_save_selected_year{
width: auto;
padding: 0.3em 1em;
margin-top: 3px;
margin-right: 0.6em;
float: right;
border-bottom-right-radius: 3px;
border-bottom-left-radius: 3px;
border-top-right-radius: 3px;
border-top-left-radius: 3px;
}
/*看板表单*/
.dg-dashboard-form.dg-dspv-form,

View File

@ -97,6 +97,18 @@ form .form-content .form-item .form-item-label label{
width: 95%;
overflow: hidden;
}
form .form-content .form-item .form-item-label label[title]{
position: relative;
}
form .form-content .form-item .form-item-label label[title]::after{
content: "?";
position: absolute;
margin-left: 0.2em;
font-size: 0.6em;
top: 0.3em;
opacity: 0.5;
filter: Alpha(Opacity=50);
}
form .form-content .form-item .form-item-value{
display: inline-block;
width: 80%;
@ -146,7 +158,21 @@ form .form-content .form-item .input-desc{
display: inline-block;
}
form .form-content .form-item .input-desc-date{
}
form .form-content .form-item .form-item-value .form-item{
width: auto;
display: inline-block;
margin-left: 2em;
}
form .form-content .form-item .form-item-value .form-item .form-item-label{
width: auto;
}
form .form-content .form-item .form-item-value .form-item .form-item-label label{
width: auto;
}
form .form-content .form-item .form-item-value .form-item .form-item-value{
width: auto;
margin-left: 1em;
}
form .form-foot{
text-align: center;
@ -309,6 +335,33 @@ form .form-foot .ui-button{
font-size: small;
}
/*轻型选显卡*/
.ui-tabs.light-tabs{
margin: 0 0;
padding: 0 0;
}
.ui-tabs.light-tabs.ui-widget.ui-widget-content{
border: 0;
}
.ui-tabs.light-tabs .ui-tabs-nav{
border: 0;
background: none;
opacity: 0.8;
filter: Alpha(Opacity=80);
}
.ui-tabs.light-tabs .ui-tabs-nav .ui-tabs-anchor{
padding-top: 0.2em;
padding-bottom: 0.2em;
}
.ui-tabs.light-tabs .ui-tabs-panel{
position: absolute;
left: 0px;
right: 0px;
top: 2.2em;
bottom: 0px;
padding: 0 0;
}
/*jquery.layout*/
.ui-layout-pane{
border: 1px solid #c5c5c5;
@ -2111,68 +2164,92 @@ table.dataTable tbody tr td select{
}
.page-form-dataSet{}
.page-form-dataSet .workspace-editor-wrapper{
display: inline-block;
width: 50%;
.page-form-dataSet .workspace{
position: relative;
}
.page-form-dataSet .form-content{
overflow: auto;
}
.page-form-dataSet .workspace .form-item .form-item-label{
vertical-align:top;
margin-top: 0.2em;
}
.page-form-dataSet .workspace .form-item .form-item-value{
width: 39%;
position: relative;
padding-bottom: 1.4em;
}
.page-form-dataSet .workspace .form-item .form-item-value.no-padding-bottom{
padding-bottom: 0;
}
.page-form-dataSet .workspace .form-item .form-item-value input[type=text],
.page-form-dataSet .workspace .form-item .form-item-value input[type=password],
.page-form-dataSet .workspace .form-item .form-item-value textarea,
.page-form-dataSet .workspace .form-item .form-item-value .input{
width: 90%;
}
.page-form-dataSet .workspace .form-item .form-item-value input.file-display-name{
width: 60%;
}
.page-form-dataSet .workspace .form-item .form-item-value .fileinput-wrapper{
padding-top: 0.41em;
}
.page-form-dataSet .workspace .form-item label.error{
position: absolute;
left: 0;
bottom: 0;
white-space: nowrap;
}
.page-form-dataSet .workspace .workspace-editor-wrapper{
height: 10em;
margin-right: 0.3em;
}
.page-form-dataSet .workspace-editor-wrapper .workspace-editor{
.page-form-dataSet .workspace .workspace-editor-wrapper .workspace-editor{
width: 100%;
height: 100%;
}
.page-form-dataSet .workspace-operation-wrapper{
position: relative;
.page-form-dataSet .workspace .workspace-operation-wrapper{
position: absolute;
display: inline-block;
width: 48%;
width: 41%;
top: 0.3em;
right: 0;
height: 10em;
vertical-align: top;
}
.page-form-dataSet .workspace-operation-wrapper.ui-widget.ui-widget-content{
border: 0;
}
.page-form-dataSet .workspace-operation-wrapper.ui-tabs{
margin: 0 0;
padding: 0 0;
}
.page-form-dataSet .workspace-operation-wrapper.ui-tabs .ui-tabs-nav{
border: 0;
background: none;
opacity: 0.8;
filter: Alpha(Opacity=80);
}
.page-form-dataSet .workspace-operation-wrapper.ui-tabs .ui-tabs-nav .ui-tabs-anchor{
padding-top: 0.2em;
padding-bottom: 0.2em;
}
.page-form-dataSet .workspace-operation-wrapper.ui-tabs .ui-tabs-panel{
position: absolute;
left: 0px;
right: 0px;
top: 2.2em;
bottom: 0px;
padding: 0 0;
}
.page-form-dataSet .workspace-operation-wrapper .operation{
.page-form-dataSet .workspace .workspace-operation-wrapper .operation{
position: absolute;
right: 0px;
top: -1.5em;
}
.page-form-dataSet .workspace-operation-wrapper .operation .ui-button.ui-button-icon-only{
.page-form-dataSet .workspace .workspace-operation-wrapper .operation .ui-button.ui-button-icon-only,
.page-form-dataSet .workspace .show-resolved-source-button{
height: 1.2em;
width: 2.5em;
vertical-align: top;
border: 0px;
}
.page-form-dataSet .workspace-operation-wrapper .preview-result-table-wrapper .result-resolved-source{
.page-form-dataSet .workspace .workspace-operation-wrapper .preview-result-table-wrapper .result-resolved-source{
position: relative;
top: 0.1em;
display: none;
width: 100%;
text-align: right;
}
.page-form-dataSet .workspace .workspace-operation-wrapper .preview-result-table-wrapper .result-resolved-source-panel{
position: absolute;
display: none;
bottom: -4em;
height: 3.5em;
right: 0;
left: 0;
width: 90%;
height: 8em;
}
.page-form-dataSet .workspace-operation-wrapper .preview-result-table-wrapper .result-resolved-source textarea{
.page-form-dataSet .workspace .workspace-operation-wrapper .preview-result-table-wrapper .result-resolved-source-panel-content{
position: absolute;
left: 0.41em;
top: 0.41em;
right: 0.41em;
bottom: 0.41em;
}
.page-form-dataSet .workspace .workspace-operation-wrapper .preview-result-table-wrapper .result-resolved-source-panel-content textarea{
width: 100%;
height: 100%;
margin: 0 0;
@ -2180,16 +2257,13 @@ table.dataTable tbody tr td select{
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
}
.page-form-dataSet .workspace-operation-wrapper .input-in-table{
.page-form-dataSet .workspace .workspace-operation-wrapper .input-in-table{
width: 90%!important;
}
.page-form-dataSet .workspace-operation-wrapper .input-in-table.readonly{
.page-form-dataSet .workspace .workspace-operation-wrapper .input-in-table.readonly{
border: none;
background: none;
}
.page-form-dataSet .form-item-workspace .error{
display: block;
}
.page-form-dataSet .preview-param-value-panel{
position: absolute;
display: none;
@ -2212,20 +2286,15 @@ table.dataTable tbody tr td select{
.page-form-dataSet .form-foot-placeholder{
height: 2.5em;
}
.page-form-dataSet-jsonFile{}
.page-form-dataSet-jsonFile .form-item-workspace .form-item-label{
vertical-align:top;
margin-top: 0.3em;
.page-form-dataSet .encoding-selectmenu-menu .ui-menu{
height: 10em;
}
.page-form-dataSet-jsonFile .form-item-workspace .form-item-value .file-display-name{
width: 60% !important;
}
.page-form-dataSet-jsonFile .form-item-workspace .form-item-value .fileinput-wrapper{
margin-top: 0.41em;
}
.page-form-dataSet-jsonFile .encoding-selectmenu-menu .ui-menu{
max-height: 14em;
overflow: auto;
.page-form-dataSet-Http .form-item-value-requestMethod .ui-selectmenu-button,
.page-form-dataSet-Http .form-item-value-requestContentType .ui-selectmenu-button,
.page-form-dataSet-Http .form-item-value-requestContentCharset .ui-selectmenu-button,
.page-form-dataSet-Http .form-item-value-responseContentType .ui-selectmenu-button{
width: auto;
min-width: 6em;
}
.page-form-chart .chart-plugin{
@ -2663,7 +2732,7 @@ table.dataTable tbody tr td select{
position: absolute;
display: inline-block;
z-index: 10;
top: -0.6em;
bottom: -1em;
}
.page-form-dashboard .form-item-value-template .resize-editor-wrapper.resize-left{
left: -0.6em;
@ -2672,9 +2741,9 @@ table.dataTable tbody tr td select{
right: -0.6em;
}
.page-form-dashboard .form-item-value-template .resize-editor-wrapper .ui-button.ui-button-icon-only{
width: 1.2em;
height: 1.2em;
padding: 0.3em 0.3em;
width: 1em;
height: 1em;
padding: 0 0.3em;
border: 0;
}
.page-form-dashboard .show-button{

View File

@ -1323,7 +1323,7 @@
*
* @param chartDataSet 图表数据集对象
* @param dataSign 数据标记对象标记名称
* @return {...}
* @return {...}undefined
*/
chartBase.dataSetPropertyOfSign = function(chartDataSet, dataSign)
{
@ -1825,7 +1825,7 @@
* 图表事件支持函数获取/设置图表事件对象的数据chartEvent.data
*
* @param chartEvent 图表事件对象
* @param data 可选要设置的数据对象
* @param data 可选要设置的数据对象绘制图表条目的数据对象{ 数据标记名 : 数据值, ... }
*/
chartBase.eventData = function(chartEvent, data)
{
@ -1839,7 +1839,7 @@
* 图表事件支持函数获取/设置图表事件对象的原始数据chartEvent.originalData
*
* @param chartEvent 图表事件对象
* @param originalData 可选要设置的原始数据对象
* @param originalData 可选要设置的原始数据绘制图表条目的原始数据集结果数据可以是单个数据对象数据对象数组
*/
chartBase.eventOriginalData = function(chartEvent, originalData)
{
@ -1850,7 +1850,7 @@
};
/**
* 图表事件支持函数获取/设置图表事件对象的原始数据对应的图表数据集索引chartEvent.originalChartDataSetIndex
* 图表事件支持函数获取/设置图表事件对象的原始数据对应的图表数据集索引chartEvent.originalChartDataSetIndex
*
* @param chartEvent 图表事件对象
* @param originalChartDataSetIndex 可选要设置的图表数据集索引数值
@ -1864,10 +1864,11 @@
};
/**
* 图表事件支持函数获取/设置图表事件对象的原始数据对应的结果数据索引chartEvent.originalResultDataIndex
* 图表事件支持函数获取/设置图表事件对象的原始数据在数据集结果数据中的索引chartEvent.originalResultDataIndex
*
* @param chartEvent 图表事件对象
* @param originalResultDataIndex 可选要设置的结果数据索引数值
* @param originalResultDataIndex 可选要设置的结果数据索引当chartEvent.originalData为单个对象时应是单个索引值
* 当chartEvent.originalData为对象数组时应是与之对应的索引值数组
*/
chartBase.eventOriginalResultDataIndex = function(chartEvent, originalResultDataIndex)
{
@ -1877,6 +1878,41 @@
chartEvent["originalResultDataIndex"] = originalResultDataIndex;
};
/**
* 图表事件支持函数设置图表事件对象的原始图表数据集索引原始数据原始数据索引
*
* @param chartEvent 图表事件对象
* @param originalChartDataSetIndex 原始图表数据集索引
* @param originalResultDataIndex 原始数据索引格式允许数值数值数组
*/
chartBase.eventOriginalInfo = function(chartEvent, originalChartDataSetIndex, originalResultDataIndex)
{
var result = this.resultAt(this.getUpdateResults(), originalChartDataSetIndex);
var resultDatas = (result == null ? [] : this.resultDatas(result));
var originalData = undefined;
var rdi = originalResultDataIndex;
//索引数值
if(typeof(rdi) == "number")
{
originalData = resultDatas[rdi];
}
//索引数值数组
else if($.isArray(rdi))
{
originalData = [];
for(var i=0; i<rdi.length; i++)
originalData.push(resultDatas[rdi[i]]);
}
this.eventOriginalData(chartEvent, originalData);
this.eventOriginalChartDataSetIndex(chartEvent, originalChartDataSetIndex);
this.eventOriginalResultDataIndex(chartEvent, rdi);
};
/**
* 图表事件支持函数绑定图表事件处理函数代理
* 注意此函数在图表渲染完成后才可调用
@ -2278,6 +2314,38 @@
return "dataGearClientElement" + nextIdSeq;
};
/**
* 将给定值按照HTML规范转义如果不是字符串直接返回原值
*/
chartFactory.escapeHtml = function(value)
{
if(typeof(value) != "string")
return value;
var epn = "";
for(var i=0; i<value.length; i++)
{
var c = value.charAt(i);
if(c == '<')
epn += '&lt;';
else if(c == '>')
epn += '&gt;';
else if(c == '&')
epn += '&amp;';
else if(c == '"')
epn += '&quot;';
else if(c == '\'')
epn += '&#39;';
else
epn += c;
}
return epn;
};
/**
* 记录异常日志
*

View File

@ -62,6 +62,9 @@
}
});
//是否禁用日期组件输入框的浏览器自动完成功能,浏览器自动完成功能会阻挡日期选择框,默认禁用
chartForm.disableDateAwareInputAutocomplete = (chartForm.disableDateAwareInputAutocomplete || true);
/**
* 渲染数据集参数值表单
*
@ -397,6 +400,9 @@
var $input = $("<input type='text' class='dg-dspv-form-input' />").attr("name", dataSetParam.name)
.attr("value", (value || "")).appendTo($parent);
if(chartForm.disableDateAwareInputAutocomplete)
$input.attr("autocomplete", "off");
if((dataSetParam.required+"") == "true")
$input.attr("dg-validation-required", "true");
@ -435,6 +441,9 @@
var $input = $("<input type='text' class='dg-dspv-form-input' />").attr("name", dataSetParam.name)
.attr("value", (value || "")).appendTo($parent);
if(chartForm.disableDateAwareInputAutocomplete)
$input.attr("autocomplete", "off");
if((dataSetParam.required+"") == "true")
$input.attr("dg-validation-required", "true");
@ -472,6 +481,9 @@
var $input = $("<input type='text' class='dg-dspv-form-input' />").attr("name", dataSetParam.name)
.attr("value", (value || "")).appendTo($parent);
if(chartForm.disableDateAwareInputAutocomplete)
$input.attr("autocomplete", "off");
if((dataSetParam.required+"") == "true")
$input.attr("dg-validation-required", "true");
@ -626,6 +638,9 @@
chartForm.datetimepicker = function($input, datetimepickerOptions, chartTheme)
{
//在这里检查并重写,避免依赖加载顺序
global.overwriteDateFormatter_parseDate();
if(chartForm._datetimepickerSetLocale !== true)
{
$.datetimepicker.setLocale('zh');
@ -652,6 +667,45 @@
},
datetimepickerOptions);
//初始化为年份选择器
var isOnlySelectYear = ("Y" == datetimepickerOptions.format || "y" == datetimepickerOptions.format);
if(isOnlySelectYear)
{
//显示确定按钮,用于直接选中默认年份
datetimepickerOptions.showApplyButton = true;
datetimepickerOptions.onGenerate = function(currentValue,$input)
{
var yearPickerInited = $(this).attr("yearPickerInited");
if(!yearPickerInited)
{
$(this).attr("yearPickerInited", "yes");
$(".xdsoft_prev", this).hide();
$(".xdsoft_today_button", this).hide();
$(".xdsoft_month", this).hide();
$(".xdsoft_next", this).hide();
$(".xdsoft_calendar", this).hide();
$(".xdsoft_save_selected", this).removeClass("blue-gradient-button")
.addClass("xdsoft_save_selected_year ui-button ui-corner-all").html(chartForm.labels.confirm);
}
};
datetimepickerOptions.onShow = function(currentValue,$input)
{
if(!$input.val())
{
//这样可以直接点击【确定】按钮选择默认年份
this.setOptions({value: currentValue});
$input.val("");
}
};
datetimepickerOptions.onChangeYear = function(currentValue,$input)
{
this.setOptions({value: currentValue});
$(".xdsoft_save_selected", this).click();
};
}
$input.datetimepicker(datetimepickerOptions);
};
@ -738,6 +792,14 @@
+" box-shadow: none;"
+" -webkit-box-shadow: none;"
+"} "
+parentSelector + " .xdsoft_datetimepicker .xdsoft_save_selected.xdsoft_save_selected_year{"
+" color: "+color+";"
+" background: "+bgColor+";"
+" border: 1px solid "+borderColor+" !important;"
+"} "
+parentSelector + " .xdsoft_datetimepicker .xdsoft_save_selected.xdsoft_save_selected_year:hover{"
+" background: "+hoverColor+";"
+"} "
;
global.chartFactory.createStyleSheet(styleId, cssText);
@ -1280,4 +1342,139 @@
return ($panel.length == 0 || $panel.is(":hidden"));
};
})
(this);
(function(global)
{
global.overwriteDateFormatter_parseDate = function()
{
if(global._overwriteDateFormatter_parseDate == true)
return;
global._overwriteDateFormatter_parseDate = true;
//重写datetimepicker的DateFormatter.prototype.parseDate函数
//解决当options配置为仅选择年份{format:'Y'}选择完毕后输入框blur后年份会被重置的的BUG
DateFormatter.prototype.parseDate = function(e, r) {
var n, a, u, i, s, o, c, f, l, h, d = this, g = !1, m = !1, p = d.dateSettings, y = {
date: null,
year: null,
month: null,
day: null,
hour: 0,
min: 0,
sec: 0
};
if (!e)
return null;
if (e instanceof Date)
return e;
if ("U" === r)
return u = parseInt(e),
u ? new Date(1e3 * u) : e;
switch (typeof e) {
case "number":
return new Date(e);
case "string":
break;
default:
return null
}
if (n = r.match(d.validParts),
!n || 0 === n.length)
throw new Error("Invalid date format definition.");
for (a = e.replace(d.separators, "\x00").split("\x00"),
u = 0; u < a.length; u++)
switch (i = a[u],
s = parseInt(i),
n[u]) {
case "y":
case "Y":
if (!s)
return null;
l = i.length,
y.year = 2 === l ? parseInt((70 > s ? "20" : "19") + i) : s,
g = !0;
break;
case "m":
case "n":
case "M":
case "F":
if (isNaN(s)) {
if (o = d.getMonth(i),
!(o > 0))
return null;
y.month = o
} else {
if (!(s >= 1 && 12 >= s))
return null;
y.month = s
}
g = !0;
break;
case "d":
case "j":
if (!(s >= 1 && 31 >= s))
return null;
y.day = s,
g = !0;
break;
case "g":
case "h":
if (c = n.indexOf("a") > -1 ? n.indexOf("a") : n.indexOf("A") > -1 ? n.indexOf("A") : -1,
h = a[c],
c > -1)
f = t(h, p.meridiem[0]) ? 0 : t(h, p.meridiem[1]) ? 12 : -1,
s >= 1 && 12 >= s && f > -1 ? y.hour = s + f - 1 : s >= 0 && 23 >= s && (y.hour = s);
else {
if (!(s >= 0 && 23 >= s))
return null;
y.hour = s
}
m = !0;
break;
case "G":
case "H":
if (!(s >= 0 && 23 >= s))
return null;
y.hour = s,
m = !0;
break;
case "i":
if (!(s >= 0 && 59 >= s))
return null;
y.min = s,
m = !0;
break;
case "s":
if (!(s >= 0 && 59 >= s))
return null;
y.sec = s,
m = !0
}
//----添加的内容
//如果选择了年份,检查并设置月份、日的初值,使下面的:
//if (g === !0 && y.year && y.month && y.day)
//逻辑可以执行到
if (g === !0 && y.year)
{
if(!y.month)
y.month = 1;
if(!y.day)
y.day = 1;
}
//----添加的内容
if (g === !0 && y.year && y.month && y.day)
y.date = new Date(y.year,y.month - 1,y.day,y.hour,y.min,y.sec,0);
else {
if (m !== !0)
return null;
y.date = new Date(0,0,0,y.hour,y.min,y.sec,0)
}
return y.date
};
}
})
(this);

View File

@ -133,6 +133,20 @@
return re;
};
/**
* 为数组追加单个元素数组
*/
chartSupport.appendElement = function(array, eles)
{
if($.isArray(eles))
{
for(var i=0; i<eles.length; i++)
array.push(eles[i]);
}
else
array.push(eles);
};
/**
* 为源数组追加不重复的元素
*
@ -249,82 +263,164 @@
else
chart.extValue("signNameMap", signNameMap);
};
chartSupport.KEY_CHART_ORIGINAL_DATA_INFO = "__dataGearChartOriginalDataInfo";
/**
* 设置图表数据对象的原始数据信息
* 根据原始数据索引对象设置图表事件对象的原始数据相关信息
*
* @param chartData 图表数据对象对象数组
* @param chartDataSetIndex 原始图表数据集索引
* @param resultDataIndex 可选原始图表数据集结果数据索引默认为0
* @param chart
* @param chartEvent
* @param originalDataIndex 原始数据索引对象格式为
* {
* //图表数据集索引数值
* chartDataSetIndex: 数值,
*
* //图表数据集结果数据索引信息,格式为:
* //数值:单条结果数据索引;
* //[数值, ...]:多条结果数据索引;
* //{start: 数值, end: 数值}:范围结果数据索引;
* resultDataIndex: ...
* }
*/
chartSupport.setChartOriginalDataInfo = function(chartData, chartDataSetIndex, resultDataIndex)
chartSupport.setChartEventOriginalDataByIndex = function(chart, chartEvent, originalDataIndex)
{
if(!chartData)
return;
if(resultDataIndex == null)
resultDataIndex = 0;
if($.isArray(chartData))
if(!originalDataIndex)
{
for(var i=0; i<chartData.length; i++)
{
chartData[i][chartSupport.KEY_CHART_ORIGINAL_DATA_INFO] =
{
chartDataSetIndex : chartDataSetIndex, resultDataIndex: resultDataIndex+i
};
}
chart.eventOriginalData(chartEvent, null);
chart.eventOriginalChartDataSetIndex(chartEvent, null);
chart.eventOriginalResultDataIndex(chartEvent, null);
return;
}
else
chartData[chartSupport.KEY_CHART_ORIGINAL_DATA_INFO] = { chartDataSetIndex : chartDataSetIndex, resultDataIndex: resultDataIndex };
var rdi = originalDataIndex.resultDataIndex;
if(rdi.start != null && rdi.end != null)
{
var rdiAry = [];
for(var i=rdi.start; i<rdi.end; i++)
rdiAry.push(i);
rdi = rdiAry;
}
chart.eventOriginalInfo(chartEvent, originalDataIndex.chartDataSetIndex, rdi);
};
chartSupport.setChartEventOriginalDataForChartData = function(chart, chartEvent, chartData)
{
var index = chartSupport.chartDataOriginalDataIndex(chartData);
this.setChartEventOriginalDataByIndex(chart, chartEvent, index);
};
chartSupport.setChartEventOriginalDataForEchartsRange = function(chart, chartEvent, echartsEventParams)
{
var index = this.getChartOriginalDataIndexForRange(chart, echartsEventParams.seriesIndex, echartsEventParams.dataIndex);
this.setChartEventOriginalDataByIndex(chart, chartEvent, index);
};
chartSupport.KEY_ORIGINAL_DATA_INDEX = "__DataGearOriginalDataIndex";
/**
* 获取图表数据对象的原始数据信息
* 获取/设置图表数据对象的原始数据索引对象originalDataIndex
*
* @param chartData 图表数据对象
* @param chartDataSetIndex 原始图表数据集索引
* @param resultDataIndex 参考setChartEventOriginalDataByIndex函数的originalDataIndex.resultDataIndex参数说明
*/
chartSupport.getChartOriginalDataInfo = function(chartData)
chartSupport.chartDataOriginalDataIndex = function(chartData, chartDataSetIndex, resultDataIndex)
{
if(!chartData)
return undefined;
return chartData[chartSupport.KEY_CHART_ORIGINAL_DATA_INFO];
if(chartDataSetIndex === undefined)
return chartData[chartSupport.KEY_ORIGINAL_DATA_INDEX];
var index = { chartDataSetIndex : chartDataSetIndex, resultDataIndex: resultDataIndex };
chartData[chartSupport.KEY_ORIGINAL_DATA_INDEX] = index;
};
/**
* 获取/设置HTML元素的图表原始数据信息
* 获取/设置DOM元素的原始数据索引对象originalDataIndex
*
* @param $dom DOM对象
* @param chartDataSetIndex 原始图表数据集索引
* @param resultDataIndex 参考setChartEventOriginalDataByIndex函数的originalDataIndex.resultDataIndex参数说明
*/
chartSupport.chartOriginalDataInfoHtml = function($dom, chartDataSetIndex, resultDataIndex)
chartSupport.domOriginalDataIndex = function($dom, chartDataSetIndex, resultDataIndex)
{
if(chartDataSetIndex === undefined && resultDataIndex === undefined)
return $dom.data(chartSupport.KEY_CHART_ORIGINAL_DATA_INFO);
if(chartDataSetIndex === undefined)
return $dom.data(chartSupport.KEY_ORIGINAL_DATA_INDEX);
else
$dom.data(chartSupport.KEY_CHART_ORIGINAL_DATA_INFO,
{
chartDataSetIndex : chartDataSetIndex, resultDataIndex: resultDataIndex
});
{
var index = { chartDataSetIndex : chartDataSetIndex, resultDataIndex: resultDataIndex };
$dom.data(chartSupport.KEY_ORIGINAL_DATA_INDEX, index);
}
};
/**
* 设置图表数据范围的原始信息
* 设置图表特定系列的指定数据范围对应的的原始数据索引对象
*
* @param chart
* @param seriesIndex 图表系列索引
* @param dataIndexStart 图表系列数据起始索引
* @param dataIndexEnd 图表系列数据结束索引
* @param resultDataIndexStart 可选数据结果起始索引默认为0
* @param dataIndexStart 图表系列的数据范围起始索引
* @param dataIndexEnd 图表系列的数据范围结束索引为null则表示为dataIndexStart+1
* @param resultDataIndexInfo 可选原始图表数据集结果数据索引信息格式为
* {
* //与图表系列的数据范围顺序对应
* type: "ordinal",
* //可选数据集结果数据索引起始索引默认为0
* start: 数值
* }
* 或者
* {
* //图表系列的数据范围内都是相同的
* type: "identical",
* //数据集结果数据索引起始索引
* start: 数值,
* //可选数据集结果数据索引结束索引默认为start+1
* end: 数值
* }
* 或者
* {
* //图表系列的数据范围内都是相同的
* type: "identical",
* //数据集结果数据索引值、值数组
* value: 数值[ 数值, ... ]
* }
* 或者
* 数值表示{ type: "ordinal", start: 此数值 }
*
* 如果不设置则默认为{ type: "ordinal", start: 0 }
*/
chartSupport.setChartOriginalDataInfoRange = function(chart, seriesIndex, dataIndexStart, dataIndexEnd,
chartDataSetIndex, resultDataIndexStart)
chartSupport.setChartOriginalDataIndexForRange = function(chart, seriesIndex, dataIndexStart, dataIndexEnd,
chartDataSetIndex, resultDataIndexInfo)
{
var originObj = chart.extValue("chartOriginalDataInfoRange");
dataIndexEnd = (dataIndexEnd == null ? dataIndexStart+1 : dataIndexEnd);
if(resultDataIndexInfo == null)
resultDataIndexInfo = { type: "ordinal", start: 0 };
else if(typeof(resultDataIndexInfo) == "number")
resultDataIndexInfo = { type: "ordinal", start: resultDataIndexInfo };
if(resultDataIndexInfo.type == "ordinal")
{
if(resultDataIndexInfo.start == null)
resultDataIndexInfo.start = 0;
}
else if(resultDataIndexInfo.type == "identical")
{
if(resultDataIndexInfo.start != null)
if(resultDataIndexInfo.end == null)
resultDataIndexInfo.end = resultDataIndexInfo.start + 1;
}
else
throw new Error("Unknown type ["+resultDataIndexInfo.type+"]");
var originObj = chart.extValue("chartOriginalDataIndexForRange");
if(originObj == null)
{
originObj = {};
chart.extValue("chartOriginalDataInfoRange", originObj);
chart.extValue("chartOriginalDataIndexForRange", originObj);
}
var ary = originObj[seriesIndex];
@ -334,22 +430,25 @@
originObj[seriesIndex] = ary;
}
ary.push({ dataIndexStart: dataIndexStart, dataIndexEnd: dataIndexEnd,
ary.push({
dataIndexStart: dataIndexStart,
dataIndexEnd: dataIndexEnd,
chartDataSetIndex: chartDataSetIndex,
resultDataIndexStart: (resultDataIndexStart == undefined ? 0 : resultDataIndexStart) });
resultDataIndexInfo: resultDataIndexInfo
});
};
/**
* 获取图表数据原始信息
* 获取图表特定系列的指定数据对应的原始数据索引对象
*
* @param chart
* @param seriesIndex 图表系列索引
* @param dataIndex 图表系列数据索引
* @return { chartDataSetIndex: ..., resultDataIndex: ... }undefined
* @param dataIndex 图表系列数据索引
* @return 参考setChartEventOriginalDataByIndex函数的originalDataIndex参数说明可能返回undefined
*/
chartSupport.getChartOriginalDataInfoForRange = function(chart, seriesIndex, dataIndex)
chartSupport.getChartOriginalDataIndexForRange = function(chart, seriesIndex, dataIndex)
{
var originObj = chart.extValue("chartOriginalDataInfoRange");
var originObj = chart.extValue("chartOriginalDataIndexForRange");
if(!originObj)
return undefined;
@ -364,30 +463,45 @@
var ele = ary[i];
if(ele.dataIndexStart <= dataIndex && ele.dataIndexEnd > dataIndex)
return { chartDataSetIndex: ele.chartDataSetIndex, resultDataIndex: dataIndex - ele.dataIndexStart + ele.resultDataIndexStart };
{
var originalDataIndex=
{
chartDataSetIndex: ele.chartDataSetIndex
};
var indexInfo = ele.resultDataIndexInfo;
if(indexInfo.type == "ordinal")
{
originalDataIndex.resultDataIndex = dataIndex - ele.dataIndexStart + indexInfo.start;
}
else if(indexInfo.type == "identical")
{
if(indexInfo.value != null)
originalDataIndex.resultDataIndex = indexInfo.value;
else
originalDataIndex.resultDataIndex = { start: indexInfo.start , end: indexInfo.end };
}
else
throw new Error("Unknown type ["+indexInfo.type+"]");
return originalDataIndex;
}
}
return undefined;
};
/**
* 清除图表数据范围的原始信息
* 清除图表所有数据范围对应的的原始数据索引对象
*
* @param chart
*/
chartSupport.clearChartOriginalDataInfoRange = function(chart)
chartSupport.clearChartOriginalDataIndexForRange = function(chart)
{
chart.extValue("chartOriginalDataInfoRange", null);
chart.extValue("chartOriginalDataIndexForRange", null);
};
chartSupport.getChartOriginalDataInfoForRangeEcharts = function(chart, echartsEventParams)
{
if(!echartsEventParams)
return undefined;
return this.getChartOriginalDataInfoForRange(chart, echartsEventParams.seriesIndex, echartsEventParams.dataIndex);
}
chartSupport.bindChartEventHandlerDelegationEcharts = function(chart, eventType, chartEventHanlder, chartEventDataSetter)
{
var echartsEventHandler = function(params)
@ -419,36 +533,6 @@
}
};
chartSupport.setChartEventOriginalDataForEchartsRange = function(chart, chartEvent, echartsEventParams)
{
var originDataInfo = this.getChartOriginalDataInfoForRangeEcharts(chart, echartsEventParams);
this.setChartEventOriginalDataByInfo(chart, chartEvent, originDataInfo);
};
chartSupport.setChartEventOriginalDataForChartData = function(chart, chartEvent, chartData)
{
var originDataInfo = chartSupport.getChartOriginalDataInfo(chartData);
this.setChartEventOriginalDataByInfo(chart, chartEvent, originDataInfo);
};
chartSupport.setChartEventOriginalDataByInfo = function(chart, chartEvent, originDataInfo)
{
if(!originDataInfo)
{
chart.eventOriginalData(chartEvent, null);
chart.eventOriginalChartDataSetIndex(chartEvent, null);
chart.eventOriginalResultDataIndex(chartEvent, null);
return;
}
var resultDatas = chart.resultDatas(chart.resultAt(chart.getUpdateResults(), originDataInfo.chartDataSetIndex));
var originalData = resultDatas[originDataInfo.resultDataIndex];
chart.eventOriginalData(chartEvent, originalData);
chart.eventOriginalChartDataSetIndex(chartEvent, originDataInfo.chartDataSetIndex);
chart.eventOriginalResultDataIndex(chartEvent, originDataInfo.resultDataIndex);
};
//折线图
chartSupport.lineRender = function(chart, nameSign, valueSign, options)
@ -507,7 +591,7 @@
var isCategory = (initOptions.xAxis.type == "category");
chartSupport.clearChartOriginalDataInfoRange(chart);
chartSupport.clearChartOriginalDataIndexForRange(chart);
var legendData = [];
var xAxisData = [];
@ -540,7 +624,7 @@
legendData.push(legendName);
series.push(mySeries);
chartSupport.setChartOriginalDataInfoRange(chart, series.length-1, 0, data.length, i);
chartSupport.setChartOriginalDataIndexForRange(chart, series.length-1, 0, data.length, i);
//类目轴需要设置data不然图表刷新数据有变化时类目轴坐标不能自动更新
if(isCategory)
@ -668,7 +752,7 @@
var isCategory = ((horizontal ? initOptions.yAxis.type : initOptions.xAxis.type) == "category");
chartSupport.clearChartOriginalDataInfoRange(chart);
chartSupport.clearChartOriginalDataIndexForRange(chart);
var legendData = [];
var axisData = [];
@ -700,7 +784,7 @@
legendData.push(legendName);
series.push(mySeries);
chartSupport.setChartOriginalDataInfoRange(chart, series.length-1, 0, data.length, i);
chartSupport.setChartOriginalDataIndexForRange(chart, series.length-1, 0, data.length, i);
//类目轴需要设置data不然图表刷新数据有变化时类目轴坐标不能自动更新
if(isCategory)
@ -810,7 +894,7 @@
var initOptions= chartSupport.initOptions(chart);
var chartDataSets = chart.chartDataSetsNonNull();
chartSupport.clearChartOriginalDataInfoRange(chart);
chartSupport.clearChartOriginalDataIndexForRange(chart);
var legendData = [];
var seriesName = "";
@ -832,7 +916,7 @@
seriesName = dataSetName;
seriesData = seriesData.concat(nvv);
chartSupport.setChartOriginalDataInfoRange(chart, 0, seriesData.length - nvv.length, seriesData.length, i);
chartSupport.setChartOriginalDataIndexForRange(chart, 0, seriesData.length - nvv.length, seriesData.length, i);
}
var series = [ chartSupport.optionsSeries(initOptions, 0, {name: seriesName, data: seriesData}) ];
@ -913,7 +997,7 @@
var chartDataSet = chart.chartDataSetFirst();
var result = chart.resultFirst(results);
chartSupport.clearChartOriginalDataInfoRange(chart);
chartSupport.clearChartOriginalDataIndexForRange(chart);
var seriesName = chart.chartDataSetName(chartDataSet);
@ -927,7 +1011,7 @@
var seriesData = [ { value: value, name: chart.dataSetPropertyLabel(vp), min: min, max: max } ];
chartSupport.setChartOriginalDataInfoRange(chart, 0, 0, seriesData.length, 0);
chartSupport.setChartOriginalDataIndexForRange(chart, 0, 0, seriesData.length, 0);
var options = { series : [ { name: seriesName, min: min, max: max, data: seriesData } ]};
@ -1030,7 +1114,7 @@
var isCategory = (initOptions.xAxis.type == "category");
chartSupport.clearChartOriginalDataInfoRange(chart);
chartSupport.clearChartOriginalDataIndexForRange(chart);
var legendData = [];
var xAxisData = [];
@ -1070,7 +1154,7 @@
legendData.push(legendName);
series.push(mySeries);
chartSupport.setChartOriginalDataInfoRange(chart, series.length-1, 0, data.length, i);
chartSupport.setChartOriginalDataIndexForRange(chart, series.length-1, 0, data.length, i);
//类目轴需要设置data不然图表刷新数据有变化时类目轴坐标不能自动更新
if(isCategory)
@ -1197,7 +1281,7 @@
var initOptions= chartSupport.initOptions(chart);
var chartDataSets = chart.chartDataSetsNonNull();
chartSupport.clearChartOriginalDataInfoRange(chart);
chartSupport.clearChartOriginalDataIndexForRange(chart);
var legendData = [];
var series = [];
@ -1231,7 +1315,7 @@
legendData.push(dataSetName);
series.push(mySeries);
chartSupport.setChartOriginalDataInfoRange(chart, series.length-1, 0, data.length, i);
chartSupport.setChartOriginalDataIndexForRange(chart, series.length-1, 0, data.length, i);
}
if(min != null && max != null && max <= min)
@ -1387,11 +1471,11 @@
var initOptions= chartSupport.initOptions(chart);
var chartDataSets = chart.chartDataSetsNonNull();
chartSupport.clearChartOriginalDataInfoRange(chart);
chartSupport.clearChartOriginalDataIndexForRange(chart);
var legendData = [];
var indicatorData = [];
var series = [];
var seriesData = [];
for(var i=0; i<chartDataSets.length; i++)
{
@ -1400,46 +1484,109 @@
var result = chart.resultAt(results, i);
var ip = chart.dataSetPropertyOfSign(chartDataSet, signNameMap.item);
var iv = chart.resultColumnArrays(result, ip);
legendData = legendData.concat(iv);
if(i == 0)
//行式雷达网数据,必设置【雷达网条目名称】标记
if(ip)
{
var dnp = chart.dataSetPropertiesOfSign(chartDataSet, signNameMap.name);
var dnpv = chart.resultRowArrays(result, dnp, 0, 1);
dnpv = (dnpv.length > 0 ? dnpv[0] : []);
var dmp = chart.dataSetPropertiesOfSign(chartDataSet, signNameMap.max);
var dmpv = chart.resultRowArrays(result, dmp, 0, 1);
dmpv = (dmpv.length > 0 ? dmpv[0] : []);
var indicatorLen = Math.min(dnp.length, dmp.length);
for(var j=0; j<indicatorLen; j++)
{
var indicator = {name: dnpv[j], max: dmpv[j]};
indicatorData[j] = indicator;
}
chartSupport.radarUpdateForRowData(chart, results, signNameMap, initOptions,
chartDataSet, i, result, legendData, indicatorData, seriesData)
}
var dvp = chart.dataSetPropertiesOfSign(chartDataSet, signNameMap.value);
var dvpv = chart.resultRowArrays(result, dvp);
for(var j=0; j<iv.length; j++)
//列式雷达网数据
else
{
var seriesData = [ { name: iv[j], value: dvpv[j] } ];
series.push(chartSupport.optionsSeries(initOptions, i*iv.length+j, { data: seriesData }));
chartSupport.setChartOriginalDataInfoRange(chart, series.length-1, 0, seriesData.length, i, j);
chartSupport.radarUpdateForColumnData(chart, results, signNameMap, initOptions,
chartDataSet, i, result, legendData, indicatorData, seriesData)
}
}
var series = [ { data: seriesData } ];
var options = { legend: {data: legendData}, radar: {indicator: indicatorData}, series: series };
chart.echartsOptions(options);
chart.extValue("radarIndicatorData", indicatorData);
};
//行式雷达网数据处理,一行数据表示一条雷达网,行式结构为:雷达网条目名称, [指标名, 指标值, 指标上限值]*n
chartSupport.radarUpdateForRowData = function(chart, results, signNameMap, initOptions,
chartDataSet, chartDataSetIdx, result, legendData, indicatorData, seriesData)
{
var ip = chart.dataSetPropertyOfSign(chartDataSet, signNameMap.item);
var iv = chart.resultColumnArrays(result, ip);
chartSupport.appendElement(legendData, iv);
//仅使用第一个数据集构建指示器
if(chartDataSetIdx == 0)
{
var np = chart.dataSetPropertiesOfSign(chartDataSet, signNameMap.name);
var npv = chart.resultRowArrays(result, np, 0, 1);
npv = (npv.length > 0 ? npv[0] : []);
var mp = chart.dataSetPropertiesOfSign(chartDataSet, signNameMap.max);
var mpv = chart.resultRowArrays(result, mp, 0, 1);
mpv = (mpv.length > 0 ? mpv[0] : []);
var indicatorLen = Math.min(np.length, mp.length);
for(var j=0; j<indicatorLen; j++)
{
var indicator = {name: npv[j], max: mpv[j]};
indicatorData.push(indicator);
}
}
var vp = chart.dataSetPropertiesOfSign(chartDataSet, signNameMap.value);
var vpv = chart.resultRowArrays(result, vp);
for(var j=0; j<iv.length; j++)
{
var myData = { name: iv[j], value: vpv[j] };
seriesData.push(myData);
chartSupport.setChartOriginalDataIndexForRange(chart, 0, seriesData.length-1, seriesData.length, chartDataSetIdx, j);
}
};
//列式雷达网数据处理,一列【指标值】数据表示一条雷达网,列式结构为:指标名, 指标上限值, [指标值]*n其中【指标值】列名将作为雷达网条目名称
chartSupport.radarUpdateForColumnData = function(chart, results, signNameMap, initOptions,
chartDataSet, chartDataSetIdx, result, legendData, indicatorData, seriesData)
{
//仅使用第一个数据集构建指示器
if(chartDataSetIdx == 0)
{
var np = chart.dataSetPropertyOfSign(chartDataSet, signNameMap.name);
var nv = chart.resultColumnArrays(result, np);
var mp = chart.dataSetPropertyOfSign(chartDataSet, signNameMap.max);
var mv = chart.resultColumnArrays(result, mp);
var indicatorLen = Math.min(nv.length, mv.length);
for(var i=0; i<indicatorLen; i++)
{
var indicator = {name: nv[i], max: mv[i]};
indicatorData.push(indicator);
}
}
var vp = chart.dataSetPropertiesOfSign(chartDataSet, signNameMap.value);
var vv = chart.resultColumnArrays(result, vp);
for(var i=0; i<vv.length; i++)
{
var name = chart.dataSetPropertyLabel(vp[i]);
chartSupport.appendElement(legendData, name);
var myData = { name: name, value: vv[i] };
seriesData.push(myData);
chartSupport.setChartOriginalDataIndexForRange(chart, 0, seriesData.length-1, seriesData.length, chartDataSetIdx,
{
type: "identical",
start: 0,
end: indicatorData.length//所有行数据
});
}
};
chartSupport.radarResize = function(chart)
{
chartSupport.resizeChartEcharts(chart);
@ -1540,7 +1687,7 @@
var initOptions= chartSupport.initOptions(chart);
var chartDataSets = chart.chartDataSetsNonNull();
chartSupport.clearChartOriginalDataInfoRange(chart);
chartSupport.clearChartOriginalDataIndexForRange(chart);
var legendData = [];
var seriesName = "";
@ -1564,7 +1711,7 @@
seriesName = dataSetName;
seriesData = seriesData.concat(nvv);
chartSupport.setChartOriginalDataInfoRange(chart, 0, seriesData.length - nvv.length, seriesData.length, i);
chartSupport.setChartOriginalDataIndexForRange(chart, 0, seriesData.length - nvv.length, seriesData.length, i);
}
for(var i=0; i<seriesData.length; i++)
@ -1664,11 +1811,12 @@
map = (updateOptions.geo ? updateOptions.geo.map : undefined);
else
map = (updateOptions.series && updateOptions.series.length > 0 ? updateOptions.series[0].map : undefined);
var presetMap = chart.extValue("presetMap");
if(!map)
{
var currentMap = chart.map();
var presetMap = chart.extValue("presetMap");
//通过chart.map(...)设置了新的地图
if(currentMap && currentMap != presetMap)
@ -1694,6 +1842,21 @@
}
}
//如果更新了地图,则要重置缩放比例和中心位置,避免出现某些地图无法显示的情况
if(map && map != presetMap)
{
if(isGeo)
{
updateOptions.geo.center = null;
updateOptions.geo.zoom = 1;//此项非必须
}
else
{
updateOptions.series[0].center = null;
updateOptions.series[0].zoom = 1;//此项非必须
}
}
//没有更新地图、或者更新的地图已注册
if(!map || chart.echartsMapRegistered(map))
{
@ -1763,7 +1926,7 @@
var initOptions= chartSupport.initOptions(chart);
var chartDataSets = chart.chartDataSetsNonNull();
chartSupport.clearChartOriginalDataInfoRange(chart);
chartSupport.clearChartOriginalDataIndexForRange(chart);
var min = undefined;
var max = undefined;
@ -1797,7 +1960,7 @@
seriesData = seriesData.concat(nvv);
chartSupport.setChartOriginalDataInfoRange(chart, 0, seriesData.length - nvv.length, seriesData.length, i);
chartSupport.setChartOriginalDataIndexForRange(chart, 0, seriesData.length - nvv.length, seriesData.length, i);
if(nvv && nvv.length)
{
@ -1910,7 +2073,7 @@
var initOptions= chartSupport.initOptions(chart);
var chartDataSets = chart.chartDataSetsNonNull();
chartSupport.clearChartOriginalDataInfoRange(chart);
chartSupport.clearChartOriginalDataIndexForRange(chart);
var legendData = [];
var series = [];
@ -1958,7 +2121,7 @@
legendData[i] = dataSetName;
series[i] = chartSupport.optionsSeries(initOptions, i, { name: dataSetName, data: data });
chartSupport.setChartOriginalDataInfoRange(chart, series.length-1, 0, data.length, i);
chartSupport.setChartOriginalDataIndexForRange(chart, series.length-1, 0, data.length, i);
}
if(min != null && max != null && max <= min)
@ -2080,7 +2243,7 @@
var initOptions= chartSupport.initOptions(chart);
var chartDataSets = chart.chartDataSetsNonNull();
chartSupport.clearChartOriginalDataInfoRange(chart);
chartSupport.clearChartOriginalDataIndexForRange(chart);
var legendData = [];
var seriesName = "";
@ -2183,7 +2346,7 @@
//新插入
if(sidx == seriesData.length - 1 && seriesData[seriesData.length - 1] === sd)
{
chartSupport.setChartOriginalDataInfo(sd, i, j);
chartSupport.chartDataOriginalDataIndex(sd, i, j);
}
var tidx = chartSupport.appendDistinct(seriesData, td, (tip ? "id" : "name"));
@ -2191,7 +2354,7 @@
//新插入
if(tidx == seriesData.length - 1 && seriesData[seriesData.length - 1] === td)
{
chartSupport.setChartOriginalDataInfo(td, i, j);
chartSupport.chartDataOriginalDataIndex(td, i, j);
}
//如果使用id值表示关系对于数值型idecharts会误当做数据索引所以这里直接使用数据索引
@ -2199,7 +2362,7 @@
link.source = sidx;
link.target = tidx;
chartSupport.setChartOriginalDataInfo(link, i, j);
chartSupport.chartDataOriginalDataIndex(link, i, j);
seriesLinks.push(link);
}
@ -2364,7 +2527,7 @@
var initOptions= chartSupport.initOptions(chart);
var chartDataSets = chart.chartDataSetsNonNull();
chartSupport.clearChartOriginalDataInfoRange(chart);
chartSupport.clearChartOriginalDataIndexForRange(chart);
var legendData = [];
var series = [];
@ -2383,7 +2546,8 @@
chart.dataSetPropertyOfSign(chartDataSet, signNameMap.max)
]);
chartSupport.setChartOriginalDataInfo(data, i);
for(var j=0; j<data.length; j++)
chartSupport.chartDataOriginalDataIndex(data[j], i, j);
series.push(chartSupport.optionsSeries(initOptions, i, {name: dataSetName, data: data}));
}
@ -2509,7 +2673,7 @@
var initOptions= chartSupport.initOptions(chart);
var chartDataSets = chart.chartDataSetsNonNull();
chartSupport.clearChartOriginalDataInfoRange(chart);
chartSupport.clearChartOriginalDataIndexForRange(chart);
var xAxisData = [];
var yAxisData = [];
@ -2543,7 +2707,7 @@
seriesData = seriesData.concat(data);
chartSupport.setChartOriginalDataInfoRange(chart, 0, seriesData.length - data.length, seriesData.length, i);
chartSupport.setChartOriginalDataIndexForRange(chart, 0, seriesData.length - data.length, seriesData.length, i);
}
if(min == undefined)
@ -2897,7 +3061,7 @@
chartSupport.treeNodeEvalValueMark(node);
}
chartSupport.setChartOriginalDataInfo(node, i, j);
chartSupport.chartDataOriginalDataIndex(node, i, j);
var added = false;
for(var k=0; k<seriesData.length; k++)
@ -3065,14 +3229,14 @@
if(tvp)
td.value = chart.resultRowCell(data[j], tvp);
chartSupport.setChartOriginalDataInfo(sd, i, j);
chartSupport.chartDataOriginalDataIndex(sd, i, j);
var sidx = chartSupport.appendDistinct(seriesData, sd, "name");
//新插入
if(sidx == seriesData.length - 1 && seriesData[seriesData.length - 1] === sd)
{
chartSupport.setChartOriginalDataInfo(sd, i, j);
chartSupport.chartDataOriginalDataIndex(sd, i, j);
}
var tidx = chartSupport.appendDistinct(seriesData, td, "name");
@ -3080,7 +3244,7 @@
//新插入
if(tidx == seriesData.length - 1 && seriesData[seriesData.length - 1] === td)
{
chartSupport.setChartOriginalDataInfo(td, i, j);
chartSupport.chartDataOriginalDataIndex(td, i, j);
}
var link = {};
@ -3091,7 +3255,7 @@
link._sourceIndex = sidx;
link._targetIndex = tidx;
chartSupport.setChartOriginalDataInfo(link, i, j);
chartSupport.chartDataOriginalDataIndex(link, i, j);
seriesLinks.push(link);
}
@ -3305,7 +3469,7 @@
//新插入
if(sidx == seriesData.length - 1 && seriesData[seriesData.length - 1] === sd)
{
chartSupport.setChartOriginalDataInfo(sd, i, j);
chartSupport.chartDataOriginalDataIndex(sd, i, j);
}
var tidx = chartSupport.appendDistinct(seriesData, td, (tip ? "id" : "name"));
@ -3313,7 +3477,7 @@
//新插入
if(tidx == seriesData.length - 1 && seriesData[seriesData.length - 1] === td)
{
chartSupport.setChartOriginalDataInfo(td, i, j);
chartSupport.chartDataOriginalDataIndex(td, i, j);
}
//如果使用id值表示关系对于数值型idecharts会误当做数据索引所以这里直接使用数据索引
@ -3324,7 +3488,7 @@
if(vp)
link.value = chart.resultRowCell(data[j], vp);
chartSupport.setChartOriginalDataInfo(link, i, j);
chartSupport.chartDataOriginalDataIndex(link, i, j);
seriesLinks.push(link);
}
@ -3541,10 +3705,10 @@
{
min = (min == undefined ? data[j].value : Math.min(min, data[j].value));
max = (max == undefined ? data[j].value : Math.max(max, data[j].value));
chartSupport.chartDataOriginalDataIndex(data[j], i, j);
}
chartSupport.setChartOriginalDataInfo(data, i);
seriesData = seriesData.concat(data);
}
@ -3663,9 +3827,9 @@
}
},
//单元格内容渲染函数
renderValue: function(value, type, row, meta)
renderValue: function(value, name, rowIndex, columnIndex, row, meta)
{
return value;
return chartFactory.escapeHtml(value);
}
},
@ -3810,7 +3974,7 @@
for(var j=0; j<resultDatas.length; j++)
{
var data = $.extend({}, resultDatas[j]);
chartSupport.setChartOriginalDataInfo(data, i, j);
chartSupport.chartDataOriginalDataIndex(data, i, j);
datas.push(data);
}
}
@ -3862,7 +4026,7 @@
var dataTable = chartSupport.tableGetChartDataTable(chart);
var chartData = dataTable.row(rowElement).data();
var originalDataInfo = chartSupport.getChartOriginalDataInfo(chartData);
var originalDataInfo = chartSupport.chartDataOriginalDataIndex(chartData);
var data = {};
@ -3878,7 +4042,7 @@
}
chart.eventData(chartEvent, data);
chartSupport.setChartEventOriginalDataByInfo(chart, chartEvent, originalDataInfo);
chartSupport.setChartEventOriginalDataByIndex(chart, chartEvent, originalDataInfo);
};
chartSupport.tableChartEventDelegationEventBinder =
@ -4039,7 +4203,7 @@
$label.removeClass("dg-chart-label-item-pending");
$label.data("_dgChartLabelChartData", { name: name, value: value });
chartSupport.chartOriginalDataInfoHtml($label, i, j);
chartSupport.domOriginalDataIndex($label, i, j);
var $labelName = $(".label-name", $label);
var $labelValue = $(".label-value", $label);
@ -4115,7 +4279,7 @@
{
var signNameMap = chartSupport.chartSignNameMap(chart);
var originalDataInfo = chartSupport.chartOriginalDataInfoHtml($label);
var originalDataInfo = chartSupport.domOriginalDataIndex($label);
var chartData = $label.data("_dgChartLabelChartData");
var data = {};
@ -4127,7 +4291,7 @@
}
chart.eventData(chartEvent, data);
chartSupport.setChartEventOriginalDataByInfo(chart, chartEvent, originalDataInfo);
chartSupport.setChartEventOriginalDataByIndex(chart, chartEvent, originalDataInfo);
};
chartSupport.labelChartEventDelegationEventBinder =

View File

@ -206,6 +206,44 @@
webContext: "webContext"
};
/**
* 获取对象的指定属性路径的值
*
* @param obj
* @param propertyPath 属性路径示例orderorder.product[0].nameorder['product'].name
* @return 属性路径值属性路径不存在则返回undefined
*/
dashboardFactory.getPropertyPathValue = function(obj, propertyPath)
{
if(obj == null)
return undefined;
var value = undefined;
//简单属性值
value = obj[propertyPath];
if(value !== undefined)
return value;
//构建eval表达式
if(propertyPath.charAt(0) == '[')
propertyPath = "obj" + propertyPath;
else
propertyPath = "obj." + propertyPath;
try
{
value = eval(propertyPath);
}
catch(e)
{
value = undefined;
}
return value;
};
//----------------------------------------
// chartBaseExt start
//----------------------------------------
@ -382,9 +420,10 @@
originalData: this.eventOriginalData(chartEvent),
getValue: function(name)
{
var val = this.data[name];
//需支持属性路径格式的name
var val = dashboardFactory.getPropertyPathValue(this.data, name);
if(val === undefined && this.originalData != null)
val = this.originalData[name];
val = dashboardFactory.getPropertyPathValue(this.originalData, name);
return val;
}
@ -1326,6 +1365,8 @@
* }
* }
*
* 上述源参数名可以是简单参数名例如"name""value"也可以是源参数对象的属性路径例如"order.name""[0].name""['order'].product.name"
*
* 图表数据集参数索引对象用于确定源参数值要设置到的目标图表数据集参数格式为
* {
* //可选目标图表在批量设置对象的target数组中的索引数值默认为0
@ -1344,7 +1385,7 @@
* }
* 或者可简写为上述图表数据集参数索引对象的"param"属性值
*
* @param sourceData 源参数值对象格式为{ 源参数名 : 源参数值, ...} 或者 { getValue: function(name){ return ...; } }
* @param sourceData 源参数值对象格式为{ 源参数名 : 源参数值, ...} 或者 { getValue: function(name){ return ...; } }需支持属性路径
* @param batchSet 批量设置对象
* @param sourceValueContext 可选传递给图表数据集参数索引对象的value函数sourceValueContext参数的对象如果为数组则传递多个参数默认为sourceData
* @return 批量设置的图表对象数组
@ -1367,7 +1408,8 @@
for(var name in map)
{
var dataValue = (hasGetValueFunc ? sourceData.getValue(name) : sourceData[name]);
var dataValue = (hasGetValueFunc ? sourceData.getValue(name)
: dashboardFactory.getPropertyPathValue(sourceData, name));
var indexes = map[name];
if(!$.isArray(indexes))

View File

@ -246,4 +246,15 @@ table.dataTable.display tbody > tr > .selected:hover {
background-color: #f99117;
box-shadow: none;
-webkit-box-shadow: none;
}
/**年份选择器确定按钮*/
.xdsoft_datetimepicker .xdsoft_save_selected.xdsoft_save_selected_year{
color: #eeeeee !important;
border: 1px solid #666666 !important;
background: #555555 !important;
}
.xdsoft_datetimepicker .xdsoft_save_selected.xdsoft_save_selected_year:hover{
color: #ffffff !important;
border: 1px solid #59b4d4 !important;
background: #0078a3 !important;
}

View File

@ -246,4 +246,15 @@ table.dataTable.display tbody > tr > .selected:hover {
background: #59c908;
box-shadow: none;
-webkit-box-shadow: none;
}
/**年份选择器确定按钮*/
.xdsoft_datetimepicker .xdsoft_save_selected.xdsoft_save_selected_year{
color: #ffffff !important;
border: 1px solid #45930b !important;
background: #3c8009 !important;
}
.xdsoft_datetimepicker .xdsoft_save_selected.xdsoft_save_selected_year:hover{
color: #ffffff !important;
border: 1px solid #6cc510 !important;
background: #459e05 !important;
}

View File

@ -246,4 +246,15 @@ table.dataTable.display tbody > tr > .selected:hover {
background-color: #007FFF;
box-shadow: none;
-webkit-box-shadow: none;
}
/**年份选择器确定按钮*/
.xdsoft_datetimepicker .xdsoft_save_selected.xdsoft_save_selected_year{
color: #454545 !important;
border: 1px solid #c5c5c5 !important;
background: #f6f6f6 !important;
}
.xdsoft_datetimepicker .xdsoft_save_selected.xdsoft_save_selected_year:hover{
color: #2b2b2b !important;
border: 1px solid #cccccc !important;
background: #ededed !important;
}

View File

@ -41,7 +41,9 @@ readonly 是否只读操作允许为null
</div>
<div class="form-item">
<div class="form-item-label">
<label><@spring.message code='chart.chartDataSets' /></label>
<label title="<@spring.message code='chart.chartDataSets.desc' />">
<@spring.message code='chart.chartDataSets' />
</label>
</div>
<div class="form-item-value form-item-value-chartDataSet">
<input type="text" name="dataSignValidation" style="display: none" />
@ -64,7 +66,9 @@ readonly 是否只读操作允许为null
</div>
<div class="form-item">
<div class="form-item-label">
<label><@spring.message code='chart.updateInterval' /></label>
<label title="<@spring.message code='chart.updateInterval.desc' />">
<@spring.message code='chart.updateInterval' />
</label>
</div>
<div class="form-item-value">
<div class="updateInterval-radios">

View File

@ -132,7 +132,7 @@ readonly 是否只读操作允许为null
{
return "${contextPath}/analysis/dashboard/" + action;
};
po.getLastTagText = function(text)
{
if(!text)

Some files were not shown because too many files have changed in this diff Show More