• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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      */
getClassPackageName(final Class c)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 
accessFlagsToString(final int flags, final boolean isClass)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      */
javaNameToVMName(final String javaName)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      */
vmNameToJavaName(final String vmName)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      */
signatureToDescriptor(Method method)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      */
signatureToDescriptor(Class [] parameterTypes, Class returnType)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      */
typeToDescriptor(Class type)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      */
descriptorToType(String typedescriptor)178     public static Class descriptorToType (String typedescriptor) throws ClassNotFoundException
179     {
180         return new typeDescriptorCompiler ().descriptorToClass (typedescriptor);
181     }
182 
183 
184 
descriptorToReturnType(String methoddescriptor)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 
descriptorToParameterTypes(String methoddescriptor)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 
signatureToMethodDescriptor(final String [] parameterTypeDescriptors, final String returnTypeDescriptor)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 
typeDescriptorToUserName(final String typedescriptor)333     public static String typeDescriptorToUserName (final String typedescriptor)
334     {
335         return new typeDescriptorCompiler2 ().descriptorToClass (typedescriptor);
336     }
337 
methodDescriptorToUserName(final String methoddescriptor)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 
fullMethodDescriptorToUserName(final String classJavaName, String methodName, final String methoddescriptor)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
fullMethodDescriptorToFullUserName(final String classJavaName, String methodName, final String methoddescriptor)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 
simpleClassName(final String classJavaName)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     {
signatureDescriptor(Class [] _parameterTypes, Class _returnType)405         String signatureDescriptor (Class [] _parameterTypes, Class _returnType)
406         {
407             emit ('(');    parameterTypes (_parameterTypes); emit (')'); returnType (_returnType);
408 
409             return m_desc.toString ();
410         }
411 
typeDescriptor(Class type)412         String typeDescriptor (Class type)
413         {
414             parameterType (type);
415 
416             return m_desc.toString ();
417         }
418 
419 
parameterTypes(Class [] _parameterTypes)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 
returnType(Class _returnType)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 
parameterType(Class _parameterType)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 
emit(String s)468         private void emit (String s)
469         {
470             m_desc.append (s);
471         }
472 
emit(char c)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 
descriptorToClass(String typedescriptor)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 
arrayOf(String typedescriptor)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 
primitive(char c)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     {
descriptorToClass(String typedescriptor)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 
arrayOf(String typedescriptor)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 
primitive(char c)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     {
methodDescriptorCompiler(String methoddescriptor)611         methodDescriptorCompiler (String methoddescriptor)
612         {
613             m_in = new java.io.PushbackReader (new java.io.StringReader (methoddescriptor));
614         }
615 
getResult()616         String [] getResult ()
617         {
618             final String [] result = new String [m_result.size ()];
619             m_result.toArray (result);
620 
621             return result;
622         }
623 
methodDescriptor()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 
parameterDescriptor()637         void parameterDescriptor () throws IOException
638         {
639             fieldType ();
640             newToken ();
641         }
642 
returnDescriptor()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 
componentType()661         void componentType () throws IOException
662         {
663             fieldType ();
664         }
665 
objectType()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 
arrayType()679         void arrayType () throws IOException
680         {
681             consume ('[');
682             m_token.append ('[');
683 
684             componentType ();
685         }
686 
fieldType()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 
baseType()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 
consume(char expected)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 
newToken()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