mirror of
https://github.com/LuckPerms/LuckPerms.git
synced 2025-01-04 07:28:13 +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.contexts = contexts;
|
||||||
this.metadata = metadata;
|
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();
|
this.hashCode = calculateHashCode();
|
||||||
}
|
}
|
||||||
|
@ -26,45 +26,123 @@
|
|||||||
package me.lucko.luckperms.common.node.utils;
|
package me.lucko.luckperms.common.node.utils;
|
||||||
|
|
||||||
import com.google.common.base.Splitter;
|
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.HashSet;
|
||||||
import java.util.List;
|
import java.util.Iterator;
|
||||||
import java.util.Set;
|
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())
|
* Expands "1-4" to ["1", "2", "3", "4"]
|
||||||
.add(new CharacterRangeParser())
|
*/
|
||||||
.add(new NumericRangeParser())
|
NUMERIC_RANGE {
|
||||||
.build();
|
@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) {
|
return new RangeIterator(Integer.parseInt(input.substring(0, index)), Integer.parseInt(input.substring(index + 1))) {
|
||||||
Set<String> results = new HashSet<>(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);
|
results.add(s);
|
||||||
|
|
||||||
|
Set<String> workSet = new HashSet<>();
|
||||||
while (true) {
|
while (true) {
|
||||||
Set<String> working = new HashSet<>(results.size());
|
|
||||||
int beforeSize = results.size();
|
|
||||||
|
|
||||||
|
boolean work = false;
|
||||||
for (String str : results) {
|
for (String str : results) {
|
||||||
Set<String> ret = captureResults(str);
|
Set<String> expanded = matchGroup(str);
|
||||||
if (ret != null) {
|
if (expanded != null) {
|
||||||
working.addAll(ret);
|
work = true;
|
||||||
|
workSet.addAll(expanded);
|
||||||
} else {
|
} else {
|
||||||
working.add(str);
|
workSet.add(str);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (working.size() != beforeSize) {
|
if (work) {
|
||||||
results = working;
|
// 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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,13 +155,13 @@ public final class ShorthandParser {
|
|||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Set<String> captureResults(String s) {
|
private static Set<String> matchGroup(String s) {
|
||||||
int openingIndex = s.indexOf('{');
|
int openingIndex = s.indexOf(OPEN_GROUP);
|
||||||
if (openingIndex == -1) {
|
if (openingIndex == -1) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
int closingIndex = s.indexOf('}');
|
int closingIndex = s.indexOf(CLOSE_GROUP);
|
||||||
if (closingIndex < openingIndex) {
|
if (closingIndex < openingIndex) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -92,91 +170,49 @@ public final class ShorthandParser {
|
|||||||
String after = s.substring(closingIndex + 1);
|
String after = s.substring(closingIndex + 1);
|
||||||
String between = s.substring(openingIndex + 1, closingIndex);
|
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) {
|
for (ShorthandParser parser : PARSERS) {
|
||||||
Iterable<String> res = parser.apply(between);
|
try {
|
||||||
|
Iterator<String> res = parser.extract(between);
|
||||||
if (res != null) {
|
if (res != null) {
|
||||||
for (String r : res) {
|
while (res.hasNext()) {
|
||||||
results.add(before + r + after);
|
results.add(before + res.next() + after);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// break after one parser has matched
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
// ignore
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return results;
|
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
|
@Override
|
||||||
public Iterable<String> apply(String s) {
|
public final boolean hasNext() {
|
||||||
if (!s.contains(",")) {
|
return this.next <= this.max;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Iterable<String> apply(String s) {
|
public final String next() {
|
||||||
int index = s.indexOf("-");
|
return toString(this.next++);
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user