View Javadoc

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      privateong> String packageName;   //
56  
57      // TODO should this really be static???
58      protected static HashMap resolutions = new HashMap();  // cleared on build(), to be safe
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      publicong> 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         // Use our cache of resolutions, if possible
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             //fall through
133         }
134 
135         do {
136             //
137             // If the type name contains a ".", it's probably fully
138             // qualified, and we don't take it to verification here.
139 
140             if (name.indexOf(".") >= 0) {
141                 resolution = name;
142                 break;                                            // <<< FLOW CONTROL <<<<<<<<<
143             }
144 
145 
146             //
147             // Otherwise, we'll need the scalar type for checking, and
148             // the postfix for reassembly.
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             // Primitive types are all valid...
159 
160             if (Types.ofType(Types.lookupKeyword(scalar), Types.PRIMITIVE_TYPE)) {
161                 resolution = name;
162                 break;                                            // <<< FLOW CONTROL <<<<<<<<<
163             }
164 
165 
166             //
167             // Next, check our imports and return the qualified name,
168             // if available.
169 
170             if (this.imports.containsKey(scalar)) {
171                 resolution = ((String) this.imports.get(scalar)) + postfix;
172                 break;                                            // <<< FLOW CONTROL <<<<<<<<<
173             }
174 
175 
176             //
177             // Next, see if our class loader can resolve it in the current package.
178 
179             if (packageName != null && packageName.length() > 0) {
180                 try {
181                     getClassLoader().loadClass(dot(packageName, scalar));
182                     resolution = dot(packageName, name);
183 
184                     break;                                        // <<< FLOW CONTROL <<<<<<<<<
185                 } catch (ClassNotFoundException cnfe){
186                     if (cnfe.getCause() instanceof CompilationFailedException) {
187                         resolution = dot(packageName, name);
188                         break;
189                     }
190                 } catch (NoClassDefFoundError ncdfe) {
191                     //fall through
192                 }
193             }
194 
195             // search the package imports path
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                     //fall through
211                 }
212             }
213             if (resolution != null) {
214                 break;
215             }
216 
217             //
218             // Last chance, check the default imports.
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;                                        // <<< FLOW CONTROL <<<<<<<<<
227                 } catch (ClassNotFoundException cnfe){
228                     if (cnfe.getCause() instanceof CompilationFailedException) {
229                         resolution = qualified + postfix;
230                         break;
231                     }
232                 } catch (NoClassDefFoundError ncdfee) {
233                     // fall through
234                 }
235             }
236 
237         }
238         while (false);
239 
240 
241         //
242         // Cache the solution and return it
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         // There appears to be a bug in the previous code for
305         // single imports, in that the old code passed unqualified
306         // class names to module.addImport().  This hasn't been a
307         // problem apparently because those names are resolved here.
308         // Passing module.addImport() a fully qualified name does
309         // currently causes problems with classgen, possibly because
310         // of name collisions.  So, for now, we use the old method...
311 
312         if (as==null) as=name;
313         output.addImport( as, name );  // unqualified
314 
315         name = dot( importPackage, name );
316 
317         // module.addImport( as, name );  // qualified
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 }