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

History | View | Annotate | Download (14.6 KB)

1
package org.gvsig.expressionevaluator.impl;
2

    
3
import java.io.OutputStreamWriter;
4
import java.io.Writer;
5
import java.util.ArrayList;
6
import java.util.Comparator;
7
import java.util.Date;
8
import java.util.HashMap;
9
import java.util.List;
10
import java.util.Map;
11
import java.util.Objects;
12
import org.apache.commons.lang3.tuple.Pair;
13
import org.gvsig.expressionevaluator.SymbolTable;
14
import org.gvsig.expressionevaluator.Interpreter;
15
import org.gvsig.expressionevaluator.Code;
16
import org.gvsig.expressionevaluator.Code.Constant;
17
import org.gvsig.expressionevaluator.Code.Identifier;
18
import org.gvsig.expressionevaluator.Code.Method;
19
import org.gvsig.expressionevaluator.Codes;
20
import org.gvsig.expressionevaluator.ExpressionRuntimeException;
21
import org.gvsig.expressionevaluator.Function;
22
import org.gvsig.expressionevaluator.MutableSymbolTable;
23
import org.gvsig.expressionevaluator.impl.DefaultCodeBuilder.RecursionControlSupport;
24
import org.gvsig.expressionevaluator.impl.function.operator.BinaryOperator;
25
import org.gvsig.expressionevaluator.impl.function.operator.UnaryOperator;
26
import org.gvsig.expressionevaluator.impl.function.programming.ReturnFunction.ReturnException;
27
import org.gvsig.expressionevaluator.spi.AbstractSymbolTable;
28
import org.gvsig.tools.resourcesstorage.ResourcesStorage;
29
import org.gvsig.expressionevaluator.Code.Callable;
30
import org.gvsig.expressionevaluator.CodeBuilder;
31
import org.gvsig.expressionevaluator.ExpressionEvaluatorLocator;
32
import org.gvsig.expressionevaluator.ExpressionEvaluatorManager;
33

    
34
public class DefaultInterpreter implements Interpreter {
35

    
36
    public static int DEFAULT_MAX_RECURSION_LIMIT = 20;
37
    
38
    private class DefaultCache implements Cache {
39
        
40
        private Map<Pair<Object,Object>,Pair<Object,Long>> data;
41
        
42
        public DefaultCache() {
43
            this.data = new  HashMap<>();
44
        }
45
        
46
        @Override
47
        public Object get(Object context, Object key) {
48
            Pair<Object, Long> v = this.data.get( Pair.of(context, key) );
49
            if( v == null ) {
50
                return null;
51
            }
52
            return v.getLeft();
53
        }
54

    
55
        @Override
56
        public void put(Object context, Object key, Object value) {
57
            if( this.data.size()>this.getMaxElements() ) {
58
                this.reduce();
59
            }
60
            this.data.put(Pair.of(context, key), Pair.of(value, new Date().getTime()));
61
        }
62

    
63
        @Override
64
        public void remove(Object context, Object key) {
65
            this.data.remove(Pair.of(context, key));
66
        }
67

    
68
        @Override
69
        public void removeAll() {
70
            this.data = new  HashMap<>();
71
        }
72

    
73
        private int getMaxElements() {
74
            return 100;
75
        }
76

    
77
        private void reduce() {
78
            List<Map.Entry<Pair<Object, Object>, Pair<Object, Long>>> entries = 
79
                new ArrayList<>(this.data.entrySet());
80
            entries.sort(new Comparator<Map.Entry<Pair<Object, Object>, Pair<Object, Long>>>() {
81
                @Override
82
                public int compare(Map.Entry<Pair<Object, Object>, Pair<Object, Long>> o1, Map.Entry<Pair<Object, Object>, Pair<Object, Long>> o2) {
83
                    return o1.getValue().getRight().compareTo(o2.getValue().getRight());
84
                }
85
            });
86
            for( int i=0; i<this.getMaxElements()/2; i++ ) {
87
                this.data.remove(entries.get(i).getKey());
88
            }
89
        }
90
        
91
    }
92
    
93
    private SymbolTable symbolTable = null;
94
    private Double accuracy;
95
    private Code currentCode;
96
    private Cache cache;
97
    private int maxRecursionLimit;
98
    private boolean sqlCompatible = false;
99
    private Writer writer ;
100
    private ResourcesStorage resourcesStorage;
101
    private CodeBuilder codeBuilder;
102

    
103
    public DefaultInterpreter() {
104
        this.cache = new DefaultCache();
105
        this.maxRecursionLimit = DEFAULT_MAX_RECURSION_LIMIT;
106
        this.writer = new OutputStreamWriter(System.out);
107
        this.resourcesStorage = ResourcesStorage.EMPTY_RESOURCESSTORAGE;
108
    }
109

    
110
    @Override
111
    public Interpreter clone() throws CloneNotSupportedException {
112
        DefaultInterpreter other = (DefaultInterpreter) super.clone();
113
        other.cache = new DefaultCache();
114
        if( this.symbolTable!=null ) {
115
            other.symbolTable = this.symbolTable.clone();
116
        }
117
        return other;
118
    }
119

    
120
    public CodeBuilder getCodeBuilder() {
121
        if( this.codeBuilder == null ) {
122
            ExpressionEvaluatorManager manager = ExpressionEvaluatorLocator.getExpressionEvaluatorManager();
123
            this.codeBuilder = manager.createCodeBuilder();
124
        }
125
        return this.codeBuilder;
126
    }
127
    
128
    @Override
129
    public ResourcesStorage getResourcesStorage() {
130
        return resourcesStorage;
131
    }
132

    
133
    @Override
134
    public void setResourcesStorage(ResourcesStorage resourcesStorage) {
135
        if( resourcesStorage==null ) {
136
            this.resourcesStorage = ResourcesStorage.EMPTY_RESOURCESSTORAGE;
137
        } else {
138
            this.resourcesStorage = resourcesStorage;
139
        }
140
    }
141
    
142
    @Override
143
    public Writer getWriter() {
144
        return this.writer;
145
    }
146
    
147
    @Override
148
    public void setWriter(Writer writer) {
149
        if( writer == null ) {
150
            writer = new OutputStreamWriter(System.out);
151
        }
152
        this.writer = writer;
153
    }
154

    
155
    @Override
156
    public Object call(SymbolTable symbolTable, String funcname, Object... args) throws Exception {
157
      if( this.symbolTable==null ) {
158
        try {
159
            this.symbolTable = symbolTable;
160
            return this.call(funcname, args);
161
        } finally {
162
            this.symbolTable = null;
163
        }
164
      } else {
165
        SymbolTable savedSymbolTable = this.symbolTable;
166
        try {
167
            symbolTable.addSymbolTable(this.symbolTable);
168
            this.symbolTable = symbolTable;
169
            return this.call(funcname, args);
170
        } finally {
171
            this.symbolTable = savedSymbolTable;
172
            symbolTable.removeSymbolTable(this.symbolTable);
173
        }
174
      }
175
    }
176
    
177
    @Override
178
    public Object call(String function, Object... args) throws Exception {
179
        Function fn = this.symbolTable.function(function);
180
        Object value = fn.call(this, args);
181
        return value;
182
    }
183

    
184
    @Override
185
    public boolean hasFunction(String function) {
186
        Function fn = this.getSymbolTable().function(function);
187
        if( fn == null ) {
188
            return false;
189
        }
190
//        if( fn instanceof AbstractSymbolTable.ScriptFunction ) {
191
//            return false;
192
//        }
193
        return true;
194
    }
195
    
196
    public int getMaxRecursionLimit() {
197
        return this.maxRecursionLimit;
198
    }
199
            
200
    public void setMaxRecursionLimit(int limit) {
201
        this.maxRecursionLimit = limit;
202
    }
203
    
204
    @Override
205
    public Cache getCache() {
206
        return this.cache;
207
    }
208

    
209
    @Override
210
    public void setSymbolTable(SymbolTable symbolTable) {
211
        this.symbolTable = symbolTable;
212
    }
213

    
214
    @Override
215
    public SymbolTable getSymbolTable() {
216
        if( this.symbolTable==null ) {
217
            this.symbolTable = new DefaultSymbolTable();
218
        }
219
        return this.symbolTable;
220
    }
221
    @Override
222
    public Double getAccuracy() {
223
        return this.accuracy;
224
    }
225
    
226
    @Override
227
    public void setAccuracy(Double accuracy) {
228
        this.accuracy = accuracy;
229
    }
230

    
231
    @Override
232
    public void setSQLCompatible(boolean sqlCompatible) {
233
        this.sqlCompatible = sqlCompatible;
234
    }
235

    
236
    @Override
237
    public boolean isSQLCompatible() {
238
        return sqlCompatible;
239
    }
240

    
241
    @Override
242
    public void run(MutableSymbolTable symbolTable, Code code) {
243
      if( this.symbolTable==null ) {
244
        try {
245
            this.symbolTable = symbolTable;
246
            this.run(code);
247
        } finally {
248
            this.symbolTable = null;
249
        }
250
      } else {
251
        SymbolTable savedSymbolTable = this.symbolTable;
252
        try {
253
            symbolTable.addSymbolTable(this.symbolTable);
254
            this.symbolTable = symbolTable;
255
            this.run(code);
256
        } finally {
257
            this.symbolTable = savedSymbolTable;
258
            symbolTable.removeSymbolTable(this.symbolTable);
259
        }
260
      }
261
    }
262
    
263
    @Override
264
    public Object run(Code code) {
265
        try {
266
            return this.runCode(code);
267
        } catch(ReturnException ex) {
268
            return ex.getValue();
269
        } catch(RuntimeException ex) {
270
            throw ex;
271
        } catch(Exception ex) {
272
            throw new ExpressionRuntimeException(code, "", ex);
273
        }
274
    }
275

    
276
    @Override
277
    public void link(Code code) {
278
        code.link(this.getSymbolTable());
279
    }
280

    
281
    @Override
282
    public Object runCode(Code code) throws Exception {
283
        RecursionControlSupport recursionControl = null;
284
        if( code instanceof RecursionControlSupport ) {
285
            recursionControl = (RecursionControlSupport) code;
286
            if( !recursionControl.enterCode(this.maxRecursionLimit) ) {
287
                recursionControl.resetRecursionState();
288
                throw new ExpressionRuntimeException(code, I18N.Maximum_recursion_limit_exceeded());
289
            }
290
        }
291
        Object value = null;
292
        this.currentCode = code;
293
        try {
294
            switch( code.code() ) {
295
            case Code.CONSTANT:
296
                value = ((Constant) code).value();
297
                break;
298

    
299
            case Code.IDENTIFIER:
300
                String name = ((Identifier) code).name();
301
                if( !this.getSymbolTable().exists(name) ) {
302
                    throw new ExpressionRuntimeException(
303
                            code, 
304
                            I18N.Undefined_variable_XIdentifierX(name),
305
                            I18N.Use_single_quotes_to_enter_literal_strings()
306
                    );
307
                }
308
                value = this.getSymbolTable().value(name);
309
                break;
310

    
311
            case Code.METHOD: {
312
                    Method method = (Method) code;
313
                    Codes args = method.parameters();
314
                    int argc = (args == null) ? 0 : args.size();
315
                    Object[] argvalues = new Object[argc];
316
                    if( args != null ) {
317
                        int i = 0;
318
                        for( Code arg : args ) {
319
                            argvalues[i++] = runCode(arg);
320
                        }
321
                    }
322
                    value = method.call(this, argvalues);
323
                    break;
324
                }
325
            case Code.CALLABLE:
326
                Callable caller = (Callable) code;
327
                Function function = caller.function();
328
                if( function == null ) {
329
                    function = this.getSymbolTable().function(caller.name());
330
                    if( function == null ) {
331
                        try {
332
                            function = (Function) this.getSymbolTable().value(caller.name());
333
                        } catch(Exception ex) {
334
                        }
335
                        if( function == null ) {
336
                            throw new ExpressionRuntimeException(code, I18N.Undefined_function_XIdentifierX(caller.name()));
337
                        }
338
                    }
339
                    caller.function(function);
340
                }
341
//                if( !function.isSQLCompatible() && this.sqlCompatible ) {
342
//                    throw new ExpressionRuntimeException(code, I18N.Cant_use_non_SQL_compatible_functions(function.name()));
343
//                }
344
                Codes args = caller.parameters();
345
                try {
346
                    switch( caller.type() ) {
347
                    case Callable.UNARY_OPERATOR:
348
                        if( args == null || args.size() != 1 ) {
349
                            throw new ExpressionRuntimeException(code, I18N.Number_of_argument_mistmatch_in_operator_XIdentifierX_expected_1_got_XargcX(function.name(),args==null?0:args.size()));
350
                        }
351
                        if( function.useArgumentsInsteadObjects() ) {
352
                            value = function.call(this, args);
353
                        } else {
354
                            value = ((UnaryOperator) function).call(this, runCode(args.get(0)));
355
                        }
356
                        break;
357

    
358
                    case Callable.BINARY_OPERATOR:
359
                        if( args == null || args.size() != 2 ) {
360
                            throw new ExpressionRuntimeException(code, I18N.Number_of_argument_mistmatch_in_operator_XIdentifierX_expected_2_got_XargcX(function.name(),args==null?0:args.size()));
361
                        }
362
                        if( function.useArgumentsInsteadObjects() ) {
363
                            value = function.call(this, args);
364
                        } else {
365
                            value = ((BinaryOperator) function).call(this, runCode(args.get(0)), runCode(args.get(1)));
366
                        }
367
                        break;
368

    
369
                    case Callable.FUNCTION:
370
                        int argc = (args == null) ? 0 : args.size();
371
                        if( !function.argc().contains(argc) ) {
372
                            throw new ExpressionRuntimeException(code, I18N.Number_of_argument_mistmatch_in_function_XIdentifierX_expected_XexpectedX_got_XfoundX(function.name(),function.argc(),argc));
373
                        }
374
                        if( function.useArgumentsInsteadObjects() ) {
375
                            value = function.call(this, args);
376
                        } else {
377
                            Object[] argvalues = new Object[argc];
378
                            if( args != null ) {
379
                                int i = 0;
380
                                for( Code arg : args ) {
381
                                    argvalues[i++] = runCode(arg);
382
                                }
383
                            }
384
                            value = function.call(this, argvalues);
385
                        }
386
                    }
387
                } catch (RuntimeException ex) {
388
                    throw ex;
389
                } catch (Exception ex) {
390
                    String argsstr = "???";
391
                    try {
392
                        argsstr = Objects.toString(args);
393
                    } catch(Exception ex2) {
394
                        // Ignore.
395
                    }
396
                    throw new ExpressionRuntimeException(code, I18N.Problems_calling_function_XIdentifierX_with_args_XargsX(function.name(),argsstr), ex);
397
                }
398
                break;
399

    
400
            }
401
        } finally {
402
            this.currentCode = null;
403
            if( recursionControl!=null ) {
404
                recursionControl.exitCode();
405
            }
406
        }
407
        return value;
408
    }
409

    
410
    @Override
411
    public Code getCurrentCode() {
412
        return this.currentCode;
413
    }
414
}