blob: 05b423033f5107da9cb612244b88dc98d266d106 [file] [log] [blame]
/* 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.<P>
*
* Example:<BR>
* 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.<P>
*
* Example:<BR>
* 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.<P>
*
* Example:<BR>
* signatureToDescriptor(new Object().getClass().getMethod("equals" ,new Class[0])) = "(Ljava/lang/Object;)Z"
* <P>
*
* Equivalent to
* <CODE>signatureToDescriptor(method.getParameterTypes (), method.getReturnType ())</CODE>.
*/
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.<P>
*/
public static String signatureToDescriptor (Class [] parameterTypes, Class returnType)
{
return new signatureCompiler ().signatureDescriptor (parameterTypes, returnType);
}
/**
* Converts a type (a Class) to its VM descriptor representation.<P>
*
* Example:<BR>
* typeToDescriptor(Object.class) = "Ljava/lang/Object;" <BR>
* typeToDescriptor(boolean.class) = "Z"
* <P>
* 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.<P>
*
* Example:<BR>
* descriptorToType("[[I") = int[][].class <BR>
* descriptorToType("B") = byte.class
* <P>
* 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 ("<init>".equals (methodName))
methodName = simpleClassName (classJavaName);
if ("<clinit>".equals (methodName))
methodName = "<static class initializer>";
return methodName + ' ' + methodDescriptorToUserName (methoddescriptor);
}
// TODO: added most recently
public static String fullMethodDescriptorToFullUserName (final String classJavaName, String methodName, final String methoddescriptor)
{
if ("<init>".equals (methodName))
methodName = simpleClassName (classJavaName);
if ("<clinit>".equals (methodName))
methodName = "<static class initializer>";
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
// ----------------------------------------------------------------------------