KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > reflect > plugins > javassist > JavassistReflectionFactory


1 /*
2 * JBoss, Home of Professional Open Source
3 * Copyright 2006, JBoss Inc., and individual contributors as indicated
4 * by the @authors tag. See the copyright.txt in the distribution for a
5 * full listing of individual contributors.
6 *
7 * This is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as
9 * published by the Free Software Foundation; either version 2.1 of
10 * the License, or (at your option) any later version.
11 *
12 * This software is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this software; if not, write to the Free
19 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
21 */

22 package org.jboss.reflect.plugins.javassist;
23
24 import java.security.AccessController JavaDoc;
25 import java.security.PrivilegedActionException JavaDoc;
26 import java.security.PrivilegedExceptionAction JavaDoc;
27
28 import javassist.ClassPool;
29 import javassist.CtClass;
30 import javassist.CtConstructor;
31 import javassist.CtField;
32 import javassist.CtMethod;
33 import javassist.CtNewMethod;
34 import javassist.Modifier;
35 import javassist.NotFoundException;
36
37 import org.jboss.util.JBossStringBuilder;
38 import org.jboss.util.UnreachableStatementException;
39
40 import EDU.oswego.cs.dl.util.concurrent.SynchronizedInt;
41
42 /**
43  * JavassistReflectionFactory.
44  *
45  * @todo proper classpool with pruning
46  * @todo non compiler based implementation
47  * @author <a HREF="adrian@jboss.com">Adrian Brock</a>
48  * @version $Revision: 45764 $
49  */

50 public class JavassistReflectionFactory
51 {
52    /** The method class counter */
53    private static final SynchronizedInt counter = new SynchronizedInt(0);
54    
55    /** Whether to check arguments */
56    private final boolean check;
57    
58    /**
59     * Create a new JavassistReflectionFactory.
60     *
61     * @param check whether to check arguments
62     */

63    public JavassistReflectionFactory(boolean check)
64    {
65       this.check = check;
66    }
67    
68    /**
69     * Create a javassist method
70     *
71     * @param ctMethod the method
72     * @return the method
73     * @throws Throwable for any error
74     */

75    public JavassistMethod createMethod(CtMethod ctMethod) throws Throwable JavaDoc
76    {
77       ClassPool pool = JavassistTypeInfoFactoryImpl.pool;
78       final CtClass result = pool.makeClass(JavassistMethod.class.getName() + counter.increment());
79       try
80       {
81          CtClass magic = pool.get("sun.reflect.MagicAccessorImpl");
82          result.setSuperclass(magic);
83       }
84       catch (NotFoundException ignored)
85       {
86       }
87       result.addInterface(pool.get(JavassistMethod.class.getName()));
88       
89       CtConstructor constructor = new CtConstructor(null, result);
90       constructor.setBody("super();");
91       result.addConstructor(constructor);
92       
93       CtClass object = pool.get(Object JavaDoc.class.getName());
94       
95       JBossStringBuilder buffer = new JBossStringBuilder();
96       
97       // Signature
98
buffer.append("public Object invoke(Object target, Object[] args) throws Throwable {");
99
100       boolean isInstanceMethod = Modifier.isStatic(ctMethod.getModifiers()) == false;
101
102       // Check for null target
103
if (check && isInstanceMethod)
104       {
105          buffer.append("if (target == null) throw new IllegalArgumentException(\"Null target for ");
106          buffer.append(ctMethod.getName()).append(ctMethod.getSignature()).append("\");");
107       }
108
109       // Check the target
110
CtClass declaring = ctMethod.getDeclaringClass();
111       boolean needsCast = isInstanceMethod && object.equals(declaring) == false;
112       if (check && needsCast)
113       {
114          buffer.append("if (target instanceof ").append(declaring.getName()).append(" == false) ");
115          buffer.append("throw new IllegalArgumentException(\"Target \" + target + \"");
116          buffer.append(" is not an instance of ").append(declaring.getName());
117          buffer.append(" for ").append(ctMethod.getName()).append(ctMethod.getSignature()).append("\");");
118       }
119
120       // Check the parameters
121
CtClass[] params = ctMethod.getParameterTypes();
122       if (check)
123       {
124          // Wrong number of args?
125
if (params.length != 0)
126          {
127             buffer.append("if (args == null || args.length != ").append(params.length).append(") ");
128             buffer.append("throw new IllegalArgumentException(\"Expected ").append(params.length).append(" parameter(s)");
129             buffer.append(" for ").append(ctMethod.getName()).append(ctMethod.getSignature()).append("\");");
130          }
131          else
132          {
133             // Didn't expect args
134
buffer.append("if (args != null && args.length != 0)");
135             buffer.append("throw new IllegalArgumentException(\"Expected no parameters");
136             buffer.append(" for ").append(ctMethod.getName()).append(ctMethod.getSignature()).append("\");");
137          }
138          for (int i = 0; i < params.length; ++i)
139          {
140             if (object.equals(params[i]) == false)
141             {
142                String JavaDoc paramType = getBoxedType(params[i]);
143                // Primitives can't be null
144
if (params[i].isPrimitive())
145                {
146                   buffer.append("if (args[").append(i).append("] == null) ");
147                   buffer.append("throw new IllegalArgumentException(\"Parameter ").append(i);
148                   buffer.append(" cannot be null for ").append(params[i].getName());
149                   buffer.append(" for ").append(ctMethod.getName()).append(ctMethod.getSignature()).append("\");");
150                }
151                // Check the parameter types
152
buffer.append("if (args[").append(i).append("] != null && ");
153                buffer.append("args[").append(i).append("] instanceof ").append(paramType).append(" == false) ");
154                buffer.append("throw new IllegalArgumentException(\"Parameter ").append(i).append(" \" + args[").append(i).append("] + \"");
155                buffer.append(" is not an instance of ").append(paramType);
156                buffer.append(" for ").append(ctMethod.getName()).append(ctMethod.getSignature()).append("\");");
157             }
158          }
159       }
160       
161       // Add a return and box the return type if necessary
162
CtClass returnType = ctMethod.getReturnType();
163       boolean isVoid = CtClass.voidType.equals(returnType);
164       boolean isPrimitive = returnType.isPrimitive();
165       if (isVoid == false)
166       {
167          buffer.append("return ");
168          if (isPrimitive)
169             buffer.append("new ").append(getBoxedType(returnType)).append('(');
170       }
171
172       // Instance method
173
if (isInstanceMethod)
174       {
175          buffer.append('(');
176          if (needsCast)
177             buffer.append("(").append(declaring.getName()).append(')');
178          
179          buffer.append("target).");
180       }
181       else
182       {
183          // Static method
184
buffer.append(declaring.getName()).append('.');
185       }
186
187       // Add the method name
188
buffer.append(ctMethod.getName()).append('(');
189
190       // Add the parameters
191
for (int i = 0; i < params.length; ++i)
192       {
193          buffer.append('(');
194          // Cast the parameters
195
if (object.equals(params[i]) == false)
196              buffer.append("(").append(getBoxedType(params[i])).append(')');
197          buffer.append("args[").append(i).append("])");
198          // Unbox primitive parameters
199
if (params[i].isPrimitive())
200             unbox(buffer, params[i]);
201          
202          if (i < params.length - 1)
203             buffer.append(", ");
204       }
205       buffer.append(')');
206
207       // Complete the boxing of the return value
208
if (isVoid == false && isPrimitive)
209          buffer.append(')');
210       
211       buffer.append(';');
212       
213       // Add a return null if there is no return value
214
if (isVoid)
215          buffer.append("return null;");
216       buffer.append('}');
217
218       // Compile it
219
String JavaDoc code = buffer.toString();
220       try
221       {
222          CtMethod invoke = CtNewMethod.make(code, result);
223          result.addMethod(invoke);
224       }
225       catch (Exception JavaDoc e)
226       {
227          throw new RuntimeException JavaDoc("Cannot compile " + code, e);
228       }
229       
230       // Create it
231
try
232       {
233          return AccessController.doPrivileged(new PrivilegedExceptionAction JavaDoc<JavassistMethod>()
234          {
235             public JavassistMethod run() throws Exception JavaDoc
236             {
237                Class JavaDoc clazz = result.toClass();
238                return (JavassistMethod) clazz.newInstance();
239             }
240          });
241       }
242       catch (PrivilegedActionException JavaDoc e)
243       {
244          throw e.getCause();
245       }
246    }
247    
248    /**
249     * Create a javassist constructor
250     *
251     * @param ctConstructor the constructor
252     * @return the constructor
253     * @throws Throwable for any error
254     */

255    public JavassistConstructor createConstructor(CtConstructor ctConstructor) throws Throwable JavaDoc
256    {
257       ClassPool pool = JavassistTypeInfoFactoryImpl.pool;
258       final CtClass result = pool.makeClass(JavassistConstructor.class.getName() + counter.increment());
259       try
260       {
261          CtClass magic = pool.get("sun.reflect.MagicAccessorImpl");
262          result.setSuperclass(magic);
263       }
264       catch (NotFoundException ignored)
265       {
266       }
267       result.addInterface(pool.get(JavassistConstructor.class.getName()));
268       
269       CtConstructor constructor = new CtConstructor(null, result);
270       constructor.setBody("super();");
271       result.addConstructor(constructor);
272       
273       CtClass object = pool.get(Object JavaDoc.class.getName());
274       
275       JBossStringBuilder buffer = new JBossStringBuilder();
276       
277       // Signature
278
buffer.append("public Object newInstance(Object[] args) throws Throwable {");
279
280       String JavaDoc declaring = ctConstructor.getDeclaringClass().getName();
281       
282       // Check the parameters
283
CtClass[] params = ctConstructor.getParameterTypes();
284       if (check)
285       {
286          // Wrong number of args?
287
if (params.length != 0)
288          {
289             buffer.append("if (args == null || args.length != ").append(params.length).append(") ");
290             buffer.append("throw new IllegalArgumentException(\"Expected ").append(params.length).append(" parameter(s)");
291             buffer.append(" for ").append("new ").append(declaring).append(ctConstructor.getSignature()).append("\");");
292          }
293          else
294          {
295             // Didn't expect args
296
buffer.append("if (args != null && args.length != 0)");
297             buffer.append("throw new IllegalArgumentException(\"Expected no parameters");
298             buffer.append(" for ").append("new ").append(declaring).append(ctConstructor.getSignature()).append("\");");
299          }
300          for (int i = 0; i < params.length; ++i)
301          {
302             if (object.equals(params[i]) == false)
303             {
304                String JavaDoc paramType = getBoxedType(params[i]);
305                // Primitives can't be null
306
if (params[i].isPrimitive())
307                {
308                   buffer.append("if (args[").append(i).append("] == null) ");
309                   buffer.append("throw new IllegalArgumentException(\"Parameter ").append(i);
310                   buffer.append(" cannot be null for ").append(params[i].getName());
311                   buffer.append(" for ").append("new ").append(declaring).append(ctConstructor.getSignature()).append("\");");
312                }
313                // Check the parameter types
314
buffer.append("if (args[").append(i).append("] != null && ");
315                buffer.append("args[").append(i).append("] instanceof ").append(paramType).append(" == false) ");
316                buffer.append("throw new IllegalArgumentException(\"Parameter ").append(i).append(" \" + args[").append(i).append("] + \"");
317                buffer.append(" is not an instance of ").append(paramType);
318                buffer.append(" for ").append("new ").append(declaring).append(ctConstructor.getSignature()).append("\");");
319             }
320          }
321       }
322       
323       // Add the return new
324
buffer.append("return new ").append(declaring).append('(');
325
326       // Add the parameters
327
for (int i = 0; i < params.length; ++i)
328       {
329          buffer.append('(');
330          // Cast the parameters
331
if (object.equals(params[i]) == false)
332              buffer.append("(").append(getBoxedType(params[i])).append(')');
333          buffer.append("args[").append(i).append("])");
334          // Unbox primitive parameters
335
if (params[i].isPrimitive())
336             unbox(buffer, params[i]);
337          
338          if (i < params.length - 1)
339             buffer.append(", ");
340       }
341       buffer.append(");}");
342
343       // Compile it
344
String JavaDoc code = buffer.toString();
345       try
346       {
347          CtMethod newInstance = CtNewMethod.make(code, result);
348          result.addMethod(newInstance);
349       }
350       catch (Exception JavaDoc e)
351       {
352          throw new RuntimeException JavaDoc("Cannot compile " + code, e);
353       }
354       
355       // Create it
356
try
357       {
358          return AccessController.doPrivileged(new PrivilegedExceptionAction JavaDoc<JavassistConstructor>()
359          {
360             public JavassistConstructor run() throws Exception JavaDoc
361             {
362                Class JavaDoc clazz = result.toClass();
363                return (JavassistConstructor) clazz.newInstance();
364             }
365          });
366       }
367       catch (PrivilegedActionException JavaDoc e)
368       {
369          throw e.getCause();
370       }
371    }
372    
373    /**
374     * Create a javassist field
375     *
376     * @param ctField the field
377     * @return the field
378     * @throws Throwable for any error
379     */

380    public JavassistField createField(CtField ctField) throws Throwable JavaDoc
381    {
382       ClassPool pool = JavassistTypeInfoFactoryImpl.pool;
383       final CtClass result = pool.makeClass(JavassistField.class.getName() + counter.increment());
384       try
385       {
386          CtClass magic = pool.get("sun.reflect.MagicAccessorImpl");
387          result.setSuperclass(magic);
388       }
389       catch (NotFoundException ignored)
390       {
391       }
392       result.addInterface(pool.get(JavassistField.class.getName()));
393       
394       CtConstructor constructor = new CtConstructor(null, result);
395       constructor.setBody("super();");
396       result.addConstructor(constructor);
397       
398       CtClass object = pool.get(Object JavaDoc.class.getName());
399       
400       // GET
401
JBossStringBuilder buffer = new JBossStringBuilder();
402       
403       // Signature
404
buffer.append("public Object get(Object target) throws Throwable {");
405
406       boolean isInstanceField= Modifier.isStatic(ctField.getModifiers()) == false;
407
408       // Check for null target
409
if (check && isInstanceField)
410       {
411          buffer.append("if (target == null) throw new IllegalArgumentException(\"Null target");
412          buffer.append(" for ").append(ctField.getName()).append("\");");
413       }
414
415       // Check the target
416
CtClass declaring = ctField.getDeclaringClass();
417       boolean needsCast = isInstanceField && object.equals(declaring) == false;
418       if (check && needsCast)
419       {
420          buffer.append("if (target instanceof ").append(declaring.getName()).append(" == false) ");
421          buffer.append("throw new IllegalArgumentException(\"Target \" + target + \"");
422          buffer.append(" is not an instance of ").append(declaring.getName());
423          buffer.append(" for ").append(ctField.getName()).append("\");");
424       }
425       
426       // Add a return and box the return type if necessary
427
CtClass type = ctField.getType();
428       boolean isPrimitive = type.isPrimitive();
429       buffer.append("return ");
430       if (isPrimitive)
431          buffer.append("new ").append(getBoxedType(type)).append('(');
432
433       // Instance field
434
if (isInstanceField)
435       {
436          buffer.append('(');
437          if (needsCast)
438             buffer.append("(").append(declaring.getName()).append(')');
439          
440          buffer.append("target).");
441       }
442       else
443       {
444          // Static field
445
buffer.append(declaring.getName()).append('.');
446       }
447
448       // Add the field name
449
buffer.append(ctField.getName());
450
451       // Complete the boxing of the return value
452
if (isPrimitive)
453          buffer.append(')');
454       
455       buffer.append(";}");
456
457       // Compile it
458
String JavaDoc code = buffer.toString();
459       CtMethod get = CtNewMethod.make(code, result);
460       try
461       {
462          result.addMethod(get);
463       }
464       catch (Exception JavaDoc e)
465       {
466          throw new RuntimeException JavaDoc("Cannot compile " + code, e);
467       }
468       
469       // SET
470
buffer = new JBossStringBuilder();
471       
472       // Signature
473
buffer.append("public void set(Object target, Object value) throws Throwable {");
474
475       // Check for null target
476
if (check && isInstanceField)
477       {
478          buffer.append("if (target == null) throw new IllegalArgumentException(\"Null target");
479          buffer.append(" for ").append(ctField.getName()).append("\");");
480       }
481
482       // Check the target
483
if (check && needsCast)
484       {
485          buffer.append("if (target instanceof ").append(declaring.getName()).append(" == false) ");
486          buffer.append("throw new IllegalArgumentException(\"Target \" + target + \"");
487          buffer.append(" is not an instance of ").append(declaring.getName());
488          buffer.append(" for ").append(ctField.getName()).append("\");");
489       }
490
491       // Check the parameter
492
if (check)
493       {
494          if (object.equals(type) == false)
495          {
496             String JavaDoc paramType = getBoxedType(type);
497             // Primitives can't be null
498
if (type.isPrimitive())
499             {
500                buffer.append("if (type == null) ");
501                buffer.append("throw new IllegalArgumentException(\"Value ");
502                buffer.append(" cannot be null for ").append(type.getName());
503                buffer.append(" for ").append(ctField.getName()).append("\");");
504             }
505             // Check the parameter types
506
buffer.append("if (value != null && ");
507             buffer.append("value instanceof ").append(paramType).append(" == false) ");
508             buffer.append("throw new IllegalArgumentException(\"Value \" + value + \"");
509             buffer.append(" is not an instance of ").append(paramType);
510             buffer.append(" for ").append(ctField.getName()).append("\");");
511          }
512       }
513
514       // Instance Field
515
if (isInstanceField)
516       {
517          buffer.append('(');
518          if (needsCast)
519             buffer.append("(").append(declaring.getName()).append(')');
520          
521          buffer.append("target).");
522       }
523       else
524       {
525          // Static field
526
buffer.append(declaring.getName()).append('.');
527       }
528
529       // Add the field name
530
buffer.append(ctField.getName()).append("=");
531
532       // Add the value
533
buffer.append('(');
534       // Cast the value
535
if (object.equals(type) == false)
536           buffer.append("(").append(getBoxedType(type)).append(')');
537       buffer.append("value)");
538       // Unbox primitive parameters
539
if (type.isPrimitive())
540          unbox(buffer, type);
541       
542       buffer.append(";}");
543
544       // Compile it
545
code = buffer.toString();
546       try
547       {
548          CtMethod set = CtNewMethod.make(code, result);
549          result.addMethod(set);
550       }
551       catch (Exception JavaDoc e)
552       {
553          throw new RuntimeException JavaDoc("Cannot compile " + code, e);
554       }
555       
556       // Create it
557
try
558       {
559          return AccessController.doPrivileged(new PrivilegedExceptionAction JavaDoc<JavassistField>()
560          {
561             public JavassistField run() throws Exception JavaDoc
562             {
563                Class JavaDoc clazz = result.toClass();
564                return (JavassistField) clazz.newInstance();
565             }
566          });
567       }
568       catch (PrivilegedActionException JavaDoc e)
569       {
570          throw e.getCause();
571       }
572    }
573    
574    /**
575     * Unbox a primitive
576     *
577     * @param buffer the buffer
578     * @param primitive the primitive
579     */

580    protected void unbox(JBossStringBuilder buffer, CtClass primitive)
581    {
582       if (CtClass.booleanType.equals(primitive))
583          buffer.append(".booleanValue()");
584       else if (CtClass.byteType.equals(primitive))
585          buffer.append(".byteValue()");
586       else if (CtClass.charType.equals(primitive))
587          buffer.append(".charValue()");
588       else if (CtClass.doubleType.equals(primitive))
589          buffer.append(".doubleValue()");
590       else if (CtClass.floatType.equals(primitive))
591          buffer.append(".floatValue()");
592       else if (CtClass.intType.equals(primitive))
593          buffer.append(".intValue()");
594       else if (CtClass.longType.equals(primitive))
595          buffer.append(".longValue()");
596       else if (CtClass.shortType.equals(primitive))
597          buffer.append(".shortValue()");
598       else
599       {
600          throw new UnreachableStatementException();
601       }
602    }
603
604    /**
605     * Get the boxed type
606     *
607     * @todo integer progression?
608     * @param type the type to box
609     * @return the boxed type name
610     */

611    protected String JavaDoc getBoxedType(CtClass type)
612    {
613       if (type.isPrimitive())
614       {
615          if (CtClass.booleanType.equals(type))
616             return Boolean JavaDoc.class.getName();
617          else if (CtClass.byteType.equals(type))
618             return Byte JavaDoc.class.getName();
619          else if (CtClass.charType.equals(type))
620             return Character JavaDoc.class.getName();
621          else if (CtClass.doubleType.equals(type))
622             return Double JavaDoc.class.getName();
623          else if (CtClass.floatType.equals(type))
624             return Float JavaDoc.class.getName();
625          else if (CtClass.intType.equals(type))
626             return Integer JavaDoc.class.getName();
627          else if (CtClass.longType.equals(type))
628             return Long JavaDoc.class.getName();
629          else if (CtClass.shortType.equals(type))
630             return Short JavaDoc.class.getName();
631          throw new UnreachableStatementException();
632       }
633       return type.getName();
634    }
635 }
636
Popular Tags