1 /***
2 *
3 * Copyright 2004 James Strachan
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 **/
18 package org.codehaus.groovy.syntax;
19
20 import org.codehaus.groovy.ast.ModuleNode;
21 import org.codehaus.groovy.control.CompilationFailedException;
22 import org.codehaus.groovy.control.SourceUnit;
23 import org.codehaus.groovy.syntax.Types;
24
25 import java.util.ArrayList;
26 import java.util.HashMap;
27 import java.util.List;
28 import java.util.Map;
29
30 /***
31 * A common base class of AST helper methods which can be shared across the classic and new parsers
32 *
33 * @author James Strachan
34 * @author Bob McWhirter
35 * @author Sam Pullara
36 * @author Chris Poirier
37 * @version $Revision: 1.7 $
38 */
39 public class ASTHelper {
40
41 private static final String[] EMPTY_STRING_ARRAY = new String[0];
42 private static final String[] DEFAULT_IMPORTS = {"java.lang.", "java.io.", "java.net.", "java.util.", "groovy.lang.", "groovy.util."};
43
44 /*** The SourceUnit controlling us */
45 private SourceUnit controller;
46
47 /*** Our ClassLoader, which provides information on external types */
48 private ClassLoader classLoader;
49
50 /*** Our imports, simple name => fully qualified name */
51 private Map imports;
52 protected ModuleNode output;
53
54 /**</package-summary/html">The package name in which the module sits *//package-summary.html">em>* The package name in which the module sits */
55 private String packageName/package-summary.html">ong> String packageName;
56
57
58 protected static HashMap resolutions = new HashMap();
59
60 private static String NOT_RESOLVED = new String();
61
62 /*** temporarily store the class names that the current modulenode contains */
63 private List newClasses = new ArrayList();
64
65 public ASTHelper(SourceUnit controller, ClassLoader classLoader) {
66 this();
67 this.controller = controller;
68 this.classLoader = classLoader;
69 }
70
71 public ASTHelper() {
72 imports = new HashMap();
73 }
74
75 public String getPackageName() {
76 return</strong> packageName;
77 }
78
79 public void setPackageName(String packageName) {/package-summary.html">ong> void setPackageName(String packageName) {
80 this.packageName = packageName;
81
82 output.setPackageName(packageName);
83 }
84
85
86 /***
87 * Returns our class loader (as supplied on construction).
88 */
89 public ClassLoader getClassLoader() {
90 return classLoader;
91 }
92
93 public void setClassLoader(ClassLoader classLoader) {
94 this.classLoader = classLoader;
95 }
96
97 public SourceUnit getController() {
98 return controller;
99 }
100
101 public void setController(SourceUnit controller) {
102 this.controller = controller;
103 }
104
105 /***
106 * Returns a fully qualified name for any given potential type
107 * name. Returns null if no qualified name could be determined.
108 */
109
110 protected String resolveName(String name, boolean safe) {
111
112
113
114 String resolution = (String) resolutions.get(name);
115 if (NOT_RESOLVED.equals(resolution)) {
116 return (safe ? name : null);
117 }
118 else if (resolution != null) {
119 return (String) resolution;
120 }
121
122 try {
123 getClassLoader().loadClass(name);
124 resolutions.put(name,name);
125 return name;
126 } catch (ClassNotFoundException cnfe){
127 if (cnfe.getCause() instanceof CompilationFailedException) {
128 resolutions.put(name,name);
129 return name;
130 }
131 } catch (NoClassDefFoundError ncdfe) {
132
133 }
134
135 do {
136
137
138
139
140 if (name.indexOf(".") >= 0) {
141 resolution = name;
142 break;
143 }
144
145
146
147
148
149
150 String scalar = name, postfix = "";
151 while (scalar.endsWith("[]")) {
152 scalar = scalar.substring(0, scalar.length() - 2);
153 postfix += "[]";
154 }
155
156
157
158
159
160 if (Types.ofType(Types.lookupKeyword(scalar), Types.PRIMITIVE_TYPE)) {
161 resolution = name;
162 break;
163 }
164
165
166
167
168
169
170 if (this.imports.containsKey(scalar)) {
171 resolution = ((String) this.imports.get(scalar)) + postfix;
172 break;
173 }
174
175
176
177
178
179 if (packageName != null && packageName.length() > 0) {
180 try {
181 getClassLoader().loadClass(dot(packageName, scalar));
182 resolution = dot(packageName, name);
183
184 break;
185 } catch (ClassNotFoundException cnfe){
186 if (cnfe.getCause() instanceof CompilationFailedException) {
187 resolution = dot(packageName, name);
188 break;
189 }
190 } catch (NoClassDefFoundError ncdfe) {
191
192 }
193 }
194
195
196 List packageImports = output.getImportPackages();
197 for (int i = 0; i < packageImports.size(); i++) {
198 String pack = (String) packageImports.get(i);
199 String clsName = pack + name;
200 try {
201 getClassLoader().loadClass(clsName);
202 resolution = clsName;
203 break;
204 } catch (ClassNotFoundException cnfe){
205 if (cnfe.getCause() instanceof CompilationFailedException) {
206 resolution = clsName;
207 break;
208 }
209 } catch (NoClassDefFoundError ncdfe) {
210
211 }
212 }
213 if (resolution != null) {
214 break;
215 }
216
217
218
219
220 for (int i = 0; i < DEFAULT_IMPORTS.length; i++) {
221 String qualified = DEFAULT_IMPORTS[i] + scalar;
222 try {
223 getClassLoader().loadClass(qualified);
224
225 resolution = qualified + postfix;
226 break;
227 } catch (ClassNotFoundException cnfe){
228 if (cnfe.getCause() instanceof CompilationFailedException) {
229 resolution = qualified + postfix;
230 break;
231 }
232 } catch (NoClassDefFoundError ncdfee) {
233
234 }
235 }
236
237 }
238 while (false);
239
240
241
242
243
244 if (resolution == null) {
245 resolutions.put(name, NOT_RESOLVED);
246 return (safe ? name : null);
247 }
248 else {
249 resolutions.put(name, resolution);
250 return resolution;
251 }
252 }
253
254 /***
255 * Returns two names joined by a dot. If the base name is
256 * empty, returns the name unchanged.
257 */
258
259 protected String dot(String base, String name) {
260 if (base != null && base.length() > 0) {
261 return base + "." + name;
262 }
263
264 return name;
265 }
266
267 protected void makeModule() {
268 this.newClasses.clear();
269 this.output = new ModuleNode(controller);
270 resolutions.clear();
271 }
272
273 /***
274 * Returns true if the specified name is a known type name.
275 */
276
277 protected boolean isDatatype(String name) {
278 return resolveName(name, false) != null;
279 }
280
281 /***
282 * A synonym for <code>dot( base, "" )</code>.
283 */
284
285 protected String dot(String base) {
286 return dot(base, "");
287 }
288
289 protected String resolveNewClassOrName(String name, boolean safe) {
290 if (this.newClasses.contains(name)) {
291 return dot(packageName, name);
292 }
293 else {
294 return resolveName(name, safe);
295 }
296 }
297
298 protected void addNewClassName(String name) {
299 this.newClasses.add(name);
300 }
301
302 protected void importClass(String importPackage, String name, String as) {
303
304
305
306
307
308
309
310
311
312 if (as==null) as=name;
313 output.addImport( as, name );
314
315 name = dot( importPackage, name );
316
317
318 imports.put( as, name );
319 }
320
321 protected void importPackageWithStar(String importPackage) {
322 String[] classes = output.addImportPackage( dot(importPackage) );
323 for( int i = 0; i < classes.length; i++ )
324 {
325 imports.put( classes[i], dot(importPackage, classes[i]) );
326 }
327 }
328 }