diff --git a/core/src/main/java/tc/oc/pgm/filters/matcher/match/VariableFilter.java b/core/src/main/java/tc/oc/pgm/filters/matcher/match/VariableFilter.java index 2dc99a1693..fbd093c58b 100644 --- a/core/src/main/java/tc/oc/pgm/filters/matcher/match/VariableFilter.java +++ b/core/src/main/java/tc/oc/pgm/filters/matcher/match/VariableFilter.java @@ -1,31 +1,58 @@ package tc.oc.pgm.filters.matcher.match; import com.google.common.collect.Range; -import tc.oc.pgm.api.filter.FilterDefinition; import tc.oc.pgm.api.filter.Filterables; import tc.oc.pgm.api.filter.query.MatchQuery; +import tc.oc.pgm.api.filter.query.PartyQuery; import tc.oc.pgm.api.filter.query.Query; +import tc.oc.pgm.api.party.Competitor; +import tc.oc.pgm.api.party.Party; import tc.oc.pgm.filters.Filterable; +import tc.oc.pgm.filters.matcher.WeakTypedFilter; +import tc.oc.pgm.filters.matcher.party.CompetitorFilter; +import tc.oc.pgm.util.xml.InvalidXMLException; +import tc.oc.pgm.util.xml.Node; +import tc.oc.pgm.variables.Variable; import tc.oc.pgm.variables.VariableDefinition; +import tc.oc.pgm.variables.types.IndexedVariable; -public class VariableFilter implements FilterDefinition { +public abstract class VariableFilter implements WeakTypedFilter { private final VariableDefinition variable; private final Range values; - public VariableFilter(VariableDefinition variable, Range values) { + private VariableFilter(VariableDefinition variable, Range values) { this.variable = variable; this.values = values; } + public static VariableFilter of( + VariableDefinition var, Integer idx, Range range, Node node) + throws InvalidXMLException { + if (var.isIndexed()) { + if (idx == null) + throw new InvalidXMLException("Array variables must contain an index.", node); + return var.getScope() == Party.class + ? new TeamIndexed(var, idx, range) + : new Indexed(var, idx, range); + } else { + if (idx != null) + throw new InvalidXMLException("Non-array variables cannot contain an index.", node); + return var.getScope() == Party.class ? new Team(var, range) : new Generic(var, range); + } + } + @Override - public QueryResponse query(Query query) { - Filterable filterable = - query instanceof MatchQuery ? ((MatchQuery) query).extractFilterable() : null; + public QueryResponse queryTyped(Q query) { + Filterable filterable = query.extractFilterable(); if (!Filterables.isAssignable(filterable, variable.getScope())) return QueryResponse.ABSTAIN; return QueryResponse.fromBoolean( - values.contains(variable.getVariable(filterable.getMatch()).getValue(filterable))); + values.contains(getValue(variable.getVariable(filterable.getMatch()), filterable))); + } + + protected double getValue(Variable variable, Filterable filterable) { + return variable.getValue(filterable); } @Override @@ -44,4 +71,64 @@ public boolean isDynamic() { public String toString() { return "VariableFilter{" + "variable=" + variable + ", values=" + values + '}'; } + + public static class Generic extends VariableFilter { + + public Generic(VariableDefinition variable, Range values) { + super(variable, values); + } + + @Override + public Class queryType() { + return MatchQuery.class; + } + } + + /** + * Specialization for team variables implementing CompetitorFilter. Allows team to be set to a + * specific one. + */ + public static class Team extends VariableFilter implements CompetitorFilter { + + public Team(VariableDefinition variable, Range values) { + super(variable, values); + } + + @Override + public boolean matches(MatchQuery query, Competitor competitor) { + QueryResponse response = super.query(competitor); + if (!response.isPresent()) + throw new UnsupportedOperationException( + "Filter " + this + " did not respond to the query " + query); + return response.isAllowed(); + } + } + + public static class Indexed extends Generic { + private final int idx; + + public Indexed(VariableDefinition variable, int idx, Range values) { + super(variable, values); + this.idx = idx; + } + + @Override + protected double getValue(Variable variable, Filterable filterable) { + return ((IndexedVariable) variable).getValue(filterable, idx); + } + } + + public static class TeamIndexed extends Team { + private final int idx; + + public TeamIndexed(VariableDefinition variable, int idx, Range values) { + super(variable, values); + this.idx = idx; + } + + @Override + protected double getValue(Variable variable, Filterable filterable) { + return ((IndexedVariable) variable).getValue(filterable, idx); + } + } } diff --git a/core/src/main/java/tc/oc/pgm/filters/matcher/party/TeamVariableFilter.java b/core/src/main/java/tc/oc/pgm/filters/matcher/party/TeamVariableFilter.java deleted file mode 100644 index 1d74055161..0000000000 --- a/core/src/main/java/tc/oc/pgm/filters/matcher/party/TeamVariableFilter.java +++ /dev/null @@ -1,30 +0,0 @@ -package tc.oc.pgm.filters.matcher.party; - -import com.google.common.collect.Range; -import tc.oc.pgm.api.filter.query.MatchQuery; -import tc.oc.pgm.api.filter.query.PartyQuery; -import tc.oc.pgm.api.party.Competitor; -import tc.oc.pgm.filters.matcher.match.VariableFilter; -import tc.oc.pgm.variables.VariableDefinition; - -public class TeamVariableFilter extends VariableFilter implements CompetitorFilter { - - public TeamVariableFilter(VariableDefinition variable, Range values) { - super(variable, values); - } - - @Override - public Class queryType() { - return PartyQuery.class; - } - - @Override - public boolean matches(MatchQuery query, Competitor competitor) { - QueryResponse response = super.query(competitor); - if (!response.isPresent()) - throw new UnsupportedOperationException( - "Filter " + this + " did not respond to the query " + query); - - return response.isAllowed(); - } -} diff --git a/core/src/main/java/tc/oc/pgm/filters/parse/FeatureFilterParser.java b/core/src/main/java/tc/oc/pgm/filters/parse/FeatureFilterParser.java index 89be03f195..1af55e2fd8 100644 --- a/core/src/main/java/tc/oc/pgm/filters/parse/FeatureFilterParser.java +++ b/core/src/main/java/tc/oc/pgm/filters/parse/FeatureFilterParser.java @@ -97,11 +97,10 @@ public Filter parseNot(Element el) throws InvalidXMLException { private static final Pattern INLINE_VARIABLE = Pattern.compile( - "(" - + VariablesModule.Factory.VARIABLE_ID.pattern() - + ")\\s*=\\s*(" - + XMLUtils.RANGE_DOTTED.pattern() - + "|-?\\d*\\.?\\d+)"); + "(%VAR%)(?:\\[(\\d+)])?\\s*=\\s*(%RANGE%|%NUM%)" + .replace("%VAR%", VariablesModule.Factory.VARIABLE_ID.pattern()) + .replace("%RANGE%", XMLUtils.RANGE_DOTTED.pattern()) + .replace("%NUM%", "-?\\d*\\.?\\d+")); private @Nullable Filter parseInlineFilter(Node node, String text) throws InvalidXMLException { // Formula-style inline filter @@ -114,12 +113,14 @@ public Filter parseNot(Element el) throws InvalidXMLException { } // Parse variable filter - Matcher varMatch = INLINE_VARIABLE.matcher(text); - if (varMatch.matches()) { + Matcher match = INLINE_VARIABLE.matcher(text); + if (match.matches()) { VariableDefinition variable = - features.resolve(node, varMatch.group(1), VariableDefinition.class); - Range range = XMLUtils.parseNumericRange(node, varMatch.group(2), Double.class); - return new VariableFilter(variable, range); + features.resolve(node, match.group(1), VariableDefinition.class); + Integer index = + match.group(2) == null ? null : XMLUtils.parseNumber(node, match.group(2), Integer.class); + Range range = XMLUtils.parseNumericRange(node, match.group(3), Double.class); + return VariableFilter.of(variable, index, range, node); } return null; diff --git a/core/src/main/java/tc/oc/pgm/filters/parse/FilterParser.java b/core/src/main/java/tc/oc/pgm/filters/parse/FilterParser.java index 4520a1bae2..fd16699c13 100644 --- a/core/src/main/java/tc/oc/pgm/filters/parse/FilterParser.java +++ b/core/src/main/java/tc/oc/pgm/filters/parse/FilterParser.java @@ -18,7 +18,6 @@ import tc.oc.pgm.api.filter.Filter; import tc.oc.pgm.api.filter.FilterDefinition; import tc.oc.pgm.api.map.factory.MapFactory; -import tc.oc.pgm.api.party.Party; import tc.oc.pgm.api.player.PlayerRelation; import tc.oc.pgm.api.region.Region; import tc.oc.pgm.classes.ClassModule; @@ -48,7 +47,6 @@ import tc.oc.pgm.filters.matcher.party.RankFilter; import tc.oc.pgm.filters.matcher.party.ScoreFilter; import tc.oc.pgm.filters.matcher.party.TeamFilter; -import tc.oc.pgm.filters.matcher.party.TeamVariableFilter; import tc.oc.pgm.filters.matcher.player.CanFlyFilter; import tc.oc.pgm.filters.matcher.player.CarryingFlagFilter; import tc.oc.pgm.filters.matcher.player.CarryingItemFilter; @@ -633,11 +631,15 @@ public PlayerCountFilter parsePlayerCountFilter(Element el) throws InvalidXMLExc public Filter parseVariableFilter(Element el) throws InvalidXMLException { VariableDefinition varDef = features.resolve(Node.fromRequiredAttr(el, "var"), VariableDefinition.class); + Integer index = null; + if (varDef.isIndexed()) + index = XMLUtils.parseNumber(Node.fromRequiredAttr(el, "index"), Integer.class); Range range = XMLUtils.parseNumericRange(new Node(el), Double.class); - if (varDef.getScope() == Party.class) - return parseExplicitTeam(el, new TeamVariableFilter(varDef, range)); - else return new VariableFilter(varDef, range); + VariableFilter filter = VariableFilter.of(varDef, index, range, new Node(el)); + return filter instanceof CompetitorFilter + ? parseExplicitTeam(el, (CompetitorFilter) filter) + : filter; } @MethodParser("blocks")