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

History | View | Annotate | Download (14.5 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
    public Object runCode(Code code) throws Exception {
282
        RecursionControlSupport recursionControl = null;
283
        if( code instanceof RecursionControlSupport ) {
284
            recursionControl = (RecursionControlSupport) code;
285
            if( !recursionControl.enterCode(this.maxRecursionLimit) ) {
286
                recursionControl.resetRecursionState();
287
                throw new ExpressionRuntimeException(code, I18N.Maximum_recursion_limit_exceeded());
288
            }
289
        }
290
        Object value = null;
291
        this.currentCode = code;
292
        try {
293
            switch( code.code() ) {
294
            case Code.CONSTANT:
295
                value = ((Constant) code).value();
296
                break;
297

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

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

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

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

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

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