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 @ 44338

History | View | Annotate | Download (11 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.operator.UnaryOperator;
24
import org.gvsig.expressionevaluator.impl.function.programming.ReturnFunction.ReturnException;
25
import org.gvsig.expressionevaluator.spi.AbstractSymbolTable;
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
    @Override
109
    public Object call(String function, Object... args) throws Exception {
110
        Function fn = this.symbolTable.function(function);
111
        Object value = fn.call(this, args);
112
        return value;
113
    }
114

    
115
    @Override
116
    public boolean hasFunction(String function) {
117
        Function fn = this.symbolTable.function(function);
118
        if( fn == null ) {
119
            return false;
120
        }
121
        if( fn instanceof AbstractSymbolTable.ScriptFunction ) {
122
            return false;
123
        }
124
        return true;
125
    }
126
    
127
    public int getMaxRecursionLimit() {
128
        return this.maxRecursionLimit;
129
    }
130
            
131
    public void setMaxRecursionLimit(int limit) {
132
        this.maxRecursionLimit = limit;
133
    }
134
    
135
    @Override
136
    public Cache getCache() {
137
        return this.cache;
138
    }
139

    
140
    @Override
141
    public void setSymbolTable(SymbolTable symbolTable) {
142
        this.symbolTable = symbolTable;
143
    }
144

    
145
    @Override
146
    public SymbolTable getSymbolTable() {
147
        if( this.symbolTable==null ) {
148
            this.symbolTable = new DefaultSymbolTable();
149
        }
150
        return this.symbolTable;
151
    }
152
    @Override
153
    public Double getAccuracy() {
154
        return this.accuracy;
155
    }
156
    
157
    @Override
158
    public void setAccuracy(Double accuracy) {
159
        this.accuracy = accuracy;
160
    }
161

    
162
    @Override
163
    public void setSQLCompatible(boolean sqlCompatible) {
164
        this.sqlCompatible = sqlCompatible;
165
    }
166

    
167
    @Override
168
    public boolean isSQLCompatible() {
169
        return sqlCompatible;
170
    }
171

    
172
    @Override
173
    public Object run(Code code) {
174
        try {
175
            return this.runCode(code);
176
        } catch(ReturnException ex) {
177
            return ex.getValue();
178
        } catch(RuntimeException ex) {
179
            throw ex;
180
        } catch(Exception ex) {
181
            throw new ExpressionRuntimeException(code, "", ex);
182
        }
183
    }
184

    
185
    @Override
186
    public void link(Code code) {
187
        code.link(this.getSymbolTable());
188
    }
189

    
190
    public Object runCode(Code code) throws Exception {
191
        RecursionControlSupport recursionControl = null;
192
        if( code instanceof RecursionControlSupport ) {
193
            recursionControl = (RecursionControlSupport) code;
194
            if( !recursionControl.enterCode(this.maxRecursionLimit) ) {
195
                recursionControl.resetRecursionState();
196
                throw new ExpressionRuntimeException(code, I18N.Maximum_recursion_limit_exceeded());
197
            }
198
        }
199
        Object value = null;
200
        this.currentCode = code;
201
        try {
202
            switch( code.code() ) {
203
            case Code.CONSTANT:
204
                value = ((Constant) code).value();
205
                break;
206

    
207
            case Code.IDENTIFIER:
208
                String name = ((Identifier) code).name();
209
                if( !this.getSymbolTable().exists(name) ) {
210
                    throw new ExpressionRuntimeException(
211
                            code, 
212
                            I18N.Undefined_variable_XIdentifierX(name),
213
                            I18N.Use_single_quotes_to_enter_literal_strings()
214
                    );
215
                }
216
                value = this.getSymbolTable().value(name);
217
                break;
218

    
219
            case Code.METHOD: {
220
                    Method method = (Method) code;
221
                    Codes args = method.parameters();
222
                    int argc = (args == null) ? 0 : args.size();
223
                    Object[] argvalues = new Object[argc];
224
                    if( args != null ) {
225
                        int i = 0;
226
                        for( Code arg : args ) {
227
                            argvalues[i++] = runCode(arg);
228
                        }
229
                    }
230
                    value = method.call(this, argvalues);
231
                    break;
232
                }
233
            case Code.CALLER:
234
                Caller caller = (Caller) code;
235
                Function function = caller.function();
236
                if( function == null ) {
237
                    function = this.getSymbolTable().function(caller.name());
238
                    if( function == null ) {
239
                        throw new ExpressionRuntimeException(code, I18N.Undefined_function_XIdentifierX(caller.name()));
240
                    }
241
                    caller.function(function);
242
                }
243
//                if( !function.isSQLCompatible() && this.sqlCompatible ) {
244
//                    throw new ExpressionRuntimeException(code, I18N.Cant_use_non_SQL_compatible_functions(function.name()));
245
//                }
246
                Codes args = caller.parameters();
247
                try {
248
                    switch( caller.type() ) {
249
                    case Caller.UNARY_OPERATOR:
250
                        if( args == null || args.size() != 1 ) {
251
                            throw new ExpressionRuntimeException(code, I18N.Number_of_argument_mistmatch_in_operator_XIdentifierX_expected_1_got_XargcX(function.name(),args==null?0:args.size()));
252
                        }
253
                        value = ((UnaryOperator) function).call(this, runCode(args.get(0)));
254
                        break;
255

    
256
                    case Caller.BINARY_OPERATOR:
257
                        if( args == null || args.size() != 2 ) {
258
                            throw new ExpressionRuntimeException(code, I18N.Number_of_argument_mistmatch_in_operator_XIdentifierX_expected_2_got_XargcX(function.name(),args==null?0:args.size()));
259
                        }
260
                        value = ((BinaryOperator) function).call(this, runCode(args.get(0)), runCode(args.get(1)));
261
                        break;
262

    
263
                    case Caller.FUNCTION:
264
                        int argc = (args == null) ? 0 : args.size();
265
                        if( !function.argc().contains(argc) ) {
266
                            throw new ExpressionRuntimeException(code, I18N.Number_of_argument_mistmatch_in_function_XIdentifierX_expected_XexpectedX_got_XfoundX(function.name(),function.argc(),argc));
267
                        }
268
                        if( function.useArgumentsInsteadObjects() ) {
269
                            value = function.call(this, args);
270
                        } else {
271
                            Object[] argvalues = new Object[argc];
272
                            if( args != null ) {
273
                                int i = 0;
274
                                for( Code arg : args ) {
275
                                    argvalues[i++] = runCode(arg);
276
                                }
277
                            }
278
                            value = function.call(this, argvalues);
279
                        }
280
                    }
281
                } catch (RuntimeException ex) {
282
                    throw ex;
283
                } catch (Exception ex) {
284
                    String argsstr = "???";
285
                    try {
286
                        argsstr = Objects.toString(args);
287
                    } catch(Exception ex2) {
288
                        // Ignore.
289
                    }
290
                    throw new ExpressionRuntimeException(code, I18N.Problems_calling_function_XIdentifierX_with_args_XargsX(function.name(),argsstr));
291
                }
292
                break;
293

    
294
            }
295
        } finally {
296
            this.currentCode = null;
297
            if( recursionControl!=null ) {
298
                recursionControl.exitCode();
299
            }
300
        }
301
        return value;
302
    }
303

    
304
    @Override
305
    public Code getCurrentCode() {
306
        return this.currentCode;
307
    }
308
}