Added searching of card mana costs. Added simple boolean search parsing to card search boxes.

This commit is contained in:
Krazy
2014-07-17 22:38:11 +00:00
parent 354a802044
commit 0728a42eb7
4 changed files with 311 additions and 19 deletions

View File

@@ -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()) {