001package com.hammurapi.extract; 002 003import java.util.Collections; 004import java.util.Map; 005import java.util.Set; 006import java.util.TreeSet; 007import java.util.concurrent.TimeUnit; 008 009public abstract class ComparisonPredicate<T, V, C> extends ExtractorBase<T, Boolean, C> implements Predicate<T, C>, Mappable<T,Boolean,C>, BinaryExtractor<T,V,C> { 010 011 protected Extractor<T, V, C> leftExtractor; 012 protected Extractor<T, V, C> rightExtractor; 013 014 protected Set<Integer> parameterIndices = new TreeSet<Integer>(); 015 016 protected enum PromotionLevel { 017 INT(0), 018 LONG(1), 019 FLOAT(2), 020 DOUBLE(3); 021 022 private PromotionLevel(int level) { 023 this.level = level; 024 } 025 026 int level; 027 } 028 029 protected ComparisonPredicate(double initialCost, TimeUnit costUnit, Extractor<T, V, C> leftExtractor, Extractor<T, V, C> rightExtractor) { 030 super(initialCost, costUnit); 031 this.leftExtractor = leftExtractor; 032 this.rightExtractor = rightExtractor; 033 parameterIndices.addAll(leftExtractor.parameterIndices()); 034 parameterIndices.addAll(rightExtractor.parameterIndices()); 035 } 036 037 public Set<Integer> parameterIndices() { 038 return Collections.unmodifiableSet(parameterIndices); 039 } 040 041 protected boolean operandsAreEqual(ComparisonPredicate<T, V, C> other) { 042 return other.leftExtractor.equals(leftExtractor) && other.rightExtractor.equals(rightExtractor); 043 } 044 045 protected boolean operandsAreReverseEqual(ComparisonPredicate<T, V, C> other) { 046 return other.leftExtractor.equals(rightExtractor) && other.rightExtractor.equals(leftExtractor); 047 } 048 049 @Override 050 public int hashCode() { 051 final int prime = 31; 052 int result = 1; 053 result = prime * result 054 + ((leftExtractor == null) ? 0 : leftExtractor.hashCode()); 055 result = prime 056 * result 057 + ((parameterIndices == null) ? 0 : parameterIndices.hashCode()); 058 result = prime * result 059 + ((rightExtractor == null) ? 0 : rightExtractor.hashCode()); 060 return result; 061 } 062 063 @Override 064 public boolean equals(Object obj) { 065 if (this == obj) 066 return true; 067 if (obj == null) 068 return false; 069 if (getClass() != obj.getClass()) 070 return false; 071 @SuppressWarnings("rawtypes") 072 ComparisonPredicate other = (ComparisonPredicate) obj; 073 if (leftExtractor == null) { 074 if (other.leftExtractor != null) 075 return false; 076 } else if (!leftExtractor.equals(other.leftExtractor)) 077 return false; 078 if (parameterIndices == null) { 079 if (other.parameterIndices != null) 080 return false; 081 } else if (!parameterIndices.equals(other.parameterIndices)) 082 return false; 083 if (rightExtractor == null) { 084 if (other.rightExtractor != null) 085 return false; 086 } else if (!rightExtractor.equals(other.rightExtractor)) 087 return false; 088 return true; 089 } 090 091 public boolean isContextDependent() { 092 return leftExtractor.isContextDependent() || rightExtractor.isContextDependent(); 093 } 094 095 @Override 096 public String toString() { 097 return getClass().getName()+"(cost="+getCost()+", "+leftExtractor+", "+rightExtractor+")"; 098 } 099 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}