Home | History | Annotate | Download | only in lib
      1 /* Copyright (C) 2003 Vladimir Roubtsov. All rights reserved.
      2  *
      3  * This program and the accompanying materials are made available under
      4  * the terms of the Common Public License v1.0 which accompanies this distribution,
      5  * and is available at http://www.eclipse.org/legal/cpl-v10.html
      6  *
      7  * $Id: Types.java,v 1.1.1.1 2004/05/09 16:57:50 vlad_r Exp $
      8  */
      9 package com.vladium.jcd.lib;
     10 
     11 import java.io.IOException;
     12 import java.lang.reflect.*;
     13 
     14 import com.vladium.jcd.cls.IAccessFlags;
     15 
     16 // ----------------------------------------------------------------------------
     17 /**
     18  * Utility methods for manipulating type signatures and descriptors.
     19  *
     20  * TODO: fix usage of chars in parsers
     21  *
     22  * @author (C) 2001, Vlad Roubtsov
     23  */
     24 public abstract class Types
     25 {
     26     // public: ................................................................
     27 
     28     /**
     29      * Returns 'c''s package name [does not include trailing '.'] or ""
     30      * if 'c' is in the default package.
     31      */
     32     public static String getClassPackageName (final Class c)
     33     {
     34         // TODO: handle array and other types
     35 
     36         final String className = c.getName ();
     37         final int lastDot = className.lastIndexOf ('.');
     38         return lastDot >= 0 ? className.substring (0, lastDot) : "";
     39     }
     40 
     41 
     42     public static String accessFlagsToString (final int flags, final boolean isClass)
     43     {
     44         final StringBuffer result = new StringBuffer ();
     45 
     46         boolean first = true;
     47 
     48         if (isClass)
     49         {
     50             for (int f = 0; f < IAccessFlags.ALL_ACC.length; ++ f)
     51             {
     52                 final int bit = IAccessFlags.ALL_ACC [f];
     53 
     54                 if ((flags & bit) != 0)
     55                 {
     56                     if (first)
     57                         first = false;
     58                     else
     59                         result.append (" ");
     60 
     61                     if (bit == IAccessFlags.ACC_SUPER)
     62                         result.append ("super");
     63                     else
     64                         result.append (IAccessFlags.ALL_ACC_NAMES [f]);
     65                 }
     66             }
     67         }
     68         else
     69         {
     70             for (int f = 0; f < IAccessFlags.ALL_ACC.length; ++ f)
     71             {
     72                 final int bit = IAccessFlags.ALL_ACC [f];
     73 
     74                 if ((flags & bit) != 0)
     75                 {
     76                     if (first)
     77                         first = false;
     78                     else
     79                         result.append (" ");
     80 
     81                     result.append (IAccessFlags.ALL_ACC_NAMES [f]);
     82                 }
     83             }
     84         }
     85 
     86         return result.toString ();
     87     }
     88 
     89 
     90     /**
     91      * Converts Java-styled package/class name to how it would be
     92      * represented in the VM.<P>
     93      *
     94      * Example:<BR>
     95      * javaNameToVMName("java.lang.Object") = "java/lang/Object"
     96      *
     97      * @see #vmNameToJavaName
     98      */
     99     public static String javaNameToVMName (final String javaName)
    100     {
    101         if (javaName == null) return null;
    102         return javaName.replace ('.', '/');
    103     }
    104 
    105 
    106     /**
    107      * Converts a VM-styled package/class name to how it would be
    108      * represented in Java.<P>
    109      *
    110      * Example:<BR>
    111      * vmNameToJavaName("java/lang/Object") = "java.lang.Object"
    112      *
    113      * @see #javaNameToVMName
    114      */
    115     public static String vmNameToJavaName (final String vmName)
    116     {
    117         if (vmName == null) return null;
    118         return vmName.replace ('/', '.');
    119     }
    120 
    121 
    122     /**
    123      * Converts a method signature to its VM descriptor representation.
    124      * See $4.3 of the VM spec 1.0 for the descriptor grammar.<P>
    125      *
    126      * Example:<BR>
    127      * signatureToDescriptor(new Object().getClass().getMethod("equals" ,new Class[0])) = "(Ljava/lang/Object;)Z"
    128      * <P>
    129      *
    130      * Equivalent to
    131      * <CODE>signatureToDescriptor(method.getParameterTypes (), method.getReturnType ())</CODE>.
    132      */
    133     public static String signatureToDescriptor (Method method)
    134     {
    135         if (method == null) throw new IllegalArgumentException ("null input: method");
    136         return signatureToDescriptor (method.getParameterTypes (), method.getReturnType ());
    137     }
    138 
    139 
    140     /**
    141      * Converts a method signature (parameter types + return type) to its VM descriptor
    142      * representation. See $4.3 of the VM spec 1.0 for the descriptor grammar.<P>
    143      */
    144     public static String signatureToDescriptor (Class [] parameterTypes, Class returnType)
    145     {
    146         return new signatureCompiler ().signatureDescriptor (parameterTypes, returnType);
    147     }
    148 
    149 
    150     /**
    151      * Converts a type (a Class) to its VM descriptor representation.<P>
    152      *
    153      * Example:<BR>
    154      * typeToDescriptor(Object.class) = "Ljava/lang/Object;" <BR>
    155      * typeToDescriptor(boolean.class) = "Z"
    156      * <P>
    157      * Note the invariant typeToDescriptor(descriptorToType(desc)) == desc.
    158      *
    159      * @see #descriptorToType
    160      */
    161     public static String typeToDescriptor (Class type)
    162     {
    163         return new signatureCompiler ().typeDescriptor (type);
    164     }
    165 
    166 
    167     /**
    168      * Converts a VM descriptor to the corresponding type.<P>
    169      *
    170      * Example:<BR>
    171      * descriptorToType("[[I") = int[][].class <BR>
    172      * descriptorToType("B") = byte.class
    173      * <P>
    174      * Note the invariant descriptorToType(typeToDescriptor(c)) == c.
    175      *
    176      * @see #descriptorToType
    177      */
    178     public static Class descriptorToType (String typedescriptor) throws ClassNotFoundException
    179     {
    180         return new typeDescriptorCompiler ().descriptorToClass (typedescriptor);
    181     }
    182 
    183 
    184 
    185     public static String descriptorToReturnType (String methoddescriptor)
    186     {
    187         final int i1 = methoddescriptor.indexOf ('(');
    188         final int i2 = methoddescriptor.lastIndexOf (')');
    189 
    190         if ((i1 < 0) || (i2 <= 0) || (i1 >= i2) || (i2 >= methoddescriptor.length () - 1))
    191             throw new IllegalArgumentException ("malformed method descriptor: [" + methoddescriptor + "]");
    192 
    193         return methoddescriptor.substring (i2 + 1);
    194     }
    195 
    196 
    197     public static String [] descriptorToParameterTypes (String methoddescriptor)
    198     {
    199         //System.out.println ("METHOD DESCRIPTOR: [" + methoddescriptor + "]");
    200 
    201         try
    202         {
    203             final methodDescriptorCompiler compiler = new methodDescriptorCompiler (methoddescriptor);
    204             compiler.methodDescriptor ();
    205             return compiler.getResult ();
    206         }
    207         catch (IOException e)
    208         {
    209             throw new IllegalArgumentException ("error parsing [" + methoddescriptor + "]: " + e.toString ());
    210         }
    211 
    212         /*
    213         final java.util.Vector _result = new java.util.Vector ();
    214         final StringBuffer token = new StringBuffer ();
    215 
    216         char c = '*';
    217         int scan = 0;
    218 
    219         for (int state = 0; state != 4; )
    220         {
    221             try
    222             {
    223                 switch (state)
    224                 {
    225                 case 0:
    226                     c = methoddescriptor.charAt (scan++);
    227                     if (c == '(')
    228                         state = 1;
    229                     else
    230                         throw new IllegalArgumentException ("malformed method descriptor: [" + methoddescriptor + "]");
    231                     break;
    232 
    233                 case 1:
    234                     c = methoddescriptor.charAt (scan);
    235                     switch (c)
    236                     {
    237                     case 'B':
    238                     case 'C':
    239                     case 'D':
    240                     case 'F':
    241                     case 'I':
    242                     case 'J':
    243                     case 'S':
    244                     case 'Z':
    245                         token.append (c);
    246                         _result.addElement (token.toString ());
    247                         token.setLength (0);
    248                         scan++;
    249                         break;
    250 
    251                     case 'L':
    252                         state = 2;
    253                         token.append (c);
    254                         scan++;
    255                         break;
    256 
    257                     case '[':
    258                         state = 3;
    259                         token.append (c);
    260                         scan++;
    261                         break;
    262 
    263                     case ')':
    264                         if (token.length () > 0)
    265                         {
    266                             _result.addElement (token.toString ());
    267                             token.setLength (0);
    268                         }
    269                         state = 4;
    270                         break;
    271 
    272 
    273                     default:
    274                         throw new IllegalArgumentException ("[state = " + state + ", c = " + c + "] malformed method descriptor: [" + methoddescriptor + "]");
    275 
    276                     } // end of nested switch
    277                     break;
    278 
    279                 case 2:
    280                     c = methoddescriptor.charAt (scan++);
    281                     token.append (c);
    282                     if (c == ';')
    283                     {
    284                         _result.addElement (token.toString ());
    285                         token.setLength (0);
    286                         state = 1;
    287                     }
    288                     break;
    289 
    290                 case 3:
    291                     c = methoddescriptor.charAt (scan++);
    292                     token.append (c);
    293                     if (c != '[')
    294                     {
    295                         state = 1;
    296                     }
    297                     break;
    298 
    299                 } // end of switch
    300 
    301                 //System.out.println ("[state = " + state + ", c = " + c + "]");
    302             }
    303             catch (StringIndexOutOfBoundsException e)
    304             {
    305                 throw new IllegalArgumentException ("malformed method descriptor: [" + methoddescriptor + "]");
    306             }
    307         }
    308 
    309         String [] result = new String [_result.size ()];
    310         _result.copyInto (result);
    311 
    312         return result;
    313         */
    314     }
    315 
    316 
    317     public static String signatureToMethodDescriptor (final String [] parameterTypeDescriptors, final String returnTypeDescriptor)
    318     {
    319         final StringBuffer result = new StringBuffer ("(");
    320 
    321         for (int p = 0; p < parameterTypeDescriptors.length; p++)
    322         {
    323             result.append (parameterTypeDescriptors [p]);
    324         }
    325 
    326         result.append (')');
    327         result.append (returnTypeDescriptor);
    328 
    329         return result.toString ();
    330     }
    331 
    332 
    333     public static String typeDescriptorToUserName (final String typedescriptor)
    334     {
    335         return new typeDescriptorCompiler2 ().descriptorToClass (typedescriptor);
    336     }
    337 
    338     public static String methodDescriptorToUserName (final String methoddescriptor)
    339     {
    340         final String [] parameterTypes = descriptorToParameterTypes (methoddescriptor);
    341 
    342         final StringBuffer result = new StringBuffer ("(");
    343 
    344         for (int p = 0; p < parameterTypes.length; p++)
    345         {
    346             //System.out.println ("DESCRIPTOR: [" + parameterTypes [p] + "]");
    347 
    348             if (p > 0) result.append (", ");
    349 
    350             final String typeUserName = typeDescriptorToUserName (parameterTypes [p]);
    351             int lastDot = typeUserName.lastIndexOf ('.');
    352 
    353             if ((lastDot < 0) || ! "java.lang.".equals (typeUserName.substring (0, lastDot + 1)))
    354                 result.append (typeUserName);
    355             else
    356                 result.append (typeUserName.substring (lastDot + 1));
    357         }
    358 
    359         result.append (')');
    360         return result.toString ();
    361     }
    362 
    363     public static String fullMethodDescriptorToUserName (final String classJavaName, String methodName, final String methoddescriptor)
    364     {
    365         if ("<init>".equals (methodName))
    366             methodName = simpleClassName (classJavaName);
    367         if ("<clinit>".equals (methodName))
    368             methodName = "<static class initializer>";
    369 
    370         return methodName + ' ' + methodDescriptorToUserName (methoddescriptor);
    371     }
    372 
    373     // TODO: added most recently
    374     public static String fullMethodDescriptorToFullUserName (final String classJavaName, String methodName, final String methoddescriptor)
    375     {
    376         if ("<init>".equals (methodName))
    377             methodName = simpleClassName (classJavaName);
    378         if ("<clinit>".equals (methodName))
    379             methodName = "<static class initializer>";
    380 
    381         return classJavaName + '.' + methodName + ' ' + methodDescriptorToUserName (methoddescriptor);
    382     }
    383 
    384     // protected: .............................................................
    385 
    386     // package: ...............................................................
    387 
    388     // private: ...............................................................
    389 
    390 
    391     private static String simpleClassName (final String classJavaName)
    392     {
    393         int lastDot = classJavaName.lastIndexOf ('.');
    394 
    395         if (lastDot < 0)
    396             return classJavaName;
    397         else
    398             return classJavaName.substring (lastDot + 1);
    399     }
    400 
    401 
    402 
    403     private static final class signatureCompiler
    404     {
    405         String signatureDescriptor (Class [] _parameterTypes, Class _returnType)
    406         {
    407             emit ('(');    parameterTypes (_parameterTypes); emit (')'); returnType (_returnType);
    408 
    409             return m_desc.toString ();
    410         }
    411 
    412         String typeDescriptor (Class type)
    413         {
    414             parameterType (type);
    415 
    416             return m_desc.toString ();
    417         }
    418 
    419 
    420         private void parameterTypes (Class [] _parameterTypes)
    421         {
    422             if (_parameterTypes != null)
    423             {
    424                 for (int p = 0; p < _parameterTypes.length; p++)
    425                 {
    426                     parameterType (_parameterTypes [p]);
    427                 }
    428             }
    429         }
    430 
    431 
    432         private void returnType (Class _returnType)
    433         {
    434             if ((_returnType == null) || (_returnType == Void.TYPE))
    435                 emit ('V');
    436             else
    437                 parameterType (_returnType);
    438         }
    439 
    440 
    441         private void parameterType (Class _parameterType)
    442         {
    443             if (_parameterType != null)
    444             {
    445                 if (_parameterType.isPrimitive ()) // base type:
    446                 {
    447                     if (byte.class == _parameterType)            emit ('B');
    448                     else if (char.class == _parameterType)        emit ('C');
    449                     else if (double.class == _parameterType)    emit ('D');
    450                     else if (float.class == _parameterType)        emit ('F');
    451                     else if (int.class == _parameterType)        emit ('I');
    452                     else if (long.class == _parameterType)        emit ('J');
    453                     else if (short.class == _parameterType)        emit ('S');
    454                     else if (boolean.class == _parameterType)    emit ('Z');
    455                 }
    456                 else if (_parameterType.isArray ()) // array type:
    457                 {
    458                     emit ('[');    parameterType (_parameterType.getComponentType ());
    459                 }
    460                 else // object type:
    461                 {
    462                     emit ('L');    emit (javaNameToVMName (_parameterType.getName ())); emit (';');
    463                 }
    464             }
    465         }
    466 
    467 
    468         private void emit (String s)
    469         {
    470             m_desc.append (s);
    471         }
    472 
    473         private void emit (char c)
    474         {
    475             m_desc.append (c);
    476         }
    477 
    478 
    479         private StringBuffer m_desc = new StringBuffer ();
    480 
    481     } // end of static class
    482 
    483 
    484 
    485     private static class typeDescriptorCompiler
    486     {
    487         /*
    488         NOTE: the following would be a very simple solution to this problem
    489 
    490             Class.forName ('[' + descriptor).getComponentType ();
    491 
    492         except it only works in MS VM.
    493         */
    494 
    495         Class descriptorToClass (String typedescriptor) throws ClassNotFoundException
    496         {
    497             char first = typedescriptor.charAt (0);
    498 
    499             if (first == '[')
    500                 // array type:
    501                 return arrayOf (typedescriptor.substring (1));
    502             else if (first == 'L')
    503                 // object type:
    504                 return Class.forName (vmNameToJavaName (typedescriptor.substring (1, typedescriptor.length() - 1)));
    505             else // primitive type
    506             {
    507                 return primitive (first);
    508             }
    509         }
    510 
    511 
    512         Class arrayOf (String typedescriptor) throws ClassNotFoundException
    513         {
    514             char first = typedescriptor.charAt (0);
    515             Class component;
    516 
    517             if (first == '[')
    518                 // array type:
    519                 component = arrayOf (typedescriptor.substring (1));
    520             else if (first == 'L')
    521                 // object type:
    522                 component =  Class.forName (vmNameToJavaName (typedescriptor.substring (1, typedescriptor.length() - 1)));
    523             else // primitive type
    524             {
    525                 component = primitive (first);
    526             }
    527 
    528             Object array = Array.newInstance (component, 0);
    529             return array.getClass ();
    530         }
    531 
    532 
    533         Class primitive (char c) throws ClassNotFoundException
    534         {
    535             if (c == 'B') return byte.class;
    536             else if (c == 'C') return char.class;
    537             else if (c == 'D') return double.class;
    538             else if (c == 'F') return float.class;
    539             else if (c == 'I') return int.class;
    540             else if (c == 'J') return long.class;
    541             else if (c == 'S') return short.class;
    542             else if (c == 'Z') return boolean.class;
    543             else throw new ClassNotFoundException ("unknown base type: " + c);
    544         }
    545 
    546     } // end of static class
    547 
    548 
    549     private static class typeDescriptorCompiler2
    550     {
    551         String descriptorToClass (String typedescriptor)
    552         {
    553             //System.out.println ("typedesc1 -> " + typedescriptor);
    554 
    555             char first = typedescriptor.charAt (0);
    556 
    557             if (first == '[')
    558                 // array type:
    559                 return arrayOf (typedescriptor.substring (1));
    560             else if (first == 'L')
    561                 // object type:
    562                 return vmNameToJavaName (typedescriptor.substring (1, typedescriptor.length() - 1));
    563             else // primitive type
    564                 return primitive (first);
    565         }
    566 
    567 
    568         String arrayOf (String typedescriptor)
    569         {
    570             //System.out.println ("typedesc2 -> " + typedescriptor);
    571 
    572             char first = typedescriptor.charAt (0);
    573             String component;
    574 
    575             if (first == '[')
    576                 // array type:
    577                 component = arrayOf (typedescriptor.substring (1));
    578             else if (first == 'L')
    579                 // object type:
    580                 component = vmNameToJavaName (typedescriptor.substring (1, typedescriptor.length() - 1));
    581             else // primitive type
    582                 component = primitive (first);
    583 
    584             String array = component + " []";
    585             return array;
    586         }
    587 
    588 
    589         String primitive (char c)
    590         {
    591             switch (c)
    592             {
    593             case 'B': return "byte";
    594             case 'C': return "char";
    595             case 'D': return "double";
    596             case 'F': return "float";
    597             case 'I': return "int";
    598             case 'J': return "long";
    599             case 'S': return "short";
    600             case 'Z': return "boolean";
    601             default:
    602                 throw new IllegalArgumentException ("unknown primitive: " + c);
    603             }
    604         }
    605 
    606     } // end of static class
    607 
    608 
    609     private static class methodDescriptorCompiler
    610     {
    611         methodDescriptorCompiler (String methoddescriptor)
    612         {
    613             m_in = new java.io.PushbackReader (new java.io.StringReader (methoddescriptor));
    614         }
    615 
    616         String [] getResult ()
    617         {
    618             final String [] result = new String [m_result.size ()];
    619             m_result.toArray (result);
    620 
    621             return result;
    622         }
    623 
    624         void methodDescriptor () throws IOException
    625         {
    626             consume ('(');
    627 
    628             char c;
    629             while ((c = (char) m_in.read ()) != ')')
    630             {
    631                 m_in.unread (c);
    632                 parameterDescriptor ();
    633             }
    634             returnDescriptor ();
    635         }
    636 
    637         void parameterDescriptor () throws IOException
    638         {
    639             fieldType ();
    640             newToken ();
    641         }
    642 
    643         void returnDescriptor () throws IOException
    644         {
    645             char c = (char) m_in.read ();
    646 
    647             switch (c)
    648             {
    649             case 'V':
    650                 m_token.append (c);
    651                 break;
    652 
    653             default:
    654                 m_in.unread (c);
    655                 fieldType ();
    656 
    657             }
    658             // ignore return type for now: newToken ();
    659         }
    660 
    661         void componentType () throws IOException
    662         {
    663             fieldType ();
    664         }
    665 
    666         void objectType () throws IOException
    667         {
    668             consume ('L');
    669             m_token.append ('L');
    670 
    671             char c;
    672             while ((c = (char) m_in.read ()) != ';')
    673             {
    674                 m_token.append (c);
    675             }
    676             m_token.append (';');
    677         }
    678 
    679         void arrayType () throws IOException
    680         {
    681             consume ('[');
    682             m_token.append ('[');
    683 
    684             componentType ();
    685         }
    686 
    687         void fieldType () throws IOException
    688         {
    689             char c = (char) m_in.read ();
    690             m_in.unread (c);
    691 
    692             switch (c)
    693             {
    694             case 'L':
    695                 objectType ();
    696                 break;
    697 
    698             case '[':
    699                 arrayType ();
    700                 break;
    701 
    702             default:
    703                 baseType ();
    704                 break;
    705             }
    706         }
    707 
    708 
    709         void baseType () throws IOException
    710         {
    711             char c = (char) m_in.read ();
    712 
    713             switch (c)
    714             {
    715             case 'B':
    716             case 'C':
    717             case 'D':
    718             case 'F':
    719             case 'I':
    720             case 'J':
    721             case 'S':
    722             case 'Z':
    723                 m_token.append (c);
    724                 break;
    725 
    726             default:
    727                 throw new IllegalArgumentException ("unknown base type: " + c);
    728             }
    729         }
    730 
    731 
    732         private void consume (char expected) throws IOException
    733         {
    734             char c = (char) m_in.read ();
    735 
    736             if (c != expected)
    737                 throw new IllegalArgumentException ("consumed '" + c + "' while expecting '" + expected + "'");
    738         }
    739 
    740 
    741 
    742         private void newToken ()
    743         {
    744             //System.out.println ("NEW TOKEN [" + m_token.toString () + "]");
    745 
    746             m_result.add (m_token.toString ());
    747             m_token.setLength (0);
    748         }
    749 
    750         final java.util.List m_result = new java.util.ArrayList ();
    751         private StringBuffer m_token = new StringBuffer ();
    752         private java.io.PushbackReader m_in;
    753     } // end of nested class
    754 
    755 } // end of class
    756 // ----------------------------------------------------------------------------
    757