From bcb644e82fdd346a8a4246073642a9094b3618a5 Mon Sep 17 00:00:00 2001 From: Pablo Herrera Date: Thu, 7 Nov 2024 02:54:25 +0100 Subject: [PATCH] Allow value with formula in variable filter Signed-off-by: Pablo Herrera --- .../filters/matcher/match/VariableFilter.java | 62 +++++++++++++++++++ .../tc/oc/pgm/filters/parse/FilterParser.java | 24 +++++-- 2 files changed, 81 insertions(+), 5 deletions(-) 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 aed2733c85..bbe645c720 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,6 +1,8 @@ package tc.oc.pgm.filters.matcher.match; import com.google.common.collect.Range; +import tc.oc.pgm.api.filter.Filter; +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; @@ -10,6 +12,7 @@ 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.math.Formula; import tc.oc.pgm.util.xml.InvalidXMLException; import tc.oc.pgm.util.xml.Node; import tc.oc.pgm.variables.Variable; @@ -39,6 +42,14 @@ public static VariableFilter of(Variable var, Integer idx, Range r } } + public static > Filter of( + Formula formula, Class scope, Range range) throws InvalidXMLException { + //noinspection unchecked + return scope == Party.class + ? new TeamFormula((Formula) formula, range) + : new GenericFormula<>((Formula) formula, scope, range); + } + @Override public QueryResponse queryTyped(Q query) { Filterable filterable = query.extractFilterable(); @@ -127,4 +138,55 @@ protected double getValue(Variable variable, Filterable filterable) { return ((Variable.Indexed) variable).getValue(filterable, idx); } } + + public static class GenericFormula> implements FilterDefinition { + private final Formula formula; + private final Class scope; + private final Range values; + + public GenericFormula(Formula variable, Class scope, Range values) { + this.formula = variable; + this.scope = scope; + this.values = values; + } + + @Override + public QueryResponse query(Query q) { + T target; + if (!(q instanceof MatchQuery mq) || (target = mq.filterable(scope)) == null) + return QueryResponse.ABSTAIN; + return QueryResponse.fromBoolean(values.contains(formula.apply(target))); + } + + @Override + public boolean respondsTo(Class queryType) { + //noinspection unchecked + return Filterable.class.isAssignableFrom(queryType) + && Filterables.isAssignable((Class>) queryType, scope); + } + } + + /** + * Specialization for team formulas implementing CompetitorFilter. Allows team to be set to a + * specific one. + */ + public static class TeamFormula implements CompetitorFilter { + private final Formula formula; + private final Range values; + + public TeamFormula(Formula variable, Range values) { + this.formula = variable; + this.values = values; + } + + @Override + public boolean matches(PartyQuery query) { + return values.contains(formula.apply(query.getParty())); + } + + @Override + public boolean matches(MatchQuery query, Competitor competitor) { + return matches(competitor); + } + } } 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 ccd4a32d0f..0a88a0db77 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 @@ -16,6 +16,7 @@ import org.jetbrains.annotations.Nullable; import tc.oc.pgm.api.filter.Filter; import tc.oc.pgm.api.filter.FilterDefinition; +import tc.oc.pgm.api.filter.Filterables; import tc.oc.pgm.api.map.factory.MapFactory; import tc.oc.pgm.api.player.PlayerRelation; import tc.oc.pgm.api.region.Region; @@ -90,6 +91,7 @@ import tc.oc.pgm.util.math.OffsetVector; import tc.oc.pgm.util.xml.InvalidXMLException; import tc.oc.pgm.util.xml.Node; +import tc.oc.pgm.util.xml.XMLFluentParser; import tc.oc.pgm.util.xml.XMLUtils; import tc.oc.pgm.variables.Variable; @@ -97,10 +99,12 @@ public abstract class FilterParser implements XMLParser methodParsers; protected final MapFactory factory; + protected final XMLFluentParser parser; protected final FeatureDefinitionContext features; public FilterParser(MapFactory factory) { this.factory = factory; + this.parser = factory.getParser(); this.features = factory.getFeatures(); this.methodParsers = MethodParsers.getMethodParsersForClass(getClass()); @@ -621,13 +625,23 @@ public PlayerCountFilter parsePlayerCountFilter(Element el) throws InvalidXMLExc @MethodParser("variable") public Filter parseVariableFilter(Element el) throws InvalidXMLException { - Variable varDef = features.resolve(Node.fromRequiredAttr(el, "var"), Variable.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); - VariableFilter filter = VariableFilter.of(varDef, index, range, new Node(el)); + Filter filter; + if (el.getAttribute("var") != null) { + Variable varDef = parser.variable(el, "var").required(); + Integer index = varDef.isIndexed() ? parser.parseInt(el, "index").required() : null; + + filter = VariableFilter.of(varDef, index, range, new Node(el)); + } else if (el.getAttribute("value") != null) { + var scope = Filterables.parse(Node.fromRequiredAttr(el, "scope")); + var formula = parser.formula(scope, el, "value").attr().required(); + + filter = VariableFilter.of(formula, scope, range); + } else { + throw new InvalidXMLException("Expected one of 'var' or 'value'", el); + } + return filter instanceof CompetitorFilter ? parseExplicitTeam(el, (CompetitorFilter) filter) : filter;