001package com.hammurapi.extract;
002
003import java.util.Arrays;
004import java.util.HashMap;
005import java.util.HashSet;
006import java.util.Iterator;
007import java.util.List;
008import java.util.Map;
009import java.util.Set;
010import java.util.TreeSet;
011
012import com.hammurapi.extract.ComparisonResult.Type;
013
014/**
015 * Base class for compiled java extractors/predicates.
016 * @author Pavel Vlasov
017 *
018 * @param <T>
019 * @param <V>
020 * @param <C>
021 */
022@SuppressWarnings("unchecked")
023public abstract class CompiledExtractorBase<T, V, C> extends AbstractExtractor<T, V, C> implements Mappable<T, V, C> {
024        
025        protected int[] map;
026        
027        protected CompiledExtractorBase(int[] map, boolean contextDependent, int... parameterIndices) {
028                super(0, null, false, parameterIndices); 
029                this.map = map;
030        }
031        
032        protected abstract CompiledExtractorBase<T, V, C> newInstance(int[] map);
033        
034        protected abstract List<Object> getIdentity();
035        
036        /**
037         * Maps argument indices for invocation.
038         * @param idx
039         * @return
040         */
041        protected int mapArg(int idx) {
042                if (map==null) {
043                        return idx;
044                }
045                
046                for (int i=0; i<map.length; ++i) {
047                        if (map[i]==idx) {
048                                return i;
049                        }
050                }
051                
052                throw new IllegalArgumentException("Cannot map index "+idx+" using map "+Arrays.toString(map));
053        }
054
055        @Override
056        public Extractor<T,V,C> map(int[] map) {
057                if (ComparisonResult.isOneToOneMapping(map)) {
058                        return this;
059                }
060                if (this.map==null) {
061                        return newInstance(map);
062                }
063                
064                int[] newMap = new int[map.length];
065                Arrays.fill(newMap, -1);
066                
067                Set<Integer> pi = new HashSet<Integer>(parameterIndices());
068                
069                for (int i=0; i<newMap.length; ++i) {
070                        if (map[i]!=-1) {
071                                if (pi.remove(map[i])) {
072                                        newMap[i]=this.map[map[i]];
073                                }
074                        } 
075                }
076                
077                if (!pi.isEmpty()) { // Not all indexes were mapped.
078                        return null;
079                }
080                
081                return newInstance(newMap);             
082                
083        }
084                
085        @Override
086        public ComparisonResult compareTo(Extractor<T, V, C> other) {
087                if (other==this || other instanceof CompiledExtractorBase && getIdentity().equals(((CompiledExtractorBase<T,V,C>) other).getIdentity())) {
088                        return ComparisonResult.EQUAL_NM;
089                }
090                
091                if (other instanceof CompiledExtractorBase) {
092                        Object oi = ((CompiledExtractorBase<T,V,C>) other).getIdentity();
093                        if (oi instanceof List) {
094                                Map<Integer,Integer> mapping = new HashMap<Integer,Integer>();
095                                if (map(getIdentity(), (List<Object>) oi, mapping)) {
096                                        int maxIdx = -1;
097                                        for (Integer idx: mapping.keySet()) {
098                                                if (idx>maxIdx) {
099                                                        maxIdx = idx;
100                                                }
101                                        }
102                                        int[] retMap = new int[maxIdx+1];
103                                        Arrays.fill(retMap, -1);
104                                        for (Map.Entry<Integer, Integer> e: mapping.entrySet()) {
105                                                retMap[e.getKey()] = e.getValue();
106                                        }
107                                        return new ComparisonResult(Type.EQUAL, retMap);
108                                }
109                        }
110                }
111                return super.compareTo(other);
112        }
113        
114        private static boolean map(List<Object> thisIdentity, List<Object> otherIdentity, Map<Integer,Integer> mapping) {
115                if (thisIdentity.size()!=otherIdentity.size()) {
116                        return false;
117                }
118                Iterator<Object> tit = thisIdentity.iterator();
119                Iterator<Object> oit = otherIdentity.iterator();
120                if (!(tit.hasNext() && tit.next().equals(oit.next()))) {
121                        return false;
122                }
123                
124                Object thisName = tit.next();
125                Object otherName = oit.next();
126                if (thisName instanceof Integer && otherName instanceof Integer) {
127                        Integer existingMapping = mapping.get(thisName);
128                        if (existingMapping==null) {
129                                mapping.put((Integer) thisName, (Integer) otherName);
130                        } else if (!existingMapping.equals(otherName)) {
131                                return false;
132                        }                       
133                } else if (!cmp(thisName, otherName)) {
134                        return false;
135                }
136                
137                // At this point types are equal, values are equal or mapped.
138                if (tit.hasNext()) {
139                        if (!oit.hasNext()) {
140                                return false;
141                        }
142                        List<List<Object>> tChildren = (List<List<Object>>) tit.next();
143                        List<List<Object>> oChildren = (List<List<Object>>) oit.next();
144                        if (tChildren.size()!=oChildren.size()) {
145                                return false;
146                        }
147                        Iterator<List<Object>> tcit = tChildren.iterator();
148                        Iterator<List<Object>> ocit = oChildren.iterator();
149                        while (tcit.hasNext()) {
150                                if (!map(tcit.next(), ocit.next(), mapping)) {
151                                        return false;
152                                }
153                        }
154                }
155                return true;
156        }
157        
158        private static boolean cmp(Object o1, Object o2) {
159                if (o1==null) {
160                        return o2==null;
161                }
162                
163                return o1.equals(o2);
164        }
165        
166        @Override
167        public Set<Integer> parameterIndices() {
168                if (map==null) {
169                        return super.parameterIndices();
170                }
171                
172                Set<Integer> original = super.parameterIndices();
173                Set<Integer> ret = new TreeSet<Integer>();
174                for (int i=0; i<map.length; ++i) {
175                        if (map[i]!=-1 && original.contains(map[i])) {
176                                ret.add(i);
177                        }
178                }
179                
180                return ret;
181        }
182
183}