    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}