// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.prelude.semantics.rule;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import com.yahoo.prelude.semantics.engine.Choicepoint;
import com.yahoo.prelude.semantics.engine.RuleEvaluation;
/**
* A condition which is true of the values of its two subconditions are true
* and both have the same value
*
* @author bratseth
*/
public class ComparisonCondition extends CompositeCondition {
private Operator operator;
public ComparisonCondition(Condition leftCondition,String operatorString,Condition rightCondition) {
operator=Operator.get(operatorString);
addCondition(leftCondition);
addCondition(rightCondition);
}
protected boolean doesMatch(RuleEvaluation evaluation) {
Object left=null;
Object right=null;
boolean matches=false;
Choicepoint choicepoint=evaluation.getChoicepoint(this,true);
try {
matches=getLeftCondition().matches(evaluation);
if (!matches) return false;
left=evaluation.getValue();
evaluation.setValue(null);
choicepoint.backtrackPosition();
matches=getRightCondition().matches(evaluation);
if (!matches) return false;
right=evaluation.getValue();
evaluation.setValue(right);
matches=operator.compare(left,right);
return matches;
}
finally {
if (!matches)
choicepoint.backtrack();
traceResult(matches,evaluation,left,right);
}
}
protected void traceResult(boolean matches,RuleEvaluation e) {
// Uses our own logging method instead
}
protected void traceResult(boolean matches,RuleEvaluation e,Object left,Object right) {
if (matches && e.getTraceLevel()>=3)
e.trace(3,"Matched '" + this + "'" + getMatchInfoString(e) + " at " + e.previousItem() + " as " + left + operator + right + " is true");
if (!matches && e.getTraceLevel()>=3)
e.trace(3,"Did not match '" + this + "' at " + e.currentItem() + " as " + left + operator + right + " is false");
}
public Condition getLeftCondition() {
return getCondition(0);
}
public void setLeftCondition(Condition leftCondition) {
setCondition(0,leftCondition);
}
public Condition getRightCondition() {
return getCondition(1);
}
public void setRightCondition(Condition rightCondition) {
setCondition(1,rightCondition);
}
protected String toInnerString() {
return toInnerString(operator.toString());
}
private static final class Operator {
private String operatorString;
private static Map operators=new HashMap<>();
public static final Operator equals=new Operator("=");
public static final Operator largerequals=new Operator(">=");
public static final Operator smallerequals=new Operator("<=");
public static final Operator larger=new Operator(">");
public static final Operator smaller=new Operator("<");
public static final Operator different=new Operator("!=");
public static final Operator contains=new Operator("=~");
private Operator(String operator) {
this.operatorString=operator;
operators.put(operatorString,this);
}
private static Operator get(String operatorString) {
Operator operator=operators.get(operatorString);
if (operator==null)
throw new IllegalArgumentException("Unknown operator '" + operatorString + "'");
return operator;
}
public boolean compare(Object left,Object right) {
if (this==equals)
return equals(left,right);
if (this==different)
return !equals(left,right);
if (left==null || right==null) return false;
if (this==contains)
return contains(left,right);
if (this==largerequals)
return larger(left,right) || equals(left,right);
if (this==smallerequals)
return !larger(left,right);
if (this==larger)
return larger(left,right);
if (this==smaller)
return !larger(left,right) && !equals(left,right);
throw new RuntimeException("Programming error, fix this method");
}
private boolean equals(Object left,Object right) {
if (left==null && right==null) return true;
if (left==null) return false;
return left.equals(right);
}
/** True if left contains right */
private boolean contains(Object left,Object right) {
if (left instanceof Collection)
return ((Collection>)left).contains(right);
else
return left.toString().indexOf(right.toString())>=0;
}
/** true if left is larger than right */
private boolean larger(Object left,Object right) {
if ((left instanceof Number) && (right instanceof Number))
return ((Number)left).doubleValue()>((Number)right).doubleValue();
else
return left.toString().compareTo(right.toString())>0;
}
public int hashCode() {
return operatorString.hashCode();
}
public boolean equals(Object other) {
if ( ! (other instanceof Operator)) return false;
return other.toString().equals(this.toString());
}
public String toString() {
return operatorString;
}
}
}