/* Copyright (C) 2003 Vladimir Roubtsov. All rights reserved. * * This program and the accompanying materials are made available under * the terms of the Common Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/cpl-v10.html * * $Id: Types.java,v 1.1.1.1 2004/05/09 16:57:50 vlad_r Exp $ */ package com.vladium.jcd.lib; import java.io.IOException; import java.lang.reflect.*; import com.vladium.jcd.cls.IAccessFlags; // ---------------------------------------------------------------------------- /** * Utility methods for manipulating type signatures and descriptors. * * TODO: fix usage of chars in parsers * * @author (C) 2001, Vlad Roubtsov */ public abstract class Types { // public: ................................................................ /** * Returns 'c''s package name [does not include trailing '.'] or "" * if 'c' is in the default package. */ public static String getClassPackageName (final Class c) { // TODO: handle array and other types final String className = c.getName (); final int lastDot = className.lastIndexOf ('.'); return lastDot >= 0 ? className.substring (0, lastDot) : ""; } public static String accessFlagsToString (final int flags, final boolean isClass) { final StringBuffer result = new StringBuffer (); boolean first = true; if (isClass) { for (int f = 0; f < IAccessFlags.ALL_ACC.length; ++ f) { final int bit = IAccessFlags.ALL_ACC [f]; if ((flags & bit) != 0) { if (first) first = false; else result.append (" "); if (bit == IAccessFlags.ACC_SUPER) result.append ("super"); else result.append (IAccessFlags.ALL_ACC_NAMES [f]); } } } else { for (int f = 0; f < IAccessFlags.ALL_ACC.length; ++ f) { final int bit = IAccessFlags.ALL_ACC [f]; if ((flags & bit) != 0) { if (first) first = false; else result.append (" "); result.append (IAccessFlags.ALL_ACC_NAMES [f]); } } } return result.toString (); } /** * Converts Java-styled package/class name to how it would be * represented in the VM.

* * Example:
* javaNameToVMName("java.lang.Object") = "java/lang/Object" * * @see #vmNameToJavaName */ public static String javaNameToVMName (final String javaName) { if (javaName == null) return null; return javaName.replace ('.', '/'); } /** * Converts a VM-styled package/class name to how it would be * represented in Java.

* * Example:
* vmNameToJavaName("java/lang/Object") = "java.lang.Object" * * @see #javaNameToVMName */ public static String vmNameToJavaName (final String vmName) { if (vmName == null) return null; return vmName.replace ('/', '.'); } /** * Converts a method signature to its VM descriptor representation. * See $4.3 of the VM spec 1.0 for the descriptor grammar.

* * Example:
* signatureToDescriptor(new Object().getClass().getMethod("equals" ,new Class[0])) = "(Ljava/lang/Object;)Z" *

* * Equivalent to * signatureToDescriptor(method.getParameterTypes (), method.getReturnType ()). */ public static String signatureToDescriptor (Method method) { if (method == null) throw new IllegalArgumentException ("null input: method"); return signatureToDescriptor (method.getParameterTypes (), method.getReturnType ()); } /** * Converts a method signature (parameter types + return type) to its VM descriptor * representation. See $4.3 of the VM spec 1.0 for the descriptor grammar.

*/ public static String signatureToDescriptor (Class [] parameterTypes, Class returnType) { return new signatureCompiler ().signatureDescriptor (parameterTypes, returnType); } /** * Converts a type (a Class) to its VM descriptor representation.

* * Example:
* typeToDescriptor(Object.class) = "Ljava/lang/Object;"
* typeToDescriptor(boolean.class) = "Z" *

* Note the invariant typeToDescriptor(descriptorToType(desc)) == desc. * * @see #descriptorToType */ public static String typeToDescriptor (Class type) { return new signatureCompiler ().typeDescriptor (type); } /** * Converts a VM descriptor to the corresponding type.

* * Example:
* descriptorToType("[[I") = int[][].class
* descriptorToType("B") = byte.class *

* Note the invariant descriptorToType(typeToDescriptor(c)) == c. * * @see #descriptorToType */ public static Class descriptorToType (String typedescriptor) throws ClassNotFoundException { return new typeDescriptorCompiler ().descriptorToClass (typedescriptor); } public static String descriptorToReturnType (String methoddescriptor) { final int i1 = methoddescriptor.indexOf ('('); final int i2 = methoddescriptor.lastIndexOf (')'); if ((i1 < 0) || (i2 <= 0) || (i1 >= i2) || (i2 >= methoddescriptor.length () - 1)) throw new IllegalArgumentException ("malformed method descriptor: [" + methoddescriptor + "]"); return methoddescriptor.substring (i2 + 1); } public static String [] descriptorToParameterTypes (String methoddescriptor) { //System.out.println ("METHOD DESCRIPTOR: [" + methoddescriptor + "]"); try { final methodDescriptorCompiler compiler = new methodDescriptorCompiler (methoddescriptor); compiler.methodDescriptor (); return compiler.getResult (); } catch (IOException e) { throw new IllegalArgumentException ("error parsing [" + methoddescriptor + "]: " + e.toString ()); } /* final java.util.Vector _result = new java.util.Vector (); final StringBuffer token = new StringBuffer (); char c = '*'; int scan = 0; for (int state = 0; state != 4; ) { try { switch (state) { case 0: c = methoddescriptor.charAt (scan++); if (c == '(') state = 1; else throw new IllegalArgumentException ("malformed method descriptor: [" + methoddescriptor + "]"); break; case 1: c = methoddescriptor.charAt (scan); switch (c) { case 'B': case 'C': case 'D': case 'F': case 'I': case 'J': case 'S': case 'Z': token.append (c); _result.addElement (token.toString ()); token.setLength (0); scan++; break; case 'L': state = 2; token.append (c); scan++; break; case '[': state = 3; token.append (c); scan++; break; case ')': if (token.length () > 0) { _result.addElement (token.toString ()); token.setLength (0); } state = 4; break; default: throw new IllegalArgumentException ("[state = " + state + ", c = " + c + "] malformed method descriptor: [" + methoddescriptor + "]"); } // end of nested switch break; case 2: c = methoddescriptor.charAt (scan++); token.append (c); if (c == ';') { _result.addElement (token.toString ()); token.setLength (0); state = 1; } break; case 3: c = methoddescriptor.charAt (scan++); token.append (c); if (c != '[') { state = 1; } break; } // end of switch //System.out.println ("[state = " + state + ", c = " + c + "]"); } catch (StringIndexOutOfBoundsException e) { throw new IllegalArgumentException ("malformed method descriptor: [" + methoddescriptor + "]"); } } String [] result = new String [_result.size ()]; _result.copyInto (result); return result; */ } public static String signatureToMethodDescriptor (final String [] parameterTypeDescriptors, final String returnTypeDescriptor) { final StringBuffer result = new StringBuffer ("("); for (int p = 0; p < parameterTypeDescriptors.length; p++) { result.append (parameterTypeDescriptors [p]); } result.append (')'); result.append (returnTypeDescriptor); return result.toString (); } public static String typeDescriptorToUserName (final String typedescriptor) { return new typeDescriptorCompiler2 ().descriptorToClass (typedescriptor); } public static String methodDescriptorToUserName (final String methoddescriptor) { final String [] parameterTypes = descriptorToParameterTypes (methoddescriptor); final StringBuffer result = new StringBuffer ("("); for (int p = 0; p < parameterTypes.length; p++) { //System.out.println ("DESCRIPTOR: [" + parameterTypes [p] + "]"); if (p > 0) result.append (", "); final String typeUserName = typeDescriptorToUserName (parameterTypes [p]); int lastDot = typeUserName.lastIndexOf ('.'); if ((lastDot < 0) || ! "java.lang.".equals (typeUserName.substring (0, lastDot + 1))) result.append (typeUserName); else result.append (typeUserName.substring (lastDot + 1)); } result.append (')'); return result.toString (); } public static String fullMethodDescriptorToUserName (final String classJavaName, String methodName, final String methoddescriptor) { if ("".equals (methodName)) methodName = simpleClassName (classJavaName); if ("".equals (methodName)) methodName = ""; return methodName + ' ' + methodDescriptorToUserName (methoddescriptor); } // TODO: added most recently public static String fullMethodDescriptorToFullUserName (final String classJavaName, String methodName, final String methoddescriptor) { if ("".equals (methodName)) methodName = simpleClassName (classJavaName); if ("".equals (methodName)) methodName = ""; return classJavaName + '.' + methodName + ' ' + methodDescriptorToUserName (methoddescriptor); } // protected: ............................................................. // package: ............................................................... // private: ............................................................... private static String simpleClassName (final String classJavaName) { int lastDot = classJavaName.lastIndexOf ('.'); if (lastDot < 0) return classJavaName; else return classJavaName.substring (lastDot + 1); } private static final class signatureCompiler { String signatureDescriptor (Class [] _parameterTypes, Class _returnType) { emit ('('); parameterTypes (_parameterTypes); emit (')'); returnType (_returnType); return m_desc.toString (); } String typeDescriptor (Class type) { parameterType (type); return m_desc.toString (); } private void parameterTypes (Class [] _parameterTypes) { if (_parameterTypes != null) { for (int p = 0; p < _parameterTypes.length; p++) { parameterType (_parameterTypes [p]); } } } private void returnType (Class _returnType) { if ((_returnType == null) || (_returnType == Void.TYPE)) emit ('V'); else parameterType (_returnType); } private void parameterType (Class _parameterType) { if (_parameterType != null) { if (_parameterType.isPrimitive ()) // base type: { if (byte.class == _parameterType) emit ('B'); else if (char.class == _parameterType) emit ('C'); else if (double.class == _parameterType) emit ('D'); else if (float.class == _parameterType) emit ('F'); else if (int.class == _parameterType) emit ('I'); else if (long.class == _parameterType) emit ('J'); else if (short.class == _parameterType) emit ('S'); else if (boolean.class == _parameterType) emit ('Z'); } else if (_parameterType.isArray ()) // array type: { emit ('['); parameterType (_parameterType.getComponentType ()); } else // object type: { emit ('L'); emit (javaNameToVMName (_parameterType.getName ())); emit (';'); } } } private void emit (String s) { m_desc.append (s); } private void emit (char c) { m_desc.append (c); } private StringBuffer m_desc = new StringBuffer (); } // end of static class private static class typeDescriptorCompiler { /* NOTE: the following would be a very simple solution to this problem Class.forName ('[' + descriptor).getComponentType (); except it only works in MS VM. */ Class descriptorToClass (String typedescriptor) throws ClassNotFoundException { char first = typedescriptor.charAt (0); if (first == '[') // array type: return arrayOf (typedescriptor.substring (1)); else if (first == 'L') // object type: return Class.forName (vmNameToJavaName (typedescriptor.substring (1, typedescriptor.length() - 1))); else // primitive type { return primitive (first); } } Class arrayOf (String typedescriptor) throws ClassNotFoundException { char first = typedescriptor.charAt (0); Class component; if (first == '[') // array type: component = arrayOf (typedescriptor.substring (1)); else if (first == 'L') // object type: component = Class.forName (vmNameToJavaName (typedescriptor.substring (1, typedescriptor.length() - 1))); else // primitive type { component = primitive (first); } Object array = Array.newInstance (component, 0); return array.getClass (); } Class primitive (char c) throws ClassNotFoundException { if (c == 'B') return byte.class; else if (c == 'C') return char.class; else if (c == 'D') return double.class; else if (c == 'F') return float.class; else if (c == 'I') return int.class; else if (c == 'J') return long.class; else if (c == 'S') return short.class; else if (c == 'Z') return boolean.class; else throw new ClassNotFoundException ("unknown base type: " + c); } } // end of static class private static class typeDescriptorCompiler2 { String descriptorToClass (String typedescriptor) { //System.out.println ("typedesc1 -> " + typedescriptor); char first = typedescriptor.charAt (0); if (first == '[') // array type: return arrayOf (typedescriptor.substring (1)); else if (first == 'L') // object type: return vmNameToJavaName (typedescriptor.substring (1, typedescriptor.length() - 1)); else // primitive type return primitive (first); } String arrayOf (String typedescriptor) { //System.out.println ("typedesc2 -> " + typedescriptor); char first = typedescriptor.charAt (0); String component; if (first == '[') // array type: component = arrayOf (typedescriptor.substring (1)); else if (first == 'L') // object type: component = vmNameToJavaName (typedescriptor.substring (1, typedescriptor.length() - 1)); else // primitive type component = primitive (first); String array = component + " []"; return array; } String primitive (char c) { switch (c) { case 'B': return "byte"; case 'C': return "char"; case 'D': return "double"; case 'F': return "float"; case 'I': return "int"; case 'J': return "long"; case 'S': return "short"; case 'Z': return "boolean"; default: throw new IllegalArgumentException ("unknown primitive: " + c); } } } // end of static class private static class methodDescriptorCompiler { methodDescriptorCompiler (String methoddescriptor) { m_in = new java.io.PushbackReader (new java.io.StringReader (methoddescriptor)); } String [] getResult () { final String [] result = new String [m_result.size ()]; m_result.toArray (result); return result; } void methodDescriptor () throws IOException { consume ('('); char c; while ((c = (char) m_in.read ()) != ')') { m_in.unread (c); parameterDescriptor (); } returnDescriptor (); } void parameterDescriptor () throws IOException { fieldType (); newToken (); } void returnDescriptor () throws IOException { char c = (char) m_in.read (); switch (c) { case 'V': m_token.append (c); break; default: m_in.unread (c); fieldType (); } // ignore return type for now: newToken (); } void componentType () throws IOException { fieldType (); } void objectType () throws IOException { consume ('L'); m_token.append ('L'); char c; while ((c = (char) m_in.read ()) != ';') { m_token.append (c); } m_token.append (';'); } void arrayType () throws IOException { consume ('['); m_token.append ('['); componentType (); } void fieldType () throws IOException { char c = (char) m_in.read (); m_in.unread (c); switch (c) { case 'L': objectType (); break; case '[': arrayType (); break; default: baseType (); break; } } void baseType () throws IOException { char c = (char) m_in.read (); switch (c) { case 'B': case 'C': case 'D': case 'F': case 'I': case 'J': case 'S': case 'Z': m_token.append (c); break; default: throw new IllegalArgumentException ("unknown base type: " + c); } } private void consume (char expected) throws IOException { char c = (char) m_in.read (); if (c != expected) throw new IllegalArgumentException ("consumed '" + c + "' while expecting '" + expected + "'"); } private void newToken () { //System.out.println ("NEW TOKEN [" + m_token.toString () + "]"); m_result.add (m_token.toString ()); m_token.setLength (0); } final java.util.List m_result = new java.util.ArrayList (); private StringBuffer m_token = new StringBuffer (); private java.io.PushbackReader m_in; } // end of nested class } // end of class // ----------------------------------------------------------------------------