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}