package context.arch.intelligibility.expression; import java.util.ArrayList; import java.util.Collection; /** * List of expressions as an AND combination. * @author Brian Y. Lim * */ public class Conjunction<E extends Expression> extends ArrayList<E> implements Expression, Cloneable { private static final long serialVersionUID = 98079915437383558L; public Conjunction() { super(); } public Conjunction(Collection<E> original) { super(original); } public Conjunction(E ... original) { super(); for (E child : original) { add(child); } } /** * Convenience method to search for expression that has the name and return its value * @param name * @return null if no expression with such name exists */ public Object getValue(String name) { Parameter<?> child = this.getChild(name); if (child != null) { return child.getValue(); } else { return null; } } /** * Convenience method to search for expression that has the name and return it * @param name * @return */ public Parameter<?> getChild(String name) { for (E exp : this) { if (exp instanceof Parameter<?>) { Parameter<?> param = (Parameter<?>)exp; if (param.getName().equals(name)) { return param; } } } return null; } public String toString() { return "AND" + super.toString(); } /** * If the child expression is a Parameter that has the same name as an existing child, then it would be merged with that. * If Comparison, then the range of the original would be set (and possibly tightened). * If Parameter, then there actually is a contradiction, and would be ignored. * @param child */ @SuppressWarnings("unchecked") public void addOrMerge(E child) { if (child instanceof Parameter<?>) { String name = ((Parameter<?>) child).getName(); Parameter<?> origChild = this.getChild(name); if (origChild != null) { // comparison, so we adjust range if (origChild instanceof Comparison<?>) { /* * create a new copy of child * don't use original reference, since that would affect other Conjunctions that also reference it */ Comparison<?> newChild = ((Comparison<?>) origChild).clone(); newChild.setRange((Comparison<?>) child); // replace original with new child; retains position super.set(super.indexOf(origChild), (E) newChild); return; } // parameter else { // actually, this should not happen, as it would lead to a contradiction new RuntimeException("Parameter reassignment: name=" + name + ", orig=" + origChild.getValue() + ", to=" + ((Parameter<?>) child).getValue()).printStackTrace(); // end up not adding too } } } // just add normally super.add(child); } @Override public boolean isSatisfiedBy(Expression other) { if (other instanceof Conjunction<?>) { // need all of other's children to satisfy this for (Expression otherChild : (Conjunction<?>)other) { // ignore if this does not contain otherChild if (otherChild instanceof Parameter<?> && this.getChild(((Parameter<?>) otherChild).getName()) == null) { continue; } // any failure would fail all if (!this.isSatisfiedBy(otherChild)) { return false; } } return true; } else if (other instanceof Disjunction) { // any of other's children satisfies => overall satisfies for (Expression otherChild : (Disjunction<?>)other) { // any success would succeed overall if (this.isSatisfiedBy(otherChild)) { return true; } } return false; } else { // assume terminal literal // scan through children for (Expression child : this) { // if any child is satisfied by other, then other fits this trace if (child.isSatisfiedBy(other)) { return true; } } return false; } } }