From 5e0c54af174d215f14dbb5fba0cb8c4a82684be1 Mon Sep 17 00:00:00 2001 From: owent <> Date: Sat, 13 Jul 2024 00:35:48 +0800 Subject: [PATCH] Prepare v2.17.0 --- HISTORY.md | 5 + README.md | 2 + pom.xml | 2 +- src/org/xresloader/core/ProgramOptions.java | 9 ++ .../core/data/vfy/DataVerifyImpl.java | 126 ++++++++++++------ .../core/data/vfy/DataVerifyRegex.java | 89 +++++++++++++ 6 files changed, 191 insertions(+), 42 deletions(-) create mode 100644 src/org/xresloader/core/data/vfy/DataVerifyRegex.java diff --git a/HISTORY.md b/HISTORY.md index eeaa860c..bc7ab05a 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -2,6 +2,11 @@ ## Unrelease +## 2.17.0 + +1. 增加 `--disable-data-validator` 允许跳过数据验证。 +2. 增加正则表达式验证器 `Regex("正则表达式")` 。 + ## 2.16.1 1. 修复字段别名的一处错误 diff --git a/README.md b/README.md index e1c28bce..90403092 100644 --- a/README.md +++ b/README.md @@ -120,6 +120,7 @@ echo " | --javascript-global | 导出javascript全局空间 | 导出数据到全局时,可以指定写入的名字空间 | | --ignore-unknown-dependency | 忽略未知的依赖项 | 忽略未知的输入协议的依赖项(>=2.9.0版本) | | --validator-rules | 指定自定义验证器配置文件路径 | 指定自定义验证器配置文件路径 | +| --disable-data-validator | 允许忽略数据验证错误 | (>=2.17.0版本) | | --data-source-lru-cache-rows | 数据源的LRU Cache行数 | 仅缓存流式索引 | | --tolerate-max-empty-rows | 连续空行检测的行数 | 设置连续空行检测的行数(>=2.14.1版本) ,大量的连续空行通常是误操作 | @@ -287,6 +288,7 @@ Excel里的Key使用@后缀的字段名,@后面的部分都属于验证器。 + 函数: `InText("文件名"[, 第几个字段[, \"字段分隔正则表达式\"]])` : 从文本文件(UTF-8编码),可以指定读第几个字段和用于字段分隔的正则表达式 + 函数: `InTableColumn("文件名", "Sheet名", 从第几行开始, 从第几列开始)` : 从Excel数据列读取可用值,指定数据行和数据列 + 函数: `InTableColumn("文件名", "Sheet名", 从第几行开始, KeyRow, KeyValue)` : 从Excel数据列读取可用值,指定数据行并通过某一行的的值获取数据列 ++ 函数: `Regex("正则表达式")` : 验证匹配正则表达式(>=2.17.0版本) + 自定义验证器名(通过 `--validator-rules` 加载) + 协议类型(对应protobuf的message里的每个field,excel里可以填field number或者field name) + 枚举类型(对应protobuf的enum里的每个number,excel里可以填enum number或者enum name) diff --git a/pom.xml b/pom.xml index e0d1ce26..72c3d1d0 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ org.xresloader xresloader - 2.16.1 + 2.17.0 jar xresloader diff --git a/src/org/xresloader/core/ProgramOptions.java b/src/org/xresloader/core/ProgramOptions.java index 85911bfe..9ac19d87 100644 --- a/src/org/xresloader/core/ProgramOptions.java +++ b/src/org/xresloader/core/ProgramOptions.java @@ -57,6 +57,7 @@ public class RenameRule { public int prettyIndent = 0; public boolean enableStdin = false; public String[] customValidatorRules = null; + public boolean enableDataValidator = true; public String protoDumpFile = ""; public ProtoDumpType protoDumpType = ProtoDumpType.NONE; @@ -231,6 +232,8 @@ private static synchronized Options get_options_group() { options.addOption(null, "lua-module", true, "module(MODULE_NAME, package.seeall) if in lua mode"); options.addOption(Option.builder().longOpt("validator-rules") .desc("set file to load custom validator").hasArg().argName("FILE PATH").build()); + options.addOption(null, "disable-data-validator", false, + "disable data validator, so it will not show warnings when data checking failed."); options.addOption(Option.builder().longOpt("data-source-lru-cache-rows") .desc("set row number for LRU cache").hasArg().argName("NUMBER").build()); options.addOption(Option.builder().longOpt("tolerate-max-empty-rows") @@ -492,6 +495,12 @@ public int init(String[] args) { // custom validator rule file customValidatorRules = cmd.getOptionValues("validator-rules"); + if (cmd.hasOption("disable-data-validator")) { + enableDataValidator = false; + } else { + enableDataValidator = true; + } + return 0; } diff --git a/src/org/xresloader/core/data/vfy/DataVerifyImpl.java b/src/org/xresloader/core/data/vfy/DataVerifyImpl.java index 515f91d0..3513e681 100644 --- a/src/org/xresloader/core/data/vfy/DataVerifyImpl.java +++ b/src/org/xresloader/core/data/vfy/DataVerifyImpl.java @@ -8,6 +8,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.xresloader.core.ProgramOptions; import org.xresloader.core.data.err.ConvException; /** @@ -149,10 +150,10 @@ static public double getAndVerify(List verifyEngine, String path return n; } - try { - DataVerifyResult verify_cache = new DataVerifyResult(); + DataVerifyResult verify_cache = new DataVerifyResult(); - for (DataVerifyImpl vfy : verifyEngine) { + for (DataVerifyImpl vfy : verifyEngine) { + try { if (vfy.get(n, verify_cache)) { if (verify_cache.value == null) { return 0; @@ -165,16 +166,22 @@ static public double getAndVerify(List verifyEngine, String path } return doubleValueOf(verify_cache.value.toString()); } + } catch (Exception e) { + String value; + if (n == (long) n) { + value = String.format("%d", (long) n); + } else { + value = String.format("%g", n); + } + String message = String.format("Check %s for %s with validator %s failed, %s", value, path, + vfy.getDescription(), e.getMessage()); + if (ProgramOptions.getInstance().enableDataValidator) { + throw new ConvException( + message); + } else { + ProgramOptions.getLoger().warn(message); + } } - } catch (Exception e) { - String value; - if (n == (long) n) { - value = String.format("%d", (long) n); - } else { - value = String.format("%g", n); - } - throw new ConvException(String.format("Check %s for %s with %s %s failed, %s", value, path, - getValidatorWord(verifyEngine), collectValidatorNames(verifyEngine), e.getMessage())); } String value; @@ -183,9 +190,16 @@ static public double getAndVerify(List verifyEngine, String path } else { value = String.format("%g", n); } - throw new ConvException( - String.format("Check %s for %s with %s %s failed, check data failed.", value, path, - getValidatorWord(verifyEngine), collectValidatorNames(verifyEngine))); + + String message = String.format("Check %s for %s with %s %s failed, check data failed.", value, path, + getValidatorWord(verifyEngine), collectValidatorNames(verifyEngine)); + if (ProgramOptions.getInstance().enableDataValidator) { + throw new ConvException( + message); + } else { + ProgramOptions.getLoger().warn(message); + return 0.0; + } } static public double getAndVerifyToDouble(List verifyEngine, String path, String val) @@ -222,42 +236,60 @@ static public double getAndVerifyToDouble(List verifyEngine, Str DataVerifyResult verify_cache = new DataVerifyResult(); for (DataVerifyImpl vfy : verifyEngine) { - if (vfy.get(val, verify_cache)) { - if (verify_cache.value == null) { - return 0; - } - if (verify_cache.value instanceof Double) { - return (double) verify_cache.value; + try { + if (vfy.get(val, verify_cache)) { + if (verify_cache.value == null) { + return 0; + } + if (verify_cache.value instanceof Double) { + return (double) verify_cache.value; + } + if (verify_cache.value instanceof Long) { + return ((Long) verify_cache.value).doubleValue(); + } + return doubleValueOf(verify_cache.value.toString()); } - if (verify_cache.value instanceof Long) { - return ((Long) verify_cache.value).doubleValue(); + } catch (Exception e) { + String message = String.format("Check %s for %s with validator %s failed, %s", val, path, + vfy.getDescription(), e.getMessage()); + if (ProgramOptions.getInstance().enableDataValidator) { + throw new ConvException( + message); + } else { + ProgramOptions.getLoger().warn(message); } - return doubleValueOf(verify_cache.value.toString()); } } } catch (Exception e) { - if (verifyEngine == null || verifyEngine.isEmpty()) { - throw new ConvException(String.format("Convert %s for %s failed, %s", val, path, e.getMessage())); + String message = String.format("Convert %s for %s failed, %s", val, path, e.getMessage()); + if (ProgramOptions.getInstance().enableDataValidator) { + throw new ConvException( + message); } else { - throw new ConvException(String.format("Convert %s for %s with %s %s failed, %s", val, path, - getValidatorWord(verifyEngine), collectValidatorNames(verifyEngine), e.getMessage())); + ProgramOptions.getLoger().warn(message); } } - throw new ConvException(String.format("Convert %s for %s with %s %s failed, check data failed.", val, - path, getValidatorWord(verifyEngine), collectValidatorNames(verifyEngine))); + String message = String.format("Convert %s for %s with %s %s failed, check data failed.", val, + path, getValidatorWord(verifyEngine), collectValidatorNames(verifyEngine)); + if (ProgramOptions.getInstance().enableDataValidator) { + throw new ConvException(); + } else { + ProgramOptions.getLoger().warn(message); + return 0.0; + } } static public String getAndVerifyToString(List verifyEngine, String path, String val) throws ConvException { - try { - if (verifyEngine == null || verifyEngine.isEmpty()) { - return val; - } + if (verifyEngine == null || verifyEngine.isEmpty()) { + return val; + } - DataVerifyResult verify_cache = new DataVerifyResult(); + DataVerifyResult verify_cache = new DataVerifyResult(); - for (DataVerifyImpl vfy : verifyEngine) { + for (DataVerifyImpl vfy : verifyEngine) { + try { if (vfy.get(val, verify_cache)) { if (verify_cache.value == null) { return ""; @@ -276,14 +308,26 @@ static public String getAndVerifyToString(List verifyEngine, Str } return verify_cache.value.toString(); } + } catch (Exception e) { + String message = String.format("Check %s for %s with validator %s failed, %s", val, path, + vfy.getDescription(), e.getMessage()); + if (ProgramOptions.getInstance().enableDataValidator) { + throw new ConvException( + message); + } else { + ProgramOptions.getLoger().warn(message); + } } - } catch (Exception e) { - throw new ConvException(String.format("Convert %s for %s with %s %s failed, %s", val, path, - getValidatorWord(verifyEngine), collectValidatorNames(verifyEngine), e.getMessage())); } - throw new ConvException(String.format("Convert %s for %s with %s %s failed, check data failed.", val, - path, getValidatorWord(verifyEngine), collectValidatorNames(verifyEngine))); + String message = String.format("Convert %s for %s with %s %s failed, check data failed.", val, + path, getValidatorWord(verifyEngine), collectValidatorNames(verifyEngine)); + if (ProgramOptions.getInstance().enableDataValidator) { + throw new ConvException(message); + } else { + ProgramOptions.getLoger().warn(message); + return ""; + } } static public long getAndVerifyToLong(List verifyEngine, String path, String val) diff --git a/src/org/xresloader/core/data/vfy/DataVerifyRegex.java b/src/org/xresloader/core/data/vfy/DataVerifyRegex.java new file mode 100644 index 00000000..87834960 --- /dev/null +++ b/src/org/xresloader/core/data/vfy/DataVerifyRegex.java @@ -0,0 +1,89 @@ +package org.xresloader.core.data.vfy; + +import java.util.ArrayList; +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; + +import org.xresloader.core.ProgramOptions; + +public class DataVerifyRegex extends DataVerifyImpl { + private boolean valid = false; + private ArrayList rules = new ArrayList<>(); + + static Pattern SPACE_SPLITOR = Pattern.compile("\\s+"); + + public DataVerifyRegex(ValidatorTokens tokens) { + super(tokens); + + this.valid = false; + if (tokens.parameters.size() < 2) { + ProgramOptions.getLoger().error("Invalid in regex validator %s", tokens.name); + return; + } + + this.valid = true; + for (int i = 1; i < tokens.parameters.size(); ++i) { + try { + this.rules.add(Pattern.compile(tokens.parameters.get(i))); + } catch (PatternSyntaxException e) { + ProgramOptions.getLoger().error("Can not parse regex %s for validator %s : %s", + tokens.parameters.get(i), + tokens.name, + e.getMessage()); + this.valid = false; + } + } + } + + public boolean isValid() { + return this.valid; + } + + @Override + public boolean get(double number, DataVerifyResult res) { + // 0 值永久有效,因为空数据项会被填充默认值 + if (0 == number) { + res.success = true; + res.value = number; + return true; + } + + String value; + if (number == (long) number) { + value = String.format("%d", (long) number); + } else { + value = String.format("%g", number); + } + + for (Pattern rule : this.rules) { + if (rule.matcher(value).matches()) { + res.success = true; + res.value = number; + return true; + } + } + res.success = false; + return false; + } + + @Override + public boolean get(String input, DataVerifyResult res) { + // 空值永久有效,因为空数据项会被填充默认值 + if (input.isEmpty()) { + res.success = true; + res.value = ""; + return true; + } + + for (Pattern rule : this.rules) { + if (rule.matcher(input.trim()).matches()) { + res.success = true; + res.value = input; + return true; + } + } + + res.success = false; + return false; + } +}