KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jruby > runtime > callback > ReflectionCallback


1 /***** BEGIN LICENSE BLOCK *****
2  * Version: CPL 1.0/GPL 2.0/LGPL 2.1
3  *
4  * The contents of this file are subject to the Common Public
5  * License Version 1.0 (the "License"); you may not use this file
6  * except in compliance with the License. You may obtain a copy of
7  * the License at http://www.eclipse.org/legal/cpl-v10.html
8  *
9  * Software distributed under the License is distributed on an "AS
10  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
11  * implied. See the License for the specific language governing
12  * rights and limitations under the License.
13  *
14  * Copyright (C) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
15  * Copyright (C) 2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
16  * Copyright (C) 2004-2005 Thomas E Enebo <enebo@acm.org>
17  * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
18  * Copyright (C) 2005 Charles O Nutter <headius@headius.com>
19  *
20  * Alternatively, the contents of this file may be used under the terms of
21  * either of the GNU General Public License Version 2 or later (the "GPL"),
22  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
23  * in which case the provisions of the GPL or the LGPL are applicable instead
24  * of those above. If you wish to allow use of your version of this file only
25  * under the terms of either the GPL or the LGPL, and not to allow others to
26  * use your version of this file under the terms of the CPL, indicate your
27  * decision by deleting the provisions above and replace them with the notice
28  * and other provisions required by the GPL or the LGPL. If you do not delete
29  * the provisions above, a recipient may use your version of this file under
30  * the terms of any one of the CPL, the GPL or the LGPL.
31  ***** END LICENSE BLOCK *****/

32 package org.jruby.runtime.callback;
33
34 import java.lang.reflect.InvocationTargetException JavaDoc;
35 import java.lang.reflect.Method JavaDoc;
36
37 import org.jruby.exceptions.JumpException;
38 import org.jruby.exceptions.RaiseException;
39 import org.jruby.exceptions.MainExitException;
40 import org.jruby.exceptions.ThreadKill;
41 import org.jruby.runtime.Arity;
42 import org.jruby.runtime.Block;
43 import org.jruby.runtime.builtin.IRubyObject;
44
45 /**
46  * A wrapper for <code>java.lang.reflect.Method</code> objects which implement Ruby methods.
47  */

48 public class ReflectionCallback implements Callback {
49     private Method JavaDoc method;
50     private Class JavaDoc type;
51     private String JavaDoc methodName;
52     private Class JavaDoc[] argumentTypes;
53     private boolean isRestArgs;
54     private Arity arity;
55     private boolean isStaticMethod;
56     private boolean fast;
57     
58     public ReflectionCallback(Class JavaDoc type, String JavaDoc methodName, Class JavaDoc[] argumentTypes,
59             boolean isRestArgs, boolean isStaticMethod, Arity arity, boolean fast) {
60         this.type = type;
61         this.methodName = methodName;
62         this.argumentTypes = argumentTypes;
63         this.isRestArgs = isRestArgs;
64         this.isStaticMethod = isStaticMethod;
65         this.arity = arity;
66         this.fast = fast;
67         
68         assert type != null;
69         assert methodName != null;
70         assert arity != null;
71         
72         loadMethod(fast);
73     }
74     
75     private void loadMethod(boolean fast) {
76         Class JavaDoc[] args;
77         
78         if (isStaticMethod) {
79             Class JavaDoc[] types = new Class JavaDoc[argumentTypes.length + 1];
80             System.arraycopy(argumentTypes, 0, types, 1, argumentTypes.length);
81             types[0] = IRubyObject.class;
82             args = types;
83         } else {
84             args = argumentTypes;
85         }
86         
87         // ENEBO: Perhaps slow but simple for now
88
if (!fast) {
89             Class JavaDoc[] types = new Class JavaDoc[args.length + 1];
90             System.arraycopy(args, 0, types, 0, args.length);
91             types[args.length] = Block.class;
92             args = types;
93         }
94         
95         try {
96             method = type.getMethod(methodName, args);
97         } catch (NoSuchMethodException JavaDoc e) {
98             throw new RuntimeException JavaDoc("NoSuchMethodException: Cannot get method \"" + methodName
99                     + "\" in class \"" + type.getName() + "\" by Reflection.");
100         } catch (SecurityException JavaDoc e) {
101             throw new RuntimeException JavaDoc("SecurityException: Cannot get method \"" + methodName
102                     + "\" in class \"" + type.getName() + "\" by Reflection.");
103         }
104     }
105     
106     /**
107      * Returns an object array that collects all rest arguments in its own object array which
108      * is then put into the last slot of the first object array. That is, assuming that this
109      * callback expects one required argument and any number of rest arguments, an input of
110      * <code>[1, 2, 3]</code> is transformed into <code>[1, [2, 3]]</code>.
111      */

112     protected final Object JavaDoc[] packageRestArgumentsForReflection(final Object JavaDoc[] originalArgs) {
113         IRubyObject[] restArray = new IRubyObject[originalArgs.length - (argumentTypes.length - 1)];
114         Object JavaDoc[] result = new Object JavaDoc[argumentTypes.length];
115         try {
116             System.arraycopy(originalArgs, argumentTypes.length - 1, restArray, 0, originalArgs.length - (argumentTypes.length - 1));
117         } catch (ArrayIndexOutOfBoundsException JavaDoc e) {
118             assert false : e;
119             return null;
120         }
121         System.arraycopy(originalArgs, 0, result, 0, argumentTypes.length - 1);
122         result[argumentTypes.length - 1] = restArray;
123         return result;
124     }
125
126     /**
127      * Invokes the Ruby method. Actually, this methods delegates to an internal version
128      * that may throw the usual Java reflection exceptions. Ruby exceptions are rethrown,
129      * other exceptions throw an AssertError and abort the execution of the Ruby program.
130      * They should never happen.
131      */

132     /**
133      * Calls a wrapped Ruby method for the specified receiver with the specified arguments.
134      */

135     public IRubyObject execute(IRubyObject recv, IRubyObject[] oargs, Block block) {
136         arity.checkArity(recv.getRuntime(), oargs);
137
138         Object JavaDoc[] methodArgs = oargs;
139         
140         if (isRestArgs) {
141             methodArgs = packageRestArgumentsForReflection(methodArgs);
142         }
143         try {
144             IRubyObject receiver = recv;
145             if (isStaticMethod) {
146                 Object JavaDoc[] args = new Object JavaDoc[methodArgs.length + (fast ? 1 : 2)];
147                 System.arraycopy(methodArgs, 0, args, 1, methodArgs.length);
148                 args[0] = recv;
149                 if (!fast) args[methodArgs.length + 1] = block;
150                 receiver = null;
151                 methodArgs = args;
152             } else {
153                 Object JavaDoc[] args = new Object JavaDoc[methodArgs.length + (fast ? 0 : 1)];
154                 System.arraycopy(methodArgs, 0, args, 0, methodArgs.length);
155                 if (!fast) args[methodArgs.length] = block;
156                 methodArgs = args;
157             }
158             return (IRubyObject) method.invoke(receiver, methodArgs);
159         } catch (InvocationTargetException JavaDoc e) {
160             if (e.getTargetException() instanceof RaiseException) {
161                 throw (RaiseException) e.getTargetException();
162             } else if (e.getTargetException() instanceof JumpException) {
163                 throw (JumpException) e.getTargetException();
164             } else if (e.getTargetException() instanceof ThreadKill) {
165                 // allow it to bubble up
166
throw (ThreadKill) e.getTargetException();
167             } else if (e.getTargetException() instanceof Exception JavaDoc) {
168                 if(e.getTargetException() instanceof MainExitException) {
169                     throw (RuntimeException JavaDoc)e.getTargetException();
170                 }
171                 recv.getRuntime().getJavaSupport().handleNativeException(e.getTargetException());
172                 return recv.getRuntime().getNil();
173             } else {
174                 throw (Error JavaDoc) e.getTargetException();
175             }
176         } catch (IllegalAccessException JavaDoc e) {
177             StringBuffer JavaDoc message = new StringBuffer JavaDoc();
178             message.append(e.getMessage());
179             message.append(':');
180             message.append(" methodName=").append(methodName);
181             message.append(" recv=").append(recv.toString());
182             message.append(" type=").append(type.getName());
183             message.append(" methodArgs=[");
184             for (int i = 0; i < methodArgs.length; i++) {
185                 message.append(methodArgs[i]);
186                 message.append(' ');
187             }
188             message.append(']');
189             assert false : message.toString();
190             return null;
191         } catch (final IllegalArgumentException JavaDoc e) {
192 /* StringBuffer message = new StringBuffer();
193             message.append(e.getMessage());
194             message.append(':');
195             message.append(" methodName=").append(methodName);
196             message.append(" recv=").append(recv.toString());
197             message.append(" type=").append(type.getName());
198             message.append(" methodArgs=[");
199             for (int i = 0; i < methodArgs.length; i++) {
200                 message.append(methodArgs[i]);
201                 message.append(' ');
202             }
203             message.append(']');*/

204             assert false : e;
205             return null;
206         }
207     }
208
209     /**
210      * Returns the arity of the wrapped Ruby method.
211      */

212     public Arity getArity() {
213         return arity;
214     }
215 }
216
Popular Tags