校验C结构体基本数据类型以及数组,和数组、结构体嵌套其他类型
public class DataTypeSpecCheckUtil {
static final String MIN = "min";
static final String MAX = "max";
static final String UNIT = "unit";
static final String UNIT_NAME = "unitName";
static final String STEP = "step";
static final String RANGE = "range";
static final String ITEM = "item";
static final String DATA_TYPE = "dataType";
static final String TYPE = "type";
static final String SPECS = "specs";
static final String LENGTH = "length";
static final String RULE = "rule";
static final String SIZE = "size";
static final String IDENTIFIER = "identifier";
static final String NAME = "name";
static final String DESCRIPTION = "description";
static final String PROPERTY = "property";
private static final long LONG_MAX_VALUE = 9176873029745254541L;
private static final long LONG_MIN_VALUE = -9176873029745254541L;
private static final long INT_MAX_VALUE = 2147483647L;
private static final long INT_MIN_VALUE = -2147483647L;
/**
* 浮点型数据校验
*/
public static boolean isValidDouble(String doubleStr) {
if (StringUtils.isNotEmpty(doubleStr)) {
String regx = "^(-?\\d+)(\\.\\d{0,6})?$;
Pattern p = Pattern.compile(regx);
Matcher matcher = p.matcher(doubleStr);
return matcher.find();
}
return false;
}
public static boolean isValidNaming4Name(String identifier) {
if (identifier.length() < 1 || identifier.length() > 64) {
log.error("标识符长度异常!");
return false;
}
String regx = "^[_A-Za-z0-9\\-\\u4e00-\\u9fa5]+$;
Pattern p = Pattern.compile(regx);
Matcher matcher = p.matcher(identifier);
return matcher.find();
}
public static boolean isValidNaming4Identifier(final String identifier) {
/** List<String> commonTypes = Arrays.asList("byte","int","float","double","char","array","enum","struct","bool");
if (commonTypes.contains(identifier)){
log.error("基本类型不能作为标识符!");
return false;
}*/
if (identifier.length() < 1 || identifier.length() > 64) {
log.error("标识符长度异常!");
return false;
}
String regx = "^[_a-zA-Z0-9]+$;
Pattern p = Pattern.compile(regx);
Matcher matcher = p.matcher(identifier);
return matcher.find();
}
public static boolean isValidNumStr(String num) {
if ("0".equals(num)) return true;
if (StringUtils.isNotEmpty(num)) {
return num.matches("^[\\-1-9]+[0-9]$");
}
return false;
}
private static boolean isValidArraySpecs(JSONObject arrayJSONObject) {
try {
if (arrayJSONObject != null && arrayJSONObject.containsKey(SIZE)
&& ((arrayJSONObject.getIntValue(SIZE) > 0
&& arrayJSONObject.getIntValue(SIZE) <= 65535))
&& arrayJSONObject.containsKey(ITEM)
&& arrayJSONObject.get(ITEM) != null) {
JSONObject itemJSONObject = arrayJSONObject.getJSONObject(ITEM);
if (!itemJSONObject.containsKey(DATA_TYPE)) {
log.error("===> isValidArraySpecs array型,数据规格定义 itemJSONObject = {} 错误!缺少dataType节点", itemJSONObject);
return false;
}
JSONObject dataTypeJSONObject = itemJSONObject.getJSONObject(DATA_TYPE);
if (!dataTypeJSONObject.containsKey(TYPE) || !dataTypeJSONObject.containsKey(SPECS)) {
log.error("===> array型,数据规格定义 dataTypeJSONObject = {} 错误!必须有节点specs和type", dataTypeJSONObject);
return false;
}
int type = dataTypeJSONObject.getIntValue(TYPE);
String specs = dataTypeJSONObject.getString(SPECS);
if (0 == type || 7 == type) {
log.error("===> array型,数据规格定义 数组层级下不能是Bytes或者数组 {}", dataTypeJSONObject);
return false;
}
// 结构体校验
if (8 == type && dataTypeJSONObject.containsKey(SPECS)) {
boolean validStructSpecs = isValidStructSpecs(specs, 2);
if (!validStructSpecs) {
log.error("===> array型,数据规格定义 数组层级下结构体不合法 {}", dataTypeJSONObject);
return false;
}
}
// 数组里的普通类型
if (type == 1 || type == 2 || type == 3 || type == 4 || type == 5 || type == 6) {
if (!isValidCommonSpecs(type, specs)) return false;
}
return true;
} else {
log.error("===> array型,数据规格定义为空!");
return true;
}
} catch (Exception e) {
if (e instanceof IotBusinessException)
throw e;
return false;
}
}
private static boolean isValidStructSpecs(String specs, int level) {
JSONArray retArray = JSON.parseArray(specs);
if (CollectionUtils.isEmpty(retArray)) {
throw new IotBusinessException("结构体数据规格定义不允许为空!");
}
Set<String> identifierSet = new HashSet<>();
for (int i = 0; i < retArray.size(); i++) {
JSONObject jsonObject = retArray.getJSONObject(i);
log.info("jsonObject = {}, i = {}", jsonObject, i);
if (jsonObject == null
|| !jsonObject.containsKey(IDENTIFIER)
|| !jsonObject.containsKey(NAME)
|| !jsonObject.containsKey(DATA_TYPE)
|| !jsonObject.containsKey(DESCRIPTION)) {
return false;
}
if (!isValidNaming4Name(jsonObject.getString(NAME))) {
log.error("名称,长度异常或者是名称不合法");
return false;
}
if (!isValidNaming4Identifier(jsonObject.getString(IDENTIFIER))) {
log.error("标识符,长度异常或者是名称不合法");
return false;
}
identifierSet.add(jsonObject.getString(IDENTIFIER));
JSONObject dataTypeObject = jsonObject.getJSONObject(DATA_TYPE);
if (!dataTypeObject.containsKey(TYPE) || !dataTypeObject.containsKey(SPECS)) {
log.error("结构体校验,子元素缺少key dataTypeObject = {}", dataTypeObject);
return false;
}
if (dataTypeObject.getIntValue(TYPE) == 8) {
level = level - 1;
if (!isValidStructSpecs(dataTypeObject.getString(SPECS), level)) {
log.error("结构体校验嵌套结构体,specs = {}", dataTypeObject.getString(SPECS));
return false;
}
}
if (dataTypeObject.getIntValue(TYPE) == 7) {
if (!isValidArraySpecsByArraySpecsAndLevel(dataTypeObject.getString(SPECS), level - 1))
return false;
}
}
}
private static boolean isValidStructSpecs(String specs, int level) {
JSONArray retArray = JSON.parseArray(specs);
if (CollectionUtils.isEmpty(retArray)) {
throw new IotBusinessException("结构体数据规格定义不允许为空!");
}
Set<String> identifierSet = new HashSet<>();
for (int i = 0; i < retArray.size(); i++) {
JSONObject jsonObject = retArray.getJSONObject(i);
log.info("jsonObject = {}, i = {}", jsonObject, i);
if (jsonObject == null
|| !jsonObject.containsKey(IDENTIFIER)
|| !jsonObject.containsKey(NAME)
|| !jsonObject.containsKey(DATA_TYPE)
|| !jsonObject.containsKey(DESCRIPTION)) {
return false;
}
if (!isValidNaming4Name(jsonObject.getString(NAME))) {
log.error("名称,长度异常或者是名称不合法");
return false;
}
if (dataTypeObject.getIntValue(TYPE) != 8 && dataTypeObject.getIntValue(TYPE) != 7) {
int type = dataTypeObject.getIntValue(TYPE);
String specsInner = dataTypeObject.getString(SPECS);
// level = 0;
if (!isValidCommonSpecs(type, specsInner)) {
log.error("结构体内层定义: type = {}, specsInner = {}", type, specsInner);
return false;
}
}
// level = 0;
}
if (identifierSet.size() != retArray.size()) {
return false;
}
return true;
}
private static boolean isValidArraySpecsByArraySpecsAndLevel(String arraySpecs, int level) {
try {
JSONObject arrayJSONObject = JSONObject.parseObject(arraySpecs);
if (arrayJSONObject.containsKey(SIZE)
&& ((Integer) arrayJSONObject.get(SIZE) >= 0
&& (Integer) arrayJSONObject.get(SIZE) <= 65535)
&& arrayJSONObject.containsKey(ITEM)
&& arrayJSONObject.get(ITEM) != null) {
JSONObject itemJSONObject = arrayJSONObject.getJSONObject(ITEM);
if (!itemJSONObject.containsKey(DATA_TYPE)) {
log.error("===> isValidArraySpecs array型,数据规格定义 itemJSONObject = {} 错误!缺少dataType节点", itemJSONObject);
return false;
}
JSONObject dataTypeJSONObject = itemJSONObject.getJSONObject(DATA_TYPE);
if (!dataTypeJSONObject.containsKey(TYPE) || !dataTypeJSONObject.containsKey(SPECS)) {
log.error("===> array型,数据规格定义 dataTypeJSONObject = {} 错误!必须有节点specs和type", dataTypeJSONObject);
return false;
}
int type = dataTypeJSONObject.getInteger(TYPE);
String specs = dataTypeJSONObject.getString(SPECS);
// 数组下不能包含Bytes、数组
if (0 == type || 7 == type) {
log.error("===> array型,数据规格定义 数组层级下不能是Bytes或者数组 {}", dataTypeJSONObject);
return false;
}
// 结构体校验
if (8 == type && dataTypeJSONObject.containsKey(SPECS)) {
boolean validStructSpecs = isValidStructSpecs(dataTypeJSONObject.getString(SPECS), level - 1);
if (!validStructSpecs) {
log.error("===> array型,数据规格定义 数组层级下结构体不合法 {}", dataTypeJSONObject);
return false;
}
}
// 数组里的普通类型
if (type == 1 || type == 2 || type == 3 || type == 4 || type == 5 || type == 6) {
if (!isValidCommonSpecs(type, specs)) {
log.error("结构体内层定义,一般数据类型: type = {}, specs = {}", type, specs);
return false;
}
}
return true;
} else {
log.error("===> array型,数据规格定义 {} 错误!节点缺少或者个数不在规定范围内", arrayJSONObject);
return false;
}
} catch (Exception e) {
if (e instanceof IotBusinessException)
throw e;
log.error("===> array型,数据规格定义错误!某些节点解析错误!");
return false;
}
}
private static boolean isValidCommonSpecs(int dataType, String specs) {
JSONObject specsRet = new JSONObject();
JSONObject parseObject;
try {
parseObject = JSON.parseObject(specs);
if (dataType != 4 && parseObject == null) {
return false;
}
} catch (Exception e) {
return false;
}
}
private static boolean isValidArraySpecs(JSONObject arrayJSONObject) {
try {
if (arrayJSONObject != null && arrayJSONObject.containsKey(SIZE)
&& ((arrayJSONObject.getIntValue(SIZE) > 0
&& arrayJSONObject.getIntValue(SIZE) <= 65535))
&& arrayJSONObject.containsKey(ITEM)
&& arrayJSONObject.get(ITEM) != null) {
JSONObject itemJSONObject = arrayJSONObject.getJSONObject(ITEM);
if (!itemJSONObject.containsKey(DATA_TYPE)) {
log.error("===> isValidArraySpecs array型,数据规格定义 itemJSONObject = {} 错误!缺少dataType节点", itemJSONObject);
return false;
}
JSONObject dataTypeJSONObject = itemJSONObject.getJSONObject(DATA_TYPE);
if (!dataTypeJSONObject.containsKey(TYPE) || !dataTypeJSONObject.containsKey(SPECS)) {
log.error("===> isValidArraySpecs array型,数据规格定义 dataTypeJSONObject = {} 错误!缺少type节点或者缺少spec节点", dataTypeJSONObject);
return false;
}
switch (dataType) {
case 0: // Bytes型
/**
* {"min":"","max":"","step":"","unit":"","length":1024}
*/
if (parseObject != null && parseObject.containsKey(LENGTH) && parseObject.get(LENGTH) != null) {
int length;
try {
length = parseObject.getIntValue(LENGTH);
} catch (Exception e) {
return false;
}
if (length <= 0 || 65535 < length) {
return false;
}
} else {
log.error("===> Bytes型,数据规格定义 {} 错误!", specs);
return false;
}
break;
case 1: // float型 TODO 精细化浮点型数据 string
/**
* {"min":"0.1","max":"11.0","step":"0.1","unit":"min","range":["0.1","11.0"]}
* 需要注意的是单位能不能为空
*/
if (parseObject.containsKey(STEP)
&& parseObject.containsKey(UNIT)
&& parseObject.containsKey(MAX)
&& parseObject.containsKey(MIN)) {
if (parseObject.get(STEP) == null
|| parseObject.get(UNIT) == null
|| parseObject.get(MIN) == null
|| parseObject.get(MAX) == null) { // key 对应的 value不能为空
log.error("float型,数据规格定义错误!value不能为空");
return false;
}
String minStr = parseObject.getString(MIN);
String maxStr = parseObject.getString(MAX);
String stepStr = parseObject.getString(STEP);
if (!isValidDouble(maxStr) || !isValidDouble(minStr) || !isValidDouble(stepStr)) {
log.error("浮点型数据定义错误!");
return false;
}
BigDecimal min, max, step;
try {
min = new BigDecimal(parseObject.getString(MIN));
max = new BigDecimal(parseObject.getString(MAX));
step = new BigDecimal(parseObject.getString(STEP));
} catch (Exception e) {
return false;
}
BigDecimal maxValue = new BigDecimal("9007199254740991.0");
BigDecimal minValue = new BigDecimal("-9007199254740992.0");
if (max.compareTo(maxValue) > 0) {
log.error("浮点型数据超过最大值!");
return false;
}
specsRet.put(RANGE, Arrays.asList(parseObject.getDoubleValue(MIN), parseObject.getDoubleValue(MAX)));
if (min.compareTo(minValue) < 0) {
log.error("浮点型数据不能小于最小值!");
return false;
}
try {
parseObject.getDoubleValue(STEP);
} catch (Exception e) {
log.error("浮点型,数据规格定义错误! step不能转为浮点型数据!");
return false;
}
if (step.compareTo(BigDecimal.ZERO) < 0) {
log.error("浮点型,数据规格定义错误 步长必须非负!");
return false;
}
if (min.add(step).compareTo(max) > 0) {
throw new IotBusinessException("浮点型,数据规格定义错误 步长错误!最小值加上一个步长不能大于最大值");
}
} else {
log.error("===> 浮点型,数据规格定义 {} 错误!", specs);
return false;
}
break;
case 2: // int型
/**
* {"min":1,"max":10,"step":1,"unit":"min"}
* 需要注意的是单位能不能为空
*/
if (parseObject.containsKey(STEP)
&& parseObject.containsKey(UNIT)
&& parseObject.containsKey(MIN)
&& parseObject.containsKey(MAX)) {
if (parseObject.get(STEP) == null
|| parseObject.get(UNIT) == null
|| parseObject.get(MIN) == null || parseObject.get(MAX) == null) { // key 对应的 value不能为空
throw new IotBusinessException("int型,数据规格定义错误!value不能为空");
}
String numMax = parseObject.getString(MAX);
String numMin = parseObject.getString(MIN);
String numStep = parseObject.getString(STEP);
if (!isValidNumStr(numMax) || !isValidNumStr(numMin) || !isValidNumStr(numStep)) {
log.error("不能转换成数值!{}", specs);
return false;
}
if (numMax.replaceAll("-", "").length() > 16 || numMin.replaceAll("-", "").length() > 16 || numStep.replaceAll("-", "").length() > 16) {
log.error("超过最大整型表示范围!{}", specs);
return false;
}
long step;
long min;
long max = parseObject.getLongValue(MAX);
step = parseObject.getLongValue(STEP);
min = parseObject.getLongValue(MIN);
String maxStr = parseObject.getString(MAX);
if (new BigDecimal(maxStr).compareTo(new BigDecimal(LONG_MAX_VALUE)) > 0) {
throw new IotBusinessException("最大值错误!");
}
if (new BigDecimal(LONG_MIN_VALUE).compareTo(new BigDecimal(min)) > 0)
throw new IotBusinessException("最小值错误!");
if (step < 0) {
throw new IotBusinessException("int型,数据规格定义错误 步长必须为正整数!");
}
if (IoTALUtil.addWithNoScale(new BigDecimal(step), new BigDecimal(min)).compareTo(new BigDecimal(max)) > 0) {
throw new IotBusinessException("int型,数据规格定义错误 步长错误!最小值加上一个步长不能大于最大值");
}
} else {
throw new IotBusinessException("===> int型,数据规格定义错误!specs = " + specs);
}
break;
case 3: // text型
/**
* {"length":1024}
* 设计文档
* rule String 文本合法校验规则,正则表达式,允许填空
* length Int32 字符串最大长度,整型值,必须大于0,允许最大长度65535
*/
if (parseObject.containsKey(LENGTH) && parseObject.get(LENGTH) != null) {
int textLength;
try {
textLength = parseObject.getIntValue(LENGTH);
} catch (Exception e) {
throw new IotBusinessException("===> text型,数据规格定义 length错误!length = " + parseObject.get(LENGTH));
}
if (0 < textLength && 65535 >= textLength) {
return true;
} else {
return false;
}
} else {
log.error("===> text型,数据规格定义 {} 错误!", specs);
return false;
}
case 4: // date型
if (parseObject.size() != 0)
return false;
break;
case 5: // bool型
/**
* {"0":"关","1":"开"}
*/
if (!parseObject.containsKey("0")
|| parseObject.get("0") == null
|| !parseObject.containsKey("1")
|| parseObject.get("1") == null) {
log.error("===> bool型,数据规格定义 {} 错误!", specs);
return false;
}
break;
case 6: // enum型
if (parseObject.containsKey("list") && parseObject.get("list") != null /*&& !(parseObject.get("list") instanceof List)*/) {
// enum 外层结构正确 校验数组内部元素
List<SpecsEnumDTO> specsEnumDTOS;
String list = parseObject.getString("list");
JSONArray objects = JSON.parseArray(list);
int size = objects.size();
for (int i = 0; i < size; i++) {
JSONObject o = (JSONObject) JSON.parseArray(list).get(i);
if (!o.containsKey("description") || !o.containsKey("value") || o.size() != 2) {
log.error("枚举类型校验失败,缺少description字段!i = {}, JSONObject = {}", i, o);
return false;
}
}
try {
specsEnumDTOS = JSON.parseArray(list, SpecsEnumDTO.class);
} catch (Exception e) {
log.error("=====>ProductPropertiesService#add#switch case 6 enum: " + parseObject.get("list"));
return false;
}
if (CollectionUtils.isEmpty(specsEnumDTOS)) {
log.error("=====>ProductPropertiesService#add#switch case 6 enum definition specs is empty list! ");
return false;
}
Set<Long> collect = specsEnumDTOS.stream().map(SpecsEnumDTO::getValue).collect(Collectors.toSet());
boolean present = collect.stream().anyMatch(a -> a > INT_MAX_VALUE || a < INT_MIN_VALUE);
if (present) {
log.error("enum型,数据规格定义错误!枚举定义值超过范围");
return false;
}
if (collect.size() < specsEnumDTOS.size()) {
log.error("enum型,数据规格定义错误!枚举定义重复");
return false;
}
} else {
log.error("===> enum型,数据规格定义 {} 错误!", specs);
return false;
}
break;
case 7: // array型 数组可以有0个元素 0~65535 数组下不能有:数组、Bytes
/**
* {
* "size": 10,
* "item": {
* "dataType": {
* "type": 2,
* "specs": {
* "min": 1,
* "max": 11,
* "step": 1,
* "unit": "min",
* "range": [
* 1,
* 11
* ]
* }
* }
* }
* }
*/
boolean isValidArray = isValidArraySpecs(parseObject);
if (!isValidArray) {
return false;
}
break;
}
return true;
}
}
}
}