1 | package com.hammurapi.extract; |
2 | |
3 | import java.util.Collections; |
4 | import java.util.Map; |
5 | import java.util.Set; |
6 | import java.util.TreeSet; |
7 | import java.util.concurrent.TimeUnit; |
8 | |
9 | public abstract class ComparisonPredicate<T, V, C> extends ExtractorBase<T, Boolean, C> implements Predicate<T, C>, Mappable<T,Boolean,C>, BinaryExtractor<T,V,C> { |
10 | |
11 | protected Extractor<T, V, C> leftExtractor; |
12 | protected Extractor<T, V, C> rightExtractor; |
13 | |
14 | protected Set<Integer> parameterIndices = new TreeSet<Integer>(); |
15 | |
16 | protected enum PromotionLevel { |
17 | INT(0), |
18 | LONG(1), |
19 | FLOAT(2), |
20 | DOUBLE(3); |
21 | |
22 | private PromotionLevel(int level) { |
23 | this.level = level; |
24 | } |
25 | |
26 | int level; |
27 | } |
28 | |
29 | protected ComparisonPredicate(double initialCost, TimeUnit costUnit, Extractor<T, V, C> leftExtractor, Extractor<T, V, C> rightExtractor) { |
30 | super(initialCost, costUnit); |
31 | this.leftExtractor = leftExtractor; |
32 | this.rightExtractor = rightExtractor; |
33 | parameterIndices.addAll(leftExtractor.parameterIndices()); |
34 | parameterIndices.addAll(rightExtractor.parameterIndices()); |
35 | } |
36 | |
37 | public Set<Integer> parameterIndices() { |
38 | return Collections.unmodifiableSet(parameterIndices); |
39 | } |
40 | |
41 | protected boolean operandsAreEqual(ComparisonPredicate<T, V, C> other) { |
42 | return other.leftExtractor.equals(leftExtractor) && other.rightExtractor.equals(rightExtractor); |
43 | } |
44 | |
45 | protected boolean operandsAreReverseEqual(ComparisonPredicate<T, V, C> other) { |
46 | return other.leftExtractor.equals(rightExtractor) && other.rightExtractor.equals(leftExtractor); |
47 | } |
48 | |
49 | @Override |
50 | public int hashCode() { |
51 | final int prime = 31; |
52 | int result = 1; |
53 | result = prime * result |
54 | + ((leftExtractor == null) ? 0 : leftExtractor.hashCode()); |
55 | result = prime |
56 | * result |
57 | + ((parameterIndices == null) ? 0 : parameterIndices.hashCode()); |
58 | result = prime * result |
59 | + ((rightExtractor == null) ? 0 : rightExtractor.hashCode()); |
60 | return result; |
61 | } |
62 | |
63 | @Override |
64 | public boolean equals(Object obj) { |
65 | if (this == obj) |
66 | return true; |
67 | if (obj == null) |
68 | return false; |
69 | if (getClass() != obj.getClass()) |
70 | return false; |
71 | @SuppressWarnings("rawtypes") |
72 | ComparisonPredicate other = (ComparisonPredicate) obj; |
73 | if (leftExtractor == null) { |
74 | if (other.leftExtractor != null) |
75 | return false; |
76 | } else if (!leftExtractor.equals(other.leftExtractor)) |
77 | return false; |
78 | if (parameterIndices == null) { |
79 | if (other.parameterIndices != null) |
80 | return false; |
81 | } else if (!parameterIndices.equals(other.parameterIndices)) |
82 | return false; |
83 | if (rightExtractor == null) { |
84 | if (other.rightExtractor != null) |
85 | return false; |
86 | } else if (!rightExtractor.equals(other.rightExtractor)) |
87 | return false; |
88 | return true; |
89 | } |
90 | |
91 | public boolean isContextDependent() { |
92 | return leftExtractor.isContextDependent() || rightExtractor.isContextDependent(); |
93 | } |
94 | |
95 | @Override |
96 | public String toString() { |
97 | return getClass().getName()+"(cost="+getCost()+", "+leftExtractor+", "+rightExtractor+")"; |
98 | } |
99 | |
100 | @SuppressWarnings("unchecked") |
101 | protected Boolean extractInternal( |
102 | C context, |
103 | Map<C, Map<Extractor<T, ? super Boolean, C>, ? super Boolean>> cache, |
104 | T... obj) { |
105 | |
106 | @SuppressWarnings("rawtypes") |
107 | Object o1 = leftExtractor.extract(context, (Map) cache, obj); |
108 | @SuppressWarnings("rawtypes") |
109 | Object o2 = rightExtractor.extract(context, (Map) cache, obj); |
110 | |
111 | if (isPromoteable(o1) && isPromoteable(o2)) { |
112 | Number n1 = (Number) o1; |
113 | Number n2 = (Number) o2; |
114 | switch (promote(n1, n2)) { |
115 | case INT: |
116 | return compare(n1.intValue(), n2.intValue()); |
117 | case LONG: |
118 | return compare(n1.longValue(), n2.longValue()); |
119 | case FLOAT: |
120 | return compare(n1.floatValue(), n2.floatValue()); |
121 | case DOUBLE: |
122 | return compare(n1.doubleValue(), n2.doubleValue()); |
123 | default: |
124 | throw new IllegalArgumentException("Unexpected promotion level "+promote(n1, n2)); |
125 | } |
126 | } else { |
127 | return compare(o1, o2); |
128 | } |
129 | } |
130 | |
131 | private boolean isPromoteable(Object obj) { |
132 | return obj instanceof Integer |
133 | || obj instanceof Long |
134 | || obj instanceof Float |
135 | || obj instanceof Double; |
136 | } |
137 | |
138 | protected abstract boolean compare(Object o1, Object o2); |
139 | |
140 | protected abstract boolean compare(int n1, int n2); |
141 | protected abstract boolean compare(long n1, long n2); |
142 | protected abstract boolean compare(float n1, float n2); |
143 | protected abstract boolean compare(double n1, double n2); |
144 | |
145 | private PromotionLevel nLevel(Number n) { |
146 | if (n instanceof Integer) { |
147 | return PromotionLevel.INT; |
148 | } |
149 | if (n instanceof Long) { |
150 | return PromotionLevel.LONG; |
151 | } |
152 | if (n instanceof Float) { |
153 | return PromotionLevel.FLOAT; |
154 | } |
155 | if (n instanceof Double) { |
156 | return PromotionLevel.DOUBLE; |
157 | } |
158 | throw new IllegalArgumentException(n.getClass()+" is not supported"); |
159 | } |
160 | |
161 | protected PromotionLevel promote(Number n1, Number n2) { |
162 | PromotionLevel pl1 = nLevel(n1); |
163 | PromotionLevel pl2 = nLevel(n2); |
164 | return pl1.level>=pl2.level ? pl1 : pl2; |
165 | } |
166 | |
167 | @Override |
168 | public double getCost() { |
169 | if (cost==0 && costUnit==null) { |
170 | return leftExtractor.getCost()+rightExtractor.getCost(); |
171 | } |
172 | return super.getCost(); |
173 | } |
174 | |
175 | @Override |
176 | public Extractor<T, Boolean, C> map(int[] map) { |
177 | Mappable<T, V, C> leftMappable = ExtractorUtil.toMappable(leftExtractor); |
178 | if (leftMappable!=null) { |
179 | Mappable<T, V, C> rightMappable = ExtractorUtil.toMappable(rightExtractor); |
180 | if (rightMappable!=null) { |
181 | Extractor<T,V,C> lMapped = leftMappable.map(map); |
182 | if (lMapped!=null) { |
183 | Extractor<T,V,C> rMapped = rightMappable.map(map); |
184 | if (rMapped!=null) { |
185 | return newInstance(lMapped, rMapped); |
186 | } |
187 | } |
188 | } |
189 | } |
190 | return null; |
191 | } |
192 | |
193 | protected abstract Extractor<T, Boolean, C> newInstance(Extractor<T, V, C> leftExtractor, Extractor<T, V, C> rightExtractor); |
194 | |
195 | public Extractor<T, V, C> getLeftExtractor() { |
196 | return leftExtractor; |
197 | } |
198 | |
199 | public Extractor<T, V, C> getRightExtractor() { |
200 | return rightExtractor; |
201 | } |
202 | } |