fix(接口测试): json-schema按照正则表达式自动生成数据报错
--bug=1044111 --user=陈建星 【接口测试】接口-json请求参数-schema格式-string类型字段-参数值为空-json自动生成-正则表达式获取失败 https://www.tapd.cn/55049933/s/1550058
This commit is contained in:
parent
0480e75c43
commit
3dbde4f287
|
@ -94,12 +94,6 @@
|
||||||
</exclusion>
|
</exclusion>
|
||||||
</exclusions>
|
</exclusions>
|
||||||
</dependency>
|
</dependency>
|
||||||
<!-- 根据正则表达式,生成随机字符串 -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.github.krraghavan</groupId>
|
|
||||||
<artifactId>xeger</artifactId>
|
|
||||||
<version>${xeger.version}</version>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|
|
@ -2,14 +2,16 @@ package io.metersphere.api.utils;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
import com.fasterxml.jackson.databind.node.*;
|
import com.fasterxml.jackson.databind.node.*;
|
||||||
|
import io.metersphere.api.utils.regex.model.Node;
|
||||||
|
import io.metersphere.api.utils.regex.model.OrdinaryNode;
|
||||||
import io.metersphere.project.constants.PropertyConstant;
|
import io.metersphere.project.constants.PropertyConstant;
|
||||||
import nl.flotsam.xeger.Xeger;
|
|
||||||
import org.apache.commons.collections4.MapUtils;
|
import org.apache.commons.collections4.MapUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.commons.text.RandomStringGenerator;
|
import org.apache.commons.text.RandomStringGenerator;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
|
import java.security.SecureRandom;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
@ -143,13 +145,17 @@ public class JsonSchemaBuilder {
|
||||||
int max = isTextNotBlank(maxLength) ? maxLength.asInt() : 20;
|
int max = isTextNotBlank(maxLength) ? maxLength.asInt() : 20;
|
||||||
int min = isTextNotBlank(minLength) ? minLength.asInt() : 1;
|
int min = isTextNotBlank(minLength) ? minLength.asInt() : 1;
|
||||||
if (enumValues != null && enumValues instanceof ArrayNode) {
|
if (enumValues != null && enumValues instanceof ArrayNode) {
|
||||||
value = enumValues.get(new Random().nextInt(enumValues.size())).asText();
|
value = enumValues.get(new SecureRandom().nextInt(enumValues.size())).asText();
|
||||||
if (value.length() > max) {
|
if (value.length() > max) {
|
||||||
value = value.substring(0, max);
|
value = value.substring(0, max);
|
||||||
}
|
}
|
||||||
} else if (isTextNotBlank(pattern)) {
|
} else if (isTextNotBlank(pattern)) {
|
||||||
Xeger generator = new Xeger(pattern.asText());
|
try {
|
||||||
value = generator.generate();
|
Node node = new OrdinaryNode(pattern.asText());
|
||||||
|
value = node.random();
|
||||||
|
} catch (Exception e) {
|
||||||
|
value = pattern.asText();
|
||||||
|
}
|
||||||
} else if (isTextNotBlank(defaultValue)) {
|
} else if (isTextNotBlank(defaultValue)) {
|
||||||
value = defaultValue.asText();
|
value = defaultValue.asText();
|
||||||
if (value.length() > max) {
|
if (value.length() > max) {
|
||||||
|
@ -159,7 +165,7 @@ public class JsonSchemaBuilder {
|
||||||
value = value + generateStr(min - value.length());
|
value = value + generateStr(min - value.length());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
value = generateStr(new Random().nextInt(max - min + 1) + min);
|
value = generateStr(new SecureRandom().nextInt(max - min + 1) + min);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
yield new TextNode(value);
|
yield new TextNode(value);
|
||||||
|
@ -169,7 +175,7 @@ public class JsonSchemaBuilder {
|
||||||
JsonNode enumValues = propertyNode.get(PropertyConstant.ENUM_VALUES);
|
JsonNode enumValues = propertyNode.get(PropertyConstant.ENUM_VALUES);
|
||||||
JsonNode defaultValue = propertyNode.get(PropertyConstant.DEFAULT_VALUE);
|
JsonNode defaultValue = propertyNode.get(PropertyConstant.DEFAULT_VALUE);
|
||||||
if (enumValues != null && enumValues instanceof ArrayNode) {
|
if (enumValues != null && enumValues instanceof ArrayNode) {
|
||||||
value = enumValues.get(new Random().nextInt(enumValues.size())).asText();
|
value = enumValues.get(new SecureRandom().nextInt(enumValues.size())).asText();
|
||||||
} else if (isTextNotBlank(defaultValue)) {
|
} else if (isTextNotBlank(defaultValue)) {
|
||||||
value = defaultValue.asText();
|
value = defaultValue.asText();
|
||||||
} else {
|
} else {
|
||||||
|
@ -178,7 +184,7 @@ public class JsonSchemaBuilder {
|
||||||
int max = isTextNotBlank(maximum) ? maximum.asInt() : Integer.MAX_VALUE;
|
int max = isTextNotBlank(maximum) ? maximum.asInt() : Integer.MAX_VALUE;
|
||||||
int min = isTextNotBlank(minimum) ? minimum.asInt() : Integer.MIN_VALUE;
|
int min = isTextNotBlank(minimum) ? minimum.asInt() : Integer.MIN_VALUE;
|
||||||
// 这里减去负数可能超过整型最大值,使用 Long 类型
|
// 这里减去负数可能超过整型最大值,使用 Long 类型
|
||||||
value = new Random().nextLong(Long.valueOf(max) - Long.valueOf(min)) + min + StringUtils.EMPTY;
|
value = new SecureRandom().nextLong(Long.valueOf(max) - Long.valueOf(min)) + min + StringUtils.EMPTY;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (isVariable(value)) {
|
if (isVariable(value)) {
|
||||||
|
@ -196,7 +202,7 @@ public class JsonSchemaBuilder {
|
||||||
JsonNode enumValues = propertyNode.get(PropertyConstant.ENUM_VALUES);
|
JsonNode enumValues = propertyNode.get(PropertyConstant.ENUM_VALUES);
|
||||||
JsonNode defaultValue = propertyNode.get(PropertyConstant.DEFAULT_VALUE);
|
JsonNode defaultValue = propertyNode.get(PropertyConstant.DEFAULT_VALUE);
|
||||||
if (enumValues != null && enumValues instanceof ArrayNode) {
|
if (enumValues != null && enumValues instanceof ArrayNode) {
|
||||||
value = enumValues.get(new Random().nextInt(enumValues.size())).asText();
|
value = enumValues.get(new SecureRandom().nextInt(enumValues.size())).asText();
|
||||||
} else if (isTextNotBlank(defaultValue)) {
|
} else if (isTextNotBlank(defaultValue)) {
|
||||||
value = defaultValue.asText();
|
value = defaultValue.asText();
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
package io.metersphere.api.utils.regex.exception;
|
||||||
|
|
||||||
|
public class RegexpIllegalException extends Exception {
|
||||||
|
|
||||||
|
public RegexpIllegalException() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public RegexpIllegalException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RegexpIllegalException(String regexp, int index) {
|
||||||
|
super(String.format("Invalid regular expression: %s, Index: %d", regexp, index));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
package io.metersphere.api.utils.regex.exception;
|
||||||
|
|
||||||
|
public class TypeNotMatchException extends Exception {
|
||||||
|
|
||||||
|
public TypeNotMatchException() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public TypeNotMatchException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
package io.metersphere.api.utils.regex.exception;
|
||||||
|
|
||||||
|
public class UninitializedException extends Exception {
|
||||||
|
|
||||||
|
public UninitializedException() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public UninitializedException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,171 @@
|
||||||
|
package io.metersphere.api.utils.regex.model;
|
||||||
|
|
||||||
|
import io.metersphere.api.utils.regex.exception.*;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 参考 https://github.com/GitHub-Laziji/reverse-regexp 代码
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public abstract class BaseNode implements Node {
|
||||||
|
|
||||||
|
private String expression;
|
||||||
|
private List<String> expressionFragments;
|
||||||
|
private boolean initialized;
|
||||||
|
|
||||||
|
protected BaseNode(String expression) throws RegexpIllegalException, TypeNotMatchException {
|
||||||
|
this(expression, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected BaseNode(String expression, boolean initialize) throws RegexpIllegalException, TypeNotMatchException {
|
||||||
|
this.expression = expression;
|
||||||
|
this.expressionFragments = spliceExpression(expression);
|
||||||
|
if (initialize) {
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected BaseNode(List<String> expressionFragments) throws RegexpIllegalException, TypeNotMatchException {
|
||||||
|
this(expressionFragments, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected BaseNode(List<String> expressionFragments, boolean initialize)
|
||||||
|
throws RegexpIllegalException, TypeNotMatchException {
|
||||||
|
this.expressionFragments = expressionFragments;
|
||||||
|
StringBuilder stringBuilder = new StringBuilder();
|
||||||
|
for (String fragment : expressionFragments) {
|
||||||
|
stringBuilder.append(fragment);
|
||||||
|
}
|
||||||
|
this.expression = stringBuilder.toString();
|
||||||
|
if (initialize) {
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String random() throws UninitializedException, RegexpIllegalException {
|
||||||
|
if (!initialized) {
|
||||||
|
throw new UninitializedException();
|
||||||
|
}
|
||||||
|
return random(expression, expressionFragments);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isInitialized() {
|
||||||
|
return initialized;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean test() {
|
||||||
|
return test(expression, expressionFragments);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init() throws RegexpIllegalException, TypeNotMatchException {
|
||||||
|
if (!initialized) {
|
||||||
|
if (!test()) {
|
||||||
|
throw new TypeNotMatchException();
|
||||||
|
}
|
||||||
|
init(expression, expressionFragments);
|
||||||
|
initialized = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getExpression() {
|
||||||
|
return expression;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String random(String expression, List<String> expressionFragments)
|
||||||
|
throws RegexpIllegalException, UninitializedException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void init(String expression, List<String> expressionFragments)
|
||||||
|
throws RegexpIllegalException, TypeNotMatchException {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean test(String expression, List<String> expressionFragments) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> spliceExpression(String expression) throws RegexpIllegalException {
|
||||||
|
int l = 0;
|
||||||
|
int r = expression.length();
|
||||||
|
List<String> fragments = new ArrayList<>();
|
||||||
|
while (true) {
|
||||||
|
String result = findFirst(expression, l, r);
|
||||||
|
if (result == null || result.isEmpty()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
fragments.add(result);
|
||||||
|
l += result.length();
|
||||||
|
}
|
||||||
|
return fragments;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String findFirst(String expression, int l, int r) throws RegexpIllegalException {
|
||||||
|
if (l == r) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (expression.charAt(l) == '\\') {
|
||||||
|
if (l + 1 >= r) {
|
||||||
|
throw new RegexpIllegalException(expression, l + 1);
|
||||||
|
}
|
||||||
|
return expression.substring(l, l + 2);
|
||||||
|
}
|
||||||
|
if (expression.charAt(l) == '[') {
|
||||||
|
int i = l + 1;
|
||||||
|
while (i < r) {
|
||||||
|
if (expression.charAt(i) == ']') {
|
||||||
|
return expression.substring(l, i + 1);
|
||||||
|
}
|
||||||
|
if (expression.charAt(i) == '\\') {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
throw new RegexpIllegalException(expression, r);
|
||||||
|
}
|
||||||
|
if (expression.charAt(l) == '{') {
|
||||||
|
int i = l + 1;
|
||||||
|
boolean hasDelimiter = false;
|
||||||
|
while (i < r) {
|
||||||
|
if (expression.charAt(i) == '}') {
|
||||||
|
return expression.substring(l, i + 1);
|
||||||
|
}
|
||||||
|
if (expression.charAt(i) == ',') {
|
||||||
|
if (hasDelimiter) {
|
||||||
|
throw new RegexpIllegalException(expression, i);
|
||||||
|
}
|
||||||
|
hasDelimiter = true;
|
||||||
|
i++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (expression.charAt(i) < '0' || expression.charAt(i) > '9') {
|
||||||
|
throw new RegexpIllegalException(expression, i);
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
throw new RegexpIllegalException(expression, r);
|
||||||
|
}
|
||||||
|
if (expression.charAt(l) == '(') {
|
||||||
|
int i = l + 1;
|
||||||
|
while (true) {
|
||||||
|
String result = findFirst(expression, i, r);
|
||||||
|
if (result == null || result.length() == 0 || result.length() + i >= r) {
|
||||||
|
throw new RegexpIllegalException(expression, i);
|
||||||
|
}
|
||||||
|
i += result.length();
|
||||||
|
if (expression.charAt(i) == ')') {
|
||||||
|
return expression.substring(l, i + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return expression.substring(l, l + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
package io.metersphere.api.utils.regex.model;
|
||||||
|
|
||||||
|
import io.metersphere.api.utils.regex.exception.*;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class LinkNode extends BaseNode {
|
||||||
|
|
||||||
|
private List<Node> children;
|
||||||
|
|
||||||
|
protected LinkNode(List<String> expressionFragments) throws RegexpIllegalException, TypeNotMatchException {
|
||||||
|
super(expressionFragments);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected LinkNode(List<String> expressionFragments, boolean initialize)
|
||||||
|
throws RegexpIllegalException, TypeNotMatchException {
|
||||||
|
super(expressionFragments, initialize);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean test(String expression, List<String> expressionFragments) {
|
||||||
|
for (String fragment : expressionFragments) {
|
||||||
|
if ("|".equals(fragment)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void init(String expression, List<String> expressionFragments)
|
||||||
|
throws RegexpIllegalException, TypeNotMatchException {
|
||||||
|
children = new ArrayList<>();
|
||||||
|
for (int i = 0; i < expressionFragments.size(); i++) {
|
||||||
|
Node node;
|
||||||
|
if (i + 1 < expressionFragments.size()) {
|
||||||
|
node = new RepeatNode(
|
||||||
|
Arrays.asList(expressionFragments.get(i), expressionFragments.get(i + 1)),
|
||||||
|
false);
|
||||||
|
if (node.test()) {
|
||||||
|
node.init();
|
||||||
|
children.add(node);
|
||||||
|
i++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
node = new SingleNode(Collections.singletonList(expressionFragments.get(i)));
|
||||||
|
children.add(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String random(String expression, List<String> expressionFragments)
|
||||||
|
throws RegexpIllegalException, UninitializedException {
|
||||||
|
StringBuilder value = new StringBuilder();
|
||||||
|
for (Node node : children) {
|
||||||
|
value.append(node.random());
|
||||||
|
}
|
||||||
|
return value.toString();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
package io.metersphere.api.utils.regex.model;
|
||||||
|
|
||||||
|
import io.metersphere.api.utils.regex.exception.*;
|
||||||
|
|
||||||
|
public interface Node {
|
||||||
|
|
||||||
|
String getExpression();
|
||||||
|
|
||||||
|
String random() throws UninitializedException, RegexpIllegalException;
|
||||||
|
|
||||||
|
boolean test();
|
||||||
|
|
||||||
|
void init() throws RegexpIllegalException, TypeNotMatchException;
|
||||||
|
|
||||||
|
boolean isInitialized();
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
package io.metersphere.api.utils.regex.model;
|
||||||
|
|
||||||
|
import io.metersphere.api.utils.regex.exception.*;
|
||||||
|
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
public class OptionalNode extends BaseNode {
|
||||||
|
|
||||||
|
private List<Node> children;
|
||||||
|
|
||||||
|
protected OptionalNode(List<String> expressionFragments) throws RegexpIllegalException, TypeNotMatchException {
|
||||||
|
super(expressionFragments);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected OptionalNode(List<String> expressionFragments, boolean initialize)
|
||||||
|
throws RegexpIllegalException, TypeNotMatchException {
|
||||||
|
super(expressionFragments, initialize);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean test(String expression, List<String> expressionFragments) {
|
||||||
|
for (String fragment : expressionFragments) {
|
||||||
|
if ("|".equals(fragment)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void init(String expression, List<String> expressionFragments)
|
||||||
|
throws RegexpIllegalException, TypeNotMatchException {
|
||||||
|
children = new ArrayList<>();
|
||||||
|
List<String> subFragments = new ArrayList<>();
|
||||||
|
for (String fragment : expressionFragments) {
|
||||||
|
if ("|".equals(fragment)) {
|
||||||
|
children.add(new OrdinaryNode(subFragments));
|
||||||
|
subFragments = new ArrayList<>();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
subFragments.add(fragment);
|
||||||
|
}
|
||||||
|
children.add(new OrdinaryNode(subFragments));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String random(String expression, List<String> expressionFragments)
|
||||||
|
throws UninitializedException, RegexpIllegalException {
|
||||||
|
return children.get(new SecureRandom().nextInt(children.size())).random();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
package io.metersphere.api.utils.regex.model;
|
||||||
|
import io.metersphere.api.utils.regex.exception.*;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class OrdinaryNode extends BaseNode {
|
||||||
|
|
||||||
|
private Node proxyNode;
|
||||||
|
|
||||||
|
public OrdinaryNode(String expression) throws RegexpIllegalException, TypeNotMatchException {
|
||||||
|
super(expression);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected OrdinaryNode(List<String> expressionFragments) throws RegexpIllegalException, TypeNotMatchException {
|
||||||
|
super(expressionFragments);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void init(String expression, List<String> expressionFragments)
|
||||||
|
throws RegexpIllegalException, TypeNotMatchException {
|
||||||
|
if (expressionFragments.size() == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Node[] nodes = new Node[]{
|
||||||
|
new OptionalNode(expressionFragments, false),
|
||||||
|
new SingleNode(expressionFragments, false),
|
||||||
|
new RepeatNode(expressionFragments, false),
|
||||||
|
new LinkNode(expressionFragments, false)
|
||||||
|
};
|
||||||
|
for (Node node : nodes) {
|
||||||
|
if (node.test()) {
|
||||||
|
proxyNode = node;
|
||||||
|
proxyNode.init();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String random(String expression, List<String> expressionFragments)
|
||||||
|
throws UninitializedException, RegexpIllegalException {
|
||||||
|
if (proxyNode == null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return proxyNode.random();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,72 @@
|
||||||
|
package io.metersphere.api.utils.regex.model;
|
||||||
|
|
||||||
|
import io.metersphere.api.utils.regex.exception.*;
|
||||||
|
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
public class RepeatNode extends BaseNode {
|
||||||
|
|
||||||
|
private static final int MAX_REPEAT = 16;
|
||||||
|
|
||||||
|
private Node node;
|
||||||
|
private int minRepeat = 1;
|
||||||
|
private int maxRepeat = 1;
|
||||||
|
|
||||||
|
protected RepeatNode(List<String> expressionFragments) throws RegexpIllegalException, TypeNotMatchException {
|
||||||
|
super(expressionFragments);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected RepeatNode(List<String> expressionFragments, boolean initialize)
|
||||||
|
throws RegexpIllegalException, TypeNotMatchException {
|
||||||
|
super(expressionFragments, initialize);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean test(String expression, List<String> expressionFragments) {
|
||||||
|
if (expressionFragments.size() == 2) {
|
||||||
|
String token = expressionFragments.get(1);
|
||||||
|
return token != null
|
||||||
|
&& ("+".equals(token) || "?".equals(token) || "*".equals(token) || token.startsWith("{"));
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void init(String expression, List<String> expressionFragments)
|
||||||
|
throws RegexpIllegalException, TypeNotMatchException {
|
||||||
|
node = new SingleNode(Collections.singletonList(expressionFragments.get(0)));
|
||||||
|
String token = expressionFragments.get(1);
|
||||||
|
if ("+".equals(token)) {
|
||||||
|
maxRepeat = MAX_REPEAT;
|
||||||
|
} else if ("?".equals(token)) {
|
||||||
|
minRepeat = 0;
|
||||||
|
} else if ("*".equals(token)) {
|
||||||
|
minRepeat = 0;
|
||||||
|
maxRepeat = MAX_REPEAT;
|
||||||
|
} else if (token.startsWith("{")) {
|
||||||
|
String[] numbers = token.substring(1, token.length() - 1).split(",", 2);
|
||||||
|
minRepeat = maxRepeat = Integer.parseInt(numbers[0]);
|
||||||
|
if (numbers.length > 1) {
|
||||||
|
maxRepeat = numbers[1].isEmpty() ? Math.max(MAX_REPEAT, minRepeat) : Integer.parseInt(numbers[1]);
|
||||||
|
if (maxRepeat < minRepeat) {
|
||||||
|
throw new RegexpIllegalException("Invalid regular expression: "
|
||||||
|
+ getExpression() + " : Numbers out of order in {} quantifier");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String random(String expression, List<String> expressionFragments)
|
||||||
|
throws RegexpIllegalException, UninitializedException {
|
||||||
|
int repeat = new SecureRandom().nextInt(maxRepeat - minRepeat + 1) + minRepeat;
|
||||||
|
StringBuilder value = new StringBuilder();
|
||||||
|
while (repeat-- > 0) {
|
||||||
|
value.append(node.random());
|
||||||
|
}
|
||||||
|
return value.toString();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,152 @@
|
||||||
|
package io.metersphere.api.utils.regex.model;
|
||||||
|
|
||||||
|
import io.metersphere.api.utils.regex.exception.*;
|
||||||
|
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
public class SingleNode extends BaseNode {
|
||||||
|
|
||||||
|
private Node node;
|
||||||
|
|
||||||
|
private List<Interval> intervals;
|
||||||
|
|
||||||
|
protected SingleNode(List<String> expressionFragments) throws RegexpIllegalException, TypeNotMatchException {
|
||||||
|
super(expressionFragments);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected SingleNode(List<String> expressionFragments, boolean initialize)
|
||||||
|
throws RegexpIllegalException, TypeNotMatchException {
|
||||||
|
super(expressionFragments, initialize);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean test(String expression, List<String> expressionFragments) {
|
||||||
|
return expressionFragments != null && expressionFragments.size() == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void init(String expression, List<String> expressionFragments)
|
||||||
|
throws RegexpIllegalException, TypeNotMatchException {
|
||||||
|
if (expression.startsWith("(")) {
|
||||||
|
node = new OrdinaryNode(expression.substring(1, expression.length() - 1));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (expression.startsWith("[")) {
|
||||||
|
int i = 1;
|
||||||
|
Character preChar = null;
|
||||||
|
while (i < expression.length() - 1) {
|
||||||
|
if (expression.charAt(i) == '\\') {
|
||||||
|
if (i + 1 >= expression.length() - 1) {
|
||||||
|
throw new RegexpIllegalException(expression, i);
|
||||||
|
}
|
||||||
|
if (preChar != null && "dws".contains(expression.charAt(i + 1) + "")) {
|
||||||
|
addIntervals(preChar, null, '-', null);
|
||||||
|
preChar = null;
|
||||||
|
}
|
||||||
|
if (expression.charAt(i + 1) == 'd') {
|
||||||
|
addIntervals('0', '9');
|
||||||
|
} else if (expression.charAt(i + 1) == 'w') {
|
||||||
|
addIntervals('0', '9', 'A', 'Z', 'a', 'z', '_', null);
|
||||||
|
} else if (expression.charAt(i + 1) == 's') {
|
||||||
|
addIntervals(' ', null, '\t', null);
|
||||||
|
} else {
|
||||||
|
if (preChar != null) {
|
||||||
|
addIntervals(preChar, expression.charAt(i + 1));
|
||||||
|
preChar = null;
|
||||||
|
} else if (i + 2 < expression.length() && expression.charAt(i + 2) == '-') {
|
||||||
|
preChar = expression.charAt(i + 1);
|
||||||
|
i++;
|
||||||
|
} else {
|
||||||
|
addIntervals(expression.charAt(i + 1), null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
} else if (preChar != null) {
|
||||||
|
addIntervals(preChar, expression.charAt(i));
|
||||||
|
preChar = null;
|
||||||
|
} else if (i + 1 < expression.length() && expression.charAt(i + 1) == '-') {
|
||||||
|
preChar = expression.charAt(i);
|
||||||
|
i++;
|
||||||
|
} else {
|
||||||
|
addIntervals(expression.charAt(i), null);
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
if (preChar != null) {
|
||||||
|
addIntervals(preChar, null, '-', null);
|
||||||
|
}
|
||||||
|
} else if (".".equals(expression)) {
|
||||||
|
// 这里仅包含一般字符,参考 ASCII 码表
|
||||||
|
addIntervals((char) 33, (char) 126);
|
||||||
|
} else if ("\\s".equals(expression)) {
|
||||||
|
addIntervals(' ', null, '\t', null);
|
||||||
|
} else if ("\\d".equals(expression)) {
|
||||||
|
addIntervals('0', '9');
|
||||||
|
} else if ("\\w".equals(expression)) {
|
||||||
|
addIntervals('0', '9', 'A', 'Z', 'a', 'z', '_', null);
|
||||||
|
} else if (expression.startsWith("\\")) {
|
||||||
|
addIntervals(expression.charAt(1), null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String random(String expression, List<String> expressionFragments)
|
||||||
|
throws RegexpIllegalException, UninitializedException {
|
||||||
|
if (node != null) {
|
||||||
|
return node.random();
|
||||||
|
}
|
||||||
|
if (intervals != null && intervals.size() > 0) {
|
||||||
|
Character value = randomCharFromInterval(intervals.toArray(new Interval[0]));
|
||||||
|
return value == null ? "" : value.toString();
|
||||||
|
}
|
||||||
|
return expression;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Character randomCharFromInterval(Interval... intervals) {
|
||||||
|
int count = 0;
|
||||||
|
for (Interval interval : intervals) {
|
||||||
|
count += interval.end + 1 - interval.start;
|
||||||
|
}
|
||||||
|
int randomValue = new SecureRandom().nextInt(count);
|
||||||
|
for (Interval interval : intervals) {
|
||||||
|
if (randomValue < interval.end + 1 - interval.start) {
|
||||||
|
return (char) (interval.start + randomValue);
|
||||||
|
}
|
||||||
|
randomValue -= interval.end + 1 - interval.start;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addIntervals(Character... chars) throws RegexpIllegalException {
|
||||||
|
if (intervals == null) {
|
||||||
|
intervals = new ArrayList<>();
|
||||||
|
}
|
||||||
|
for (int i = 0; i + 1 < chars.length; i += 2) {
|
||||||
|
Character start = chars[i];
|
||||||
|
Character end = chars[i + 1] == null ? start : chars[i + 1];
|
||||||
|
if (start == null) {
|
||||||
|
throw new RegexpIllegalException("Invalid regular expression: "
|
||||||
|
+ getExpression() + " : Character class is null");
|
||||||
|
}
|
||||||
|
if (end < start) {
|
||||||
|
throw new RegexpIllegalException("Invalid regular expression: "
|
||||||
|
+ getExpression() + " : Range out of order in character class");
|
||||||
|
}
|
||||||
|
intervals.add(new Interval(start, end));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class Interval {
|
||||||
|
private char start;
|
||||||
|
private char end;
|
||||||
|
|
||||||
|
private Interval(char start, char end) {
|
||||||
|
this.start = start;
|
||||||
|
this.end = end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1
pom.xml
1
pom.xml
|
@ -85,7 +85,6 @@
|
||||||
<commons-jexl3.version>3.3</commons-jexl3.version>
|
<commons-jexl3.version>3.3</commons-jexl3.version>
|
||||||
<revision>3.x</revision>
|
<revision>3.x</revision>
|
||||||
<monitoring-engine.revision>3.0</monitoring-engine.revision>
|
<monitoring-engine.revision>3.0</monitoring-engine.revision>
|
||||||
<xeger.version>1.0.0-RELEASE</xeger.version>
|
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<modules>
|
<modules>
|
||||||
|
|
Loading…
Reference in New Issue