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}