package org.codehaus.aspectwerkz.definition; import java.util.HashMap; import java.util.Map; import java.util.StringTokenizer; /** * The signature of a method that is available from the BCEL library uses descriptors as defined in Section 4.3 of the * Java Virtual Machine specificaiton. Javadoc and Java do not use signatures in this same format. This class converts * the Javadoc/Java signature format to that used by the JVM spec. To summarize the descriptors <code> A method * descriptor represents the parameters that the method takes and the value that it returns: * <p/> * MethodDescriptor: ( ParameterDescriptor* ) ReturnDescriptor * <p/> * A parameter descriptor represents a parameter passed to a method: * <p/> * ParameterDescriptor: FieldType * <p/> * A return descriptor represents the type of the value returned from a method. It is a series of characters generated * by the grammar: * <p/> * ReturnDescriptor: FieldType V * <p/> * The character V indicates that the method returns no value (its return type is void). </code> * <p/> * <code> A field descriptor represents the type of a class, instance, or local variable. It is a series of characters * generated by the grammar: * <p/> * FieldDescriptor: FieldType * <p/> * ComponentType: FieldType * <p/> * FieldType: BaseType ObjectType ArrayType * <p/> * BaseType: B C D F I J S Z * <p/> * ObjectType: L <classname> ; * <p/> * ArrayType: [ ComponentType * <p/> * The characters of BaseType, the L and ; of ObjectType, and the [ of ArrayType are all ASCII characters. The * <classname> represents a fully qualified class or interface name. For historical reasons it is encoded in internal * form (4.2). The interpretation of the field types is as shown in Table 4.2. * <p/> * BaseType Character Type Interpretation ---------------------------------------------- B byte * signed byte C char Unicode character D double double-precision * floating-point value F float single-precision floating-point value I int * integer J long long integer L<classname>; reference an instance of class <classname> S * short signed short Z boolean true or false [ reference one array dimension * * @author <a href="mailto:mpollack@speakeasy.org">Mark Pollack</a> */ public class DescriptorUtil { private static Map _paramTypeMap = new HashMap(); private static Map _returnTypeMap = new HashMap(); static { _paramTypeMap.put("byte", "B"); _paramTypeMap.put("char", "C"); _paramTypeMap.put("double", "D"); _paramTypeMap.put("float", "F"); _paramTypeMap.put("int", "I"); _paramTypeMap.put("long", "J"); //todo: make generic...look for 'dots' of package. that algorithm doesn't handle // packageless (default package) // classes though.. _paramTypeMap.put("java.lang.Object", "Ljava/lang/Object;"); _paramTypeMap.put("short", "S"); _paramTypeMap.put("boolean", "Z"); //todo _paramTypeMap.put("array reference", "["); _returnTypeMap.put("void", "V"); } /** * Converts from the Java/Javadoc method signature the JVM spec format. * * @param javadocSig method signature as returned via Javadoc API. * @param javadocReturnType return type as returned via Javadoc API. * @return mtehod signature as defined in the JVM spec. */ public static String convert(String javadocSig, String javadocReturnType) { //remove the leading and trailing parens String javadocSigTrim = javadocSig.substring(1, javadocSig.length() - 1); StringTokenizer st = new StringTokenizer(javadocSigTrim, ","); StringBuffer jvmBuff = new StringBuffer("("); while (st.hasMoreTokens()) { //remove the leading space character. String sigElement = st.nextToken().trim(); if (_paramTypeMap.containsKey(sigElement)) { jvmBuff.append(_paramTypeMap.get(sigElement)); } } jvmBuff.append(")"); if (_returnTypeMap.containsKey(javadocReturnType)) { jvmBuff.append(_returnTypeMap.get(javadocReturnType)); } return jvmBuff.toString(); } /** * Convert a JVM signature as defined in the JVM spec to that used in the Java. * * @param jvmSignature The JVM format signature. * @return a <code>String[]</code> containing the method parameter as elements of the array. */ public static String[] getParameters(final String jvmSignature) { int i = 0; if (jvmSignature.charAt(i) != '(') { return null; } int j = 0; StringBuffer stringbuffer = new StringBuffer(); for (i++; i < jvmSignature.length();) { if (jvmSignature.charAt(i) == ')') { i++; break; //we are at the end of the signature. } if (i > 1) { //put in spaces to later tokenize on. stringbuffer.append(" "); } i = jvmFormatToJavaFormat(jvmSignature, i, stringbuffer); //count number of elements parsed. j++; } //convert to string array. String convertedString = stringbuffer.toString(); String[] as = new String[j]; int k = 0; StringTokenizer st = new StringTokenizer(convertedString); while (st.hasMoreTokens()) { as[k++] = st.nextToken(); } return as; } /** * The utility method that does the real work of parsing through the JVM formatted string and adding an converted * method parameter description to the StringBuffer. * * @param jvmFormat The JVM formatted string that is being parsed. * @param i The offset into the string being parsed. * @param stringbuffer The storage for building the converted method signature. * @return new offset location for parsing. * @TODO this an extremely ugly method (the int an stringbuffer params must be removed) */ private static int jvmFormatToJavaFormat(final String jvmFormat, int i, StringBuffer stringbuffer) { String s1 = ""; //arrays. for (; jvmFormat.charAt(i) == '['; i++) { s1 = s1 + "[]"; } startover: switch (jvmFormat.charAt(i)) { case 66: // 'B' stringbuffer.append("byte"); break; case 67: // 'C' stringbuffer.append("char"); break; case 68: // 'D' stringbuffer.append("double"); break; case 70: // 'F' stringbuffer.append("float"); break; case 73: // 'I' stringbuffer.append("int"); break; case 74: // 'J' stringbuffer.append("long"); break; case 83: // 'S' stringbuffer.append("short"); break; case 90: // 'Z' stringbuffer.append("boolean"); break; case 86: // 'V' stringbuffer.append("void"); break; case 76: // 'L' //special case for objects. for (i++; i < jvmFormat.length(); i++) { if (jvmFormat.charAt(i) == '/') { //convert to period stringbuffer.append('.'); } else { if (jvmFormat.charAt(i) == ';') { //we reached the end break startover; } //copy contents. stringbuffer.append(jvmFormat.charAt(i)); } } break; default: return jvmFormat.length(); } stringbuffer = stringbuffer.append(s1); return ++i; } }