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

History | View | Annotate | Download (10.4 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 org.apache.commons.lang3.tuple.Pair;
10
import org.gvsig.expressionevaluator.SymbolTable;
11
import org.gvsig.expressionevaluator.Interpreter;
12
import org.gvsig.expressionevaluator.Code;
13
import org.gvsig.expressionevaluator.Code.Constant;
14
import org.gvsig.expressionevaluator.Code.Identifier;
15
import org.gvsig.expressionevaluator.Code.Caller;
16
import org.gvsig.expressionevaluator.Code.Method;
17
import org.gvsig.expressionevaluator.Codes;
18
import org.gvsig.expressionevaluator.ExpressionRuntimeException;
19
import org.gvsig.expressionevaluator.Function;
20
import org.gvsig.expressionevaluator.impl.DefaultCodeBuilder.RecursionControlSupport;
21
import org.gvsig.expressionevaluator.impl.function.operator.BinaryOperator;
22
import org.gvsig.expressionevaluator.impl.function.programming.CallMethodFunction;
23
import org.gvsig.expressionevaluator.impl.function.operator.UnaryOperator;
24
import org.gvsig.expressionevaluator.impl.function.programming.ReturnFunction.ReturnException;
25

    
26
public class DefaultInterpreter implements Interpreter {
27

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

    
47
        @Override
48
        public void put(Object context, Object key, Object value) {
49
            if( this.data.size()>this.getMaxElements() ) {
50
                this.reduce();
51
            }
52
            this.data.put(Pair.of(context, key), Pair.of(value, new Date().getTime()));
53
        }
54

    
55
        @Override
56
        public void remove(Object context, Object key) {
57
            this.data.remove(Pair.of(context, key));
58
        }
59

    
60
        @Override
61
        public void removeAll() {
62
            this.data = new  HashMap<>();
63
        }
64

    
65
        private int getMaxElements() {
66
            return 100;
67
        }
68

    
69
        private void reduce() {
70
            List<Map.Entry<Pair<Object, Object>, Pair<Object, Long>>> entries = 
71
                new ArrayList<>(this.data.entrySet());
72
            entries.sort(new Comparator<Map.Entry<Pair<Object, Object>, Pair<Object, Long>>>() {
73
                @Override
74
                public int compare(Map.Entry<Pair<Object, Object>, Pair<Object, Long>> o1, Map.Entry<Pair<Object, Object>, Pair<Object, Long>> o2) {
75
                    return o1.getValue().getRight().compareTo(o2.getValue().getRight());
76
                }
77
            });
78
            for( int i=0; i<this.getMaxElements()/2; i++ ) {
79
                this.data.remove(entries.get(i).getKey());
80
            }
81
        }
82
        
83
    }
84
    
85
    private SymbolTable symbolTable = null;
86
    private Double accuracy;
87
    private Code currentCode;
88
    private Cache cache;
89
    private int maxRecursionLimit;
90

    
91
    public DefaultInterpreter() {
92
        this.cache = new DefaultCache();
93
        this.maxRecursionLimit = DEFAULT_MAX_RECURSION_LIMIT;
94
    }
95

    
96
    @Override
97
    public Interpreter clone() throws CloneNotSupportedException {
98
        DefaultInterpreter other = (DefaultInterpreter) super.clone();
99
        other.cache = new DefaultCache();
100
        if( this.symbolTable!=null ) {
101
            other.symbolTable = this.symbolTable.clone();
102
        }
103
        return other;
104
    }
105
    
106
    public int getMaxRecursionLimit() {
107
        return this.maxRecursionLimit;
108
    }
109
            
110
    public void setMaxRecursionLimit(int limit) {
111
        this.maxRecursionLimit = limit;
112
    }
113
    
114
    @Override
115
    public Cache getCache() {
116
        return this.cache;
117
    }
118

    
119
    @Override
120
    public void setSymbolTable(SymbolTable symbolTable) {
121
        this.symbolTable = symbolTable;
122
    }
123

    
124
    @Override
125
    public SymbolTable getSymbolTable() {
126
        if( this.symbolTable==null ) {
127
            this.symbolTable = new DefaultSymbolTable();
128
        }
129
        return this.symbolTable;
130
    }
131
    @Override
132
    public Double getAccuracy() {
133
        return this.accuracy;
134
    }
135
    
136
    @Override
137
    public void setAccuracy(Double accuracy) {
138
        this.accuracy = accuracy;
139
    }
140

    
141
    @Override
142
    public Object run(Code code) {
143
        try {
144
            return this.runCode(code);
145
        } catch(ReturnException ex) {
146
            return ex.getValue();
147
        } catch(RuntimeException ex) {
148
            throw ex;
149
        } catch(Exception ex) {
150
            throw new ExpressionRuntimeException(code, "", ex);
151
        }
152
    }
153

    
154
    @Override
155
    public void link(Code code) {
156
        linkCode(code);
157
    }
158

    
159
    private void linkCode(Code code) {
160
        if( code.code() == Code.CALLER ) {
161
            Caller caller = (Caller) code;
162
            if( caller.function() == null ) {
163
                caller.function(this.getSymbolTable().function(caller.name()));
164
            }
165
            if( caller.args() != null ) {
166
                for( Code arg : caller.args() ) {
167
                    linkCode(arg);
168
                }
169
            }
170
        }
171
    }
172

    
173
    public Object runCode(Code code) throws Exception {
174
        RecursionControlSupport recursionControl = null;
175
        if( code instanceof RecursionControlSupport ) {
176
            recursionControl = (RecursionControlSupport) code;
177
            if( !recursionControl.enterCode(this.maxRecursionLimit) ) {
178
                recursionControl.resetRecursionState();
179
                throw new ExpressionRuntimeException(code, I18N.Maximum_recursion_limit_exceeded());
180
            }
181
        }
182
        Object value = null;
183
        this.currentCode = code;
184
        try {
185
            switch( code.code() ) {
186
            case Code.CONSTANT:
187
                value = ((Constant) code).value();
188
                break;
189

    
190
            case Code.IDENTIFIER:
191
                String name = ((Identifier) code).name();
192
                if( !this.getSymbolTable().exists(name) ) {
193
                    throw new ExpressionRuntimeException(
194
                            code, 
195
                            I18N.Undefined_variable_XIdentifierX(name),
196
                            I18N.Use_single_quotes_to_enter_literal_strings()
197
                    );
198
                }
199
                value = this.getSymbolTable().value(name);
200
                break;
201

    
202
            case Code.METHOD: {
203
                    Method method = (Method) code;
204
                    Function function = method.function();
205
                    if( function == null ) {
206
                        Object obj = this.runCode(method.obj());
207
                        if( obj == null ) {
208
                            throw new NullPointerException("An object pointer was expected to invoke method "+method.methodname()+" and a null was received");
209
                        }
210
                        method.function(new CallMethodFunction(obj, method.methodname()));
211
                    }
212
                }
213
            case Code.CALLER:
214
                Caller caller = (Caller) code;
215
                Function function = caller.function();
216
                if( function == null ) {
217
                    function = this.getSymbolTable().function(caller.name());
218
                    if( function == null ) {
219
                        throw new ExpressionRuntimeException(code, I18N.Undefined_function_XIdentifierX(caller.name()));
220
                    }
221
                    caller.function(function);
222
                }
223
                Codes args = caller.args();
224
                try {
225
                    switch( caller.type() ) {
226
                    case Caller.UNARY_OPERATOR:
227
                        if( args == null || args.size() != 1 ) {
228
                            throw new ExpressionRuntimeException(code, I18N.Number_of_argument_mistmatch_in_operator_XIdentifierX_expected_1_got_XargcX(function.name(),args==null?0:args.size()));
229
                        }
230
                        value = ((UnaryOperator) function).call(this, runCode(args.get(0)));
231
                        break;
232

    
233
                    case Caller.BINARY_OPERATOR:
234
                        if( args == null || args.size() != 2 ) {
235
                            throw new ExpressionRuntimeException(code, I18N.Number_of_argument_mistmatch_in_operator_XIdentifierX_expected_2_got_XargcX(function.name(),args==null?0:args.size()));
236
                        }
237
                        value = ((BinaryOperator) function).call(this, runCode(args.get(0)), runCode(args.get(1)));
238
                        break;
239

    
240
                    case Caller.FUNCTION:
241
                        int argc = (args == null) ? 0 : args.size();
242
                        if( !function.argc().contains(argc) ) {
243
                            throw new ExpressionRuntimeException(code, I18N.Number_of_argument_mistmatch_in_function_XIdentifierX_expected_XexpectedX_got_XfoundX(function.name(),function.argc(),argc));
244
                        }
245
                        if( function.useArgumentsInsteadObjects() ) {
246
                            value = function.call(this, args);
247
                        } else {
248
                            Object[] argvalues = new Object[argc];
249
                            if( args != null ) {
250
                                int i = 0;
251
                                for( Code arg : args ) {
252
                                    argvalues[i++] = runCode(arg);
253
                                }
254
                            }
255
                            value = function.call(this, argvalues);
256
                        }
257
                    }
258
                } catch (RuntimeException ex) {
259
                    throw ex;
260
                } catch (Exception ex) {
261
                    String argsstr = "???";
262
                    try {
263
                        argsstr = args.toString();
264
                    } catch(Exception ex2) {
265
                        // Ignore.
266
                    }
267
                    throw new ExpressionRuntimeException(code, I18N.Problems_calling_function_XIdentifierX_with_args_XargsX(function.name(),argsstr));
268
                }
269
                break;
270

    
271
            }
272
        } finally {
273
            this.currentCode = null;
274
            if( recursionControl!=null ) {
275
                recursionControl.exitCode();
276
            }
277
        }
278
        return value;
279
    }
280

    
281
    @Override
282
    public Code getCurrentCode() {
283
        return this.currentCode;
284
    }
285
}