diff --git a/core/src/main/java/tc/oc/pgm/action/ActionParser.java b/core/src/main/java/tc/oc/pgm/action/ActionParser.java index b546f86749..f62837b9ab 100644 --- a/core/src/main/java/tc/oc/pgm/action/ActionParser.java +++ b/core/src/main/java/tc/oc/pgm/action/ActionParser.java @@ -10,10 +10,12 @@ import tc.oc.pgm.action.actions.KillEntitiesAction; import tc.oc.pgm.action.actions.ScopeSwitchAction; import tc.oc.pgm.action.actions.SetVariableAction; +import tc.oc.pgm.api.feature.FeatureValidation; import tc.oc.pgm.api.filter.Filter; import tc.oc.pgm.api.filter.Filterables; import tc.oc.pgm.api.map.factory.MapFactory; import tc.oc.pgm.features.FeatureDefinitionContext; +import tc.oc.pgm.features.XMLFeatureReference; import tc.oc.pgm.filters.Filterable; import tc.oc.pgm.filters.matcher.StaticFilter; import tc.oc.pgm.filters.parse.FilterParser; @@ -48,23 +50,13 @@ public > Action parse(Element el, @Nullable C throws InvalidXMLException { String id = FeatureDefinitionContext.parseId(el); + Node node = new Node(el); if (id != null && maybeReference(el)) { - return parseReference(new Node(el), id, bound); + return parseReference(node, id, bound); } Action result = parseDynamic(el, bound); - - Class childBound = result.getScope(); - if (bound != null && !childBound.isAssignableFrom(bound)) { - throw new InvalidXMLException( - "Wrong trigger target, expected " - + bound.getSimpleName() - + " rather than " - + childBound.getSimpleName(), - new Node(el)); - } - - // We don't need to add references, they should already be added by whoever created them. + if (bound != null) validate(result, ActionScopeValidation.of(bound), node); if (result instanceof ActionDefinition) features.addFeature(el, (ActionDefinition) result); return result; @@ -75,7 +67,7 @@ public boolean isAction(Element el) { } private boolean maybeReference(Element el) { - return "trigger".equals(el.getName()) && el.getChildren().isEmpty(); + return "action".equals(el.getName()) && el.getChildren().isEmpty(); } public Action parseReference(Node node, Class bound) @@ -85,7 +77,23 @@ public Action parseReference(Node node, Class bound) public Action parseReference(Node node, String id, Class bound) throws InvalidXMLException { - return features.addReference(new XMLActionReference<>(factory.getFeatures(), node, id, bound)); + Action action = features.addReference(new XMLActionReference<>(features, node, id)); + validate(action, ActionScopeValidation.of(bound), node); + return action; + } + + @SuppressWarnings("rawtypes, unchecked") + public void validate( + Action action, FeatureValidation> validation, Node node) + throws InvalidXMLException { + if (action instanceof XMLFeatureReference) { + factory.getFeatures().validate((XMLFeatureReference) action, validation); + } else if (action instanceof ActionDefinition) { + factory.getFeatures().validate((ActionDefinition) action, validation, node); + } else { + throw new IllegalStateException( + "Attempted validation on an action which is neither definition nor reference."); + } } protected Method getParserFor(Element el) { @@ -93,17 +101,17 @@ protected Method getParserFor(Element el) { } @SuppressWarnings("unchecked") - private > Action parseDynamic(Element el, Class bound) + private > Action parseDynamic(Element el, Class scope) throws InvalidXMLException { Method parser = getParserFor(el); if (parser != null) { try { - return (Action) parser.invoke(this, el, bound); + return (Action) parser.invoke(this, el, scope); } catch (Exception e) { throw InvalidXMLException.coerce(e, new Node(el)); } } else { - throw new InvalidXMLException("Unknown trigger type: " + el.getName(), el); + throw new InvalidXMLException("Unknown action type: " + el.getName(), el); } } @@ -112,31 +120,48 @@ public > Trigger parseTrigger(Element el) throws Inva return new Trigger<>( cls, filters.parseReference(Node.fromRequiredAttr(el, "filter")), - parseReference(Node.fromRequiredAttr(el, "trigger"), cls)); + parseReference(Node.fromRequiredAttr(el, "trigger", "action"), cls)); + } + + private > Class parseScope(Element el, Class scope) + throws InvalidXMLException { + return parseScope(el, scope, "scope"); + } + + private > Class parseScope(Element el, Class scope, String attr) + throws InvalidXMLException { + if (scope == null) return Filterables.parse(Node.fromRequiredAttr(el, attr)); + + Node node = Node.fromAttr(el, attr); + if (node != null && Filterables.parse(node) != scope) + throw new InvalidXMLException( + "Wrong scope defined for action, scope must be " + scope.getSimpleName(), el); + return scope; } @MethodParser("action") - public > ActionDefinition parseDefinition( - Element el, Class bound) throws InvalidXMLException { - if (bound == null) bound = Filterables.parse(Node.fromRequiredAttr(el, "scope")); + public > ActionNode parseAction(Element el, Class scope) + throws InvalidXMLException { + scope = parseScope(el, scope); ImmutableList.Builder> builder = ImmutableList.builder(); for (Element child : el.getChildren()) { - builder.add(parse(child, bound)); + builder.add(parse(child, scope)); } Filter filter = filters.parseFilterProperty(el, "filter", StaticFilter.ALLOW); + Filter untriggerFilter = filters.parseFilterProperty(el, "untrigger-filter", StaticFilter.DENY); - return new ActionNode<>(builder.build(), filter, bound); + return new ActionNode<>(builder.build(), filter, untriggerFilter, scope); } @MethodParser("switch-scope") public , I extends Filterable> Action parseSwitchScope( Element el, Class outer) throws InvalidXMLException { - if (outer == null) outer = Filterables.parse(Node.fromRequiredAttr(el, "outer")); - Class inner = Filterables.parse(Node.fromRequiredAttr(el, "inner")); + outer = parseScope(el, outer, "outer"); + Class inner = parseScope(el, null, "inner"); - ActionDefinition child = parseDefinition(el, inner); + ActionDefinition child = parseAction(el, inner); Action result = ScopeSwitchAction.of(child, outer, inner); if (result == null) { @@ -161,7 +186,7 @@ public > SetVariableAction parseSetVariable(Element e throws InvalidXMLException { VariableDefinition var = features.resolve(Node.fromRequiredAttr(el, "var"), VariableDefinition.class); - if (scope == null) scope = Filterables.parse(Node.fromRequiredAttr(el, "scope")); + scope = parseScope(el, scope); if (!Filterables.isAssignable(scope, var.getScope())) throw new InvalidXMLException( diff --git a/core/src/main/java/tc/oc/pgm/action/ActionScopeValidation.java b/core/src/main/java/tc/oc/pgm/action/ActionScopeValidation.java new file mode 100644 index 0000000000..f855b11c04 --- /dev/null +++ b/core/src/main/java/tc/oc/pgm/action/ActionScopeValidation.java @@ -0,0 +1,34 @@ +package tc.oc.pgm.action; + +import java.util.HashMap; +import java.util.Map; +import tc.oc.pgm.api.feature.FeatureValidation; +import tc.oc.pgm.util.xml.InvalidXMLException; +import tc.oc.pgm.util.xml.Node; + +public class ActionScopeValidation implements FeatureValidation> { + + private static final Map, ActionScopeValidation> INSTANCES = new HashMap<>(); + + public static ActionScopeValidation of(Class scope) { + return INSTANCES.computeIfAbsent(scope, ActionScopeValidation::new); + } + + private final Class scope; + + private ActionScopeValidation(Class scope) { + this.scope = scope; + } + + @Override + public void validate(ActionDefinition definition, Node node) throws InvalidXMLException { + Class scope = definition.getScope(); + if (!scope.isAssignableFrom(this.scope)) + throw new InvalidXMLException( + "Wrong action scope, got " + + scope.getSimpleName() + + " but expected " + + scope.getSimpleName(), + node); + } +} diff --git a/core/src/main/java/tc/oc/pgm/action/XMLActionReference.java b/core/src/main/java/tc/oc/pgm/action/XMLActionReference.java index 8436b14036..f301982fa4 100644 --- a/core/src/main/java/tc/oc/pgm/action/XMLActionReference.java +++ b/core/src/main/java/tc/oc/pgm/action/XMLActionReference.java @@ -3,40 +3,14 @@ import javax.annotation.Nullable; import tc.oc.pgm.features.FeatureDefinitionContext; import tc.oc.pgm.features.XMLFeatureReference; -import tc.oc.pgm.util.xml.InvalidXMLException; import tc.oc.pgm.util.xml.Node; @SuppressWarnings({"rawtypes", "unchecked"}) public class XMLActionReference extends XMLFeatureReference implements Action { - private final Class scope; - - public XMLActionReference( - FeatureDefinitionContext context, Node node, @Nullable String id, Class scope) { + public XMLActionReference(FeatureDefinitionContext context, Node node, @Nullable String id) { super(context, node, id, ActionDefinition.class); - this.scope = scope; - } - - @Override - public void resolve() throws InvalidXMLException { - Node node = this.node; // In case we need to report an error - - super.resolve(); - - if (super.referent == null) return; - - Class ref = super.referent.getScope(); - if (!ref.isAssignableFrom(this.scope)) { - throw new InvalidXMLException( - "Wrong trigger target for ID '" - + id - + "': expected " - + scope.getSimpleName() - + " rather than " - + ref.getSimpleName(), - node); - } } @Override diff --git a/core/src/main/java/tc/oc/pgm/action/actions/ActionNode.java b/core/src/main/java/tc/oc/pgm/action/actions/ActionNode.java index 0e0d91fa08..3a391d5225 100644 --- a/core/src/main/java/tc/oc/pgm/action/actions/ActionNode.java +++ b/core/src/main/java/tc/oc/pgm/action/actions/ActionNode.java @@ -8,12 +8,18 @@ public class ActionNode> extends AbstractAction { private final ImmutableList> actions; private final Filter filter; + private final Filter untrigerFilter; private final Class bound; - public ActionNode(ImmutableList> actions, Filter filter, Class bound) { + public ActionNode( + ImmutableList> actions, + Filter filter, + Filter untriggerFilter, + Class bound) { super(bound); this.actions = actions; this.filter = filter; + this.untrigerFilter = untriggerFilter; this.bound = bound; } @@ -30,4 +36,12 @@ public void trigger(B t) { } } } + + public void untrigger(B t) { + if (untrigerFilter.query(t).isAllowed()) { + for (Action action : actions) { + action.untrigger(t); + } + } + } } diff --git a/core/src/main/java/tc/oc/pgm/filters/XMLFilterReference.java b/core/src/main/java/tc/oc/pgm/filters/XMLFilterReference.java index c7037fdafe..80febd2d11 100644 --- a/core/src/main/java/tc/oc/pgm/filters/XMLFilterReference.java +++ b/core/src/main/java/tc/oc/pgm/filters/XMLFilterReference.java @@ -15,17 +15,8 @@ /** A {@link Filter} that delegates all methods to an XML reference */ public class XMLFilterReference extends XMLFeatureReference implements Filter { - public XMLFilterReference( - FeatureDefinitionContext context, Node node, Class type) { - this(context, node, null, type); - } - - public XMLFilterReference( - FeatureDefinitionContext context, - Node node, - @Nullable String id, - Class type) { - super(context, node, id, type); + public XMLFilterReference(FeatureDefinitionContext context, Node node, @Nullable String id) { + super(context, node, id, FilterDefinition.class); } @Override 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 f341fead9e..21aec44cef 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 @@ -36,11 +36,8 @@ public Filter parse(Element el) throws InvalidXMLException { } @Override - public Filter parseReference(Node node, String value) throws InvalidXMLException { - return factory - .getFeatures() - .addReference( - new XMLFilterReference(factory.getFeatures(), node, value, FilterDefinition.class)); + public Filter parseReference(Node node, String id) throws InvalidXMLException { + return features.addReference(new XMLFilterReference(factory.getFeatures(), node, id)); } @Override 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 9b75a205b0..ec640fecdc 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 @@ -128,7 +128,7 @@ public Filter parsePropertyElement(Element property) throws InvalidXMLException * Return the filter referenced by the given name/id, and assume it appears in the given {@link * Node} for error reporting purposes. */ - public abstract Filter parseReference(Node node, String value) throws InvalidXMLException; + public abstract Filter parseReference(Node node, String id) throws InvalidXMLException; public boolean isFilter(Element el) { return methodParsers.containsKey(el.getName()) || factory.getRegions().isRegion(el); diff --git a/core/src/main/java/tc/oc/pgm/filters/parse/LegacyFilterParser.java b/core/src/main/java/tc/oc/pgm/filters/parse/LegacyFilterParser.java index 977faf0c1b..28c5c69fcd 100644 --- a/core/src/main/java/tc/oc/pgm/filters/parse/LegacyFilterParser.java +++ b/core/src/main/java/tc/oc/pgm/filters/parse/LegacyFilterParser.java @@ -90,10 +90,10 @@ protected boolean isReference(Element el) { } @Override - public Filter parseReference(Node node, String value) throws InvalidXMLException { - Filter filter = this.filterContext.get(value); + public Filter parseReference(Node node, String id) throws InvalidXMLException { + Filter filter = this.filterContext.get(id); if (filter == null) { - throw new InvalidXMLException("No filter named '" + value + "'", node); + throw new InvalidXMLException("No filter named '" + id + "'", node); } return filter; } diff --git a/core/src/main/java/tc/oc/pgm/kits/ActionKit.java b/core/src/main/java/tc/oc/pgm/kits/ActionKit.java index 3f45a8630a..1283af6520 100644 --- a/core/src/main/java/tc/oc/pgm/kits/ActionKit.java +++ b/core/src/main/java/tc/oc/pgm/kits/ActionKit.java @@ -20,4 +20,11 @@ public void applyPostEvent(MatchPlayer player, boolean force, List di t.trigger(player); } } + + @Override + public void remove(MatchPlayer player) { + for (Action t : actions) { + t.untrigger(player); + } + } } diff --git a/core/src/main/java/tc/oc/pgm/kits/KitModule.java b/core/src/main/java/tc/oc/pgm/kits/KitModule.java index 5338453758..18a099e38e 100644 --- a/core/src/main/java/tc/oc/pgm/kits/KitModule.java +++ b/core/src/main/java/tc/oc/pgm/kits/KitModule.java @@ -10,6 +10,7 @@ import org.bukkit.inventory.ItemStack; import org.jdom2.Document; import org.jdom2.Element; +import tc.oc.pgm.action.ActionModule; import tc.oc.pgm.api.feature.FeatureDefinition; import tc.oc.pgm.api.filter.Filter; import tc.oc.pgm.api.map.MapModule; @@ -54,7 +55,7 @@ public static class Factory implements MapModuleFactory { @Override public Collection> getWeakDependencies() { - return ImmutableList.of(TeamModule.class); + return ImmutableList.of(ActionModule.class, TeamModule.class); } @Override diff --git a/core/src/main/java/tc/oc/pgm/regions/FeatureRegionParser.java b/core/src/main/java/tc/oc/pgm/regions/FeatureRegionParser.java index 07d28d1a5c..2b63c345d4 100644 --- a/core/src/main/java/tc/oc/pgm/regions/FeatureRegionParser.java +++ b/core/src/main/java/tc/oc/pgm/regions/FeatureRegionParser.java @@ -25,11 +25,11 @@ public Region parse(Element el) throws InvalidXMLException { } @Override - public Region parseReference(Node node, String val) throws InvalidXMLException { + public Region parseReference(Node node, String id) throws InvalidXMLException { return factory .getFeatures() .addReference( - new XMLRegionReference(factory.getFeatures(), node, val, RegionDefinition.class)); + new XMLRegionReference(factory.getFeatures(), node, id, RegionDefinition.class)); } @MethodParser("region") diff --git a/core/src/main/java/tc/oc/pgm/regions/LegacyRegionParser.java b/core/src/main/java/tc/oc/pgm/regions/LegacyRegionParser.java index 5052326287..ec9b87d7c3 100644 --- a/core/src/main/java/tc/oc/pgm/regions/LegacyRegionParser.java +++ b/core/src/main/java/tc/oc/pgm/regions/LegacyRegionParser.java @@ -38,10 +38,10 @@ public Region parse(Element el) throws InvalidXMLException { } @Override - public Region parseReference(Node node, String name) throws InvalidXMLException { - Region region = this.regionContext.get(name); + public Region parseReference(Node node, String id) throws InvalidXMLException { + Region region = this.regionContext.get(id); if (region == null) { - throw new InvalidXMLException("Unknown region '" + name + "'", node); + throw new InvalidXMLException("Unknown region '" + id + "'", node); } else { return region; } diff --git a/core/src/main/java/tc/oc/pgm/regions/RegionParser.java b/core/src/main/java/tc/oc/pgm/regions/RegionParser.java index 93647b6b40..e8c35bf957 100644 --- a/core/src/main/java/tc/oc/pgm/regions/RegionParser.java +++ b/core/src/main/java/tc/oc/pgm/regions/RegionParser.java @@ -39,7 +39,7 @@ public String type() { */ public abstract Region parse(Element el) throws InvalidXMLException; - public abstract Region parseReference(Node node, String value) throws InvalidXMLException; + public abstract Region parseReference(Node node, String id) throws InvalidXMLException; public Region parseReference(Attribute attribute) throws InvalidXMLException { return parseReference(new Node(attribute)); diff --git a/core/src/main/java/tc/oc/pgm/util/XMLParser.java b/core/src/main/java/tc/oc/pgm/util/XMLParser.java index 92f06aa269..45fc62083c 100644 --- a/core/src/main/java/tc/oc/pgm/util/XMLParser.java +++ b/core/src/main/java/tc/oc/pgm/util/XMLParser.java @@ -34,7 +34,7 @@ default F parseReference(Node node) throws InvalidXMLException { return parseReference(node, node.getValue()); } - F parseReference(Node node, String value) throws InvalidXMLException; + F parseReference(Node node, String id) throws InvalidXMLException; void validate(F f, FeatureValidation validation, Node node) throws InvalidXMLException;