aboutsummaryrefslogtreecommitdiffstats
path: root/container-search/src/main/java/com/yahoo/prelude/semantics/rule/ComparisonCondition.java
blob: b0820f82bc0448f9ec35e092bba202415e43cea1 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
// 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 <i>values</i> 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<String, Operator> 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;
        }

    }

}