/*
* $Id$
*
* Copyright (c) 2005-2012 by Rodney Kinney, Brent Easton
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License (LGPL) as published by the Free Software Foundation.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, copies are available
* at http://www.opensource.org.
*/
package VASSAL.counters;
import java.util.regex.Pattern;
import VASSAL.script.expression.BeanShellExpression;
import VASSAL.script.expression.FormattedStringExpression;
/**
* Accepts pieces based on whether the piece has properties that
* match a given set of conditions
*/
public class PropertiesPieceFilter {
private static final Pattern[] CONDITIONS = new Pattern[]{Pattern.compile("!="),
Pattern.compile("<="),
Pattern.compile(">="),
Pattern.compile(">"),
Pattern.compile("<"),
Pattern.compile("=~"),
Pattern.compile("="),
Pattern.compile("!~")};
private static final Pattern AND = Pattern.compile("&&");
private static final Pattern OR = Pattern.compile("\\|\\|");
private static PieceFilter ACCEPT_ALL = new PieceFilter() {
public boolean accept(GamePiece piece) {
return true;
}
};
/**
* Return a PieceFilter parsed from a boolean expression such as
* prop1 = value1 && prop2 = value2 || prop3 = value3
* @param expression
* @return
*/
public static PieceFilter parse(String expression) {
if (expression == null
|| expression.length() == 0) {
return ACCEPT_ALL;
}
String[] s = OR.split(expression);
PieceFilter f = null;
if (s.length > 1) {
f = parse(s[0]);
for (int i = 1; i < s.length; ++i) {
f = new BooleanOrPieceFilter(f, parse(s[i]));
}
}
else {
s = AND.split(expression);
if (s.length > 1) {
f = parse(s[0]);
for (int i = 1; i < s.length; ++i) {
f = new BooleanAndPieceFilter(f, parse(s[i]));
}
}
else {
for (int i = 0; i < CONDITIONS.length && f == null; i++) {
if (expression.indexOf(CONDITIONS[i].pattern()) >= 0) {
s = CONDITIONS[i].split(expression);
String name = "";
String value = "";
if (s.length > 0) {
name = s[0].trim();
if (s.length > 1) {
value = s[1].trim();
}
switch (i) {
case 0:
f = new NE(name, value);
break;
case 1:
f = new LE(name, value);
break;
case 2:
f = new GE(name, value);
break;
case 3:
f = new GT(name, value);
break;
case 4:
f = new LT(name, value);
break;
case 5:
f = new MATCH(name, value);
break;
case 6:
f = new EQ(name, value);
break;
case 7:
f = new NOT_MATCH(name,value);
}
}
break;
}
}
if (f == null) {
f = ACCEPT_ALL;
}
}
}
return f;
}
public static String toBeanShellString(String s) {
return toBeanShellString(parse(s));
}
public static String toBeanShellString(PieceFilter f) {
if (f instanceof BooleanAndPieceFilter) {
final BooleanAndPieceFilter and = (BooleanAndPieceFilter) f;
return "("+toBeanShellString(and.getFilter1()) + ") && (" + toBeanShellString(and.getFilter2()) + ")";
}
else if (f instanceof BooleanOrPieceFilter) {
final BooleanOrPieceFilter or = (BooleanOrPieceFilter) f;
return "("+toBeanShellString(or.getFilter1()) + ") || (" + toBeanShellString(or.getFilter2()) + ")";
}
else if (f instanceof ComparisonFilter) {
return ((ComparisonFilter) f).toBeanShellString();
}
return "";
}
private static abstract class ComparisonFilter implements PieceFilter {
protected String name;
protected String value;
protected Object alternate;
public ComparisonFilter(String name, String value) {
this.name = name;
this.value = value;
if ("true".equals(value)) {
alternate = Boolean.TRUE;
}
else if ("false".equals(value)) {
alternate = Boolean.FALSE;
}
}
protected int compareTo(GamePiece piece) {
String property = String.valueOf(piece.getProperty(name));
try {
return Integer.valueOf(property).compareTo(Integer.valueOf(value));
}
catch (NumberFormatException e) {
// If both properties are not numbers, compare alphabetically
return property.compareTo(value);
}
}
public abstract String toBeanShellString();
protected String toBeanShellName() {
if (name.indexOf('$') >= 0) {
return "GetProperty("+new FormattedStringExpression(name).toBeanShellString() + ")";
}
else {
return BeanShellExpression.convertProperty(name);
}
}
protected String toBeanShellValue() {
return new FormattedStringExpression(value).toBeanShellString();
}
}
private static class EQ extends ComparisonFilter {
public EQ(String name, String value) {
super(name, value);
}
public boolean accept(GamePiece piece) {
String property = String.valueOf(piece.getProperty(name));
boolean retVal = value.equals(property);
if (alternate != null) {
retVal = retVal || alternate.equals(property);
}
return retVal;
}
public String toString() {
return "PropertiesPieceFilter["+name+"=="+value+"]";
}
public String toBeanShellString() {
return toBeanShellName() + "==" + toBeanShellValue();
}
}
private static class NE extends ComparisonFilter {
public NE(String name, String value) {
super(name, value);
}
public boolean accept(GamePiece piece) {
String property = String.valueOf(piece.getProperty(name));
boolean retVal = !value.equals(property);
if (alternate != null) {
retVal = retVal && !alternate.equals(property);
}
return retVal;
}
public String toString() {
return "PropertiesPieceFilter["+name+"!="+value+"]";
}
public String toBeanShellString() {
return toBeanShellName() + "!=" + toBeanShellValue();
}
}
private static class LT extends ComparisonFilter {
public LT(String name, String value) {
super(name, value);
}
public boolean accept(GamePiece piece) {
return compareTo(piece) < 0;
}
public String toString() {
return "PropertiesPieceFilter["+name+"<"+value+"]";
}
public String toBeanShellString() {
return toBeanShellName() + "<" + toBeanShellValue();
}
}
private static class LE extends ComparisonFilter {
public LE(String name, String value) {
super(name, value);
}
public boolean accept(GamePiece piece) {
return compareTo(piece) <= 0;
}
public String toString() {
return "PropertiesPieceFilter["+name+"<="+value+"]";
}
public String toBeanShellString() {
return toBeanShellName() + "<=" + toBeanShellValue();
}
}
private static class GT extends ComparisonFilter {
public GT(String name, String value) {
super(name, value);
}
public boolean accept(GamePiece piece) {
return compareTo(piece) > 0;
}
public String toString() {
return "PropertiesPieceFilter["+name+">"+value+"]";
}
public String toBeanShellString() {
return toBeanShellName() + ">" + toBeanShellValue();
}
}
private static class GE extends ComparisonFilter {
public GE(String name, String value) {
super(name, value);
}
public boolean accept(GamePiece piece) {
return compareTo(piece) >= 0;
}
public String toString() {
return "PropertiesPieceFilter["+name+">="+value+"]";
}
public String toBeanShellString() {
return toBeanShellName() + ">=" + toBeanShellValue();
}
}
private static class MATCH extends ComparisonFilter {
public MATCH(String name, String value) {
super(name, value);
}
public boolean accept(GamePiece piece) {
String property = String.valueOf(piece.getProperty(name));
return Pattern.matches(value, property);
}
public String toString() {
return "PropertiesPieceFilter["+name+"~"+value+"]";
}
public String toBeanShellString() {
return toBeanShellName() + "=~" + toBeanShellValue();
}
}
private static class NOT_MATCH extends MATCH {
public NOT_MATCH(String name, String value) {
super(name, value);
}
public boolean accept(GamePiece piece) {
return !super.accept(piece);
}
public String toString() {
return "PropertiesPieceFilter["+name+"!~"+value+"]";
}
public String toBeanShellString() {
return toBeanShellName() + "!~" + toBeanShellValue();
}
}
}