1 /* TypeSignature.java -- Class used to compute type signatures
2    Copyright (C) 1998, 2000, 2002 Free Software Foundation, Inc.
3 
4 This file is part of GNU Classpath.
5 
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10 
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING.  If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
20 
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library.  Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
25 
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module.  An independent module is a module which is not derived from
33 or based on this library.  If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so.  If you do not wish to do so, delete this
36 exception statement from your version. */
37 
38 
39 package gnu.java.lang.reflect;
40 
41 import java.lang.reflect.Constructor;
42 import java.lang.reflect.Field;
43 import java.lang.reflect.Member;
44 import java.lang.reflect.Method;
45 
46 /**
47  * This class provides static methods that can be used to compute
48  * type-signatures of <code>Class</code>s or <code>Member</code>s.
49  * More specific methods are also provided for computing the
50  * type-signature of <code>Constructor</code>s and
51  * <code>Method</code>s.  Methods are also provided to go in the
52  * reverse direction.
53  *
54  * @author Eric Blake (ebb9@email.byu.edu)
55  */
56 public class TypeSignature
57 {
58   /**
59    * Returns a <code>String</code> representing the type-encoding of a class.
60    * The .class file format has different encodings for classes, depending
61    * on whether it must be disambiguated from primitive types or not; hence
62    * the descriptor parameter to choose between them. If you are planning
63    * on decoding primitive types along with classes, then descriptor should
64    * be true for correct results. Type-encodings are computed as follows:
65    *
66    * <pre>
67    * boolean -> "Z"
68    * byte    -> "B"
69    * char    -> "C"
70    * double  -> "D"
71    * float   -> "F"
72    * int     -> "I"
73    * long    -> "J"
74    * short   -> "S"
75    * void    -> "V"
76    * arrays  -> "[" + descriptor format of component type
77    * object  -> class format: fully qualified name with '.' replaced by '/'
78    *            descriptor format: "L" + class format + ";"
79    * </pre>
80    *
81    * @param type the class name to encode
82    * @param descriptor true to return objects in descriptor format
83    * @return the class name, as it appears in bytecode constant pools
84    * @see #getClassForEncoding(String)
85    */
getEncodingOfClass(String type, boolean descriptor)86   public static String getEncodingOfClass(String type, boolean descriptor)
87   {
88     if (! descriptor || type.charAt(0) == '[')
89       return type.replace('.', '/');
90     if (type.equals("boolean"))
91       return "Z";
92     if (type.equals("byte"))
93       return "B";
94     if (type.equals("short"))
95       return "S";
96     if (type.equals("char"))
97       return "C";
98     if (type.equals("int"))
99       return "I";
100     if (type.equals("long"))
101       return "J";
102     if (type.equals("float"))
103       return "F";
104     if (type.equals("double"))
105       return "D";
106     if (type.equals("void"))
107       return "V";
108     return 'L' + type.replace('.', '/') + ';';
109   }
110 
111   /**
112    * Gets the descriptor encoding for a class.
113    *
114    * @param clazz the class to encode
115    * @param descriptor true to return objects in descriptor format
116    * @return the class name, as it appears in bytecode constant pools
117    * @see #getEncodingOfClass(String, boolean)
118    */
getEncodingOfClass(Class clazz, boolean descriptor)119   public static String getEncodingOfClass(Class clazz, boolean descriptor)
120   {
121     return getEncodingOfClass(clazz.getName(), descriptor);
122   }
123 
124   /**
125    * Gets the descriptor encoding for a class.
126    *
127    * @param clazz the class to encode
128    * @return the class name, as it appears in bytecode constant pools
129    * @see #getEncodingOfClass(String, boolean)
130    */
getEncodingOfClass(Class clazz)131   public static String getEncodingOfClass(Class clazz)
132   {
133     return getEncodingOfClass(clazz.getName(), true);
134   }
135 
136 
137   /**
138    * This function is the inverse of <code>getEncodingOfClass</code>. This
139    * accepts both object and descriptor formats, but must know which style
140    * of string is being passed in (usually, descriptor should be true). In
141    * descriptor format, "I" is treated as int.class, in object format, it
142    * is treated as a class named I in the unnamed package. This method is
143    * strictly equivalent to {@link #getClassForEncoding(java.lang.String, boolean, java.lang.ClassLoader)}
144    * with a class loader equal to <code>null</code>. In that case, it
145    * uses the default class loader on the calling stack.
146    *
147    * @param type_code the class name to decode
148    * @param descriptor if the string is in descriptor format
149    * @return the corresponding Class object
150    * @throws ClassNotFoundException if the class cannot be located
151    * @see #getEncodingOfClass(Class, boolean)
152    */
getClassForEncoding(String type_code, boolean descriptor)153   public static Class getClassForEncoding(String type_code, boolean descriptor)
154     throws ClassNotFoundException
155   {
156     return getClassForEncoding(type_code, descriptor, null);
157   }
158 
159   /**
160    * This function is the inverse of <code>getEncodingOfClass</code>. This
161    * accepts both object and descriptor formats, but must know which style
162    * of string is being passed in (usually, descriptor should be true). In
163    * descriptor format, "I" is treated as int.class, in object format, it
164    * is treated as a class named I in the unnamed package.
165    *
166    * @param type_code The class name to decode.
167    * @param descriptor If the string is in descriptor format.
168    * @param loader The class loader when resolving generic object name. If
169    * <code>loader</code> is null then it uses the default class loader on the
170    * calling stack.
171    * @return the corresponding Class object.
172    * @throws ClassNotFoundException if the class cannot be located.
173    * @see #getEncodingOfClass(Class, boolean)
174    * @see #getClassForEncoding(String, boolean)
175    */
getClassForEncoding(String type_code, boolean descriptor, ClassLoader loader)176   public static Class getClassForEncoding(String type_code, boolean descriptor,
177 		 			  ClassLoader loader)
178     throws ClassNotFoundException
179   {
180     if (descriptor)
181       {
182         switch (type_code.charAt(0))
183           {
184           case 'B':
185             return byte.class;
186           case 'C':
187             return char.class;
188           case 'D':
189             return double.class;
190           case 'F':
191             return float.class;
192           case 'I':
193             return int.class;
194           case 'J':
195             return long.class;
196           case 'S':
197             return short.class;
198           case 'V':
199             return void.class;
200           case 'Z':
201             return boolean.class;
202           default:
203             throw new ClassNotFoundException("Invalid class name: "
204                                              + type_code);
205           case 'L':
206             type_code = type_code.substring(1, type_code.length() - 1);
207             // Fallthrough.
208           case '[':
209           }
210       }
211     return Class.forName(type_code.replace('/', '.'), true, loader);
212   }
213 
214   /**
215    * Gets the Class object for a type name.
216    *
217    * @param type_code the class name to decode
218    * @return the corresponding Class object
219    * @throws ClassNotFoundException if the class cannot be located
220    * @see #getClassForEncoding(String, boolean)
221    */
getClassForEncoding(String type_code)222   public static Class getClassForEncoding(String type_code)
223     throws ClassNotFoundException
224   {
225     return getClassForEncoding(type_code, true);
226   }
227 
228   /**
229    * Returns a <code>String</code> representing the type-encoding of a
230    * method.  The type-encoding of a method is:
231    *
232    * "(" + parameter type descriptors + ")" + return type descriptor
233    *
234    * XXX This could be faster if it were implemented natively.
235    *
236    * @param m the method to encode
237    * @return the encoding
238    */
getEncodingOfMethod(Method m)239   public static String getEncodingOfMethod(Method m)
240   {
241     Class[] paramTypes = m.getParameterTypes();
242     StringBuffer buf = new StringBuffer().append('(');
243     for (int i = 0; i < paramTypes.length; i++)
244       buf.append(getEncodingOfClass(paramTypes[i].getName(), true));
245     buf.append(')').append(getEncodingOfClass(m.getReturnType().getName(),
246                                               true));
247     return buf.toString();
248   }
249 
250   /**
251    * Returns a <code>String</code> representing the type-encoding of a
252    * constructor. The type-encoding of a method is:
253    *
254    * "(" + parameter type descriptors + ")V"
255    *
256    * XXX This could be faster if it were implemented natively.
257    *
258    * @param c the constructor to encode
259    * @return the encoding
260    */
getEncodingOfConstructor(Constructor c)261   public static String getEncodingOfConstructor(Constructor c)
262   {
263     Class[] paramTypes = c.getParameterTypes();
264     StringBuffer buf = new StringBuffer().append('(');
265     for (int i = 0; i < paramTypes.length; i++)
266       buf.append(getEncodingOfClass(paramTypes[i].getName(), true));
267     buf.append(")V");
268     return buf.toString();
269   }
270 
271   /**
272    * Returns a <code>String</code> representing the type-encoding of a
273    * class member. This appropriately handles Constructors, Methods, and
274    * Fields.
275    *
276    * @param mem the member to encode
277    * @return the encoding
278    */
getEncodingOfMember(Member mem)279   public static String getEncodingOfMember(Member mem)
280   {
281     if (mem instanceof Constructor)
282       return getEncodingOfConstructor((Constructor) mem);
283     if (mem instanceof Method)
284       return getEncodingOfMethod((Method) mem);
285     else // Field
286       return getEncodingOfClass(((Field) mem).getType().getName(), true);
287   }
288 } // class TypeSignature
289