    001package com.hammurapi.extract;
    002
    003import java.lang.reflect.Array;
    004import java.util.Arrays;
    005import java.util.HashSet;
    006import java.util.Map;
    007import java.util.Set;
    008import java.util.TreeSet;
    009
    010/**
    011 * Extractor which maps arguments to the target extractor.
    012 * @author Pavel Vlasov
    013 *
    014 * @param <T>
    015 * @param <V>
    016 * @param <C>
    017 */
    018public class MappedExtractor<T, V, C> implements FacadeExtractor<T, V, C>, Mappable<T,V,C> {
    019
    020        private Extractor<T, V, C> target;
    021        private int[] map;
    022        private TreeSet<Integer> parameterIndices;
    023
    024        /**
    025         * @param target Target extractor
    026         * @param map Index map, e.g. if it is {5, 3} then the first argument
    027         * of this extractor becomes the sixth argument of the target extractor
    028         * and the second argument of this extractor becomes the 4th argument
    029         * of the target extractor.
    030         */
    031        protected MappedExtractor(Extractor<T, V, C> target, int[] map) {
    032                super();
    033                this.target = target;
    034                this.map = map;
    035
    036                parameterIndices = new TreeSet<Integer>();
    037
    038                Set<Integer> tpIdx = target.parameterIndices();
    039                for (int i=0; i<map.length; ++i) {
    040                        if (tpIdx.contains(map[i])) {
    041                                parameterIndices.add(i);
    042                        }
    043                }
    044        }
    045
    046        @Override
    047        public V extract(C context,     Map<C, Map<Extractor<T, ? super V, C>, ? super V>> cache, T... obj) {
    048                int maxTargetIndex = -1;
    049                for (Integer tIdx: target.parameterIndices()) {
    050                        if (tIdx>maxTargetIndex) {
    051                                maxTargetIndex=tIdx;
    052                        }
    053                }
    054
    055                @SuppressWarnings("unchecked")
    056                T[] tObj = (T[]) Array.newInstance(obj.getClass().getComponentType(), maxTargetIndex+1);
    057                for (int i=0; i<map.length; ++i) {
    058                        if (parameterIndices.contains(i)) {
    059                                tObj[map[i]] = obj[i];
    060                        }
    061                }
    062
    063                return target.extract(context, cache, tObj);
    064        }
    065
    066        @Override
    067        public Set<Integer> parameterIndices() {
    068                return parameterIndices;
    069        }
    070
    071        @Override
    072        public boolean isContextDependent() {
    073                return target.isContextDependent();
    074        }
    075
    076        @Override
    077        public ComparisonResult compareTo(Extractor<T, V, C> other) {
    078                ComparisonResult tcr = target.compareTo(other);
    079                if (tcr==null) {
    080                        return null;
    081                }
    082                if (tcr.isOneToOneMapping()) {
    083                        // Inverse mapping.
    084                        int dim = -1;
    085                        for (int i=0; i<map.length; ++i) {
    086                                if (parameterIndices.contains(i) && map[i]>dim) {
    087                                        dim = map[i];
    088                                }
    089                        }
    090                        int[] rmap = new int[dim+1];
    091                        Arrays.fill(rmap, -1);
    092                        for (int i=0; i<map.length; ++i) {
    093                                if (parameterIndices.contains(i)) {
    094                                        rmap[map[i]] = i;
    095                                }
    096                        }
    097                        return new ComparisonResult(tcr.getType(), rmap);
    098                }
    099
    100                int[] tcrim = tcr.getIndexMap();
    101                int[] cmap = new int[map.length];
    102                Arrays.fill(cmap, -1);
    103                for (int i=0; i<map.length; ++i) {
    104                        if (parameterIndices.contains(i)) {
    105                                if (tcrim.length>map[i]) {
    106                                        cmap[i] = tcrim[map[i]];
    107                                } else {
    108                                        return null;
    109                                }
    110                        }
    111                }
    112                return new ComparisonResult(tcr.getType(), cmap);
    113        }
    114
    115        @Override
    116        public String toString() {
    117                return "MappedExtractor [target=" + target + ", map="
    118                                + Arrays.toString(map) + "]";
    119        }
    120
    121        @Override
    122        public double getCost() {
    123                return target.getCost();
    124        }
    125
    126
    127        public static <T,V,C> Extractor<T,V,C> mapExtractor(Extractor<T,V,C> target, int[] map) {
    128                if (ComparisonResult.isOneToOneMapping(map)) {
    129                        return target;
    130                }
    131
    132                // Target is not index-dependent (e.g. constant).
    133                if (target.parameterIndices().isEmpty()) {
    134                        return target;
    135                }
    136                if (target instanceof Mappable) {
    137                        @SuppressWarnings("unchecked")
    138                        Extractor<T, V, C> ret = ((Mappable<T,V,C>) target).map(map);
    139                        if (ret!=null) {
    140                                return ret;
    141                        }
    142                }
    143
    144                return new MappedExtractor<T, V, C>(target, map);
    145        }
    146
    147        @SuppressWarnings("unchecked")
    148        @Override
    149        public Extractor<T, V, C> map(int[] map) {
    150                if (ComparisonResult.isOneToOneMapping(map)) {
    151                        return this;
    152                }
    153
    154                int[] newMap = new int[map.length];
    155                Arrays.fill(newMap, -1);
    156
    157                Set<Integer> pi = new HashSet<Integer>(parameterIndices);
    158
    159                for (int i=0; i<newMap.length; ++i) {
    160                        if (map[i]!=-1) {
    161                                if (pi.remove(map[i])) {
    162                                        newMap[i]=this.map[map[i]];
    163                                }
    164                        }
    165                }
    166
    167                if (!pi.isEmpty()) { // Not all indexes were mapped.
    168                        return null;
    169                }
    170
    171                if (ComparisonResult.isOneToOneMapping(newMap)) {
    172                        return target;
    173                }
    174
    175                if (this instanceof MappedPredicate) {
    176                        return (Extractor<T, V, C>) new MappedPredicate<T, C>((Predicate<T, C>) target, newMap);
    177                }
    178                return new MappedExtractor<T, V, C>(target, newMap);
    179        }
    180
    181        public Extractor<T, V, C> getTarget() {
    182                return target;
    183        }
    184
    185        public int[] getMap() {
    186                return map;
    187        }
    188}