KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > gnu > kawa > reflect > ClassMethods


1 package gnu.kawa.reflect;
2 import gnu.bytecode.*;
3 import gnu.bytecode.ClassType;
4 import gnu.mapping.*;
5 import gnu.expr.*;
6 import gnu.lists.FString;
7 import java.util.Vector JavaDoc;
8
9 public class ClassMethods extends Procedure2
10 {
11   public static final ClassMethods classMethods = new ClassMethods();
12   static { classMethods.setName("class-methods"); }
13
14   /** Create a method or generic of the matching methods.
15    * @param arg0 a Class, ClassType, or a String, FString or Symbol
16    * that names a class.
17    * @param arg1 a method name (a String, FString, or Symbol)
18    * Loosely the same as GetNamedPart.make(arg0, arg1),
19    * but with some extra conversions and checks.
20    */

21   public Object JavaDoc apply2 (Object JavaDoc arg0, Object JavaDoc arg1)
22   {
23     return apply(this, arg0, arg1);
24   }
25
26   public static MethodProc apply(Procedure thisProc, Object JavaDoc arg0, Object JavaDoc arg1)
27   {
28     ClassType dtype;
29     String JavaDoc mname;
30     if (arg0 instanceof Class JavaDoc)
31       arg0 = Type.make((Class JavaDoc) arg0);
32     if (arg0 instanceof ClassType)
33       dtype = (ClassType) arg0;
34     else if (arg0 instanceof String JavaDoc || arg0 instanceof FString
35          || arg0 instanceof Symbol)
36       dtype = ClassType.make(arg0.toString());
37     else
38       throw new WrongType(thisProc, 0, null);
39     if (arg1 instanceof String JavaDoc || arg1 instanceof FString
40     || arg1 instanceof Symbol)
41       mname = arg1.toString();
42     else
43       throw new WrongType(thisProc, 1, null);
44     if (! ("<init>".equals(mname)))
45       mname = Compilation.mangleName(mname);
46     MethodProc result = apply(dtype, mname, '\0', Language.getDefaultLanguage());
47     if (result == null)
48       throw new RuntimeException JavaDoc("no applicable method named `"+mname+"' in "
49                                  +dtype.getName());
50     return result;
51   }
52
53   private static int removeRedundantMethods(Vector JavaDoc methods)
54   {
55     // Remove over-ridden methods.
56
int mlength = methods.size();
57   loopi:
58     for (int i = 1; i < mlength; )
59     {
60       Method method1 = (Method) methods.elementAt(i);
61       ClassType class1 = method1.getDeclaringClass();
62       Type[] types1 = method1.getParameterTypes();
63       int tlen = types1.length;
64       for (int j = 0; j < i; j++)
65     {
66       Method method2 = (Method) methods.elementAt(j);
67       Type[] types2 = method2.getParameterTypes();
68       if (tlen != types2.length)
69         continue;
70       int k;
71       for (k = tlen; --k >= 0; )
72         {
73           if (types1[k] != types2[k])
74         break;
75         }
76       if (k >= 0)
77         continue;
78       if (class1.isSubtype(method2.getDeclaringClass()))
79         methods.setElementAt(method1, j);
80       methods.setElementAt(methods.elementAt(mlength - 1), i);
81       mlength--;
82       // Re-do current i, since methods[i] replaced.
83
continue loopi;
84     }
85       i++;
86     }
87     return mlength;
88   }
89     
90   /** Return the methods of a class with the specified name and flag.
91    * @param caller if non-null, check that methods are accessible in it.
92    * @return an array containing the methods.
93    */

94   public static PrimProcedure[] getMethods(ClassType dtype, String JavaDoc mname,
95                                            char mode,
96                                            ClassType caller,
97                                            Language language)
98   {
99     MethodFilter filter = new MethodFilter(mname, 0, 0, caller);
100     // FIXME kludge until we handle "language types".
101
if (dtype == Type.tostring_type)
102       dtype = Type.string_type;
103     boolean named_class_only = mode == 'P' || "<init>".equals(mname);
104     Vector JavaDoc methods = new Vector JavaDoc();
105     dtype.getMethods(filter, named_class_only ? 0 : 2,
106              methods,
107                      caller == null ? "-" : caller.getPackageName());
108
109     int mlength = (named_class_only ? methods.size()
110            : removeRedundantMethods(methods));
111
112     PrimProcedure[] result = new PrimProcedure[mlength];
113     int count = 0;
114     for (int i = mlength; --i >= 0; )
115     {
116       Method method = (Method) methods.elementAt(i);
117       PrimProcedure pproc = new PrimProcedure(method, mode, language);
118       result[count++] = pproc;
119     }
120     return result;
121   }
122
123   /** Re-order the methods such that the ones that are definite
124    * applicable (all argtypes is subset of parameter type) are first;
125    * those possibly applicable next (argtype overlaps parameter types);
126    * and ending with those definitely not applicable (some argtype does
127    * overlap its parameter type).
128    * @return ((number of definitely applicable methods) << 32
129    * + (number of possibly applicable methods.
130    */

131   public static long selectApplicable(PrimProcedure[] methods,
132                                       Type[] atypes)
133   {
134     int limit = methods.length;
135     int numDefApplicable = 0;
136     int numPosApplicable = 0;
137     for (int i = 0; i < limit; )
138       {
139         int code = methods[i].isApplicable(atypes);
140         if (code < 0)
141           { // Definitely not applicable.
142
// swap(methods[limit-1], methods[i]):
143
PrimProcedure tmp = methods[limit-1];
144             methods[limit-1] = methods[i];
145             methods[i] = tmp;
146             limit--;
147           }
148         else if (code > 0)
149           { // Definitely applicable.
150
// swap(methods[numDefApplicable], methods[i]):
151
PrimProcedure tmp = methods[numDefApplicable];
152             methods[numDefApplicable] = methods[i];
153             methods[i] = tmp;
154             numDefApplicable++;
155             i++;
156           }
157         else
158           { // Possibly applicable.
159
numPosApplicable++;
160             i++;
161           }
162       }
163     return (((long) numDefApplicable) << 32) + (long) numPosApplicable;
164   }
165
166   /** Find methods.
167    * @param dtype class to search
168    * @param mname method name (already mangled, if need be).
169    * @param mode one of 'P' (use invokespecial). 'V' (require this argument
170    * even if method is static), or '\0' (otherwise).
171    */

172   public static MethodProc apply(ClassType dtype, String JavaDoc mname,
173                                  char mode, Language language)
174   {
175     PrimProcedure[] methods = getMethods(dtype, mname, mode, null, language);
176     GenericProc gproc = null;
177     PrimProcedure pproc = null;
178     for (int i = 0; i < methods.length; i++)
179       {
180         PrimProcedure cur = methods[i];
181         if (pproc != null && gproc == null)
182           {
183             gproc = new GenericProc();
184             gproc.add(pproc);
185           }
186         pproc = cur;
187         if (gproc != null)
188           gproc.add(pproc);
189       }
190     if (gproc != null)
191       {
192         gproc.setName(dtype.getName()+"."+mname);
193         return gproc;
194       }
195     return pproc;
196   }
197
198   /** Convert an expression to a name.
199    * @return a String if the expression has the form of a symbol or
200    * string literal, mangled as needed; otherwise null
201    */

202   static String JavaDoc checkName(Expression exp, boolean reversible)
203   {
204     if (exp instanceof QuoteExp)
205       {
206         Object JavaDoc name = ((QuoteExp) exp).getValue();
207     String JavaDoc nam;
208         if (name instanceof FString || name instanceof String JavaDoc)
209       nam = name.toString();
210     else if (name instanceof Symbol)
211       nam = ((Symbol) name).getName();
212     else
213       return null;
214     if (Compilation.isValidJavaName(nam))
215       return nam;
216     return Compilation.mangleName(nam, reversible);
217       }
218     return null;
219   }
220
221   /** Convert an expression to a name.
222    * @return a String if the expression has the form of a symbol or
223    * string literal, with no mangling; otherwise null
224    */

225   static String JavaDoc checkName(Expression exp)
226   {
227     if (exp instanceof QuoteExp)
228       {
229         Object JavaDoc name = ((QuoteExp) exp).getValue();
230         if (name instanceof FString || name instanceof String JavaDoc)
231       return name.toString();
232     else if (name instanceof Symbol)
233       return ((Symbol) name).getName();
234     else
235       return null;
236       }
237     return null;
238   }
239 }
240
241 class MethodFilter implements gnu.bytecode.Filter
242 {
243   String JavaDoc name;
244   int nlen;
245   int modifiers;
246   int modmask;
247   ClassType caller;
248
249   public MethodFilter(String JavaDoc name, int modifiers, int modmask,
250               ClassType caller)
251   {
252     this.name = name;
253     this.nlen = name.length();
254     this.modifiers = modifiers;
255     this.modmask = modmask;
256     this.caller = caller;
257   }
258
259   public boolean select(Object JavaDoc value)
260   {
261     gnu.bytecode.Method method = (gnu.bytecode.Method) value;
262     String JavaDoc mname = method.getName();
263     int mmods = method.getModifiers();
264     if ((mmods & modmask) != modifiers
265     || ! mname.startsWith(name))
266       return false;
267     int mlen = mname.length();
268     char c;
269     if (mlen != nlen
270     && (mlen != nlen + 2
271         || mname.charAt(nlen) != '$'
272         || ((c = mname.charAt(nlen+1)) != 'V' && c != 'X'))
273     && (mlen != nlen + 4
274         || ! mname.endsWith("$V$X")))
275       return false;
276     return caller == null
277       || caller.isAccessible(method.getDeclaringClass(), mmods);
278   }
279 }
280
Popular Tags