1/*
2 * ProGuard -- shrinking, optimization, obfuscation, and preverification
3 *             of Java bytecode.
4 *
5 * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu)
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the Free
9 * Software Foundation; either version 2 of the License, or (at your option)
10 * any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21package proguard.classfile.util;
22
23import proguard.classfile.ClassConstants;
24
25
26/**
27 * An <code>InternalTypeEnumeration</code> provides an enumeration of all
28 * parameter types listed in a given internal method descriptor or signature.
29 * The signature can also be a class signature. The return type of a method
30 * descriptor can be retrieved separately.
31 *
32 * @author Eric Lafortune
33 */
34public class InternalTypeEnumeration
35{
36    private String descriptor;
37    private int    firstIndex;
38    private int    lastIndex;
39    private int    index;
40
41
42    /**
43     * Creates a new InternalTypeEnumeration for the given method descriptor.
44     */
45    public InternalTypeEnumeration(String descriptor)
46    {
47        this.descriptor = descriptor;
48        this.firstIndex = descriptor.indexOf(ClassConstants.METHOD_ARGUMENTS_OPEN);
49        this.lastIndex  = descriptor.indexOf(ClassConstants.METHOD_ARGUMENTS_CLOSE);
50        this.index      = firstIndex + 1;
51
52        if (lastIndex < 0)
53        {
54            lastIndex = descriptor.length();
55        }
56    }
57
58
59    /**
60     * Returns whether the type is a method signature.
61     */
62    public boolean isMethodSignature()
63    {
64        return firstIndex >= 0;
65    }
66
67
68    /**
69     * Returns the formal type parameters from the descriptor, assuming it's a
70     * method descriptor.
71     */
72    public String formalTypeParameters()
73    {
74        return descriptor.substring(0, firstIndex);
75    }
76
77
78    /**
79     * Returns whether the enumeration can provide more types from the method
80     * descriptor.
81     */
82    public boolean hasMoreTypes()
83    {
84        return index < lastIndex;
85    }
86
87
88    /**
89     * Returns the next type from the method descriptor.
90     */
91    public String nextType()
92    {
93        int startIndex = index;
94
95        skipArray();
96
97        char c = descriptor.charAt(index++);
98        switch (c)
99        {
100            case ClassConstants.TYPE_CLASS_START:
101            case ClassConstants.TYPE_GENERIC_VARIABLE_START:
102            {
103                skipClass();
104                break;
105            }
106            case ClassConstants.TYPE_GENERIC_START:
107            {
108                skipGeneric();
109                break;
110            }
111        }
112
113        return descriptor.substring(startIndex, index);
114    }
115
116
117    /**
118     * Returns the return type from the descriptor, assuming it's a method
119     * descriptor.
120     */
121    public String returnType()
122    {
123        return descriptor.substring(lastIndex + 1);
124    }
125
126
127    // Small utility methods.
128
129    private void skipArray()
130    {
131        while (descriptor.charAt(index) == ClassConstants.TYPE_ARRAY)
132        {
133            index++;
134        }
135    }
136
137
138    private void skipClass()
139    {
140        while (true)
141        {
142            char c = descriptor.charAt(index++);
143            switch (c)
144            {
145                case ClassConstants.TYPE_GENERIC_START:
146                    skipGeneric();
147                    break;
148
149                case ClassConstants.TYPE_CLASS_END:
150                    return;
151            }
152        }
153    }
154
155
156    private void skipGeneric()
157    {
158        int nestingLevel = 1;
159
160        do
161        {
162            char c = descriptor.charAt(index++);
163            switch (c)
164            {
165                case ClassConstants.TYPE_GENERIC_START:
166                    nestingLevel++;
167                    break;
168
169                case ClassConstants.TYPE_GENERIC_END:
170                    nestingLevel--;
171                    break;
172            }
173        }
174        while (nestingLevel > 0);
175    }
176
177
178    /**
179     * A main method for testing the type enumeration.
180     */
181    public static void main(String[] args)
182    {
183        try
184        {
185            for (int index = 0; index < args.length; index++)
186            {
187                String descriptor = args[index];
188
189                System.out.println("Descriptor ["+descriptor+"]");
190                InternalTypeEnumeration enumeration = new InternalTypeEnumeration(descriptor);
191
192                if (enumeration.firstIndex >= 0)
193                {
194                    System.out.println("  Formal type parameters ["+enumeration.formalTypeParameters()+"]");
195                }
196
197                while (enumeration.hasMoreTypes())
198                {
199                    System.out.println("  Type ["+enumeration.nextType()+"]");
200                }
201
202                if (enumeration.lastIndex < descriptor.length())
203                {
204                    System.out.println("  Return type ["+enumeration.returnType()+"]");
205                }
206            }
207        }
208        catch (Exception ex)
209        {
210            ex.printStackTrace();
211        }
212    }
213}
214