Rule.java
/*
* MIT License
*
* Copyright (c) 2023-2024 Eugene Terekhov
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package ru.ewc.decita;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import lombok.EqualsAndHashCode;
import ru.ewc.decita.conditions.Condition;
/**
* I am a single Rule (i.e. the column in the decision table). My main responsibility is to check
* whether all my second-order {@link Condition}s are {@code true} and if so - compute my outcomes.
*
* @since 0.1
*/
@EqualsAndHashCode
public final class Rule {
/**
* The rule's {@link Condition}s.
*/
private final List<Condition> conditions = new ArrayList<>(5);
/**
* The rule's outcomes.
*/
private final Map<String, String> outcomes = new HashMap<>();
/**
* Adds a {@link Condition} to this rule.
*
* @param condition The {@link Condition} to add.
* @return Itself, in order to implement fluent API.
*/
public Rule withCondition(final Condition condition) {
this.conditions.add(condition);
return this;
}
/**
* Adds an outcome to this rule.
*
* @param outcome The key of an outcome to add.
* @param value The string representation of an outcome's value to add.
* @return Itself, in order to implement fluent API.
*/
public Rule withOutcome(final String outcome, final String value) {
this.outcomes.put(outcome, value);
return this;
}
/**
* Checks whether this rule is computed, i.e. all of its {@link Condition}s were resolved
* successfully.
*
* @return True if the rule is computed.
*/
public boolean isComputed() {
return this.conditions.stream().allMatch(Condition::isEvaluated);
}
/**
* Checks whether this rule is not satisfied (i.e. any of its {@link Condition}s resolved to
* {@code false}).
*
* @return True if the rule is dissatisfied.
*/
public boolean isEliminated() {
return this.conditions.stream().anyMatch(Condition::isNotSatisfied);
}
/**
* Checks whether this rule is satisfied (i.e. all of its {@link Condition}s are computed and
* resolved to {@code true}).
*
* @return True if the rule is satisfied.
*/
public boolean isSatisfied() {
return this.conditions.stream().allMatch(c -> c.isEvaluated() && !c.isNotSatisfied());
}
/**
* Checks if this {@link Rule} is satisfied.
*
* @param context The {@link ComputationContext}'s instance.
* @throws DecitaException If the rule's {@link Condition}s could not be resolved.
*/
public void check(final ComputationContext context) throws DecitaException {
if (!this.isEliminated() || !this.isComputed()) {
for (final Condition cond : this.conditions) {
cond.evaluate(context);
}
}
}
/**
* Returns this rule outcomes.
*
* @return The simple dictionary, containing all this rule's outcomes.
*/
public Map<String, String> outcome() {
return this.outcomes;
}
}