mirror of
https://github.com/plexusorg/Plex-FAWE.git
synced 2024-12-23 01:37:37 +00:00
Add timeout to regex masking
This commit is contained in:
parent
dbbb450172
commit
54398ebe1c
@ -30,6 +30,12 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
import java.util.function.BiPredicate;
|
import java.util.function.BiPredicate;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
@ -45,11 +51,16 @@ public class BlockMaskBuilder {
|
|||||||
private static final Operator NOT = (a, b) -> a != b;
|
private static final Operator NOT = (a, b) -> a != b;
|
||||||
|
|
||||||
private static final long[] ALL = new long[0];
|
private static final long[] ALL = new long[0];
|
||||||
|
private final long[][] bitSets;
|
||||||
|
private boolean[] ordinals;
|
||||||
|
private boolean optimizedStates = true;
|
||||||
|
|
||||||
private interface Operator {
|
public BlockMaskBuilder() {
|
||||||
|
this(new long[BlockTypes.size()][]);
|
||||||
boolean test(int left, int right);
|
}
|
||||||
|
|
||||||
|
protected BlockMaskBuilder(long[][] bitSets) {
|
||||||
|
this.bitSets = bitSets;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean filterRegex(BlockType blockType, PropertyKey key, String regex) {
|
private boolean filterRegex(BlockType blockType, PropertyKey key, String regex) {
|
||||||
@ -106,157 +117,174 @@ public class BlockMaskBuilder {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BlockMaskBuilder addRegex(String input) throws InputParseException {
|
public BlockMaskBuilder addRegex(final String input) throws InputParseException {
|
||||||
if (input.charAt(input.length() - 1) == ']') {
|
ExecutorService executor = Executors.newSingleThreadExecutor();
|
||||||
int propStart = StringMan.findMatchingBracket(input, input.length() - 1);
|
Future<?> fut = executor.submit(() -> {
|
||||||
if (propStart == -1) {
|
if (input.charAt(input.length() - 1) == ']') {
|
||||||
return this;
|
int propStart = StringMan.findMatchingBracket(input, input.length() - 1);
|
||||||
}
|
if (propStart == -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
MutableCharSequence charSequence = MutableCharSequence.getTemporal();
|
MutableCharSequence charSequence = MutableCharSequence.getTemporal();
|
||||||
charSequence.setString(input);
|
charSequence.setString(input);
|
||||||
charSequence.setSubstring(0, propStart);
|
charSequence.setSubstring(0, propStart);
|
||||||
|
|
||||||
BlockType type = null;
|
BlockType type = null;
|
||||||
List<BlockType> blockTypeList = null;
|
List<BlockType> blockTypeList = null;
|
||||||
if (StringMan.isAlphanumericUnd(charSequence)) {
|
if (StringMan.isAlphanumericUnd(charSequence)) {
|
||||||
type = BlockTypes.parse(charSequence.toString());
|
type = BlockTypes.parse(charSequence.toString());
|
||||||
add(type);
|
add(type);
|
||||||
} else {
|
} else {
|
||||||
String regex = charSequence.toString();
|
String regex = charSequence.toString();
|
||||||
blockTypeList = new ArrayList<>();
|
blockTypeList = new ArrayList<>();
|
||||||
for (BlockType myType : BlockTypesCache.values) {
|
for (BlockType myType : BlockTypesCache.values) {
|
||||||
if (myType.getId().matches(regex)) {
|
if (myType.getId().matches(regex)) {
|
||||||
blockTypeList.add(myType);
|
blockTypeList.add(myType);
|
||||||
add(myType);
|
add(myType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (blockTypeList.isEmpty()) {
|
||||||
|
throw new InputParseException(Caption.of("fawe.error.no-block-found", TextComponent.of(input)));
|
||||||
|
}
|
||||||
|
if (blockTypeList.size() == 1) {
|
||||||
|
type = blockTypeList.get(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (blockTypeList.isEmpty()) {
|
// Empty string
|
||||||
throw new InputParseException(Caption.of("fawe.error.no-block-found", TextComponent.of(input)));
|
charSequence.setSubstring(0, 0);
|
||||||
}
|
|
||||||
if (blockTypeList.size() == 1) {
|
|
||||||
type = blockTypeList.get(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Empty string
|
|
||||||
charSequence.setSubstring(0, 0);
|
|
||||||
|
|
||||||
PropertyKey key = null;
|
PropertyKey key = null;
|
||||||
int length = input.length();
|
int length = input.length();
|
||||||
int last = propStart + 1;
|
int last = propStart + 1;
|
||||||
Operator operator = null;
|
Operator operator = null;
|
||||||
for (int i = last; i < length; i++) {
|
for (int i = last; i < length; i++) {
|
||||||
char c = input.charAt(i);
|
char c = input.charAt(i);
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case '[':
|
case '[':
|
||||||
case '{':
|
case '{':
|
||||||
case '(':
|
case '(':
|
||||||
int next = StringMan.findMatchingBracket(input, i);
|
int next = StringMan.findMatchingBracket(input, i);
|
||||||
if (next != -1) {
|
if (next != -1) {
|
||||||
i = next;
|
i = next;
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ']':
|
|
||||||
case ',': {
|
|
||||||
charSequence.setSubstring(last, i);
|
|
||||||
if (key == null && PropertyKey.getByName(charSequence) == null) {
|
|
||||||
suggest(input, charSequence.toString(), type != null ? Collections.singleton(type) : blockTypeList);
|
|
||||||
}
|
|
||||||
if (operator == null) {
|
|
||||||
throw new SuggestInputParseException(
|
|
||||||
"No operator for " + input,
|
|
||||||
"",
|
|
||||||
() -> Arrays.asList("=", "~", "!", "<", ">", "<=", ">=")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
boolean filtered = false;
|
|
||||||
if (type != null) {
|
|
||||||
filtered = filterRegexOrOperator(type, key, operator, charSequence);
|
|
||||||
} else {
|
|
||||||
for (BlockType myType : blockTypeList) {
|
|
||||||
filtered |= filterRegexOrOperator(myType, key, operator, charSequence);
|
|
||||||
}
|
}
|
||||||
}
|
break;
|
||||||
if (!filtered) {
|
case ']':
|
||||||
String value = charSequence.toString();
|
case ',': {
|
||||||
final PropertyKey fKey = key;
|
charSequence.setSubstring(last, i);
|
||||||
Collection<BlockType> types = type != null ? Collections.singleton(type) : blockTypeList;
|
if (key == null && PropertyKey.getByName(charSequence) == null) {
|
||||||
throw new SuggestInputParseException("No value for " + input, input, () -> {
|
|
||||||
HashSet<String> values = new HashSet<>();
|
|
||||||
types.stream().filter(t -> t.hasProperty(fKey)).forEach(t -> {
|
|
||||||
Property<Object> p = t.getProperty(fKey);
|
|
||||||
for (int j = 0; j < p.getValues().size(); j++) {
|
|
||||||
if (has(t, p, j)) {
|
|
||||||
String o = p.getValues().get(j).toString();
|
|
||||||
if (o.startsWith(value)) {
|
|
||||||
values.add(o);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return new ArrayList<>(values);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// Reset state
|
|
||||||
key = null;
|
|
||||||
operator = null;
|
|
||||||
last = i + 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case '~':
|
|
||||||
case '!':
|
|
||||||
case '=':
|
|
||||||
case '<':
|
|
||||||
case '>': {
|
|
||||||
charSequence.setSubstring(last, i);
|
|
||||||
boolean extra = input.charAt(i + 1) == '=';
|
|
||||||
if (extra) {
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
switch (c) {
|
|
||||||
case '~':
|
|
||||||
operator = EQUAL_OR_NULL;
|
|
||||||
break;
|
|
||||||
case '!':
|
|
||||||
operator = NOT;
|
|
||||||
break;
|
|
||||||
case '=':
|
|
||||||
operator = EQUAL;
|
|
||||||
break;
|
|
||||||
case '<':
|
|
||||||
operator = extra ? LESS_EQUAL : LESS;
|
|
||||||
break;
|
|
||||||
case '>':
|
|
||||||
operator = extra ? GREATER_EQUAL : GREATER;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (charSequence.length() > 0 || key == null) {
|
|
||||||
key = PropertyKey.getByName(charSequence);
|
|
||||||
if (key == null) {
|
|
||||||
suggest(
|
suggest(
|
||||||
input,
|
input,
|
||||||
charSequence.toString(),
|
charSequence.toString(),
|
||||||
type != null ? Collections.singleton(type) : blockTypeList
|
type != null ? Collections.singleton(type) : blockTypeList
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if (operator == null) {
|
||||||
|
throw new SuggestInputParseException(
|
||||||
|
"No operator for " + input,
|
||||||
|
"",
|
||||||
|
() -> Arrays.asList("=", "~", "!", "<", ">", "<=", ">=")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
boolean filtered = false;
|
||||||
|
if (type != null) {
|
||||||
|
filtered = filterRegexOrOperator(type, key, operator, charSequence);
|
||||||
|
} else {
|
||||||
|
for (BlockType myType : blockTypeList) {
|
||||||
|
filtered |= filterRegexOrOperator(myType, key, operator, charSequence);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!filtered) {
|
||||||
|
String value = charSequence.toString();
|
||||||
|
final PropertyKey fKey = key;
|
||||||
|
Collection<BlockType> types = type != null ? Collections.singleton(type) : blockTypeList;
|
||||||
|
throw new SuggestInputParseException("No value for " + input, input, () -> {
|
||||||
|
HashSet<String> values = new HashSet<>();
|
||||||
|
types.stream().filter(t -> t.hasProperty(fKey)).forEach(t -> {
|
||||||
|
Property<Object> p = t.getProperty(fKey);
|
||||||
|
for (int j = 0; j < p.getValues().size(); j++) {
|
||||||
|
if (has(t, p, j)) {
|
||||||
|
String o = p.getValues().get(j).toString();
|
||||||
|
if (o.startsWith(value)) {
|
||||||
|
values.add(o);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return new ArrayList<>(values);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Reset state
|
||||||
|
key = null;
|
||||||
|
operator = null;
|
||||||
|
last = i + 1;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
last = i + 1;
|
case '~':
|
||||||
break;
|
case '!':
|
||||||
|
case '=':
|
||||||
|
case '<':
|
||||||
|
case '>': {
|
||||||
|
charSequence.setSubstring(last, i);
|
||||||
|
boolean extra = input.charAt(i + 1) == '=';
|
||||||
|
if (extra) {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
switch (c) {
|
||||||
|
case '~':
|
||||||
|
operator = EQUAL_OR_NULL;
|
||||||
|
break;
|
||||||
|
case '!':
|
||||||
|
operator = NOT;
|
||||||
|
break;
|
||||||
|
case '=':
|
||||||
|
operator = EQUAL;
|
||||||
|
break;
|
||||||
|
case '<':
|
||||||
|
operator = extra ? LESS_EQUAL : LESS;
|
||||||
|
break;
|
||||||
|
case '>':
|
||||||
|
operator = extra ? GREATER_EQUAL : GREATER;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (charSequence.length() > 0 || key == null) {
|
||||||
|
key = PropertyKey.getByName(charSequence);
|
||||||
|
if (key == null) {
|
||||||
|
suggest(
|
||||||
|
input,
|
||||||
|
charSequence.toString(),
|
||||||
|
type != null ? Collections.singleton(type) : blockTypeList
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
last = i + 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (StringMan.isAlphanumericUnd(input)) {
|
|
||||||
add(BlockTypes.parse(input));
|
|
||||||
} else {
|
} else {
|
||||||
for (BlockType myType : BlockTypesCache.values) {
|
if (StringMan.isAlphanumericUnd(input)) {
|
||||||
if (myType.getId().matches(input)) {
|
add(BlockTypes.parse(input));
|
||||||
add(myType);
|
} else {
|
||||||
|
for (BlockType myType : BlockTypesCache.values) {
|
||||||
|
if (myType.getId().matches(input)) {
|
||||||
|
add(myType);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
fut.get(5L, TimeUnit.MILLISECONDS);
|
||||||
|
} catch (ExecutionException e) {
|
||||||
|
if (e.getCause() instanceof InputParseException) {
|
||||||
|
throw (InputParseException) e.getCause();
|
||||||
|
}
|
||||||
|
} catch (InterruptedException | TimeoutException ignored) {
|
||||||
|
} finally {
|
||||||
|
executor.shutdown();
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@ -282,25 +310,10 @@ public class BlockMaskBuilder {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
///// end internal /////
|
|
||||||
|
|
||||||
private final long[][] bitSets;
|
|
||||||
private boolean[] ordinals;
|
|
||||||
|
|
||||||
private boolean optimizedStates = true;
|
|
||||||
|
|
||||||
public boolean isEmpty() {
|
public boolean isEmpty() {
|
||||||
return Arrays.stream(bitSets).noneMatch(Objects::nonNull);
|
return Arrays.stream(bitSets).noneMatch(Objects::nonNull);
|
||||||
}
|
}
|
||||||
|
|
||||||
public BlockMaskBuilder() {
|
|
||||||
this(new long[BlockTypes.size()][]);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected BlockMaskBuilder(long[][] bitSets) {
|
|
||||||
this.bitSets = bitSets;
|
|
||||||
}
|
|
||||||
|
|
||||||
public BlockMaskBuilder addAll() {
|
public BlockMaskBuilder addAll() {
|
||||||
Arrays.fill(bitSets, ALL);
|
Arrays.fill(bitSets, ALL);
|
||||||
reset(true);
|
reset(true);
|
||||||
@ -660,4 +673,10 @@ public class BlockMaskBuilder {
|
|||||||
return new BlockMask(extent, getOrdinals());
|
return new BlockMask(extent, getOrdinals());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private interface Operator {
|
||||||
|
|
||||||
|
boolean test(int left, int right);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user