001package com.hammurapi.extract.java;
002
003import java.util.ArrayList;
004import java.util.Collection;
005import java.util.Collections;
006import java.util.List;
007import java.util.Map;
008import java.util.Set;
009import java.util.concurrent.TimeUnit;
010
011import org.codehaus.commons.compiler.CompileException;
012import org.codehaus.janino.ExpressionEvaluator;
013
014import com.hammurapi.common.Identifiable;
015import com.hammurapi.extract.ComparisonResult;
016import com.hammurapi.extract.Extractor;
017import com.hammurapi.extract.ExtractorBase;
018import com.hammurapi.extract.ExtractorException;
019import com.hammurapi.extract.Mappable;
020
021public class JavaExtractor<T, V, C> extends ExtractorBase<T, V, C> implements Extractor<T, V, C> , Mappable<T,V,C>, Identifiable<List<Object>>  {
022
023        private Params<T, C> params;
024        private Class<V> valueType;
025        private ClassLoader classLoader;
026
027        public JavaExtractor(double initialCost, TimeUnit costUnit, Params<T, C> params, Class<V> valueType, ClassLoader classLoader) {
028                super(initialCost, costUnit);
029                this.params = params;
030                this.valueType = valueType;
031                this.classLoader = classLoader;
032        }
033        
034        private ThreadLocal<ExpressionEvaluator> eetl = new ThreadLocal<ExpressionEvaluator>() {
035                
036                protected ExpressionEvaluator initialValue() {                  
037                        try {
038                                // TODO - Pass classLoader?
039                                return new ExpressionEvaluator(params.expr.toString(), valueType, params.names, params.types);
040                        } catch (CompileException e) {
041                                throw new ExtractorException("Compile exception in code '"+params.expr+"': "+e, e);
042                        }
043                };
044        };
045
046        @SuppressWarnings("unchecked")
047        @Override
048        protected V extractInternal(C context, Map<C, Map<Extractor<T, ? super V, C>, ? super V>> cache, T... obj) {
049                Object[] args = params.translate(obj, context);
050                try {
051                        return (V) eetl.get().evaluate(args);
052                } catch (Exception e) {
053                        throw new ExtractorException("Invocation exception in code '"+params.expr+"': "+e, e);
054                }
055        }
056
057        @Override
058        public boolean isContextDependent() {
059                return params.isContextDependent();
060        }
061
062        @Override
063        public Set<Integer> parameterIndices() {
064                return params.indices;
065        }
066        
067        @Override
068        public String toString() {
069                return getClass().getName()+"(code: '"+params.expr+"', parameter indices "+parameterIndices()+", cost: "+getCost()+")";
070        }
071
072        @Override
073        public int hashCode() {
074                final int prime = 31;
075                int result = 1;
076                result = prime * result
077                                + ((classLoader == null) ? 0 : classLoader.hashCode());
078                result = prime * result + ((params == null) ? 0 : params.hashCode());
079                result = prime * result
080                                + ((valueType == null) ? 0 : valueType.hashCode());
081                return result;
082        }
083        
084        @Override
085        public ComparisonResult compareTo(Extractor<T, V, C> obj) {
086                if (this == obj) {
087                        return ComparisonResult.EQUAL_NM;
088                }
089                if (obj == null) {
090                        return ComparisonResult.NOT_EQUAL_NM;
091                }
092                        
093                if (getClass() != obj.getClass()) {
094                        return super.compareTo(obj); // For parenthesis, facades and other stuff.
095                }
096
097                JavaExtractor other = (JavaExtractor) obj;
098                if (classLoader == null) {
099                        if (other.classLoader != null) {
100                                return ComparisonResult.NOT_EQUAL_NM;
101                        }
102                } else if (!classLoader.equals(other.classLoader)) {
103                        return ComparisonResult.NOT_EQUAL_NM;
104                }
105
106                if (params == null) {
107                        if (other.params != null) {
108                                return ComparisonResult.NOT_EQUAL_NM;
109                        }
110                } else if (!params.equals(other.params)) {
111                        return ComparisonResult.NOT_EQUAL_NM;
112                }
113                if (valueType == null) {
114                        if (other.valueType != null){
115                                return ComparisonResult.NOT_EQUAL_NM;
116                        }
117                } else if (!valueType.equals(other.valueType)) {
118                        return ComparisonResult.NOT_EQUAL_NM;
119                }
120                return ComparisonResult.EQUAL_NM; 
121        }
122
123        @SuppressWarnings("unchecked")
124        @Override
125        public Extractor<T, V, C> map(int[] map) {
126                if (ComparisonResult.isOneToOneMapping(map)) {
127                        return this;
128                }
129                
130                Params<T,C> newParams = params.map(map);
131                if (newParams==null) {
132                        return null;
133                }
134                
135                if (this instanceof JavaPredicate) {
136                        return (Extractor<T, V, C>) new JavaPredicate<T, C>(initialCost, costUnit, newParams, classLoader);                 
137                }
138                
139                return new JavaExtractor<T, V, C>(initialCost, costUnit, newParams, valueType, classLoader);
140        }
141        
142        public List<Object> getIdentity() {
143                return params.getIdentity();
144        }
145        
146        public interface Parameter {
147                int getIndex();
148                Class<?> getType();
149                String getName();
150        }
151
152        public Iterable<Parameter> getParameters() {
153                Collection<Parameter> ret = new ArrayList<Parameter>();
154                final Integer[] ia = params.indices.toArray(new Integer[params.indices.size()]);
155                for (int i=0; i<params.names.length; ++i) {
156                        class ParameterImpl implements Parameter {
157                                
158                                private int idx;
159
160                                public ParameterImpl(int idx) {
161                                        this.idx = idx;
162                                }
163
164                                @Override
165                                public int getIndex() {
166                                        return ia[idx];
167                                }
168
169                                @Override
170                                public Class<?> getType() {
171                                        return params.types[idx];
172                                }
173
174                                @Override
175                                public String getName() {
176                                        return params.names[idx];
177                                }
178
179                                @Override
180                                public String toString() {
181                                        return "Parameter[idx=" + idx + ", type="
182                                                        + getType() + ", name=" + getName() + "]";
183                                }               
184                                
185                                
186                        }
187                        
188                        ret.add(new ParameterImpl(i));
189                        
190                }
191                
192                return Collections.unmodifiableCollection(ret);
193        }
194
195        public String getExpression() {
196                return params.expr.toString();
197        }
198}