mirror of
https://github.com/LuckPerms/LuckPerms.git
synced 2024-12-29 12:37:40 +01:00
Improve shorthand parser
This commit is contained in:
parent
500385cd3e
commit
787f691f44
@ -74,7 +74,7 @@ public abstract class AbstractNode<N extends ScopedNode<N, B>, B extends NodeBui
|
||||
this.contexts = contexts;
|
||||
this.metadata = metadata;
|
||||
|
||||
this.resolvedShorthand = this instanceof PermissionNode ? ImmutableList.copyOf(ShorthandParser.parseShorthand(this.key)) : ImmutableList.of();
|
||||
this.resolvedShorthand = this instanceof PermissionNode ? ImmutableList.copyOf(ShorthandParser.expandShorthand(this.key)) : ImmutableList.of();
|
||||
|
||||
this.hashCode = calculateHashCode();
|
||||
}
|
||||
|
@ -26,45 +26,123 @@
|
||||
package me.lucko.luckperms.common.node.utils;
|
||||
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Iterators;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
public final class ShorthandParser {
|
||||
private ShorthandParser() {}
|
||||
/**
|
||||
* Utility to expand shorthand nodes
|
||||
*/
|
||||
public enum ShorthandParser {
|
||||
|
||||
private static final List<Function<String, Iterable<String>>> PARSERS = ImmutableList.<Function<String, Iterable<String>>>builder()
|
||||
.add(new ListParser())
|
||||
.add(new CharacterRangeParser())
|
||||
.add(new NumericRangeParser())
|
||||
.build();
|
||||
/**
|
||||
* Expands "1-4" to ["1", "2", "3", "4"]
|
||||
*/
|
||||
NUMERIC_RANGE {
|
||||
@Override
|
||||
public Iterator<String> extract(String input) {
|
||||
int index = input.indexOf(RANGE_SEPARATOR);
|
||||
if (index == -1 || index == 0 || index == (input.length() - 1)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Set<String> parseShorthand(String s) {
|
||||
Set<String> results = new HashSet<>(1);
|
||||
return new RangeIterator(Integer.parseInt(input.substring(0, index)), Integer.parseInt(input.substring(index + 1))) {
|
||||
@Override
|
||||
protected String toString(int i) {
|
||||
return Integer.toString(i);
|
||||
}
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Expands "a-d" to ["a", "b", "c", "d"]
|
||||
*/
|
||||
CHARACTER_RANGE {
|
||||
@Override
|
||||
public Iterator<String> extract(String input) {
|
||||
if (input.length() != 3 || input.charAt(1) != RANGE_SEPARATOR) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new RangeIterator(input.charAt(0), input.charAt(2)) {
|
||||
@Override
|
||||
protected String toString(int i) {
|
||||
return Character.toString((char) i);
|
||||
}
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Expands "aa,bb,cc" to ["aa", "bb", "cc"]
|
||||
*/
|
||||
LIST {
|
||||
private final Splitter splitter = Splitter.on(LIST_SEPARATOR).omitEmptyStrings();
|
||||
|
||||
@Override
|
||||
public Iterator<String> extract(String input) {
|
||||
if (input.indexOf(LIST_SEPARATOR) == -1) {
|
||||
return Iterators.singletonIterator(input);
|
||||
}
|
||||
return this.splitter.split(input).iterator();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Expands the given input in shorthand notation to a number of strings.
|
||||
*
|
||||
* @param input the input shorthand
|
||||
* @return an iterator of the resultant strings, or optionally null if the input was invalid
|
||||
* @throws IllegalArgumentException if the input was invalid
|
||||
*/
|
||||
abstract Iterator<String> extract(String input) throws IllegalArgumentException;
|
||||
|
||||
/** Character used to open a group */
|
||||
private static final char OPEN_GROUP = '{';
|
||||
/** Character used to close a group */
|
||||
private static final char CLOSE_GROUP = '}';
|
||||
/** Character used to separate items in a list */
|
||||
private static final char LIST_SEPARATOR = ',';
|
||||
/** Character used to indicate a range between two values */
|
||||
private static final char RANGE_SEPARATOR = '-';
|
||||
|
||||
/** The parsers */
|
||||
private static final ShorthandParser[] PARSERS = values();
|
||||
|
||||
/**
|
||||
* Parses and expands the shorthand format.
|
||||
*
|
||||
* @param s the string to expand
|
||||
* @return the expanded result
|
||||
*/
|
||||
public static Set<String> expandShorthand(String s) {
|
||||
Set<String> results = new HashSet<>();
|
||||
results.add(s);
|
||||
|
||||
Set<String> workSet = new HashSet<>();
|
||||
while (true) {
|
||||
Set<String> working = new HashSet<>(results.size());
|
||||
int beforeSize = results.size();
|
||||
|
||||
boolean work = false;
|
||||
for (String str : results) {
|
||||
Set<String> ret = captureResults(str);
|
||||
if (ret != null) {
|
||||
working.addAll(ret);
|
||||
Set<String> expanded = matchGroup(str);
|
||||
if (expanded != null) {
|
||||
work = true;
|
||||
workSet.addAll(expanded);
|
||||
} else {
|
||||
working.add(str);
|
||||
workSet.add(str);
|
||||
}
|
||||
}
|
||||
|
||||
if (working.size() != beforeSize) {
|
||||
results = working;
|
||||
if (work) {
|
||||
// set results := workSet, and init an empty workSet for the next iteration
|
||||
// (we actually sneakily reuse the existing results HashSet to avoid having to create a new one)
|
||||
Set<String> temp = results;
|
||||
results = workSet;
|
||||
workSet = temp;
|
||||
workSet.clear();
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -77,13 +155,13 @@ public final class ShorthandParser {
|
||||
return results;
|
||||
}
|
||||
|
||||
private static Set<String> captureResults(String s) {
|
||||
int openingIndex = s.indexOf('{');
|
||||
private static Set<String> matchGroup(String s) {
|
||||
int openingIndex = s.indexOf(OPEN_GROUP);
|
||||
if (openingIndex == -1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int closingIndex = s.indexOf('}');
|
||||
int closingIndex = s.indexOf(CLOSE_GROUP);
|
||||
if (closingIndex < openingIndex) {
|
||||
return null;
|
||||
}
|
||||
@ -92,91 +170,49 @@ public final class ShorthandParser {
|
||||
String after = s.substring(closingIndex + 1);
|
||||
String between = s.substring(openingIndex + 1, closingIndex);
|
||||
|
||||
Set<String> results = new HashSet<>(10);
|
||||
Set<String> results = new HashSet<>();
|
||||
|
||||
for (Function<String, Iterable<String>> parser : PARSERS) {
|
||||
Iterable<String> res = parser.apply(between);
|
||||
if (res != null) {
|
||||
for (String r : res) {
|
||||
results.add(before + r + after);
|
||||
for (ShorthandParser parser : PARSERS) {
|
||||
try {
|
||||
Iterator<String> res = parser.extract(between);
|
||||
if (res != null) {
|
||||
while (res.hasNext()) {
|
||||
results.add(before + res.next() + after);
|
||||
}
|
||||
|
||||
// break after one parser has matched
|
||||
break;
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
private static class ListParser implements Function<String, Iterable<String>> {
|
||||
private static final Splitter SPLITTER = Splitter.on(',');
|
||||
/**
|
||||
* Implements an iterator over a given range of ints.
|
||||
*/
|
||||
private abstract static class RangeIterator implements Iterator<String> {
|
||||
private final int max;
|
||||
private int next;
|
||||
|
||||
RangeIterator(int a, int b) {
|
||||
this.max = Math.max(a, b);
|
||||
this.next = Math.min(a, b);
|
||||
}
|
||||
|
||||
protected abstract String toString(int i);
|
||||
|
||||
@Override
|
||||
public Iterable<String> apply(String s) {
|
||||
if (!s.contains(",")) {
|
||||
return Collections.singleton(s);
|
||||
}
|
||||
return SPLITTER.split(s);
|
||||
}
|
||||
}
|
||||
|
||||
private static class NumericRangeParser implements Function<String, Iterable<String>> {
|
||||
|
||||
private static Integer parseInt(String a) {
|
||||
try {
|
||||
return Integer.parseInt(a);
|
||||
} catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
public final boolean hasNext() {
|
||||
return this.next <= this.max;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<String> apply(String s) {
|
||||
int index = s.indexOf("-");
|
||||
if (index == -1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Integer before = parseInt(s.substring(0, index));
|
||||
if (before == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Integer after = parseInt(s.substring(index + 1));
|
||||
if (after == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return IntStream.rangeClosed(before, after).mapToObj(Integer::toString).collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
||||
private static class CharacterRangeParser implements Function<String, Iterable<String>> {
|
||||
|
||||
private static List<String> getCharRange(char a, char b) {
|
||||
List<String> s = new ArrayList<>();
|
||||
for (char c = a; c <= b; c++) {
|
||||
s.add(Character.toString(c));
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<String> apply(String s) {
|
||||
int index = s.indexOf("-");
|
||||
if (index == -1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String before = s.substring(0, index);
|
||||
if (before.length() != 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String after = s.substring(index + 1);
|
||||
if (after.length() != 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return getCharRange(before.charAt(0), after.charAt(0));
|
||||
public final String next() {
|
||||
return toString(this.next++);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user