    001package com.hammurapi.extract;
    002
    003import java.util.HashMap;
    004import java.util.Map;
    005import java.util.concurrent.TimeUnit;
    006
    007import com.hammurapi.extract.ComparisonResult.Type;
    008
    009
    010/**
    011 * Base class for extractors, implements value caching.
    012 * @author Pavel Vlasov.
    013 *
    014 * @param <T>
    015 * @param <V>
    016 */
    017public abstract class ExtractorBase<T, V, C> implements Extractor<T, V, C> {
    018
    019        protected TimeUnit costUnit;
    020        protected long invocations;
    021        protected double totalCost;
    022        protected boolean nanos;
    023        protected double cost;
    024        protected double initialCost;
    025
    026        /**
    027         * @param initialCost Initial cost. If positive, this value is added to the calculated cost. If costUnit is null,
    028         * then cost is always equal to the initial cost. If negative and time unit is not null, then absolute value is used as first measurement of cost.
    029         * @param costUnit If this argument is not null, then extractor cost is
    030         * calculated as average time to execute extractInternal() method in specified time unit.
    031         * If time unit is nanoseconds or microseconds, then System.nanoTime() method is used for
    032         * measurement, otherwise Systsm.currentTimeMillis() is used.
    033         */
    034        protected ExtractorBase(double initialCost, TimeUnit costUnit) {
    035                this.costUnit = costUnit;
    036                this.initialCost = initialCost;
    037                if (initialCost<0 && costUnit!=null) {
    038                        invocations = 1;
    039                        totalCost = initialCost;
    040                } else {
    041                        this.cost = initialCost;
    042                }
    043                nanos = costUnit!=null && TimeUnit.MILLISECONDS.compareTo(costUnit)>0;
    044        }
    045
    046        @SuppressWarnings("unchecked")
    047        public V extract(C context, Map<C, Map<Extractor<T, ? super V, C>, ? super V>> cache, T... obj) {
    048                long start = 0;
    049                if (costUnit!=null) {
    050                        start = nanos ? System.nanoTime() : System.currentTimeMillis();
    051                }
    052                try {
    053                        if (cache==null) {
    054                                return extractInternal(context, cache, obj);
    055                        }
    056
    057                        synchronized (cache) {
    058                                Map<Extractor<T, ? super V, C>, ? super V> ce = cache.get(context);
    059                                if (ce==null) {
    060                                        ce = createCacheEntry();
    061                                        cache.put(context, ce);
    062                                }
    063
    064                                V ret = (V) ce.get(this);
    065                                if (ret==null) {
    066                                        ret = extractInternal(context, cache, obj);
    067                                        ce.put(this, ret);
    068                                }
    069                                return ret;
    070                        }
    071                } finally {
    072                        if (costUnit!=null) {
    073                                long end = nanos ? System.nanoTime() : System.currentTimeMillis();
    074                                totalCost += costUnit.convert(end-start, nanos ? TimeUnit.NANOSECONDS : TimeUnit.MICROSECONDS);
    075                                ++invocations;
    076                        }
    077                }
    078        }
    079
    080        @Override
    081        public double getCost() {
    082
    083                if (invocations==0) {
    084                        return cost;
    085                }
    086
    087                return cost + totalCost/invocations;
    088        }
    089
    090        /**
    091         * Basic comparisons with True and False.
    092         * @param otherPredicate
    093         * @return
    094         */
    095        public ComparisonResult compareTo(Extractor<T, V, C> other) {
    096                if (this==other) {
    097                        return ComparisonResult.EQUAL_NM;
    098                }
    099
    100                // True is less restrictive than anything but self.
    101                if (other instanceof True) {
    102                        return ComparisonResult.MORE_RESTRICTIVE_NM;
    103                }
    104
    105                // False is more restrictive than anything but self.
    106                if (other instanceof False) {
    107                        return ComparisonResult.LESS_RESTRICTIVE_NM;
    108                }
    109
    110                if (other instanceof FacadeExtractor && !(this instanceof FacadeExtractor)) {
    111                        ComparisonResult cr = other.compareTo(this);
    112                        if (cr==null) {
    113                                return cr;
    114                        }
    115                        switch (cr.getType()) {
    116                        case EQUAL:
    117                        case NOT_EQUAL:
    118                        case OPPOSITE:
    119                                return cr;
    120                        case LESS_RESTRICTIVE:
    121                                return new ComparisonResult(Type.MORE_RESTRICTIVE, ComparisonResult.inverse(cr.getIndexMap(), parameterIndices()));
    122                        case MORE_RESTRICTIVE:
    123                                return new ComparisonResult(Type.LESS_RESTRICTIVE, ComparisonResult.inverse(cr.getIndexMap(), parameterIndices()));
    124                        case OPPOSITE_LESS_RESTRICTIVE:
    125                                return new ComparisonResult(Type.OPPOSITE_LESS_RESTRICTIVE, ComparisonResult.inverse(cr.getIndexMap(), parameterIndices()));
    126                        case OPPOSITE_MORE_RESTRICTIVE:
    127                                return new ComparisonResult(Type.OPPOSITE_MORE_RESTRICTIVE, ComparisonResult.inverse(cr.getIndexMap(), parameterIndices()));
    128                        }
    129                }
    130
    131                return ComparisonResult.NOT_EQUAL_NM;
    132        }
    133
    134
    135        protected abstract V extractInternal(C context, Map<C, Map<Extractor<T, ? super V, C>, ? super V>> cache, T... obj);
    136
    137        protected Map<Extractor<T, ? super V, C>, ? super V> createCacheEntry() {
    138                return new HashMap<Extractor<T,? super V,C>, V>();
    139        }
    140
    141        @Override
    142        public boolean equals(Object obj) {
    143                if (this == obj) {
    144                        return true;
    145                }
    146                if (obj instanceof Extractor) {
    147                        ComparisonResult cr = compareTo((Extractor) obj);
    148                        if (ComparisonResult.Type.EQUAL.equals(cr.getType()) && cr.isOneToOneMapping()) {
    149                                return true;
    150                        }
    151                }
    152                return false;
    153        }
    154
    155}