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;
+ }
+
+ /**
+ * 设置请求地址。
+ *
+ * 请求地址支持Freemarker
模板语言。
+ *
+ *
+ * @param uri
+ */
+ public void setUri(String uri)
+ {
+ this.uri = uri;
+ }
+
+ public String getHeaderContent()
+ {
+ return headerContent;
+ }
+
+ /**
+ * 设置请求头JSON文本,格式应为:
+ *
+ *
+ *
+ * [
+ * {name: "...", value: "..."},
+ * {name: "...", value: "..."},
+ * ...
+ * ]
+ *
+ *
+ *
+ *
+ * 请求头JSON文本支持Freemarker
模板语言。
+ *
+ *
+ * @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;
+ }
+
+ /**
+ * 设置请求内容类型,允许的值为:
+ *
+ * {@linkplain #REQUEST_CONTENT_TYPE_FORM_URLENCODED}、{@linkplain #REQUEST_CONTENT_TYPE_JSON}。
+ *
+ *
+ * @param requestContentType
+ */
+ public void setRequestContentType(String requestContentType)
+ {
+ this.requestContentType = requestContentType;
+ }
+
+ public String getRequestContentCharset()
+ {
+ return requestContentCharset;
+ }
+
+ /**
+ * 设置请求内容编码。
+ *
+ * 默认请求内容编码为{@code UTF-8}。
+ *
+ *
+ * @param requestContentCharset
+ */
+ public void setRequestContentCharset(String requestContentCharset)
+ {
+ this.requestContentCharset = requestContentCharset;
+ }
+
+ public String getRequestContent()
+ {
+ return requestContent;
+ }
+
+ /**
+ * 设置请求内容JSON文本,为{@code null}或{@code ""}表示无请求内容。
+ *
+ * 当{@linkplain #getRequestContentType()}为{@linkplain #REQUEST_CONTENT_TYPE_FORM_URLENCODED}时,请求内容JSON文本格式应为:
+ *
+ *
+ *
+ * [
+ * {name: "...", value: "..."},
+ * {name: "...", value: "..."},
+ * ...
+ * ]
+ *
+ *
+ *
+ * 其中,{@code name}表示请求参数名,{@code value}表示请求参数值。
+ *
+ *
+ * 当{@linkplain #getRequestContentType()}为{@linkplain #REQUEST_CONTENT_TYPE_JSON}时,请求内容JSON文本没有特殊格式要求。
+ *
+ *
+ * 请求内容JSON文本支持Freemarker
模板语言。
+ *
+ *
+ * @param requestContent
+ */
+ public void setRequestContent(String requestContent)
+ {
+ this.requestContent = requestContent;
+ }
+
+ public String getResponseContentType()
+ {
+ return responseContentType;
+ }
+
+ /**
+ * 设置相应类型。
+ *
+ * 目前仅支持{@linkplain #RESPONSE_CONTENT_TYPE_JSON},且是默认值。
+ *
+ *
+ * @param responseContentType
+ */
+ public void setResponseContentType(String responseContentType)
+ {
+ this.responseContentType = responseContentType;
+ }
+
+ public String getResponseDataJsonPath()
+ {
+ return responseDataJsonPath;
+ }
+
+ /**
+ * 设置响应数据的JSON路径。
+ *
+ * 当希望返回的是响应原始JSON数据的指定JSON路径值时,可以设置此项。
+ *
+ *
+ * 具体格式参考{@linkplain AbstractJsonDataSet#setDataJsonPath(String)}。
+ *
+ *
+ * 默认无数据路径,将直接返回响应原始JSON数据。
+ *
+ *
+ * @param responseDataJsonPath
+ */
+ public void setResponseDataJsonPath(String responseDataJsonPath)
+ {
+ this.responseDataJsonPath = responseDataJsonPath;
+ }
+
+ @Override
+ public TemplateResolvedDataSetResult resolve(Map paramValues) throws DataSetException
+ {
+ return resolveResult(paramValues, null);
+ }
+
+ @Override
+ protected TemplateResolvedDataSetResult resolveResult(Map paramValues, List 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 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 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 paramValues) throws Throwable
+ {
+ return resolveAsFmkTemplateIfHasParam(this.uri, paramValues);
+ }
+
+ protected String resolveTemplateHeaderContent(Map paramValues) throws Throwable
+ {
+ return resolveAsFmkTemplateIfHasParam(this.headerContent, paramValues);
+ }
+
+ protected String resolveTemplateRequestContent(Map 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 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 nameValuePairs = new ArrayList<>(collection.size());
+
+ for (Object ele : collection)
+ {
+ String name = null;
+ String value = null;
+
+ if (ele instanceof Map, ?>)
+ {
+ Map eleMap = (Map) 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
+ {
+ private List properties;
+
+ private String responseDataJsonPath = "";
+
+ public JsonResponseHandler()
+ {
+ super();
+ }
+
+ public List getProperties()
+ {
+ return properties;
+ }
+
+ /**
+ * 设置数据集属性。
+ *
+ * @param properties
+ * 如果为{@code null}或空,则执行解析
+ */
+ public void setProperties(List 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 properties, Reader responseJsonReader)
+ {
+ super(HttpResponseJsonDataSet.class.getName(), HttpResponseJsonDataSet.class.getName(), properties);
+ this.responseJsonReader = responseJsonReader;
+ }
+
+ @Override
+ protected TemplateResolvedSource getJsonReader(Map paramValues) throws Throwable
+ {
+ return new TemplateResolvedSource<>(this.responseJsonReader);
+ }
+ }
+}
diff --git a/datagear-analysis/src/main/java/org/datagear/analysis/support/JsonDataSetSupport.java b/datagear-analysis/src/main/java/org/datagear/analysis/support/JsonDataSetSupport.java
deleted file mode 100644
index c2e0ae76..00000000
--- a/datagear-analysis/src/main/java/org/datagear/analysis/support/JsonDataSetSupport.java
+++ /dev/null
@@ -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}。
- *
- * 参考{@linkplain DataSetResult#getData()}说明。
- *
- *
- * @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 resolveDataSetProperties(Object resultData) throws UnsupportedJsonResultDataException
- {
- if (resultData == null)
- {
- return Collections.EMPTY_LIST;
- }
- else if (resultData instanceof Map, ?>)
- {
- return resolveJsonObjDataSetProperties((Map) resultData);
- }
- else if (resultData instanceof List>)
- {
- List> list = (List>) resultData;
-
- if (list.size() == 0)
- return Collections.EMPTY_LIST;
- else
- return resolveJsonObjDataSetProperties((Map) list.get(0));
- }
- else if (resultData instanceof Object[])
- {
- Object[] array = (Object[]) resultData;
-
- if (array.length == 0)
- return Collections.EMPTY_LIST;
- else
- return resolveJsonObjDataSetProperties((Map) array[0]);
- }
- else
- throw new UnsupportedJsonResultDataException("Result data must be object or object array/list");
- }
-
- /**
- * 解析JSON对象的{@linkplain DataSetProperty}。
- *
- * @param jsonObj
- * @return
- */
- public List resolveJsonObjDataSetProperties(Map jsonObj)
- {
- List properties = new ArrayList<>();
-
- if (jsonObj == null)
- {
-
- }
- else
- {
- for (Map.Entry 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;
- }
-}
diff --git a/datagear-analysis/src/main/java/org/datagear/analysis/support/JsonDirectoryFileDataSet.java b/datagear-analysis/src/main/java/org/datagear/analysis/support/JsonDirectoryFileDataSet.java
index f4614ce5..80d14f47 100644
--- a/datagear-analysis/src/main/java/org/datagear/analysis/support/JsonDirectoryFileDataSet.java
+++ b/datagear-analysis/src/main/java/org/datagear/analysis/support/JsonDirectoryFileDataSet.java
@@ -18,6 +18,9 @@ import org.datagear.util.FileUtil;
/**
* 目录内JSON文件{@linkplain DataSet}。
+ *
+ * 注意:此类不支持Freemarker
模板语言。
+ *
*
* @author datagear@163.com
*
@@ -73,8 +76,7 @@ public class JsonDirectoryFileDataSet extends AbstractJsonFileDataSet
@Override
protected File getJsonFile(Map 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;
}
}
diff --git a/datagear-analysis/src/main/java/org/datagear/analysis/support/JsonValueDataSet.java b/datagear-analysis/src/main/java/org/datagear/analysis/support/JsonValueDataSet.java
index 089d2451..d43eeaed 100644
--- a/datagear-analysis/src/main/java/org/datagear/analysis/support/JsonValueDataSet.java
+++ b/datagear-analysis/src/main/java/org/datagear/analysis/support/JsonValueDataSet.java
@@ -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 paramValues) throws DataSetException
+ public TemplateResolvedDataSetResult resolve(Map 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 paramValues) throws DataSetException
+ protected TemplateResolvedSource getJsonReader(Map paramValues) throws Throwable
{
- String json = resolveTemplate(this.value, paramValues);
-
- Object data = getJsonDataSetSupport().resolveResultData(json);
- DataSetResult result = new DataSetResult(data);
-
- List properties = getJsonDataSetSupport().resolveDataSetProperties(result.getData());
-
- return new TemplateResolvedDataSetResult(result, properties, json);
+ String json = resolveAsFmkTemplateIfHasParam(this.value, paramValues);
+ return new TemplateResolvedSource<>(IOUtil.getReader(json), json);
}
}
diff --git a/datagear-analysis/src/main/java/org/datagear/analysis/support/NotNameValueObjArrayJsonException.java b/datagear-analysis/src/main/java/org/datagear/analysis/support/NotNameValueObjArrayJsonException.java
new file mode 100644
index 00000000..7b3298d8
--- /dev/null
+++ b/datagear-analysis/src/main/java/org/datagear/analysis/support/NotNameValueObjArrayJsonException.java
@@ -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;
+ }
+}
diff --git a/datagear-analysis/src/main/java/org/datagear/analysis/support/RangeExpResolver.java b/datagear-analysis/src/main/java/org/datagear/analysis/support/RangeExpResolver.java
new file mode 100644
index 00000000..2d2ed442
--- /dev/null
+++ b/datagear-analysis/src/main/java/org/datagear/analysis/support/RangeExpResolver.java
@@ -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;
+
+/**
+ * 范围解析器。
+ *
+ * 此类用于解析诸如{@code "1, 2-3, 6-, -15}之类的范围表达式。
+ *
+ *
+ * @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 resolveIndex(String exp) throws NumberFormatException
+ {
+ List ranges = resolve(exp);
+
+ if (ranges.isEmpty())
+ return Collections.EMPTY_LIST;
+
+ List indexRanges = new ArrayList<>(ranges.size());
+
+ for (Range range : ranges)
+ indexRanges.add(new IndexRange(range));
+
+ return indexRanges;
+ }
+
+ /**
+ * 解析范围表达式组。
+ *
+ * 例如:{@code "1, 2-5, 8-, -15"}
+ *
+ *
+ * @param exp
+ * @return 如果{@code exp}为{@code null}、或{@code ""},将返回空列表
+ */
+ @SuppressWarnings("unchecked")
+ public List resolve(String exp)
+ {
+ if (exp != null)
+ exp = exp.trim();
+
+ if (exp == null || exp.isEmpty())
+ return Collections.EMPTY_LIST;
+
+ List 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);
+ }
+
+ /**
+ * 解析单个范围表达式。
+ *
+ * 例如:{@code "1"}、{@code "4-5"}、{@code "8-"}、{@code "-15"}
+ *
+ *
+ * @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 + "]";
+ }
+ }
+
+ /**
+ * 索引范围。
+ *
+ * 索引指大于或等于{@code 0}的整数值。
+ *
+ *
+ * @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 indexRanges, int index)
+ {
+ for (int i = 0; i < indexRanges.size(); i++)
+ if (indexRanges.get(i).includes(index))
+ return true;
+
+ return false;
+ }
+ }
+}
diff --git a/datagear-analysis/src/main/java/org/datagear/analysis/support/ReadJsonDataPathException.java b/datagear-analysis/src/main/java/org/datagear/analysis/support/ReadJsonDataPathException.java
new file mode 100644
index 00000000..741b7b62
--- /dev/null
+++ b/datagear-analysis/src/main/java/org/datagear/analysis/support/ReadJsonDataPathException.java
@@ -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;
+ }
+}
diff --git a/datagear-analysis/src/main/java/org/datagear/analysis/support/RequestContentNotNameValueObjArrayJsonException.java b/datagear-analysis/src/main/java/org/datagear/analysis/support/RequestContentNotNameValueObjArrayJsonException.java
new file mode 100644
index 00000000..e5813096
--- /dev/null
+++ b/datagear-analysis/src/main/java/org/datagear/analysis/support/RequestContentNotNameValueObjArrayJsonException.java
@@ -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);
+ }
+}
diff --git a/datagear-analysis/src/main/java/org/datagear/analysis/support/SimpleCsvFileDataSet.java b/datagear-analysis/src/main/java/org/datagear/analysis/support/SimpleCsvFileDataSet.java
new file mode 100644
index 00000000..9b5ef2f9
--- /dev/null
+++ b/datagear-analysis/src/main/java/org/datagear/analysis/support/SimpleCsvFileDataSet.java
@@ -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文件数据集。
+ *
+ * 注意:此类不支持Freemarker
模板语言。
+ *
+ *
+ * @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 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 paramValues) throws Throwable
+ {
+ return this.file;
+ }
+}
diff --git a/datagear-analysis/src/main/java/org/datagear/analysis/support/SimpleExcelDataSet.java b/datagear-analysis/src/main/java/org/datagear/analysis/support/SimpleExcelDataSet.java
new file mode 100644
index 00000000..51660a5b
--- /dev/null
+++ b/datagear-analysis/src/main/java/org/datagear/analysis/support/SimpleExcelDataSet.java
@@ -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数据集。
+ *
+ * 注意:此类不支持Freemarker
模板语言。
+ *
+ *
+ * @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 properties, File file)
+ {
+ super(id, name, properties);
+ this.file = file;
+ }
+
+ @Override
+ protected File getExcelFile(Map paramValues) throws DataSetException
+ {
+ return this.file;
+ }
+}
diff --git a/datagear-analysis/src/main/java/org/datagear/analysis/support/SimpleJsonFileDataSet.java b/datagear-analysis/src/main/java/org/datagear/analysis/support/SimpleJsonFileDataSet.java
index 2770127d..73643a26 100644
--- a/datagear-analysis/src/main/java/org/datagear/analysis/support/SimpleJsonFileDataSet.java
+++ b/datagear-analysis/src/main/java/org/datagear/analysis/support/SimpleJsonFileDataSet.java
@@ -13,6 +13,9 @@ import org.datagear.analysis.DataSetProperty;
/**
* 简单JSON文件数据集。
+ *
+ * 注意:此类不支持Freemarker
模板语言。
+ *
*
* @author datagear@163.com
*
diff --git a/datagear-analysis/src/main/java/org/datagear/analysis/support/SqlDataSet.java b/datagear-analysis/src/main/java/org/datagear/analysis/support/SqlDataSet.java
index 812d1f4e..45231616 100644
--- a/datagear-analysis/src/main/java/org/datagear/analysis/support/SqlDataSet.java
+++ b/datagear-analysis/src/main/java/org/datagear/analysis/support/SqlDataSet.java
@@ -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 paramValues) throws DataSetException
+ public TemplateResolvedDataSetResult resolve(Map paramValues) throws DataSetException
{
- List 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 paramValues) throws DataSetException
+ protected TemplateResolvedDataSetResult resolveResult(Map paramValues, List properties)
+ throws DataSetException
{
- return getResolvedDataSetResult(paramValues, null);
- }
-
- /**
- *
- * @param paramValues
- * @param properties
- * 允许为{@code null},此时会自动解析
- * @return
- * @throws DataSetException
- */
- protected TemplateResolvedDataSetResult getResolvedDataSetResult(Map paramValues,
- List 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