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

History | View | Annotate | Download (12.9 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.Caller;
19
import org.gvsig.expressionevaluator.Code.Method;
20
import org.gvsig.expressionevaluator.Codes;
21
import org.gvsig.expressionevaluator.ExpressionRuntimeException;
22
import org.gvsig.expressionevaluator.Function;
23
import org.gvsig.expressionevaluator.MutableSymbolTable;
24
import org.gvsig.expressionevaluator.impl.DefaultCodeBuilder.RecursionControlSupport;
25
import org.gvsig.expressionevaluator.impl.function.operator.BinaryOperator;
26
import org.gvsig.expressionevaluator.impl.function.operator.UnaryOperator;
27
import org.gvsig.expressionevaluator.impl.function.programming.ReturnFunction.ReturnException;
28
import org.gvsig.expressionevaluator.spi.AbstractSymbolTable;
29
import org.gvsig.tools.resourcesstorage.ResourcesStorage;
30

    
31
public class DefaultInterpreter implements Interpreter {
32

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

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

    
60
        @Override
61
        public void remove(Object context, Object key) {
62
            this.data.remove(Pair.of(context, key));
63
        }
64

    
65
        @Override
66
        public void removeAll() {
67
            this.data = new  HashMap<>();
68
        }
69

    
70
        private int getMaxElements() {
71
            return 100;
72
        }
73

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

    
99
    public DefaultInterpreter() {
100
        this.cache = new DefaultCache();
101
        this.maxRecursionLimit = DEFAULT_MAX_RECURSION_LIMIT;
102
        this.writer = new OutputStreamWriter(System.out);
103
        this.resourcesStorage = ResourcesStorage.EMPTY_RESOURCESSTORAGE;
104
    }
105

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

    
116
    @Override
117
    public ResourcesStorage getResourcesStorage() {
118
        return resourcesStorage;
119
    }
120

    
121
    @Override
122
    public void setResourcesStorage(ResourcesStorage resourcesStorage) {
123
        if( resourcesStorage==null ) {
124
            this.resourcesStorage = ResourcesStorage.EMPTY_RESOURCESSTORAGE;
125
        } else {
126
            this.resourcesStorage = resourcesStorage;
127
        }
128
    }
129
    
130
    @Override
131
    public Writer getWriter() {
132
        return this.writer;
133
    }
134
    
135
    @Override
136
    public void setWriter(Writer writer) {
137
        if( writer == null ) {
138
            writer = new OutputStreamWriter(System.out);
139
        }
140
        this.writer = writer;
141
    }
142

    
143
    @Override
144
    public Object call(SymbolTable symbolTable, String funcname, Object... args) throws Exception {
145
        SymbolTable savedSymbolTable = this.symbolTable;
146
        try {
147
            symbolTable.addSymbolTable(this.symbolTable);
148
            this.symbolTable = symbolTable;
149
            return this.call(funcname, args);
150
        } finally {
151
            this.symbolTable = savedSymbolTable;
152
            symbolTable.removeSymbolTable(this.symbolTable);
153
        }
154
    }
155
    
156
    @Override
157
    public Object call(String function, Object... args) throws Exception {
158
        Function fn = this.symbolTable.function(function);
159
        Object value = fn.call(this, args);
160
        return value;
161
    }
162

    
163
    @Override
164
    public boolean hasFunction(String function) {
165
        Function fn = this.getSymbolTable().function(function);
166
        if( fn == null ) {
167
            return false;
168
        }
169
//        if( fn instanceof AbstractSymbolTable.ScriptFunction ) {
170
//            return false;
171
//        }
172
        return true;
173
    }
174
    
175
    public int getMaxRecursionLimit() {
176
        return this.maxRecursionLimit;
177
    }
178
            
179
    public void setMaxRecursionLimit(int limit) {
180
        this.maxRecursionLimit = limit;
181
    }
182
    
183
    @Override
184
    public Cache getCache() {
185
        return this.cache;
186
    }
187

    
188
    @Override
189
    public void setSymbolTable(SymbolTable symbolTable) {
190
        this.symbolTable = symbolTable;
191
    }
192

    
193
    @Override
194
    public SymbolTable getSymbolTable() {
195
        if( this.symbolTable==null ) {
196
            this.symbolTable = new DefaultSymbolTable();
197
        }
198
        return this.symbolTable;
199
    }
200
    @Override
201
    public Double getAccuracy() {
202
        return this.accuracy;
203
    }
204
    
205
    @Override
206
    public void setAccuracy(Double accuracy) {
207
        this.accuracy = accuracy;
208
    }
209

    
210
    @Override
211
    public void setSQLCompatible(boolean sqlCompatible) {
212
        this.sqlCompatible = sqlCompatible;
213
    }
214

    
215
    @Override
216
    public boolean isSQLCompatible() {
217
        return sqlCompatible;
218
    }
219

    
220
    @Override
221
    public void run(MutableSymbolTable symbolTable, Code code) {
222
        SymbolTable savedSymbolTable = this.symbolTable;
223
        try {
224
            symbolTable.addSymbolTable(this.symbolTable);
225
            this.symbolTable = symbolTable;
226
            this.run(code);
227
        } finally {
228
            this.symbolTable = savedSymbolTable;
229
            symbolTable.removeSymbolTable(this.symbolTable);
230
        }
231
    }
232
    
233
    @Override
234
    public Object run(Code code) {
235
        try {
236
            return this.runCode(code);
237
        } catch(ReturnException ex) {
238
            return ex.getValue();
239
        } catch(RuntimeException ex) {
240
            throw ex;
241
        } catch(Exception ex) {
242
            throw new ExpressionRuntimeException(code, "", ex);
243
        }
244
    }
245

    
246
    @Override
247
    public void link(Code code) {
248
        code.link(this.getSymbolTable());
249
    }
250

    
251
    public Object runCode(Code code) throws Exception {
252
        RecursionControlSupport recursionControl = null;
253
        if( code instanceof RecursionControlSupport ) {
254
            recursionControl = (RecursionControlSupport) code;
255
            if( !recursionControl.enterCode(this.maxRecursionLimit) ) {
256
                recursionControl.resetRecursionState();
257
                throw new ExpressionRuntimeException(code, I18N.Maximum_recursion_limit_exceeded());
258
            }
259
        }
260
        Object value = null;
261
        this.currentCode = code;
262
        try {
263
            switch( code.code() ) {
264
            case Code.CONSTANT:
265
                value = ((Constant) code).value();
266
                break;
267

    
268
            case Code.IDENTIFIER:
269
                String name = ((Identifier) code).name();
270
                if( !this.getSymbolTable().exists(name) ) {
271
                    throw new ExpressionRuntimeException(
272
                            code, 
273
                            I18N.Undefined_variable_XIdentifierX(name),
274
                            I18N.Use_single_quotes_to_enter_literal_strings()
275
                    );
276
                }
277
                value = this.getSymbolTable().value(name);
278
                break;
279

    
280
            case Code.METHOD: {
281
                    Method method = (Method) code;
282
                    Codes args = method.parameters();
283
                    int argc = (args == null) ? 0 : args.size();
284
                    Object[] argvalues = new Object[argc];
285
                    if( args != null ) {
286
                        int i = 0;
287
                        for( Code arg : args ) {
288
                            argvalues[i++] = runCode(arg);
289
                        }
290
                    }
291
                    value = method.call(this, argvalues);
292
                    break;
293
                }
294
            case Code.CALLER:
295
                Caller caller = (Caller) code;
296
                Function function = caller.function();
297
                if( function == null ) {
298
                    function = this.getSymbolTable().function(caller.name());
299
                    if( function == null ) {
300
                        throw new ExpressionRuntimeException(code, I18N.Undefined_function_XIdentifierX(caller.name()));
301
                    }
302
                    caller.function(function);
303
                }
304
//                if( !function.isSQLCompatible() && this.sqlCompatible ) {
305
//                    throw new ExpressionRuntimeException(code, I18N.Cant_use_non_SQL_compatible_functions(function.name()));
306
//                }
307
                Codes args = caller.parameters();
308
                try {
309
                    switch( caller.type() ) {
310
                    case Caller.UNARY_OPERATOR:
311
                        if( args == null || args.size() != 1 ) {
312
                            throw new ExpressionRuntimeException(code, I18N.Number_of_argument_mistmatch_in_operator_XIdentifierX_expected_1_got_XargcX(function.name(),args==null?0:args.size()));
313
                        }
314
                        value = ((UnaryOperator) function).call(this, runCode(args.get(0)));
315
                        break;
316

    
317
                    case Caller.BINARY_OPERATOR:
318
                        if( args == null || args.size() != 2 ) {
319
                            throw new ExpressionRuntimeException(code, I18N.Number_of_argument_mistmatch_in_operator_XIdentifierX_expected_2_got_XargcX(function.name(),args==null?0:args.size()));
320
                        }
321
                        value = ((BinaryOperator) function).call(this, runCode(args.get(0)), runCode(args.get(1)));
322
                        break;
323

    
324
                    case Caller.FUNCTION:
325
                        int argc = (args == null) ? 0 : args.size();
326
                        if( !function.argc().contains(argc) ) {
327
                            throw new ExpressionRuntimeException(code, I18N.Number_of_argument_mistmatch_in_function_XIdentifierX_expected_XexpectedX_got_XfoundX(function.name(),function.argc(),argc));
328
                        }
329
                        if( function.useArgumentsInsteadObjects() ) {
330
                            value = function.call(this, args);
331
                        } else {
332
                            Object[] argvalues = new Object[argc];
333
                            if( args != null ) {
334
                                int i = 0;
335
                                for( Code arg : args ) {
336
                                    argvalues[i++] = runCode(arg);
337
                                }
338
                            }
339
                            value = function.call(this, argvalues);
340
                        }
341
                    }
342
                } catch (RuntimeException ex) {
343
                    throw ex;
344
                } catch (Exception ex) {
345
                    String argsstr = "???";
346
                    try {
347
                        argsstr = Objects.toString(args);
348
                    } catch(Exception ex2) {
349
                        // Ignore.
350
                    }
351
                    throw new ExpressionRuntimeException(code, I18N.Problems_calling_function_XIdentifierX_with_args_XargsX(function.name(),argsstr), ex);
352
                }
353
                break;
354

    
355
            }
356
        } finally {
357
            this.currentCode = null;
358
            if( recursionControl!=null ) {
359
                recursionControl.exitCode();
360
            }
361
        }
362
        return value;
363
    }
364

    
365
    @Override
366
    public Code getCurrentCode() {
367
        return this.currentCode;
368
    }
369
}