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

History | View | Annotate | Download (10.9 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
        linkCode(code);
169
    }
170

    
171
    private void linkCode(Code code) {
172
        if( code.code() == Code.CALLER ) {
173
            Caller caller = (Caller) code;
174
            if( caller.function() == null ) {
175
                caller.function(this.getSymbolTable().function(caller.name()));
176
            }
177
            if( caller.args() != null ) {
178
                for( Code arg : caller.args() ) {
179
                    linkCode(arg);
180
                }
181
            }
182
        }
183
    }
184

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

    
202
            case Code.IDENTIFIER:
203
                String name = ((Identifier) code).name();
204
                if( !this.getSymbolTable().exists(name) ) {
205
                    throw new ExpressionRuntimeException(
206
                            code, 
207
                            I18N.Undefined_variable_XIdentifierX(name),
208
                            I18N.Use_single_quotes_to_enter_literal_strings()
209
                    );
210
                }
211
                value = this.getSymbolTable().value(name);
212
                break;
213

    
214
            case Code.METHOD: {
215
                    Method method = (Method) code;
216
                    Function function = method.function();
217
                    if( function == null ) {
218
                        Object obj = this.runCode(method.obj());
219
                        if( obj == null ) {
220
                            throw new NullPointerException("An object pointer was expected to invoke method "+method.methodname()+" and a null was received");
221
                        }
222
                        method.function(new CallMethodFunction(obj, method.methodname()));
223
                    }
224
                }
225
            case Code.CALLER:
226
                Caller caller = (Caller) code;
227
                Function function = caller.function();
228
                if( function == null ) {
229
                    function = this.getSymbolTable().function(caller.name());
230
                    if( function == null ) {
231
                        throw new ExpressionRuntimeException(code, I18N.Undefined_function_XIdentifierX(caller.name()));
232
                    }
233
                    caller.function(function);
234
                }
235
                if( !function.isSQLCompatible() && this.sqlCompatible ) {
236
                    throw new ExpressionRuntimeException(code, I18N.Cant_use_non_SQL_compatible_functions(function.name()));
237
                }
238
                Codes args = caller.args();
239
                try {
240
                    switch( caller.type() ) {
241
                    case Caller.UNARY_OPERATOR:
242
                        if( args == null || args.size() != 1 ) {
243
                            throw new ExpressionRuntimeException(code, I18N.Number_of_argument_mistmatch_in_operator_XIdentifierX_expected_1_got_XargcX(function.name(),args==null?0:args.size()));
244
                        }
245
                        value = ((UnaryOperator) function).call(this, runCode(args.get(0)));
246
                        break;
247

    
248
                    case Caller.BINARY_OPERATOR:
249
                        if( args == null || args.size() != 2 ) {
250
                            throw new ExpressionRuntimeException(code, I18N.Number_of_argument_mistmatch_in_operator_XIdentifierX_expected_2_got_XargcX(function.name(),args==null?0:args.size()));
251
                        }
252
                        value = ((BinaryOperator) function).call(this, runCode(args.get(0)), runCode(args.get(1)));
253
                        break;
254

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

    
286
            }
287
        } finally {
288
            this.currentCode = null;
289
            if( recursionControl!=null ) {
290
                recursionControl.exitCode();
291
            }
292
        }
293
        return value;
294
    }
295

    
296
    @Override
297
    public Code getCurrentCode() {
298
        return this.currentCode;
299
    }
300
}