KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > cojen > classfile > MethodDeclarationParser


1 /*
2  * Copyright 2005 Brian S O'Neill
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16
17 package org.cojen.classfile;
18
19 import java.util.ArrayList JavaDoc;
20
21 /**
22  * Utility class that supports parsing of Java method declarations. For
23  * example, <code>public static int myMethod(java.lang.String, int value,
24  * java.lang.String... extra)</code>.
25  *
26  * <p>The parser is fairly lenient. It doesn't care if the set of modifiers is
27  * illegal, and it doesn't require that arguments have variable names assigned.
28  * A semi-colon may appear at the end of the signature.
29  *
30  * <p>At most one variable argument is supported (at the end), and all class
31  * names must be fully qualified.
32  *
33  * @author Brian S O'Neill
34  */

35 public class MethodDeclarationParser {
36     /**
37      * @param src descriptor source string
38      * @param pos position in source, updated at exit
39      */

40     private static void skipWhitespace(String JavaDoc src, int[] pos) {
41         int length = src.length();
42         int i = pos[0];
43         while (i < length) {
44             char c = src.charAt(i);
45             if (Character.isWhitespace(c)) {
46                 i++;
47             } else {
48                 break;
49             }
50         }
51         pos[0] = i;
52     }
53
54     /**
55      * @param src descriptor source string
56      * @param pos pos[0] is position in source, updated at exit
57      * @return null if nothing left
58      */

59     private static String JavaDoc parseIdentifier(String JavaDoc src, int[] pos) {
60         // Skip leading whitespace.
61
skipWhitespace(src, pos);
62         int i = pos[0];
63         int length = src.length();
64         if (i >= length) {
65             return null;
66         }
67
68         int startPos = i;
69         char c = src.charAt(i);
70         if (!Character.isJavaIdentifierStart(c)) {
71             return null;
72         }
73         i++;
74
75         while (i < length) {
76             c = src.charAt(i);
77             if (Character.isJavaIdentifierPart(c)) {
78                 i++;
79             } else {
80                 break;
81             }
82         }
83
84         pos[0] = i;
85
86         // Skip trailing whitespace too.
87
skipWhitespace(src, pos);
88
89         return src.substring(startPos, i);
90     }
91
92     private static TypeDesc parseTypeDesc(String JavaDoc src, int[] pos) {
93         // Skip leading whitespace.
94
skipWhitespace(src, pos);
95         int i = pos[0];
96         int length = src.length();
97         if (i >= length) {
98             return null;
99         }
100
101         int startPos = i;
102         char c = src.charAt(i);
103         if (!Character.isJavaIdentifierStart(c)) {
104             return null;
105         }
106         i++;
107
108         while (i < length) {
109             c = src.charAt(i);
110             if (c == '.') {
111                 // Don't allow double dots.
112
if (i + 1 < length && src.charAt(i + 1) == '.') {
113                     break;
114                 } else {
115                     i++;
116                 }
117             } else if (Character.isJavaIdentifierPart(c) || c == '[' || c == ']') {
118                 i++;
119             } else {
120                 break;
121             }
122         }
123
124         pos[0] = i;
125
126         // Skip trailing whitespace too.
127
skipWhitespace(src, pos);
128
129         return TypeDesc.forClass(src.substring(startPos, i));
130     }
131
132     /**
133      * @param src descriptor source string
134      * @param pos pos[0] is position in source, updated at exit
135      */

136     private static Modifiers parseModifiers(String JavaDoc src, int[] pos) {
137         Modifiers modifiers = Modifiers.NONE;
138
139         int length = src.length();
140         loop: while (pos[0] < length) {
141             int savedPos = pos[0];
142             String JavaDoc ident = parseIdentifier(src, pos);
143             int newPos = pos[0];
144             pos[0] = savedPos;
145
146             if (ident == null) {
147                 break;
148             }
149
150             switch (ident.charAt(0)) {
151             case 'a':
152                 if ("abstract".equals(ident)) {
153                     modifiers = modifiers.toAbstract(true);
154                 } else {
155                     break loop;
156                 }
157                 break;
158
159             case 'f':
160                 if ("final".equals(ident)) {
161                     modifiers = modifiers.toFinal(true);
162                 } else {
163                     break loop;
164                 }
165                 break;
166
167             case 'n':
168                 if ("native".equals(ident)) {
169                     modifiers = modifiers.toNative(true);
170                 } else {
171                     break loop;
172                 }
173                 break;
174
175             case 'p':
176                 if ("public".equals(ident)) {
177                     modifiers = modifiers.toPublic(true);
178                 } else if ("private".equals(ident)) {
179                     modifiers = modifiers.toPrivate(true);
180                 } else if ("protected".equals(ident)) {
181                     modifiers = modifiers.toProtected(true);
182                 } else {
183                     break loop;
184                 }
185                 break;
186
187             case 's':
188                 if ("static".equals(ident)) {
189                     modifiers = modifiers.toStatic(true);
190                 } else if ("synchronized".equals(ident)) {
191                     modifiers = modifiers.toSynchronized(true);
192                 } else if ("strict".equals(ident)) {
193                     modifiers = modifiers.toStrict(true);
194                 } else {
195                     break loop;
196                 }
197                 break;
198
199             case 't':
200                 if ("transient".equals(ident)) {
201                     modifiers = modifiers.toTransient(true);
202                 } else {
203                     break loop;
204                 }
205                 break;
206
207             case 'v':
208                 if ("volatile".equals(ident)) {
209                     modifiers = modifiers.toVolatile(true);
210                 } else {
211                     break loop;
212                 }
213                 break;
214
215             default:
216                 break loop;
217             } // end switch
218

219             // If this point is reached, valid modifier was parsed. Advance position.
220
pos[0] = newPos;
221         }
222
223         return modifiers;
224     }
225
226     private static TypeDesc[] parseParameters(String JavaDoc src, int[] pos, boolean[] isVarArgs) {
227         // Skip trailing whitespace too.
228
skipWhitespace(src, pos);
229         int length = src.length();
230         if (pos[0] < length && src.charAt(pos[0]) != '(') {
231             throw new IllegalArgumentException JavaDoc("Left paren expected");
232         }
233         pos[0]++;
234
235         ArrayList JavaDoc list = new ArrayList JavaDoc();
236
237         boolean expectParam = false;
238         while (pos[0] < length) {
239             TypeDesc type = parseTypeDesc(src, pos);
240             if (type == null) {
241                 if (expectParam) {
242                     throw new IllegalArgumentException JavaDoc("Parameter type expected");
243                 }
244                 break;
245             }
246             list.add(type);
247
248             // Parse optional parameter name
249
parseIdentifier(src, pos);
250
251             if (pos[0] < length) {
252                 char c = src.charAt(pos[0]);
253                 if (c == ',') {
254                     // More params to follow...
255
pos[0]++;
256                     expectParam = true;
257                     continue;
258                 } else if (c == ')') {
259                     pos[0]++;
260                     break;
261                 }
262
263                 // Handle varargs.
264
if (c == '.' && pos[0] + 2 < length) {
265                     if (src.charAt(pos[0] + 1) == '.' && src.charAt(pos[0] + 2) == '.') {
266                         type = type.toArrayType();
267                         isVarArgs[0] = true;
268                         list.set(list.size() - 1, type);
269                         pos[0] += 3;
270                         expectParam = false;
271                         continue;
272                     }
273                 }
274
275                 throw new IllegalArgumentException JavaDoc("Expected comma or right paren");
276             }
277         }
278
279         return (TypeDesc[]) list.toArray(new TypeDesc[list.size()]);
280     }
281
282     private final Modifiers mModifiers;
283     private final TypeDesc mReturnType;
284     private final String JavaDoc mMethodName;
285     private final TypeDesc[] mParameters;
286
287     /**
288      * Parse the given method declaration, throwing a exception if the syntax
289      * is wrong.
290      *
291      * @param declaration declaration to parse, which matches Java syntax
292      * @throws IllegalArgumentException if declaration syntax is wrong
293      */

294     public MethodDeclarationParser(String JavaDoc declaration) throws IllegalArgumentException JavaDoc {
295         int[] pos = new int[1];
296         Modifiers modifiers = parseModifiers(declaration, pos);
297         mReturnType = parseTypeDesc(declaration, pos);
298         if (mReturnType == null) {
299             throw new IllegalArgumentException JavaDoc("No return type");
300         }
301         mMethodName = parseIdentifier(declaration, pos);
302         if (mMethodName == null) {
303             throw new IllegalArgumentException JavaDoc("No method name");
304         }
305         boolean[] isVarArgs = new boolean[1];
306         mParameters = parseParameters(declaration, pos, isVarArgs);
307         if (isVarArgs[0]) {
308             modifiers = modifiers.toVarArgs(true);
309         }
310         mModifiers = modifiers;
311     }
312
313     public Modifiers getModifiers() {
314         return mModifiers;
315     }
316
317     public TypeDesc getReturnType() {
318         return mReturnType;
319     }
320
321     public String JavaDoc getMethodName() {
322         return mMethodName;
323     }
324
325     public TypeDesc[] getParameters() {
326         return (TypeDesc[])mParameters.clone();
327     }
328 }
329
Popular Tags