Statistics
| Revision:

svn-gvsig-desktop / trunk / org.gvsig.desktop / org.gvsig.desktop.library / org.gvsig.expressionevaluator / org.gvsig.expressionevaluator.lib / org.gvsig.expressionevaluator.lib.impl / src / main / java / org / gvsig / expressionevaluator / impl / DefaultInterpreter.java @ 44198

History | View | Annotate | Download (10.5 KB)

1
package org.gvsig.expressionevaluator.impl;
2

    
3
import java.util.ArrayList;
4
import java.util.Comparator;
5
import java.util.Date;
6
import java.util.HashMap;
7
import java.util.List;
8
import java.util.Map;
9
import java.util.Objects;
10
import org.apache.commons.lang3.tuple.Pair;
11
import org.gvsig.expressionevaluator.SymbolTable;
12
import org.gvsig.expressionevaluator.Interpreter;
13
import org.gvsig.expressionevaluator.Code;
14
import org.gvsig.expressionevaluator.Code.Constant;
15
import org.gvsig.expressionevaluator.Code.Identifier;
16
import org.gvsig.expressionevaluator.Code.Caller;
17
import org.gvsig.expressionevaluator.Code.Method;
18
import org.gvsig.expressionevaluator.Codes;
19
import org.gvsig.expressionevaluator.ExpressionRuntimeException;
20
import org.gvsig.expressionevaluator.Function;
21
import org.gvsig.expressionevaluator.impl.DefaultCodeBuilder.RecursionControlSupport;
22
import org.gvsig.expressionevaluator.impl.function.operator.BinaryOperator;
23
import org.gvsig.expressionevaluator.impl.function.programming.CallMethodFunction;
24
import org.gvsig.expressionevaluator.impl.function.operator.UnaryOperator;
25
import org.gvsig.expressionevaluator.impl.function.programming.ReturnFunction.ReturnException;
26

    
27
public class DefaultInterpreter implements Interpreter {
28

    
29
    public static int DEFAULT_MAX_RECURSION_LIMIT = 20;
30
    
31
    private class DefaultCache implements Cache {
32
        
33
        private Map<Pair<Object,Object>,Pair<Object,Long>> data;
34
        
35
        public DefaultCache() {
36
            this.data = new  HashMap<>();
37
        }
38
        
39
        @Override
40
        public Object get(Object context, Object key) {
41
            Pair<Object, Long> v = this.data.get( Pair.of(context, key) );
42
            if( v == null ) {
43
                return null;
44
            }
45
            return v.getLeft();
46
        }
47

    
48
        @Override
49
        public void put(Object context, Object key, Object value) {
50
            if( this.data.size()>this.getMaxElements() ) {
51
                this.reduce();
52
            }
53
            this.data.put(Pair.of(context, key), Pair.of(value, new Date().getTime()));
54
        }
55

    
56
        @Override
57
        public void remove(Object context, Object key) {
58
            this.data.remove(Pair.of(context, key));
59
        }
60

    
61
        @Override
62
        public void removeAll() {
63
            this.data = new  HashMap<>();
64
        }
65

    
66
        private int getMaxElements() {
67
            return 100;
68
        }
69

    
70
        private void reduce() {
71
            List<Map.Entry<Pair<Object, Object>, Pair<Object, Long>>> entries = 
72
                new ArrayList<>(this.data.entrySet());
73
            entries.sort(new Comparator<Map.Entry<Pair<Object, Object>, Pair<Object, Long>>>() {
74
                @Override
75
                public int compare(Map.Entry<Pair<Object, Object>, Pair<Object, Long>> o1, Map.Entry<Pair<Object, Object>, Pair<Object, Long>> o2) {
76
                    return o1.getValue().getRight().compareTo(o2.getValue().getRight());
77
                }
78
            });
79
            for( int i=0; i<this.getMaxElements()/2; i++ ) {
80
                this.data.remove(entries.get(i).getKey());
81
            }
82
        }
83
        
84
    }
85
    
86
    private SymbolTable symbolTable = null;
87
    private Double accuracy;
88
    private Code currentCode;
89
    private Cache cache;
90
    private int maxRecursionLimit;
91
    private boolean sqlCompatible = false;
92

    
93
    public DefaultInterpreter() {
94
        this.cache = new DefaultCache();
95
        this.maxRecursionLimit = DEFAULT_MAX_RECURSION_LIMIT;
96
    }
97

    
98
    @Override
99
    public Interpreter clone() throws CloneNotSupportedException {
100
        DefaultInterpreter other = (DefaultInterpreter) super.clone();
101
        other.cache = new DefaultCache();
102
        if( this.symbolTable!=null ) {
103
            other.symbolTable = this.symbolTable.clone();
104
        }
105
        return other;
106
    }
107
    
108
    public int getMaxRecursionLimit() {
109
        return this.maxRecursionLimit;
110
    }
111
            
112
    public void setMaxRecursionLimit(int limit) {
113
        this.maxRecursionLimit = limit;
114
    }
115
    
116
    @Override
117
    public Cache getCache() {
118
        return this.cache;
119
    }
120

    
121
    @Override
122
    public void setSymbolTable(SymbolTable symbolTable) {
123
        this.symbolTable = symbolTable;
124
    }
125

    
126
    @Override
127
    public SymbolTable getSymbolTable() {
128
        if( this.symbolTable==null ) {
129
            this.symbolTable = new DefaultSymbolTable();
130
        }
131
        return this.symbolTable;
132
    }
133
    @Override
134
    public Double getAccuracy() {
135
        return this.accuracy;
136
    }
137
    
138
    @Override
139
    public void setAccuracy(Double accuracy) {
140
        this.accuracy = accuracy;
141
    }
142

    
143
    @Override
144
    public void setSQLCompatible(boolean sqlCompatible) {
145
        this.sqlCompatible = sqlCompatible;
146
    }
147

    
148
    @Override
149
    public boolean isSQLCompatible() {
150
        return sqlCompatible;
151
    }
152

    
153
    @Override
154
    public Object run(Code code) {
155
        try {
156
            return this.runCode(code);
157
        } catch(ReturnException ex) {
158
            return ex.getValue();
159
        } catch(RuntimeException ex) {
160
            throw ex;
161
        } catch(Exception ex) {
162
            throw new ExpressionRuntimeException(code, "", ex);
163
        }
164
    }
165

    
166
    @Override
167
    public void link(Code code) {
168
        code.link(this.getSymbolTable());
169
    }
170

    
171
    public Object runCode(Code code) throws Exception {
172
        RecursionControlSupport recursionControl = null;
173
        if( code instanceof RecursionControlSupport ) {
174
            recursionControl = (RecursionControlSupport) code;
175
            if( !recursionControl.enterCode(this.maxRecursionLimit) ) {
176
                recursionControl.resetRecursionState();
177
                throw new ExpressionRuntimeException(code, I18N.Maximum_recursion_limit_exceeded());
178
            }
179
        }
180
        Object value = null;
181
        this.currentCode = code;
182
        try {
183
            switch( code.code() ) {
184
            case Code.CONSTANT:
185
                value = ((Constant) code).value();
186
                break;
187

    
188
            case Code.IDENTIFIER:
189
                String name = ((Identifier) code).name();
190
                if( !this.getSymbolTable().exists(name) ) {
191
                    throw new ExpressionRuntimeException(
192
                            code, 
193
                            I18N.Undefined_variable_XIdentifierX(name),
194
                            I18N.Use_single_quotes_to_enter_literal_strings()
195
                    );
196
                }
197
                value = this.getSymbolTable().value(name);
198
                break;
199

    
200
            case Code.METHOD: {
201
                    Method method = (Method) code;
202
                    Function function = method.function();
203
                    if( function == null ) {
204
                        Object obj = this.runCode(method.obj());
205
                        if( obj == null ) {
206
                            throw new NullPointerException("An object pointer was expected to invoke method "+method.methodname()+" and a null was received");
207
                        }
208
                        method.function(new CallMethodFunction(obj, method.methodname()));
209
                    }
210
                }
211
            case Code.CALLER:
212
                Caller caller = (Caller) code;
213
                Function function = caller.function();
214
                if( function == null ) {
215
                    function = this.getSymbolTable().function(caller.name());
216
                    if( function == null ) {
217
                        throw new ExpressionRuntimeException(code, I18N.Undefined_function_XIdentifierX(caller.name()));
218
                    }
219
                    caller.function(function);
220
                }
221
                if( !function.isSQLCompatible() && this.sqlCompatible ) {
222
                    throw new ExpressionRuntimeException(code, I18N.Cant_use_non_SQL_compatible_functions(function.name()));
223
                }
224
                Codes args = caller.parameters();
225
                try {
226
                    switch( caller.type() ) {
227
                    case Caller.UNARY_OPERATOR:
228
                        if( args == null || args.size() != 1 ) {
229
                            throw new ExpressionRuntimeException(code, I18N.Number_of_argument_mistmatch_in_operator_XIdentifierX_expected_1_got_XargcX(function.name(),args==null?0:args.size()));
230
                        }
231
                        value = ((UnaryOperator) function).call(this, runCode(args.get(0)));
232
                        break;
233

    
234
                    case Caller.BINARY_OPERATOR:
235
                        if( args == null || args.size() != 2 ) {
236
                            throw new ExpressionRuntimeException(code, I18N.Number_of_argument_mistmatch_in_operator_XIdentifierX_expected_2_got_XargcX(function.name(),args==null?0:args.size()));
237
                        }
238
                        value = ((BinaryOperator) function).call(this, runCode(args.get(0)), runCode(args.get(1)));
239
                        break;
240

    
241
                    case Caller.FUNCTION:
242
                        int argc = (args == null) ? 0 : args.size();
243
                        if( !function.argc().contains(argc) ) {
244
                            throw new ExpressionRuntimeException(code, I18N.Number_of_argument_mistmatch_in_function_XIdentifierX_expected_XexpectedX_got_XfoundX(function.name(),function.argc(),argc));
245
                        }
246
                        if( function.useArgumentsInsteadObjects() ) {
247
                            value = function.call(this, args);
248
                        } else {
249
                            Object[] argvalues = new Object[argc];
250
                            if( args != null ) {
251
                                int i = 0;
252
                                for( Code arg : args ) {
253
                                    argvalues[i++] = runCode(arg);
254
                                }
255
                            }
256
                            value = function.call(this, argvalues);
257
                        }
258
                    }
259
                } catch (RuntimeException ex) {
260
                    throw ex;
261
                } catch (Exception ex) {
262
                    String argsstr = "???";
263
                    try {
264
                        argsstr = Objects.toString(args);
265
                    } catch(Exception ex2) {
266
                        // Ignore.
267
                    }
268
                    throw new ExpressionRuntimeException(code, I18N.Problems_calling_function_XIdentifierX_with_args_XargsX(function.name(),argsstr));
269
                }
270
                break;
271

    
272
            }
273
        } finally {
274
            this.currentCode = null;
275
            if( recursionControl!=null ) {
276
                recursionControl.exitCode();
277
            }
278
        }
279
        return value;
280
    }
281

    
282
    @Override
283
    public Code getCurrentCode() {
284
        return this.currentCode;
285
    }
286
}