    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}