mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-20 12:48:00 +00:00
Added searching of card mana costs. Added simple boolean search parsing to card search boxes.
This commit is contained in:
@@ -2,7 +2,6 @@ package forge.itemmanager;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.base.Predicates;
|
||||
|
||||
import forge.card.CardRules;
|
||||
import forge.card.CardRulesPredicates;
|
||||
import forge.card.CardRulesPredicates.Presets;
|
||||
@@ -16,10 +15,9 @@ import forge.itemmanager.SItemManagerUtil.StatTypes;
|
||||
import forge.util.BinaryUtil;
|
||||
import forge.util.PredicateString.StringOp;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Static factory; holds blocks of form elements and predicates
|
||||
@@ -31,11 +29,26 @@ public class SFilterUtil {
|
||||
/**
|
||||
* builds a string search filter
|
||||
*/
|
||||
public static Predicate<PaperCard> buildTextFilter(String text, boolean invert, boolean inName, boolean inType, boolean inText) {
|
||||
if (text.trim().isEmpty()) {
|
||||
public static Predicate<PaperCard> buildTextFilter(String text, boolean invert, boolean inName, boolean inType, boolean inText, boolean inCost) {
|
||||
|
||||
text = text.trim();
|
||||
|
||||
if (text.isEmpty()) {
|
||||
return Predicates.alwaysTrue();
|
||||
}
|
||||
|
||||
|
||||
if (BooleanExpression.isBooleanExpression(text)) {
|
||||
BooleanExpression expression = new BooleanExpression(text);
|
||||
try {
|
||||
Predicate<CardRules> filter = expression.evaluate(inName, inType, inText, inCost);
|
||||
if (filter != null) {
|
||||
return Predicates.compose(filter, PaperCard.FN_GET_RULES);
|
||||
} //The expression is not valid, let the regular filters work
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
String[] splitText = text.replaceAll(",", "").replaceAll(" ", " ").split(" ");
|
||||
|
||||
List<Predicate<CardRules>> terms = new ArrayList<Predicate<CardRules>>();
|
||||
@@ -44,14 +57,255 @@ public class SFilterUtil {
|
||||
|
||||
if (inName) { subands.add(CardRulesPredicates.name(StringOp.CONTAINS_IC, s)); }
|
||||
if (inType) { subands.add(CardRulesPredicates.joinedType(StringOp.CONTAINS_IC, s)); }
|
||||
if (inText) { subands.add(CardRulesPredicates.rules(StringOp.CONTAINS_IC, s)); }
|
||||
if (inText) { subands.add(CardRulesPredicates.rules(StringOp.CONTAINS_IC, s)); }
|
||||
if (inCost) { subands.add(CardRulesPredicates.cost(StringOp.CONTAINS_IC, s)); }
|
||||
|
||||
terms.add(Predicates.or(subands));
|
||||
}
|
||||
Predicate<CardRules> textFilter = invert ? Predicates.not(Predicates.or(terms)) : Predicates.and(terms);
|
||||
|
||||
return Predicates.compose(textFilter, PaperCard.FN_GET_RULES);
|
||||
|
||||
}
|
||||
|
||||
private static class BooleanExpression {
|
||||
|
||||
private static enum Operation {
|
||||
|
||||
AND("&&"), OR("||"), OPEN_PAREN("("), CLOSE_PAREN(")"), VALUE("\""), ESCAPE("\\");
|
||||
|
||||
private final String token;
|
||||
|
||||
private Operation(String token) {
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private Stack<StackItem> stack = new Stack<StackItem>();
|
||||
private String expression;
|
||||
|
||||
private BooleanExpression(String expression) {
|
||||
this.expression = expression;
|
||||
}
|
||||
|
||||
private Predicate<CardRules> evaluate(boolean inName, boolean inType, boolean inText, boolean inCost) throws IOException {
|
||||
|
||||
StringReader reader = new StringReader(expression);
|
||||
String currentItem = "";
|
||||
boolean escapeNext = false;
|
||||
boolean gettingValue = false;
|
||||
|
||||
int character;
|
||||
|
||||
while ((character = reader.read()) != -1) {
|
||||
|
||||
String token = Character.toString((char) character);
|
||||
|
||||
if (token.equals(Operation.OPEN_PAREN.token)) {
|
||||
StackOperation operation = new StackOperation();
|
||||
operation.operation = Operation.OPEN_PAREN;
|
||||
stack.push(operation);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (token.equals(Operation.CLOSE_PAREN.token)) {
|
||||
character = reader.read();
|
||||
if (character != -1) {
|
||||
evaluateStack();
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (token.equals("&")) {
|
||||
character = reader.read();
|
||||
if (character != -1 && character == '&') {
|
||||
StackOperation operation = new StackOperation();
|
||||
operation.operation = Operation.AND;
|
||||
stack.push(operation);
|
||||
currentItem = "";
|
||||
} else if (gettingValue) {
|
||||
currentItem += token + Character.toString((char) character);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (token.equals("|")) {
|
||||
character = reader.read();
|
||||
if (character != -1 && character == '|') {
|
||||
StackOperation operation = new StackOperation();
|
||||
operation.operation = Operation.OR;
|
||||
stack.push(operation);
|
||||
currentItem = "";
|
||||
} else if (gettingValue) {
|
||||
currentItem += token + Character.toString((char) character);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (token.equals(Operation.VALUE.token) && !escapeNext) {
|
||||
if (gettingValue) {
|
||||
StackValue stackValue = new StackValue();
|
||||
stackValue.value = evaluateValue(currentItem.trim(), inName, inType, inText, inCost);
|
||||
stack.push(stackValue);
|
||||
currentItem = "";
|
||||
gettingValue = false;
|
||||
} else {
|
||||
gettingValue = true;
|
||||
continue; //Don't add the quotation mark
|
||||
}
|
||||
}
|
||||
|
||||
if (token.equals(Operation.ESCAPE.token) && !escapeNext) {
|
||||
escapeNext = true;
|
||||
} else if (gettingValue) {
|
||||
currentItem += token;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
while (stack.size() > 1) {
|
||||
evaluateStack();
|
||||
}
|
||||
|
||||
if (stack.isEmpty()) {
|
||||
return null; //The expression is not valid, let the regular filters work
|
||||
}
|
||||
|
||||
return ((StackValue) stack.pop()).value;
|
||||
|
||||
}
|
||||
|
||||
private Predicate<CardRules> evaluateValue(String value, boolean inName, boolean inType, boolean inText, boolean inCost) {
|
||||
if (inName) { return CardRulesPredicates.name(StringOp.CONTAINS_IC, value); }
|
||||
if (inType) { return CardRulesPredicates.joinedType(StringOp.CONTAINS_IC, value); }
|
||||
if (inText) { return CardRulesPredicates.rules(StringOp.CONTAINS_IC, value); }
|
||||
if (inCost) { return CardRulesPredicates.cost(StringOp.CONTAINS_IC, value); }
|
||||
return Predicates.alwaysTrue();
|
||||
}
|
||||
|
||||
private void evaluateStack() {
|
||||
|
||||
StackItem stackItem;
|
||||
Predicate<CardRules> rules = null;
|
||||
Operation operation = null;
|
||||
|
||||
boolean finishedStack = false;
|
||||
|
||||
while (stack.size() > 0) {
|
||||
|
||||
stackItem = stack.pop();
|
||||
|
||||
if (stackItem.isOperation()) {
|
||||
|
||||
operation = ((StackOperation) stackItem).operation;
|
||||
|
||||
if (operation.equals(Operation.OPEN_PAREN)) {
|
||||
|
||||
if (rules != null) {
|
||||
StackValue value = new StackValue();
|
||||
value.value = rules;
|
||||
stack.push(value);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
} else if (rules == null) {
|
||||
rules = ((StackValue) stackItem).value;
|
||||
} else {
|
||||
|
||||
if (operation == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
StackValue value = new StackValue();
|
||||
|
||||
if (operation.equals(Operation.AND)) {
|
||||
value.value = Predicates.and(rules, ((StackValue) stackItem).value);
|
||||
} else if (operation.equals(Operation.OR)) {
|
||||
value.value = Predicates.or(rules, ((StackValue) stackItem).value);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
if (stack.size() == 0) {
|
||||
finishedStack = true;
|
||||
}
|
||||
|
||||
stack.push(value);
|
||||
|
||||
rules = null;
|
||||
|
||||
}
|
||||
|
||||
if (finishedStack) {
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static boolean isBooleanExpression(String s) {
|
||||
return s.contains(Operation.AND.token) || s.contains(Operation.OR.token);
|
||||
}
|
||||
|
||||
private static abstract class StackItem {
|
||||
private boolean isOperation() {
|
||||
return this instanceof StackOperation;
|
||||
}
|
||||
}
|
||||
|
||||
private static class StackOperation extends StackItem {
|
||||
private Operation operation;
|
||||
}
|
||||
|
||||
private static class StackValue extends StackItem {
|
||||
private Predicate<CardRules> value;
|
||||
}
|
||||
|
||||
/*private static abstract class BooleanExpressionNode {
|
||||
protected abstract Predicate<CardRules> evaluate(boolean inName, boolean inType, boolean inText, boolean inCost);
|
||||
}
|
||||
|
||||
private static class ExpressionNode extends BooleanExpressionNode {
|
||||
|
||||
private BooleanExpressionNode left;
|
||||
private BooleanExpressionNode right;
|
||||
private Operation operation;
|
||||
|
||||
@Override
|
||||
protected Predicate<CardRules> evaluate(boolean inName, boolean inType, boolean inText, boolean inCost) {
|
||||
switch (operation) {
|
||||
case AND:
|
||||
return Predicates.and(left.evaluate(inName, inType, inText, inCost), right.evaluate(inName, inType, inText, inCost));
|
||||
case OR:
|
||||
return Predicates.or(left.evaluate(inName, inType, inText, inCost), right.evaluate(inName, inType, inText, inCost));
|
||||
}
|
||||
return Predicates.alwaysTrue();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class ValueNode extends BooleanExpressionNode {
|
||||
|
||||
private String value;
|
||||
|
||||
@Override
|
||||
protected Predicate<CardRules> evaluate(boolean inName, boolean inType, boolean inText, boolean inCost) {
|
||||
if (inName) { return CardRulesPredicates.name(StringOp.CONTAINS_IC, value); }
|
||||
if (inType) { return CardRulesPredicates.joinedType(StringOp.CONTAINS_IC, value); }
|
||||
if (inText) { return CardRulesPredicates.rules(StringOp.CONTAINS_IC, value); }
|
||||
if (inCost) { return CardRulesPredicates.cost(StringOp.CONTAINS_IC, value); }
|
||||
return Predicates.alwaysTrue();
|
||||
}
|
||||
|
||||
}*/
|
||||
|
||||
}
|
||||
|
||||
public static <T extends InventoryItem> Predicate<T> buildItemTextFilter(String text) {
|
||||
if (text.trim().isEmpty()) {
|
||||
|
||||
Reference in New Issue
Block a user