KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > caucho > quercus > env > JavaInvoker


1 /*
2  * Copyright (c) 1998-2006 Caucho Technology -- all rights reserved
3  *
4  * This file is part of Resin(R) Open Source
5  *
6  * Each copy or derived work must preserve the copyright notice and this
7  * notice unmodified.
8  *
9  * Resin Open Source is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * Resin Open Source is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
17  * of NON-INFRINGEMENT. See the GNU General Public License for more
18  * details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with Resin Open Source; if not, write to the
22  *
23  * Free Software Foundation, Inc.
24  * 59 Temple Place, Suite 330
25  * Boston, MA 02111-1307 USA
26  *
27  * @author Scott Ferguson
28  */

29
30 package com.caucho.quercus.env;
31
32 import com.caucho.quercus.QuercusException;
33 import com.caucho.quercus.annotation.*;
34 import com.caucho.quercus.expr.*;
35 import com.caucho.quercus.function.Marshal;
36 import com.caucho.quercus.function.MarshalFactory;
37 import com.caucho.quercus.module.ModuleContext;
38 import com.caucho.quercus.parser.QuercusParser;
39 import com.caucho.util.L10N;
40
41 import java.lang.annotation.Annotation JavaDoc;
42
43 /**
44  * Represents the introspected static function information.
45  */

46 abstract public class JavaInvoker
47   extends AbstractJavaMethod
48 {
49   private static final L10N L = new L10N(JavaInvoker.class);
50
51   private static final Object JavaDoc [] NULL_ARGS = new Object JavaDoc[0];
52   private static final Value [] NULL_VALUES = new Value[0];
53
54   private final ModuleContext _moduleContext;
55   private final String JavaDoc _name;
56   private final Class JavaDoc [] _param;
57   private final Class JavaDoc _retType;
58   private final Annotation JavaDoc [][] _paramAnn;
59   private final Annotation JavaDoc []_methodAnn;
60
61   private volatile boolean _isInit;
62   
63   private boolean _hasEnv;
64   private boolean _hasThis;
65   private Expr [] _defaultExprs;
66   private Marshal [] _marshalArgs;
67   private boolean _hasRestArgs;
68   private Marshal _unmarshalReturn;
69
70   private boolean _isRestReference;
71
72   private boolean _isCallUsesVariableArgs;
73   private boolean _isCallUsesSymbolTable;
74
75   /**
76    * Creates the statically introspected function.
77    */

78   public JavaInvoker(ModuleContext moduleContext,
79                      String JavaDoc name,
80                      Class JavaDoc []param,
81                      Annotation JavaDoc [][]paramAnn,
82                      Annotation JavaDoc []methodAnn,
83                      Class JavaDoc retType)
84   {
85     _moduleContext = moduleContext;
86     _name = name;
87     _param = param;
88     _paramAnn = paramAnn;
89     _methodAnn = methodAnn;
90     _retType = retType;
91     
92     // init();
93
}
94
95   public synchronized void init()
96   {
97     if (_isInit)
98       return;
99
100     MarshalFactory marshalFactory = _moduleContext.getMarshalFactory();
101     ExprFactory exprFactory = _moduleContext.getExprFactory();
102
103     try {
104       boolean callUsesVariableArgs = false;
105       boolean callUsesSymbolTable = false;
106       boolean returnNullAsFalse = false;
107
108       for (Annotation JavaDoc ann : _methodAnn) {
109     if (VariableArguments.class.isAssignableFrom(ann.annotationType())) {
110       callUsesVariableArgs = true;
111     }
112
113     if (UsesSymbolTable.class.isAssignableFrom(ann.annotationType())) {
114       callUsesSymbolTable = true;
115     }
116
117     if (ReturnNullAsFalse.class.isAssignableFrom(ann.annotationType())) {
118       returnNullAsFalse = true;
119     }
120       }
121
122       _isCallUsesVariableArgs = callUsesVariableArgs;
123       _isCallUsesSymbolTable = callUsesSymbolTable;
124
125       _hasEnv = _param.length > 0 && _param[0].equals(Env.class);
126       int envOffset = _hasEnv ? 1 : 0;
127
128       if (envOffset < _param.length)
129     _hasThis = hasThis(_param[envOffset], _paramAnn[envOffset]);
130       else
131     _hasThis = false;
132
133       if (_hasThis)
134     envOffset++;
135
136       boolean hasRestArgs = false;
137       boolean isRestReference = false;
138
139       if (_param.length > 0 &&
140       (_param[_param.length - 1].equals(Value[].class) ||
141        _param[_param.length - 1].equals(Object JavaDoc[].class))) {
142     hasRestArgs = true;
143
144     for (Annotation JavaDoc ann : _paramAnn[_param.length - 1]) {
145       if (Reference.class.isAssignableFrom(ann.annotationType()))
146         isRestReference = true;
147     }
148       }
149
150       _hasRestArgs = hasRestArgs;
151       _isRestReference = isRestReference;
152
153       int argLength = _param.length;
154
155       if (_hasRestArgs)
156     argLength -= 1;
157
158       _defaultExprs = new Expr[argLength - envOffset];
159
160       _marshalArgs = new Marshal[argLength - envOffset];
161
162       for (int i = 0; i < argLength - envOffset; i++) {
163     boolean isReference = false;
164     boolean isNotNull = false;
165
166     for (Annotation JavaDoc ann : _paramAnn[i + envOffset]) {
167       if (Optional.class.isAssignableFrom(ann.annotationType())) {
168         Optional opt = (Optional) ann;
169
170         if (! opt.value().equals("")) {
171           Expr expr = QuercusParser.parseDefault(opt.value());
172
173           _defaultExprs[i] = expr;
174         } else
175           _defaultExprs[i] = exprFactory.createDefault();
176       } else if (Reference.class.isAssignableFrom(ann.annotationType())) {
177         isReference = true;
178       } else if (NotNull.class.isAssignableFrom(ann.annotationType())) {
179         isNotNull = true;
180       }
181     }
182
183     Class JavaDoc argType = _param[i + envOffset];
184
185     if (isReference) {
186       _marshalArgs[i] = marshalFactory.createReference();
187
188       if (! Value.class.equals(argType)
189           && ! Var.class.equals(argType)) {
190         throw new QuercusException(L.l("reference must be Value or Var for {0}",
191                        _name));
192       }
193     }
194     else
195       _marshalArgs[i] = marshalFactory.create(argType, isNotNull);
196       }
197
198       _unmarshalReturn = marshalFactory.create(_retType,
199                            false,
200                            returnNullAsFalse);
201     } finally {
202       _isInit = true;
203     }
204   }
205
206   /**
207    * Returns true if the environment is an argument.
208    */

209   public boolean getHasEnv()
210   {
211     if (! _isInit)
212       init();
213     
214     return _hasEnv;
215   }
216
217   /**
218    * Returns true if the environment has rest-style arguments.
219    */

220   public boolean getHasRestArgs()
221   {
222     if (! _isInit)
223       init();
224     
225     return _hasRestArgs;
226   }
227
228   /**
229    * Returns true if the rest argument is a reference.
230    */

231   public boolean isRestReference()
232   {
233     if (! _isInit)
234       init();
235     
236     return _isRestReference;
237   }
238
239   /**
240    * Returns the unmarshaller for the return
241    */

242   public Marshal getUnmarshalReturn()
243   {
244     if (! _isInit)
245       init();
246     
247     return _unmarshalReturn;
248   }
249
250   /**
251    * Returns true if the call uses variable arguments.
252    */

253   @Override JavaDoc
254   public boolean isCallUsesVariableArgs()
255   {
256     if (! _isInit)
257       init();
258     
259     return _isCallUsesVariableArgs;
260   }
261
262   /**
263    * Returns true if the call uses the symbol table
264    */

265   @Override JavaDoc
266   public boolean isCallUsesSymbolTable()
267   {
268     if (! _isInit)
269       init();
270     
271     return _isCallUsesSymbolTable;
272   }
273
274   /**
275    * Returns true if the result is a boolean.
276    */

277   public boolean isBoolean()
278   {
279     if (! _isInit)
280       init();
281     
282     return _unmarshalReturn.isBoolean();
283   }
284
285   /**
286    * Returns true if the result is a string.
287    */

288   public boolean isString()
289   {
290     if (! _isInit)
291       init();
292     
293     return _unmarshalReturn.isString();
294   }
295
296   /**
297    * Returns true if the result is a long.
298    */

299   public boolean isLong()
300   {
301     if (! _isInit)
302       init();
303     
304     return _unmarshalReturn.isLong();
305   }
306
307   /**
308    * Returns true if the result is a double.
309    */

310   public boolean isDouble()
311   {
312     if (! _isInit)
313       init();
314     
315     return _unmarshalReturn.isDouble();
316   }
317
318   /**
319    * Returns the marshal arguments.
320    */

321   protected Marshal []getMarshalArgs()
322   {
323     if (! _isInit)
324       init();
325     
326     return _marshalArgs;
327   }
328
329   /**
330    * Returns the parameter annotations.
331    */

332   protected Annotation JavaDoc [][]getParamAnn()
333   {
334     if (! _isInit)
335       init();
336     
337     return _paramAnn;
338   }
339
340   /**
341    * Returns the default expressions.
342    */

343   protected Expr []getDefaultExprs()
344   {
345     if (! _isInit)
346       init();
347     
348     return _defaultExprs;
349   }
350
351   /**
352    * Evaluates a function's argument, handling ref vs non-ref
353    */

354   @Override JavaDoc
355   public Value []evalArguments(Env env, Expr fun, Expr []args)
356   {
357     if (! _isInit)
358       init();
359     
360     Value []values = new Value[args.length];
361
362     for (int i = 0; i < args.length; i++) {
363       Marshal arg = null;
364
365       if (i < _marshalArgs.length)
366         arg = _marshalArgs[i];
367       else if (_isRestReference) {
368         values[i] = args[i].evalRef(env);
369     continue;
370       }
371       else {
372         values[i] = args[i].eval(env);
373     continue;
374       }
375
376       if (arg == null)
377         values[i] = args[i].eval(env).copy();
378       else if (arg.isReference())
379         values[i] = args[i].evalRef(env);
380       else {
381         // php/0d04
382
values[i] = args[i].eval(env);
383       }
384     }
385
386     return values;
387   }
388
389   public Value call(Env env, Value []value)
390   {
391     return call(env, env.getThis(), value);
392   }
393
394   /**
395    * Evaluates the function, returning a copy. Java methods don't
396    * need the copy.
397    */

398   public Value callCopy(Env env, Value []args)
399   {
400     return call(env, args);
401   }
402
403   public Value call(Env env, Value obj, Value []value)
404   {
405     return call(env, obj.toJavaObject(), value);
406   }
407
408   public Value call(Env env, Object JavaDoc obj, Expr []exprs)
409   {
410     if (! _isInit)
411       init();
412     
413     int len = (_defaultExprs.length +
414            (_hasEnv ? 1 : 0) +
415            (_hasThis ? 1 : 0) +
416            (_hasRestArgs ? 1 : 0));
417
418     Object JavaDoc []values = new Object JavaDoc[len];
419
420     int k = 0;
421
422     if (_hasEnv)
423       values[k++] = env;
424     if (_hasThis) {
425       values[k++] = (ObjectValue) obj;
426       obj = null;
427     }
428
429     for (int i = 0; i < _marshalArgs.length; i++) {
430       Expr expr;
431
432       if (i < exprs.length && exprs[i] != null)
433         expr = exprs[i];
434       else {
435         expr = _defaultExprs[i];
436
437         if (expr == null)
438           expr = _moduleContext.getExprFactory().createRequired();
439       }
440
441       values[k] = _marshalArgs[i].marshal(env, expr, _param[k]);
442
443       k++;
444     }
445
446     if (_hasRestArgs) {
447       Value []rest;
448
449       int restLen = exprs.length - _marshalArgs.length;
450
451       if (restLen <= 0)
452         rest = NULL_VALUES;
453       else {
454         rest = new Value[restLen];
455
456         for (int i = _marshalArgs.length; i < exprs.length; i++) {
457           if (_isRestReference)
458             rest[i - _marshalArgs.length] = exprs[i].evalRef(env);
459           else
460             rest[i - _marshalArgs.length] = exprs[i].eval(env);
461         }
462       }
463
464       values[values.length - 1] = rest;
465     }
466
467     Object JavaDoc result = invoke(obj, values);
468     
469     return _unmarshalReturn.unmarshal(env, result);
470   }
471
472   public Value call(Env env, Object JavaDoc obj, Value []args)
473   {
474     if (! _isInit)
475       init();
476     
477     int len = _param.length;
478
479     Object JavaDoc []javaArgs = new Object JavaDoc[len];
480
481     int k = 0;
482
483     if (_hasEnv)
484       javaArgs[k++] = env;
485
486     if (_hasThis) {
487       javaArgs[k++] = (ObjectValue) obj;
488       obj = null;
489     }
490
491     for (int i = 0; i < _marshalArgs.length; i++) {
492       Value value;
493
494       if (i < args.length && args[i] != null)
495         javaArgs[k] = _marshalArgs[i].marshal(env, args[i], _param[k]);
496       else if (_defaultExprs[i] != null) {
497         javaArgs[k] = _marshalArgs[i].marshal(env,
498                                               _defaultExprs[i],
499                                               _param[k]);
500       } else {
501     env.warning(L.l("function '{0}' has {1} required arguments, but only {2} were provided",
502             _name, _marshalArgs.length, args.length));
503     
504         javaArgs[k] = _marshalArgs[i].marshal(env,
505                           NullValue.NULL,
506                           _param[k]);
507       }
508
509       k++;
510     }
511
512     if (_hasRestArgs) {
513       Value []rest;
514
515       int restLen = args.length - _marshalArgs.length;
516
517       if (restLen <= 0)
518         rest = NULL_VALUES;
519       else {
520         rest = new Value[restLen];
521
522         for (int i = _marshalArgs.length; i < args.length; i++) {
523           if (_isRestReference)
524             rest[i - _marshalArgs.length] = args[i];
525           else
526             rest[i - _marshalArgs.length] = args[i].toValue();
527         }
528       }
529
530       javaArgs[k++] = rest;
531     }
532
533     Object JavaDoc result = invoke(obj, javaArgs);
534
535     return _unmarshalReturn.unmarshal(env, result);
536   }
537
538   public Value call(Env env, Object JavaDoc obj)
539   {
540     return call(env, obj, new Value[0]);
541   }
542
543   public Value call(Env env, Object JavaDoc obj, Value a1)
544   {
545     return call(env, obj, new Value[] {a1});
546   }
547
548   public Value call(Env env, Object JavaDoc obj, Value a1, Value a2)
549   {
550     return call(env, obj, new Value[]{a1, a2});
551   }
552
553   public Value call(Env env, Object JavaDoc obj, Value a1, Value a2, Value a3)
554   {
555     return call(env, obj, new Value[]{a1, a2, a3});
556   }
557
558   public Value call(Env env, Object JavaDoc obj,
559                     Value a1, Value a2, Value a3, Value a4)
560   {
561     return call(env, obj, new Value[]{a1, a2, a3, a4});
562   }
563
564   public Value call(Env env, Object JavaDoc obj,
565                     Value a1, Value a2, Value a3, Value a4, Value a5)
566   {
567     return call(env, obj, new Value[]{a1, a2, a3, a4, a5});
568   }
569
570   abstract public Object JavaDoc invoke(Object JavaDoc obj, Object JavaDoc []args);
571
572   //
573
// Utility methods
574
//
575
private boolean hasThis(Class JavaDoc param, Annotation JavaDoc[]ann)
576   {
577     if (! param.isAssignableFrom(ObjectValue.class))
578       return false;
579
580     for (int i = 0; i < ann.length; i++) {
581       if (This.class.isAssignableFrom(ann[i].annotationType()))
582     return true;
583     }
584
585     return false;
586   }
587 }
588
Popular Tags