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 / function / programming / CreateFnFunction.java @ 46010

History | View | Annotate | Download (12.2 KB)

1 44138 jjdelcerro
package org.gvsig.expressionevaluator.impl.function.programming;
2 43512 jjdelcerro
3 44533 jjdelcerro
import java.lang.reflect.Method;
4 44389 jjdelcerro
import java.util.ArrayList;
5
import java.util.Arrays;
6 44138 jjdelcerro
import java.util.List;
7 45528 jjdelcerro
import java.util.Map;
8 43512 jjdelcerro
import org.apache.commons.lang3.Range;
9 44533 jjdelcerro
import org.apache.commons.lang3.StringUtils;
10 44138 jjdelcerro
import org.gvsig.expressionevaluator.Code;
11
import org.gvsig.expressionevaluator.Codes;
12 43939 jjdelcerro
import org.gvsig.expressionevaluator.Function;
13 43521 jjdelcerro
import org.gvsig.expressionevaluator.Interpreter;
14 43512 jjdelcerro
import org.gvsig.expressionevaluator.spi.AbstractFunction;
15 44138 jjdelcerro
import org.gvsig.expressionevaluator.ExpressionEvaluatorLocator;
16
import org.gvsig.expressionevaluator.ExpressionEvaluatorManager;
17
import org.gvsig.expressionevaluator.ExpressionRuntimeException;
18
import org.gvsig.expressionevaluator.MutableSymbolTable;
19
import org.gvsig.expressionevaluator.SymbolTable;
20 44533 jjdelcerro
import org.gvsig.tools.ToolsLocator;
21
import org.gvsig.tools.resourcesstorage.ResourcesStorage;
22
import org.gvsig.tools.script.Script;
23
import org.gvsig.tools.script.ScriptManager;
24 45528 jjdelcerro
import org.gvsig.tools.util.MapBuilder;
25 43512 jjdelcerro
26 44750 jjdelcerro
@SuppressWarnings("UseSpecificCatch")
27 44138 jjdelcerro
public class CreateFnFunction extends AbstractFunction {
28
29
    public static final String NAME = "CREATE_FUNCTION";
30 43512 jjdelcerro
31 44138 jjdelcerro
    public CreateFnFunction() {
32
        super(Function.GROUP_PROGRAMMING,
33
                NAME,
34 44533 jjdelcerro
                Range.between(2, 6),
35 44742 jjdelcerro
                "This function allows you to define functions within the expression evaluator.\n" +
36
                  "We can define three types of functions:\n" +
37
                  "\n<ul>" +
38
                  "<li>Functions defined in the evaluator itself.</li>\n" +
39
                  "<li>Functions implemented as a static method of a java class.</li>\n" +
40
                  "<li>Functions defined in an external script.</li>\n" +
41
                  "\n</ul>" +
42
                  "<b> Functions defined in the evaluator itself. </b>\n" +
43
                  "\n" +
44
                  "They have the form:\n" +
45
                  "\n" +
46
                  "<pre>\n" +
47
                  "CREATE PROCEDURE myfun param1 AS\n" +
48
                  "BEGIN\n" +
49
                  "  RETURN 'Hello' || param1;\n" +
50
                  "END PROCEDURE\n" +
51
                  "</pre>\n" +
52
                  "\n" +
53
                  "<b> Functions implemented as a static method </b>\n" +
54
                  "\n" +
55
                  "They allow defining a function that will invoke a static method of a java class.\n" +
56
                  "\n" +
57
                  "They have the form:\n" +
58
                  "\n" +
59
                  "<pre>\n" +
60 44750 jjdelcerro
                  "CREATE FUNCTION parseInt(value) AS 'java.lang.Integer', 'parseInt' LANGUAGE 'java'\n" +
61 44742 jjdelcerro
                  "</pre>\n" +
62
                  "\n" +
63 44750 jjdelcerro
                  "Defines the \"parseInt\" function that receives a single parameter, and links it to the method\n" +
64
                  "static 'parseInt' of the class 'java.lang.Integer',\n" +
65 44742 jjdelcerro
                  "\n" +
66
                  "<b> Functions defined in an external script. </b>\n" +
67
                  "\n" +
68
                  "It allows defining functions that are implemented in an external script module.\n" +
69
                  "\n" +
70
                  "They have the form:\n" +
71
                  "\n" +
72
                  "<pre>\n" +
73 44750 jjdelcerro
                  "CREATE FUNCTION getCRS(crs) AS 'crs.py', 'getCRS' LANGUAGE 'script'\n" +
74 44742 jjdelcerro
                  "</pre>\n" +
75
                  "\n" +
76
                  "What defines a \"getCRS\" function that is implemented in the python module\n" +
77
                  "located in \"Users / crs\" with the name 'getCRS'.",
78 44924 jjdelcerro
                "CREATE FUNCTION {{name}}(param1, param2) AS\nBEGIN\n  PASS\nEND FUNCTION\n",
79 44098 jjdelcerro
                null,
80
                "Object",
81
                false
82 43939 jjdelcerro
        );
83 43512 jjdelcerro
    }
84
85
    @Override
86 44738 jjdelcerro
    public boolean isHidden() {
87 44750 jjdelcerro
      return false;
88 44738 jjdelcerro
    }
89
90
    @Override
91 43939 jjdelcerro
    public boolean useArgumentsInsteadObjects() {
92
        return true;
93
    }
94
95
    @Override
96 44138 jjdelcerro
    public boolean allowConstantFolding() {
97
        return false;
98
    }
99 44738 jjdelcerro
100
    @Override
101 43521 jjdelcerro
    public Object call(Interpreter interpreter, Object[] args) throws Exception {
102 43939 jjdelcerro
        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
103
    }
104
105 44750 jjdelcerro
    private static final int FUNCTION_NAME = 0;
106
    private static final int PARAMETERS = 1;
107
    private static final int BODY = 2;
108
    private static final int SCRIPT_PATH = 3;
109
    private static final int SCRIPT_FUNCTION = 4;
110
    private static final int LANGUAGE = 5;
111
112
    private static final int TYPE_USER_FUNCTION = 0;
113
    private static final int TYPE_JAVA_FUNCTION = 1;
114
    private static final int TYPE_SCRIPT_FUNCTION = 2;
115
116 43939 jjdelcerro
    @Override
117 44138 jjdelcerro
    public Object call(Interpreter interpreter, Codes args) throws Exception {
118 45025 jjdelcerro
        if( args.size()==2 ) {
119
            List<String> argNames = (List<String>) getObject(interpreter, args, 0);
120
            Code body = args.get(1);
121 45703 jjdelcerro
            Function fn = new UserFunction(GROUP_OTHER,"", argNames, body);
122 45025 jjdelcerro
123
            return fn;
124
        }
125 44138 jjdelcerro
        if( !(interpreter.getSymbolTable() instanceof MutableSymbolTable) ) {
126
            throw new ExpressionRuntimeException("The use of user functions require a mutable symbol table.");
127 43512 jjdelcerro
        }
128 44138 jjdelcerro
        MutableSymbolTable symbolTable = (MutableSymbolTable) interpreter.getSymbolTable();
129
130 44750 jjdelcerro
        String name = (String) getObject(interpreter, args, FUNCTION_NAME);
131
        List<String> argNames = (List<String>) getObject(interpreter, args, PARAMETERS);
132
        Code body = args.get(BODY);
133
        String script_path = (String) getObject(interpreter, args, SCRIPT_PATH);
134
        String script_function = (String) getObject(interpreter, args, SCRIPT_FUNCTION);
135
        String language = (String) getObject(interpreter, args, LANGUAGE);
136
137 44533 jjdelcerro
        if( StringUtils.isBlank(language) ) {
138
            language = "script";
139
        }
140 44750 jjdelcerro
141
        int type = TYPE_USER_FUNCTION;
142
        if( !StringUtils.isBlank(script_path) || !StringUtils.isBlank(script_function))  {
143
          switch(language.toLowerCase()) {
144
              case "script":
145
                type = TYPE_SCRIPT_FUNCTION;
146
                break;
147
              case "java":
148
                type = TYPE_JAVA_FUNCTION;
149
                break;
150
              default:
151
                throw new ExpressionRuntimeException("Unsupported language '"+language+".");
152
          }
153 44533 jjdelcerro
        }
154 44750 jjdelcerro
        Function fn = null;
155
        switch(type) {
156
          case TYPE_USER_FUNCTION:
157 45703 jjdelcerro
            fn = new UserFunction(GROUP_OTHER, name, argNames, body);
158 44750 jjdelcerro
            symbolTable.addFunction(fn);
159
            break;
160
          case TYPE_JAVA_FUNCTION:
161
            if( StringUtils.isBlank(script_path) || StringUtils.isBlank(script_function))  {
162
                throw new ExpressionRuntimeException("Requiered classname and methodname.");
163
            }
164
            fn = new JavaFunction(name, script_path, script_function);
165
            symbolTable.addFunction(fn);
166
            break;
167
          case TYPE_SCRIPT_FUNCTION:
168
            if( StringUtils.isBlank(script_path) || StringUtils.isBlank(script_function))  {
169
                throw new ExpressionRuntimeException("Requiered module and function name.");
170
            }
171
            fn = new ExternalFunction(name, script_path, script_function);
172
            symbolTable.addFunction(fn);
173
            break;
174
        }
175
        return fn;
176 43512 jjdelcerro
    }
177
178 45703 jjdelcerro
    public static class UserFunction extends AbstractFunction {
179 44138 jjdelcerro
180 45703 jjdelcerro
        protected final Code body;
181
        protected final List<String> argNames;
182 44138 jjdelcerro
183 45703 jjdelcerro
        public UserFunction(String group, String name, List<String> argNames, Code body) {
184
            super(group, name, Range.between(0, Integer.MAX_VALUE));
185 44138 jjdelcerro
            this.argNames = argNames;
186
            this.body = body;
187
        }
188 44738 jjdelcerro
189 44138 jjdelcerro
        @Override
190
        public Object call(Interpreter interpreter, Object[] args) throws Exception {
191
            Object value;
192
            ExpressionEvaluatorManager manager = ExpressionEvaluatorLocator.getManager();
193
            MutableSymbolTable localSymbolTable = manager.createSymbolTable();
194 44389 jjdelcerro
195
            List $args = new ArrayList();
196
            if( args != null ) {
197
                $args.addAll(Arrays.asList(args));
198
            }
199
            localSymbolTable.setVar("$ARGS", $args);
200
201 44138 jjdelcerro
            int max;
202
            if( this.argNames==null ) {
203
                max = 0;
204
            } else {
205
                max = Math.min(this.argNames.size(), args.length);
206
            }
207
            for (int i = 0; i < max; i++) {
208
                localSymbolTable.setVar(this.argNames.get(i), args[i]);
209
            }
210
            SymbolTable savedSymbolTable = interpreter.getSymbolTable();
211
            localSymbolTable.addSymbolTable(savedSymbolTable);
212
            try {
213
                interpreter.setSymbolTable(localSymbolTable);
214
                value = interpreter.run(this.body);
215
            } finally {
216
                interpreter.setSymbolTable(savedSymbolTable);
217
            }
218
            return value;
219
        }
220
221
    }
222
223 44533 jjdelcerro
    private static class ExternalFunction extends AbstractFunction {
224
225
        private final String script_path;
226
        private final String script_function;
227 44592 jjdelcerro
        private final Script script;
228 44533 jjdelcerro
229
        public ExternalFunction(String name, String script_path, String script_function) {
230
            super(GROUP_OTHER, name, Range.between(0, Integer.MAX_VALUE));
231
            this.script_path = script_path;
232
            this.script_function = script_function;
233
            ScriptManager scriptManager = ToolsLocator.getScriptManager();
234
            ExpressionEvaluatorManager expressionManager = ExpressionEvaluatorLocator.getExpressionEvaluatorManager();
235
            ResourcesStorage resourcesStorage = expressionManager.getScriptsResourcesStorage();
236 44592 jjdelcerro
            this.script = scriptManager.loadScript(resourcesStorage, script_path);
237
            if( this.script == null ) {
238 44533 jjdelcerro
                throw new ExpressionRuntimeException("Can't locate '"+this.script_path+"'.");
239
            }
240 44592 jjdelcerro
        }
241
242
        @Override
243
        public Object call(Interpreter interpreter, Object[] args) throws Exception {
244
            Object r = this.script.invokeFunction(this.script_function, args);
245 44533 jjdelcerro
            return r;
246
        }
247
248
    }
249
250
    private static class JavaFunction extends AbstractFunction {
251
252
        private final String fullClassName;
253
        private final String methodName;
254
255
        public JavaFunction(String name, String fullClassName, String methodName) {
256
            super(GROUP_OTHER, name, Range.between(0, Integer.MAX_VALUE));
257
            this.fullClassName = fullClassName;
258
            this.methodName = methodName;
259
        }
260
261
        @Override
262
        public Object call(Interpreter interpreter, Object[] args) throws Exception {
263 44750 jjdelcerro
            List<ClassLoader> loaders = ExpressionEvaluatorLocator.getManager().getClassLoaders();
264
            Class<?> theClass = null;
265
            for (ClassLoader loader : loaders) {
266
              try {
267
                theClass = loader.loadClass(this.fullClassName);
268
                if( theClass!=null ) {
269
                  break;
270
                }
271
              } catch(Throwable th) {
272
                // Do nothing skip
273
              }
274
            }
275
            if( theClass == null ) {
276
              throw new ExpressionRuntimeException("Can't localte the class '"+this.fullClassName+"'.");
277
            }
278 44533 jjdelcerro
            Class[] parameterTypes = new Class[args.length];
279
            for (int i = 0; i < args.length; i++) {
280
                if( args[i]==null ) {
281
                    parameterTypes[i] = null;
282
                } else {
283 45528 jjdelcerro
                    Class paramClass = args[i].getClass();
284
                    parameterTypes[i] = typeMapping.getOrDefault(paramClass, paramClass);
285 44533 jjdelcerro
                }
286
            }
287
            Method method = theClass.getMethod(this.methodName, parameterTypes);
288
            Object value = method.invoke(null, args);
289
            return value;
290
        }
291
292 45528 jjdelcerro
        private static Map<Class,Class> typeMapping = new MapBuilder()
293
                .add(Character.class, char.class)
294
                .add(Byte.class, byte.class)
295
                .add(Short.class, short.class)
296
                .add(Integer.class, int.class)
297
                .add(Long.class, long.class)
298
                .add(Float.class, float.class)
299
                .add(Double.class, double.class)
300
                .add(Boolean.class, boolean.class)
301
                .build();
302 44533 jjdelcerro
    }
303
304 43512 jjdelcerro
}