KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > directwebremoting > impl > SignatureParser


1 /*
2  * Copyright 2005 Joe Walker
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 package org.directwebremoting.impl;
17
18 import java.lang.reflect.Method JavaDoc;
19 import java.util.ArrayList JavaDoc;
20 import java.util.HashMap JavaDoc;
21 import java.util.Iterator JavaDoc;
22 import java.util.List JavaDoc;
23 import java.util.Map JavaDoc;
24 import java.util.StringTokenizer JavaDoc;
25
26 import org.directwebremoting.extend.ConverterManager;
27 import org.directwebremoting.extend.Creator;
28 import org.directwebremoting.extend.CreatorManager;
29 import org.directwebremoting.extend.TypeHintContext;
30 import org.directwebremoting.util.JavascriptUtil;
31 import org.directwebremoting.util.LocalUtil;
32 import org.directwebremoting.util.Logger;
33
34 /**
35  * A parser for type info in a dwr.xml signature.
36  * @author Joe Walker [joe at getahead dot ltd dot uk]
37  */

38 public class SignatureParser
39 {
40     /**
41      * Simple ctor
42      * @param converterManager Having understood the extra type info we add it in here.
43      * @param creatorManager If we can't find a class by Java name we can lookup by Javascript name
44      */

45     public SignatureParser(ConverterManager converterManager, CreatorManager creatorManager)
46     {
47         this.converterManager = converterManager;
48         this.creatorManager = creatorManager;
49
50         packageImports.add("java.lang");
51     }
52
53     /**
54      * Parse some text and add it into the converter manager.
55      * @param sigtext The text to parse
56      */

57     public void parse(String JavaDoc sigtext)
58     {
59         try
60         {
61             log.debug("Parsing extra type info: ");
62
63             String JavaDoc reply = JavascriptUtil.stripMultiLineComments(sigtext);
64             reply = JavascriptUtil.stripSingleLineComments(reply);
65             String JavaDoc process = reply;
66
67             process = process.replace('\n', ' ');
68             process = process.replace('\r', ' ');
69             process = process.replace('\t', ' ');
70
71             StringTokenizer JavaDoc st = new StringTokenizer JavaDoc(process, ";");
72             while (st.hasMoreTokens())
73             {
74                 String JavaDoc line = st.nextToken();
75                 line = line.trim();
76                 if (line.length() == 0)
77                 {
78                     continue;
79                 }
80
81                 if (line.startsWith("import "))
82                 {
83                     parseImportLine(line);
84                 }
85                 else
86                 {
87                     parseDeclarationLine(line);
88                 }
89             }
90         }
91         catch (Exception JavaDoc ex)
92         {
93             log.error("Unexpected Error", ex);
94         }
95     }
96
97     /**
98      * Parse a single import line
99      * @param line The import statement
100      */

101     private void parseImportLine(String JavaDoc line)
102     {
103         String JavaDoc shortcut = line.substring(7, line.length());
104         shortcut = shortcut.trim();
105
106         if (line.endsWith(".*"))
107         {
108             shortcut = shortcut.substring(0, shortcut.length() - 2);
109             packageImports.add(shortcut);
110         }
111         else
112         {
113             int lastDot = line.lastIndexOf('.');
114             if (lastDot == -1)
115             {
116                 log.error("Missing . from import statement: " + line);
117                 return;
118             }
119
120             String JavaDoc leaf = line.substring(lastDot + 1);
121             classImports.put(leaf, shortcut);
122         }
123     }
124
125     /**
126      * Parse a single declaration line.
127      * Where line is defined as being everything in between 2 ; chars.
128      * @param line The line to parse
129      */

130     private void parseDeclarationLine(String JavaDoc line)
131     {
132         int openBrace = line.indexOf('(');
133         int closeBrace = line.indexOf(')');
134
135         if (openBrace == -1)
136         {
137             log.error("Missing ( in declaration: " + line);
138             return;
139         }
140
141         if (closeBrace == -1)
142         {
143             log.error("Missing ) in declaration: " + line);
144             return;
145         }
146
147         if (openBrace > closeBrace)
148         {
149             log.error("( Must come before ) in declaration: " + line);
150             return;
151         }
152
153         // Class name and method name come before the opening (
154
String JavaDoc classMethod = line.substring(0, openBrace).trim();
155
156         Method JavaDoc method = findMethod(classMethod);
157         if (method == null)
158         {
159             // Debug is done by findMethod()
160
return;
161         }
162
163         // Now we need to get a list of all the parameters
164
String JavaDoc paramDecl = line.substring(openBrace + 1, closeBrace);
165         String JavaDoc[] paramNames = split(paramDecl);
166
167         // Check that we have the right number
168
if (method.getParameterTypes().length != paramNames.length)
169         {
170             log.error("Parameter mismatch parsing signatures section in dwr.xml on line: " + line);
171             log.info("- Reflected method had: " + method.getParameterTypes().length + " parameters: " + method.toString());
172             log.info("- Signatures section had: " + paramNames.length + " parameters");
173             log.info("- This can be caused by method overloading which is not supported by Javascript or DWR");
174             return;
175         }
176
177         for (int i = 0; i < paramNames.length; i++)
178         {
179             String JavaDoc[] genericList = getGenericParameterTypeList(paramNames[i]);
180             for (int j = 0; j < genericList.length; j++)
181             {
182                 String JavaDoc type = genericList[j].trim();
183                 Class JavaDoc clazz = findClass(type);
184
185                 if (clazz != null)
186                 {
187                     TypeHintContext thc = new TypeHintContext(converterManager, method, i).createChildContext(j);
188                     converterManager.setExtraTypeInfo(thc, clazz);
189
190                     if (log.isDebugEnabled())
191                     {
192                         log.debug("- " + thc + " = " + clazz.getName());
193                     }
194                 }
195                 else
196                 {
197                     log.warn("Missing class (" + type + ") while parsing signature section on line: " + line);
198                 }
199             }
200         }
201     }
202
203     /**
204      * Lookup a class acording to the import rules
205      * @param type The name of the class to find
206      * @return The found class, or null if it does not exist
207      */

208     private Class JavaDoc findClass(String JavaDoc type)
209     {
210         String JavaDoc itype = type;
211
212         // Handle inner classes
213
if (itype.indexOf('.') != -1)
214         {
215             log.debug("Inner class detected: " + itype);
216             itype = itype.replace('.', '$');
217         }
218
219         try
220         {
221             String JavaDoc full = (String JavaDoc) classImports.get(itype);
222             if (full == null)
223             {
224                 full = itype;
225             }
226
227             return LocalUtil.classForName(full);
228         }
229         catch (Exception JavaDoc ex)
230         {
231             // log.debug("Trying to find class in package imports");
232
}
233
234         for (Iterator JavaDoc it = packageImports.iterator(); it.hasNext();)
235         {
236             String JavaDoc pkg = (String JavaDoc) it.next();
237             String JavaDoc lookup = pkg + '.' + itype;
238
239             try
240             {
241                 return LocalUtil.classForName(lookup);
242             }
243             catch (Exception JavaDoc ex)
244             {
245                 // log.debug("Not found: " + lookup);
246
}
247         }
248
249         // So we've failed to find a Java class name. We can also lookup by
250
// Javascript name to help the situation where there is a dynamic proxy
251
// in the way.
252
Creator creator = creatorManager.getCreator(type);
253         if (creator != null)
254         {
255             return creator.getType();
256         }
257
258         log.error("Failed to find class: '" + itype + "' from <signature> block.");
259         log.info("- Looked in the following class imports:");
260         for (Iterator JavaDoc it = classImports.entrySet().iterator(); it.hasNext();)
261         {
262             Map.Entry JavaDoc entry = (Map.Entry JavaDoc) it.next();
263             log.info(" - " + entry.getKey() + " -> " + entry.getValue());
264         }
265         log.info("- Looked in the following package imports:");
266         for (Iterator JavaDoc it = packageImports.iterator(); it.hasNext();)
267         {
268             log.info(" - " + it.next());
269         }
270
271         return null;
272     }
273
274     /**
275      * Convert a parameter like "Map&lt;Integer, URL&gt;" into an array,
276      * something like [Integer, URL].
277      * @param paramName The parameter declaration string
278      * @return The array of generic types as strings
279      */

280     private String JavaDoc[] getGenericParameterTypeList(String JavaDoc paramName)
281     {
282         int openGeneric = paramName.indexOf('<');
283         if (openGeneric == -1)
284         {
285             log.debug("No < in paramter declaration: " + paramName);
286             return new String JavaDoc[0];
287         }
288
289         int closeGeneric = paramName.lastIndexOf('>');
290         if (closeGeneric == -1)
291         {
292             log.error("Missing > in generic declaration: " + paramName);
293             return new String JavaDoc[0];
294         }
295
296         String JavaDoc generics = paramName.substring(openGeneric + 1, closeGeneric);
297         StringTokenizer JavaDoc st = new StringTokenizer JavaDoc(generics, ",");
298         String JavaDoc[] types = new String JavaDoc[st.countTokens()];
299         int i = 0;
300         while (st.hasMoreTokens())
301         {
302             types[i++] = st.nextToken();
303         }
304
305         return types;
306     }
307
308     /**
309      * Find a method from the declaration string
310      * @param classMethod The declaration that comes before the (
311      * @return The found method, or null if one was not found.
312      */

313     private Method JavaDoc findMethod(String JavaDoc classMethod)
314     {
315         String JavaDoc classMethodChop = classMethod;
316
317         // If there is a return type then it must be before the last space.
318
int lastSpace = classMethodChop.lastIndexOf(' ');
319         if (lastSpace >= 0)
320         {
321             classMethodChop = classMethodChop.substring(lastSpace);
322         }
323
324         // The method name comes after the last .
325
int lastDot = classMethodChop.lastIndexOf('.');
326         if (lastDot == -1)
327         {
328             log.error("Missing . to separate class name and method: " + classMethodChop);
329             return null;
330         }
331
332         String JavaDoc className = classMethodChop.substring(0, lastDot).trim();
333         String JavaDoc methodName = classMethodChop.substring(lastDot + 1).trim();
334
335         Class JavaDoc clazz = findClass(className);
336         if (clazz == null)
337         {
338             // Debug is done by findClass()
339
return null;
340         }
341
342         Method JavaDoc method = null;
343         Method JavaDoc[] methods = clazz.getMethods();
344         for (int j = 0; j < methods.length; j++)
345         {
346             Method JavaDoc test = methods[j];
347             if (test.getName().equals(methodName))
348             {
349                 if (method == null)
350                 {
351                     method = test;
352                 }
353                 else
354                 {
355                     log.warn("Setting extra type info to overloaded methods may fail with <parameter .../>");
356                 }
357             }
358         }
359
360         if (method == null)
361         {
362             log.error("Unable to find method called: " + methodName + " on type: " + clazz.getName());
363         }
364
365         return method;
366     }
367
368     /**
369      * Chop a parameter declaration string into separate parameters
370      * @param paramDecl The full set of parameter declarations
371      * @return An array of found parameters
372      */

373     private String JavaDoc[] split(String JavaDoc paramDecl)
374     {
375         List JavaDoc params = new ArrayList JavaDoc();
376
377         boolean inGeneric = false;
378         int start = 0;
379         for (int i = 0; i < paramDecl.length(); i++)
380         {
381             char c = paramDecl.charAt(i);
382             if (c == '<')
383             {
384                 if (inGeneric)
385                 {
386                     log.error("Found < while parsing generic section: " + paramDecl);
387                     break;
388                 }
389
390                 inGeneric = true;
391             }
392
393             if (c == '>')
394             {
395                 if (!inGeneric)
396                 {
397                     log.error("Found > while not parsing generic section: " + paramDecl);
398                     break;
399                 }
400
401                 inGeneric = false;
402             }
403
404             if (!inGeneric && c == ',')
405             {
406                 // This is the start of a new parameter
407
String JavaDoc param = paramDecl.substring(start, i);
408                 params.add(param);
409                 start = i + 1;
410             }
411         }
412
413         // Add in the bit at the end:
414
String JavaDoc param = paramDecl.substring(start, paramDecl.length());
415         params.add(param);
416
417         return (String JavaDoc[]) params.toArray(new String JavaDoc[params.size()]);
418     }
419
420     /**
421      * The map of specific class imports that we have parsed.
422      */

423     private Map JavaDoc classImports = new HashMap JavaDoc();
424
425     /**
426      * The map of package imports that we have parsed.
427      */

428     private List JavaDoc packageImports = new ArrayList JavaDoc();
429
430     /**
431      * Having understood the extra type info we add it in here.
432      */

433     private ConverterManager converterManager;
434
435     /**
436      * If we can't find a class by Java name we can lookup by Javascript name
437      */

438     private CreatorManager creatorManager;
439
440     /**
441      * The log stream
442      */

443     public static final Logger log = Logger.getLogger(SignatureParser.class);
444 }
445
Popular Tags