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 | } |