/* * Copyright 2011 <a href="mailto:lincolnbaxter@gmail.com">Lincoln Baxter, III</a> * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.ocpsoft.rewrite.config; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.ocpsoft.common.pattern.Weighted; import org.ocpsoft.rewrite.context.Context; import org.ocpsoft.rewrite.context.EvaluationContext; import org.ocpsoft.rewrite.event.Rewrite; import org.ocpsoft.rewrite.exception.RewriteException; import org.ocpsoft.rewrite.param.ConfigurableParameter; import org.ocpsoft.rewrite.param.DefaultParameter; import org.ocpsoft.rewrite.param.DefaultParameterStore; import org.ocpsoft.rewrite.param.Parameter; import org.ocpsoft.rewrite.param.ParameterStore; import org.ocpsoft.rewrite.param.Parameterized; import org.ocpsoft.rewrite.param.ParameterizedRule; import org.ocpsoft.rewrite.util.Visitor; /** * Builder for fluently defining new composite {@link Rule} instances. * * @author <a href="mailto:lincolnbaxter@gmail.com">Lincoln Baxter, III</a> */ public final class RuleBuilder implements ParameterizedRule, RelocatableRule, CompositeCondition, CompositeOperation, CompositeRule, Context { private final ParameterStore store; private Integer priority = null; private String id = ""; private Condition condition; private Operation operation; protected Map<Object, Object> contextMap = new HashMap<Object, Object>(); private Rule wrapped; private RuleBuilder() { store = new DefaultParameterStore(); } private RuleBuilder(Rule rule) { store = new DefaultParameterStore(); withId(rule.getId()); if (rule instanceof Weighted) withPriority(((Weighted) rule).priority()); wrapped = rule; } /** * Returns a new {@link RuleBuilder} instance. */ public static RuleBuilder define() { return new RuleBuilder(); } /** * Returns a new {@link RuleBuilder} instance wrapping the given {@link Rule}. */ public static RuleBuilder wrap(final Rule rule) { return new RuleBuilder(rule); } /** * Returns a new {@link RuleBuilder} instance, set with the given {@link Rule} ID. */ public static RuleBuilder define(final String id) { return define().withId(id); } /** * Set the ID of this {@link Rule} instance. */ public RuleBuilder withId(final String id) { this.id = id; return this; } /** * Set the priority of this {@link Rule} instance. If {@link #priority()} differs from the priority of the * {@link ConfigurationProvider} from which this rule was returned, then relocate this rule to its new priority * position in the compiled rule set. */ public RuleBuilder withPriority(int priority) { this.priority = priority; return this; } /** * Set the {@link Condition} of this {@link Rule} instance. */ public RuleBuilder when(final Condition condition) { if (this.condition == null) this.condition = condition; else if (condition instanceof ConditionBuilder) this.condition = ((ConditionBuilder) this.condition).and(condition); else this.condition = Conditions.wrap(this.condition).and(condition); return this; } /** * Perform the given {@link Operation} when the conditions set in this {@link Rule} are met. */ public RuleBuilder perform(final Operation operation) { if (this.operation == null) this.operation = operation; else if (operation instanceof OperationBuilder) this.operation = ((OperationBuilder) this.operation).and(operation); else this.operation = Operations.wrap(this.operation).and(operation); return this; } @Override public boolean evaluate(final Rewrite event, final EvaluationContext context) { context.put(ParameterStore.class, store); if (wrapped != null && condition != null) return wrapped.evaluate(event, context) && condition.evaluate(event, context); else if (wrapped != null) return wrapped.evaluate(event, context); else if (condition != null) return condition.evaluate(event, context); return true; } @Override public void perform(final Rewrite event, final EvaluationContext context) { if (wrapped != null) wrapped.perform(event, context); if (operation != null) operation.perform(event, context); } @Override public String getId() { return id; } @Override public int priority() { return priority == null ? 0 : priority; } @Override public boolean isRelocated() { return priority != null; } /** * Return the underlying {@link ConditionBuilder} */ public DefaultConditionBuilder getConditionBuilder() { if (condition == null) condition = Conditions.create(); else if (!(condition instanceof DefaultConditionBuilder)) condition = Conditions.wrap(condition); return (DefaultConditionBuilder) condition; } /** * Return the underlying {@link OperationBuilder} */ public DefaultOperationBuilder getOperationBuilder() { if (operation == null) operation = Operations.create(); else if (!(operation instanceof DefaultOperationBuilder)) operation = Operations.wrap(operation); return (DefaultOperationBuilder) operation; } /** * This method will call the supplied visitor for all conditions attached to the rule builder. * * @param visitor visitor to process */ public void accept(Visitor<Condition> visitor) { new ConditionVisit(condition).accept(visitor); } @Override public List<Operation> getOperations() { if (wrapped != null && operation != null) return Arrays.asList(wrapped, operation); else if (wrapped != null) return Arrays.asList((Operation) wrapped); else if (operation != null) return Arrays.asList(operation); return Collections.emptyList(); } @Override public List<Condition> getConditions() { if (wrapped != null && condition != null) return Arrays.asList(wrapped, condition); else if (wrapped != null) return Arrays.asList((Condition) wrapped); else if (condition != null) return Arrays.asList(condition); return Collections.emptyList(); } @Override public List<Rule> getRules() { return Arrays.asList(wrapped); } @Override public ParameterStore getParameterStore() { return store; } public ConfigurableParameter<?> where(String name) { assertParameterExists(name); Parameter<?> result = getParameterStore().get(name, new DefaultParameter(name)); if (result instanceof ConfigurableParameter) return (ConfigurableParameter<?>) result; throw new RewriteException("Cannot configure read-only parameter [" + name + "]."); } private void assertParameterExists(String name) { final Set<String> parameterNames = new LinkedHashSet<String>(); ParameterizedCallback callback = new ParameterizedCallback() { @Override public void call(Parameterized parameterized) { Set<String> names = parameterized.getRequiredParameterNames(); parameterNames.addAll(names); } }; Visitor<Condition> conditionVisitor = new ParameterizedConditionVisitor(callback); new ConditionVisit(this).accept(conditionVisitor); Visitor<Operation> operationVisitor = new ParameterizedOperationVisitor(callback); new OperationVisit(this).accept(operationVisitor); if (!parameterNames.contains(name)) throw new IllegalArgumentException("Parameter [" + name + "] does not exist in rule [" + this + "] and cannot be configured."); } @Override public void clear() { if (wrapped instanceof Context) { ((Context) wrapped).clear();; } contextMap.clear();; } @Override public Object get(Object key) { if (wrapped instanceof Context) { return ((Context) wrapped).get(key); } return contextMap.get(key); } @Override public void put(Object key, Object value) { if (wrapped instanceof Context) { ((Context) wrapped).put(key, value); } contextMap.put(key, value); } @Override public boolean containsKey(Object key) { if (wrapped instanceof Context) { return ((Context) wrapped).containsKey(key); } return contextMap.containsKey(key); } @Override public Set<String> getRequiredParameterNames() { Set<String> result = new HashSet<String>(); if (condition instanceof Parameterized) { Set<String> names = ((Parameterized) condition).getRequiredParameterNames(); if (names != null) result.addAll(names); } if (operation instanceof Parameterized) { Set<String> names = ((Parameterized) operation).getRequiredParameterNames(); if (names != null) result.addAll(names); } if (wrapped instanceof Parameterized) { Set<String> names = ((Parameterized) wrapped).getRequiredParameterNames(); if (names != null) result.addAll(names); } return result; } @Override public void setParameterStore(ParameterStore store) { if (condition instanceof Parameterized) ((Parameterized) condition).setParameterStore(store); if (operation instanceof Parameterized) ((Parameterized) operation).setParameterStore(store); if (wrapped instanceof Parameterized) ((Parameterized) wrapped).setParameterStore(store); } @Override public String toString() { String result = ".addRule("; if (wrapped != null && !(wrapped instanceof RuleBuilder)) { result += wrapped + ")"; } else { result += ")"; if (condition instanceof RuleBuilder) { String conditionToString = ((RuleBuilder) condition).conditionToString(); if (!conditionToString.isEmpty()) result += ".when(" + conditionToString + ")"; } else if (condition != null) result += ".when(" + condition + ")"; if (operation instanceof RuleBuilder) { String operationToString = ((RuleBuilder) operation).operationToString(); if (!operationToString.isEmpty()) result += ".perform(" + operationToString + ")"; } else if (operation != null) result += ".perform(" + operation + ")"; } if (getId() != null && !getId().isEmpty()) result += ".withId(\"" + getId() + "\")"; if (priority() != 0) result += ".withPriority(" + priority() + ")"; return result; } protected String conditionToString() { if (condition instanceof RuleBuilder) return ((RuleBuilder) condition).conditionToString(); return condition == null ? "" : condition.toString(); } protected String operationToString() { if (operation instanceof RuleBuilder) return ((RuleBuilder) operation).conditionToString(); return operation == null ? "" : operation.toString(); } }