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}