新增数据字典解析功能

This commit is contained in:
zhouhao
2017-05-09 17:08:45 +08:00
parent 9ce43fa1b5
commit 7bd1c7ebdd
13 changed files with 689 additions and 10 deletions

View File

@@ -18,17 +18,27 @@
package org.hswebframework.web.service.dictionary;
import org.hswebframework.web.entity.dictionary.DictionaryEntity;
import org.hswebframework.web.entity.dictionary.DictionaryItemEntity;
import java.io.Serializable;
import java.util.List;
import java.util.Optional;
/**
* 字典解析器接口
*
* @author zhouhao
*/
public interface DictionaryParser<V, T> extends Serializable {
Optional<V> textToValue(DictionaryEntity<? extends DictionaryItemEntity> dict, T text);
public interface DictionaryParser<V> extends Serializable {
Optional<V> textToValue(String text, Object context);
Optional<String> valueToText(V value, Object context);
default Optional<V> textToValue(String text) {
return textToValue(text, null);
}
default Optional<String> valueToText(V value) {
return valueToText(value, null);
}
Optional<T> valueToText(DictionaryEntity<? extends DictionaryItemEntity> dict, V value);
}

View File

@@ -22,7 +22,7 @@ package org.hswebframework.web.service.dictionary;
* @author zhouhao
*/
public interface DictionaryParserBuilder {
DictionaryParser<Object, String> buildValueToTextParser(String config);
DictionaryParser buildValueToTextParser(String config);
DictionaryParser<String, Object> buildTextToValueParser(String config);
DictionaryParser buildTextToValueParser(String config);
}

View File

@@ -16,6 +16,8 @@
*/
package org.hswebframework.web.service.dictionary;
import org.hswebframework.web.entity.dictionary.DictionaryEntity;
import org.hswebframework.web.entity.dictionary.DictionaryItemEntity;
import org.hswebframework.web.entity.dictionary.DictionaryParserEntity;
import org.hswebframework.web.service.CrudService;
@@ -26,7 +28,7 @@ import org.hswebframework.web.service.CrudService;
*/
public interface DictionaryParserService extends CrudService<DictionaryParserEntity, String> {
<V, T> DictionaryParser<V, T> getParser(String parserId);
<V> DictionaryParser<V> getParser(DictionaryEntity<? extends DictionaryItemEntity> dict, String parserId);
}

View File

@@ -38,5 +38,9 @@
<artifactId>hsweb-system-dictionary-service-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.hswebframework</groupId>
<artifactId>hsweb-expands-script</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,83 @@
package org.hswebframework.web.service.dictionary.simple;
import org.hswebframework.web.ExpressionUtils;
import org.hswebframework.web.entity.dictionary.DictionaryEntity;
import org.hswebframework.web.entity.dictionary.DictionaryItemEntity;
import org.hswebframework.web.service.dictionary.DictionaryParser;
import org.hswebframework.web.service.dictionary.simple.parser.SimpleSingleDictParser;
import org.hswebframework.web.service.dictionary.simple.parser.SingleDictParser;
import java.io.Serializable;
import java.util.*;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
/**
* 简单的字典解析器实现,支持树形结构字典
* <p>
* e.g.
* <pre>
* //字典
* [
* {text:"苹果",value:1,
* children:[
* {text:"青苹果",value:101},
* {text:"红富士",value:102},
* {text:"其他苹果",value:103,textExpression:"其他苹果(${#context.otherApple})"}
* ]}
* {text:"梨子",value:2}
* ]
* //调用
* parser.valueToText("1,101,103",{otherApple:"其他苹果1"});
* //返回结果 苹果(青苹果,其他苹果(其他苹果1))
*
* //调用
* parser.textToValue("苹果(青苹果,其他苹果)")
* //返回结果 1,101,103
* </pre>
*
* @author zhouhao
* @since 3.0
*/
public class SimpleDictionaryParser<V> implements DictionaryParser<V> {
private SimpleSingleDictParser toTextParser = new SimpleSingleDictParser();
private SimpleSingleDictParser toValueParser = new SimpleSingleDictParser();
public SimpleSingleDictParser getToTextParser() {
return toTextParser;
}
public SimpleSingleDictParser getToValueParser() {
return toValueParser;
}
//设置DictionaryEntity作为配置
public void setDict(DictionaryEntity<? extends DictionaryItemEntity> dict) {
toTextParser.setDict(dict, DictionaryItemEntity::getValue
, DictionaryItemEntity::getText
, DictionaryItemEntity::getTextExpression);
toValueParser.setDict(dict, DictionaryItemEntity::getText
, DictionaryItemEntity::getValue,
DictionaryItemEntity::getValueExpression);
toValueParser.getTargetFormat().setSplitter(",");
toValueParser.getTargetFormat().setChildStartChar(",");
toValueParser.getTargetFormat().setChildEndChar("");
toValueParser.getTargetFormat().setChildSplitter(",");
}
@Override
public Optional<String> valueToText(V value, Object context) {
if (value == null) return Optional.empty();
return toTextParser.parse(String.valueOf(value), context);
}
@Override
public Optional<V> textToValue(String text, Object context) {
return toValueParser.parse(text, context).map(v -> (V) v);
}
}

View File

@@ -17,6 +17,8 @@
package org.hswebframework.web.service.dictionary.simple;
import org.hswebframework.web.dao.dictionary.DictionaryParserDao;
import org.hswebframework.web.entity.dictionary.DictionaryEntity;
import org.hswebframework.web.entity.dictionary.DictionaryItemEntity;
import org.hswebframework.web.entity.dictionary.DictionaryParserEntity;
import org.hswebframework.web.id.IDGenerator;
import org.hswebframework.web.service.GenericEntityService;
@@ -47,7 +49,8 @@ public class SimpleDictionaryParserService extends GenericEntityService<Dictiona
}
@Override
public <V, T> DictionaryParser<V, T> getParser(String parserId) {
public <V> DictionaryParser<V> getParser(DictionaryEntity<? extends DictionaryItemEntity> dict, String parserId) {
// TODO: 17-3-9
return null;
}

View File

@@ -0,0 +1,81 @@
package org.hswebframework.web.service.dictionary.simple.parser;
import java.io.Serializable;
import java.util.StringJoiner;
/**
* TODO 完成注释
*
* @author zhouhao
*/
public class DictParserFormat implements Serializable {
//字典选项间的分割符
private String splitter = ",";
//子节点间的分割符
private String childSplitter = ",";
//子节点开始分割符
private String childStartChar = "(";
//子节点结束分割符
private String childEndChar = ")";
//前缀
private String prefix = "";
//后缀
private String suffix = "";
public String getSplitter() {
return splitter;
}
public void setSplitter(String splitter) {
this.splitter = splitter;
}
public String getChildSplitter() {
return childSplitter;
}
public void setChildSplitter(String childSplitter) {
this.childSplitter = childSplitter;
}
public String getChildStartChar() {
return childStartChar;
}
public void setChildStartChar(String childStartChar) {
this.childStartChar = childStartChar;
}
public String getChildEndChar() {
return childEndChar;
}
public void setChildEndChar(String childEndChar) {
this.childEndChar = childEndChar;
}
public String getPrefix() {
return prefix;
}
public void setPrefix(String prefix) {
this.prefix = prefix;
}
public String getSuffix() {
return suffix;
}
public void setSuffix(String suffix) {
this.suffix = suffix;
}
public StringJoiner createJoiner() {
return new StringJoiner(splitter, prefix, suffix);
}
}

View File

@@ -0,0 +1,13 @@
package org.hswebframework.web.service.dictionary.simple.parser;
import java.util.List;
import java.util.function.BiFunction;
/**
* TODO 完成注释
*
* @author zhouhao
*/
public interface DictParserFormatter {
<T> List<FormatterResult<T>> format(DictParserFormat format, Object value, BiFunction<String, String, T> mapping);
}

View File

@@ -0,0 +1,39 @@
package org.hswebframework.web.service.dictionary.simple.parser;
/**
*
* @author zhouhao
*/
public class FormatterResult<V> {
private V result;
private String pattern;
public FormatterResult() {
}
public FormatterResult(V result) {
this(result, String.valueOf(result));
}
public FormatterResult(V result, String pattern) {
this.result = result;
this.pattern = pattern;
}
public V getResult() {
return result;
}
public void setResult(V result) {
this.result = result;
}
public String getPattern() {
return pattern;
}
public void setPattern(String pattern) {
this.pattern = pattern;
}
}

View File

@@ -0,0 +1,67 @@
package org.hswebframework.web.service.dictionary.simple.parser;
import org.hswebframework.web.RegexUtils;
import java.util.*;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* TODO 完成注释
*
* @author zhouhao
*/
public class SimpleDictParserFormatter implements DictParserFormatter {
public boolean smartParse = true;
public int smartLevel = 3;
public void setSmartParse(boolean smartParse) {
this.smartParse = smartParse;
}
public boolean needParse(String value, DictParserFormat format) {
return value.contains(format.getSplitter())
|| value.contains(format.getChildSplitter())
|| value.contains(format.getChildStartChar());
}
private <T> FormatterResult<T> createResult(T v, String pattern) {
return new FormatterResult<>(v, pattern);
}
@Override
public <T> List<FormatterResult<T>> format(DictParserFormat format
, Object value
, BiFunction<String, String, T> mapping) {
if (value == null) return Collections.emptyList();
String stringValue = String.valueOf(value);
if (!needParse(stringValue, format))
return Collections.singletonList(createResult(mapping.apply(stringValue, stringValue), stringValue));
String splitter = "[" + RegexUtils.escape(format.getSplitter() +
" " + format.getChildStartChar() +
" " + format.getChildSplitter() +
" " + format.getChildEndChar()) +
"]";
return Arrays.stream(stringValue.split(splitter))
.map(val -> {
T v = mapping.apply(val, val);
if (v == null && smartParse) {
StringBuilder tmp = new StringBuilder();
char[] arr = val.toCharArray();
for (int i = 0; i < arr.length; i++) {
tmp.append(arr[i]);
if (i >= smartLevel) {
v = mapping.apply(tmp.toString(), val);
if (null != v) break;
}
}
}
return createResult(v, val);
})
.collect(Collectors.toList());
}
}

View File

@@ -0,0 +1,251 @@
package org.hswebframework.web.service.dictionary.simple.parser;
import org.hswebframework.web.ExpressionUtils;
import org.hswebframework.web.entity.dictionary.DictionaryEntity;
import org.hswebframework.web.entity.dictionary.DictionaryItemEntity;
import java.io.Serializable;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
/**
* TODO 完成注释
*
* @author zhouhao
*/
public class SimpleSingleDictParser implements SingleDictParser {
private Map<String, DictMapping> mapping = new HashMap<>();
private DictParserFormat sourceFormat = new DictParserFormat();
private DictParserFormat targetFormat = new DictParserFormat();
private DictParserFormatter formatter = new SimpleDictParserFormatter();
//设置DictionaryEntity作为配置
public void setDict(DictionaryEntity<? extends DictionaryItemEntity> dict,
Function<DictionaryItemEntity, String> keyGetter,
Function<DictionaryItemEntity, String> valueGetter,
Function<DictionaryItemEntity, String> expressionGetter) {
dict.getItems().forEach(item -> addMapping(item, keyGetter, valueGetter, expressionGetter));
}
public DictParserFormat getTargetFormat() {
return targetFormat;
}
public DictParserFormat getSourceFormat() {
return sourceFormat;
}
private DictMapping addMapping(DictionaryItemEntity item,
Function<DictionaryItemEntity, String> keyGetter,
Function<DictionaryItemEntity, String> valueGetter,
Function<DictionaryItemEntity, String> expressionGetter) {
DictMapping dictMapping = new DictMapping();
dictMapping.setValue(valueGetter.apply(item));
dictMapping.setExpression(expressionGetter.apply(item));
if (item.getChildren() != null) {
dictMapping.setChildren(item.getChildren().stream()
.map(DictionaryItemEntity.class::cast)
.map(i -> addMapping(i, keyGetter, valueGetter, expressionGetter)).collect(Collectors.toList()));
}
String key = keyGetter.apply(item);
dictMapping.setKey(key);
mapping.put(key, dictMapping);
return dictMapping;
}
@Override
public Optional<String> parse(String value, Object context) {
if (value == null) return Optional.empty();
StringJoiner joiner = targetFormat.createJoiner();
List<DictMapping> dictMappings = formatter
.format(sourceFormat, value, (key, pattern) -> {
DictMapping dictMapping = mapping.get(key);
if (dictMapping == null) return null;
dictMapping = dictMapping.clone();
dictMapping.setDefaultVar(Collections.singletonMap("pattern", pattern));
return dictMapping;
})
.stream()
.filter(Objects::nonNull)
.map(FormatterResult::getResult)
.collect(Collectors.toList());
Set<String> notAppendList = new HashSet<>();
List<String> mappingResult = dictMappings.stream()
.filter(Objects::nonNull)
// 过滤子节点
.peek(dictMapping -> dictMapping.filterChildren((mapping -> {
String strVal = mapping.getValue();
notAppendList.add(strVal); //子节点不拼接
int index = dictMappings.indexOf(mappingOfValue(strVal));
DictMapping tmp = null;
if (-1 != index)
tmp = dictMappings.get(index);
if (null != tmp)
mapping.setDefaultVar(tmp.getDefaultVar());
return null != tmp;
})))
.filter(mapping -> !notAppendList.contains(mapping.getValue()))
//字典转为text
.map(dict -> dict.toString(context))
.collect(Collectors.toList());
mappingResult.forEach(joiner::add);
return Optional.ofNullable(joiner.toString());
}
public void setMapping(Map<String, DictMapping> mapping) {
this.mapping = mapping;
}
DictMapping mappingOfValue(String value) {
DictMapping mapping = new DictMapping();
mapping.setValue(value);
return mapping;
}
protected class DictMapping implements Serializable {
private String key;
private String value;
private String expression;
private String expressionLanguage = "spel";
private List<DictMapping> children;
private Map<String, Object> defaultVar;
public void setDefaultVar(Map<String, Object> defaultVar) {
this.defaultVar = defaultVar;
}
public Map<String, Object> getDefaultVar() {
return defaultVar;
}
@Override
public int hashCode() {
if (value == null) return 0;
return value.hashCode();
}
@Override
public boolean equals(Object obj) {
if (obj == null) return false;
return obj == this || hashCode() == obj.hashCode();
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public List<DictMapping> getChildren() {
return children;
}
public String getExpression() {
return expression;
}
public void setExpression(String expression) {
this.expression = expression;
}
public void setExpressionLanguage(String expressionLanguage) {
this.expressionLanguage = expressionLanguage;
}
public String getExpressionLanguage() {
if (expressionLanguage == null) expressionLanguage = "spel";
return expressionLanguage;
}
public void setChildren(List<DictMapping> children) {
this.children = children;
}
public String toString(Function<DictMapping, String> getter) {
StringBuilder stringBuilder = new StringBuilder(getter.apply(this));
if (children != null) {
//根据getter 获取子节点的string
String childrenString = String.join(targetFormat.getChildSplitter(), children.stream()
.map(mapping -> mapping.toString(getter)).collect(Collectors.toList()));
if (childrenString.isEmpty()) return stringBuilder.toString();
//拼接子节点
stringBuilder.append(targetFormat.getChildStartChar())
.append(childrenString)
.append(targetFormat.getChildEndChar());
}
return stringBuilder.toString();
}
public String toString(Object context) {
Function<DictMapping, String> textGetter =
context == null ? DictMapping::getValue :
dictMapping -> {
if (dictMapping.getExpression() == null || dictMapping.getExpression().isEmpty())
return dictMapping.getValue();
// 解析表达式
Map<String, Object> var = new HashMap<>();
if (dictMapping.getDefaultVar() != null) var.putAll(dictMapping.getDefaultVar());
var.put("value", dictMapping.getValue());
var.put("key", dictMapping.getKey());
var.put("context", context);
var.put("children", dictMapping.getChildren());
try {
return ExpressionUtils.analytical(dictMapping.getExpression(), var, dictMapping.getExpressionLanguage());
} catch (Exception e) {
throw new RuntimeException("analytical " + dictMapping.getExpressionLanguage() + " expression :" + dictMapping.getExpression() + " error", e);
}
};
return toString(textGetter);
}
public String toTextString() {
return toString(DictMapping::getValue);
}
public String toValueString() {
return toString(mapping -> String.valueOf(mapping.getValue()));
}
public DictMapping clone() {
DictMapping clone = new DictMapping();
clone.value = value;
clone.key = key;
clone.expression = expression;
clone.expressionLanguage = expressionLanguage;
if (children != null) {
clone.children = children.stream().map(DictMapping::clone).collect(Collectors.toList());
}
return clone;
}
public void filterChildren(Predicate<DictMapping> mappingPredicate) {
if (children != null) {
children = children.stream()
.filter(mappingPredicate)
.collect(Collectors.toList());
children.forEach(children -> children.filterChildren(mappingPredicate));
}
}
}
}

View File

@@ -0,0 +1,17 @@
package org.hswebframework.web.service.dictionary.simple.parser;
import java.io.Serializable;
import java.util.Optional;
/**
* TODO 完成注释
*
* @author zhouhao
*/
public interface SingleDictParser extends Serializable {
Optional<String> parse(String target, Object context);
default Optional<String> parse(String target) {
return parse(target, null);
}
}

View File

@@ -0,0 +1,109 @@
/*
* Copyright 2016 http://www.hswebframework.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*
*/
package org.hswebframework.web.starter.dictionary;
import com.alibaba.fastjson.JSON;
import org.hswebframework.web.entity.dictionary.SimpleDictionaryEntity;
import org.hswebframework.web.entity.dictionary.SimpleDictionaryItemEntity;
import org.hswebframework.web.service.dictionary.DictionaryParser;
import org.hswebframework.web.service.dictionary.simple.SimpleDictionaryParser;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author
*/
public class DictionaryParserTests {
DictionaryParser<String> parser;
@Before
public void init() {
SimpleDictionaryParser<String> parser = new SimpleDictionaryParser<>();
// parser.getToTextParser().getTargetFormat().setSplitter("、");
// parser.getToValueParser().getSourceFormat().setSplitter("、");
SimpleDictionaryEntity dictionaryEntity = new SimpleDictionaryEntity();
String json = "[" +
"{'value':'1','text':'水果','children':" +
"[" +
"{'value':'101','text':'苹果'," +
"'children':[" +
"{'value':'10102','text':'红富士'}" +
",{'value':'10103','text':'青苹果'}" +
//使用表达式进行解析
",{'value':'10105','text':'其他苹果'" +
",'textExpression':'${#value}[${#context[otherApple]}]'" +
",'valueExpression':'${(#context.put(\\'otherApple\\',#pattern.split(\"[ \\\\[ \\\\]]\")[1])==null)?#value:#value}'" +
"}" +
"]}" +
",{'value':'102','text':'梨子'}]" +
"}" +
",{'value':'2','text':'蔬菜'}" +
"]";
List<SimpleDictionaryItemEntity> itemEntities = JSON.parseArray(json, SimpleDictionaryItemEntity.class);
dictionaryEntity.setItems(itemEntities);
parser.setDict(dictionaryEntity);
this.parser = parser;
}
//支持表达式
@Test
public void testParseExpression() {
String val = "1,2,101,10102,10105";
Map<String, Object> data = new HashMap<>();
data.put("otherApple", "其他苹果1号");
String text = parser
.valueToText(val, data)
.get();
System.out.println(text);
data.clear();
String parseVal = parser.textToValue(text, data)
.get();
System.out.println(parseVal);
System.out.println(data);
}
//普通的解析
@Test
public void testParseText() throws Exception {
String val = "1,101,10102,10103,2";
String text = parser.valueToText(val).get();
System.out.println(text);
String parsedVal = parser.textToValue(text).get();
System.out.println(parsedVal);
Assert.assertEquals(val, parsedVal);
}
}