View Javadoc

1   package org.codehaus.aspectwerkz.definition;
2   
3   import java.util.HashMap;
4   import java.util.Map;
5   import java.util.StringTokenizer;
6   
7   /***
8    * The signature of a method that is available from the BCEL library uses descriptors as defined in Section 4.3 of the
9    * Java Virtual Machine specificaiton. Javadoc and Java do not use signatures in this same format. This class converts
10   * the Javadoc/Java signature format to that used by the JVM spec. To summarize the descriptors <code> A method
11   * descriptor represents the parameters that the method takes and the value that it returns:
12   * <p/>
13   * MethodDescriptor: ( ParameterDescriptor* ) ReturnDescriptor
14   * <p/>
15   * A parameter descriptor represents a parameter passed to a method:
16   * <p/>
17   * ParameterDescriptor: FieldType
18   * <p/>
19   * A return descriptor represents the type of the value returned from a method. It is a series of characters generated
20   * by the grammar:
21   * <p/>
22   * ReturnDescriptor: FieldType V
23   * <p/>
24   * The character V indicates that the method returns no value (its return type is void). </code>
25   * <p/>
26   * <code> A field descriptor represents the type of a class, instance, or local variable. It is a series of characters
27   * generated by the grammar:
28   * <p/>
29   * FieldDescriptor: FieldType
30   * <p/>
31   * ComponentType: FieldType
32   * <p/>
33   * FieldType: BaseType ObjectType ArrayType
34   * <p/>
35   * BaseType: B C D F I J S Z
36   * <p/>
37   * ObjectType: L <classname> ;
38   * <p/>
39   * ArrayType: [ ComponentType
40   * <p/>
41   * The characters of BaseType, the L and ; of ObjectType, and the [ of ArrayType are all ASCII characters. The
42   * <classname> represents a fully qualified class or interface name. For historical reasons it is encoded in internal
43   * form (4.2).  The interpretation of the field types is as shown in Table 4.2.
44   * <p/>
45   * BaseType Character  Type       Interpretation ---------------------------------------------- B                   byte
46   * signed byte C                   char       Unicode character D                   double     double-precision
47   * floating-point value F                   float      single-precision floating-point value I                   int
48   * integer J                   long       long integer L<classname>;       reference  an instance of class <classname> S
49   * short      signed short Z                   boolean    true or false [ reference  one array dimension
50   *
51   * @author <a href="mailto:mpollack@speakeasy.org">Mark Pollack</a>
52   */
53  public class DescriptorUtil {
54      private static Map _paramTypeMap = new HashMap();
55  
56      private static Map _returnTypeMap = new HashMap();
57  
58      static {
59          _paramTypeMap.put("byte", "B");
60          _paramTypeMap.put("char", "C");
61          _paramTypeMap.put("double", "D");
62          _paramTypeMap.put("float", "F");
63          _paramTypeMap.put("int", "I");
64          _paramTypeMap.put("long", "J");
65  
66          //todo: make generic...look for 'dots' of package. that algorithm doesn't handle
67          // packageless (default package)
68          // classes though..
69          _paramTypeMap.put("java.lang.Object", "Ljava/lang/Object;");
70          _paramTypeMap.put("short", "S");
71          _paramTypeMap.put("boolean", "Z");
72  
73          //todo
74          _paramTypeMap.put("array reference", "[");
75          _returnTypeMap.put("void", "V");
76      }
77  
78      /***
79       * Converts from the Java/Javadoc method signature the JVM spec format.
80       *
81       * @param javadocSig        method signature as returned via Javadoc API.
82       * @param javadocReturnType return type as returned via Javadoc API.
83       * @return mtehod signature as defined in the JVM spec.
84       */
85      public static String convert(String javadocSig, String javadocReturnType) {
86          //remove the leading and trailing parens
87          String javadocSigTrim = javadocSig.substring(1, javadocSig.length() - 1);
88          StringTokenizer st = new StringTokenizer(javadocSigTrim, ",");
89          StringBuffer jvmBuff = new StringBuffer("(");
90          while (st.hasMoreTokens()) {
91              //remove the leading space character.
92              String sigElement = st.nextToken().trim();
93              if (_paramTypeMap.containsKey(sigElement)) {
94                  jvmBuff.append(_paramTypeMap.get(sigElement));
95              }
96          }
97          jvmBuff.append(")");
98          if (_returnTypeMap.containsKey(javadocReturnType)) {
99              jvmBuff.append(_returnTypeMap.get(javadocReturnType));
100         }
101         return jvmBuff.toString();
102     }
103 
104     /***
105      * Convert a JVM signature as defined in the JVM spec to that used in the Java.
106      *
107      * @param jvmSignature The JVM format signature.
108      * @return a <code>String[]</code> containing the method parameter as elements of the array.
109      */
110     public static String[] getParameters(final String jvmSignature) {
111         int i = 0;
112         if (jvmSignature.charAt(i) != '(') {
113             return null;
114         }
115         int j = 0;
116         StringBuffer stringbuffer = new StringBuffer();
117         for (i++; i < jvmSignature.length();) {
118             if (jvmSignature.charAt(i) == ')') {
119                 i++;
120                 break; //we are at the end of the signature.
121             }
122             if (i > 1) {
123                 //put in spaces to later tokenize on.
124                 stringbuffer.append(" ");
125             }
126             i = jvmFormatToJavaFormat(jvmSignature, i, stringbuffer);
127 
128             //count number of elements parsed.
129             j++;
130         }
131 
132         //convert to string array.
133         String convertedString = stringbuffer.toString();
134         String[] as = new String[j];
135         int k = 0;
136         StringTokenizer st = new StringTokenizer(convertedString);
137         while (st.hasMoreTokens()) {
138             as[k++] = st.nextToken();
139         }
140         return as;
141     }
142 
143     /***
144      * The utility method that does the real work of parsing through the JVM formatted string and adding an converted
145      * method parameter description to the StringBuffer.
146      *
147      * @param jvmFormat    The JVM formatted string that is being parsed.
148      * @param i            The offset into the string being parsed.
149      * @param stringbuffer The storage for building the converted method signature.
150      * @return new offset location for parsing.
151      * @TODO this an extremely ugly method (the int an stringbuffer params must be removed)
152      */
153     private static int jvmFormatToJavaFormat(final String jvmFormat, int i, StringBuffer stringbuffer) {
154         String s1 = "";
155 
156         //arrays.
157         for (; jvmFormat.charAt(i) == '['; i++) {
158             s1 = s1 + "[]";
159         }
160         startover: switch (jvmFormat.charAt(i)) {
161             case 66: // 'B'
162                 stringbuffer.append("byte");
163                 break;
164             case 67: // 'C'
165                 stringbuffer.append("char");
166                 break;
167             case 68: // 'D'
168                 stringbuffer.append("double");
169                 break;
170             case 70: // 'F'
171                 stringbuffer.append("float");
172                 break;
173             case 73: // 'I'
174                 stringbuffer.append("int");
175                 break;
176             case 74: // 'J'
177                 stringbuffer.append("long");
178                 break;
179             case 83: // 'S'
180                 stringbuffer.append("short");
181                 break;
182             case 90: // 'Z'
183                 stringbuffer.append("boolean");
184                 break;
185             case 86: // 'V'
186                 stringbuffer.append("void");
187                 break;
188             case 76: // 'L'
189 
190                 //special case for objects.
191                 for (i++; i < jvmFormat.length(); i++) {
192                     if (jvmFormat.charAt(i) == '/') {
193                         //convert to period
194                         stringbuffer.append('.');
195                     } else {
196                         if (jvmFormat.charAt(i) == ';') {
197                             //we reached the end
198                             break startover;
199                         }
200 
201                         //copy contents.
202                         stringbuffer.append(jvmFormat.charAt(i));
203                     }
204                 }
205                 break;
206             default:
207                 return jvmFormat.length();
208         }
209         stringbuffer = stringbuffer.append(s1);
210         return ++i;
211     }
212 }