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 */
9package com.vladium.jcd.lib;
10
11import java.io.IOException;
12import java.lang.reflect.*;
13
14import 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 */
24public 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