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.parser;
19  
20  import org.codehaus.groovy.ast.ModuleNode;
21  import org.codehaus.groovy.control.SourceUnit;
22  import org.codehaus.groovy.syntax.Types;
23  
24  import java.util.ArrayList;
25  import java.util.HashMap;
26  import java.util.List;
27  import java.util.Map;
28  
29  /***
30   * A common base class of AST helper methods which can be shared across the classic and new parsers
31   *
32   * @author James Strachan
33   * @author Bob McWhirter
34   * @author Sam Pullara
35   * @author Chris Poirier
36   * @version $Revision: 1.3 $
37   */
38  public class ASTHelper {
39  
40      private static final String[] EMPTY_STRING_ARRAY = new String[0];
41      private static final String[] DEFAULT_IMPORTS = {"java.lang.", "groovy.lang.", "groovy.util."};
42  
43      /*** The SourceUnit controlling us */
44      private SourceUnit controller;
45  
46      /*** Our ClassLoader, which provides information on external types */
47      private ClassLoader classLoader;
48  
49      /*** Our imports, simple name => fully qualified name */
50      private Map imports;
51      protected ModuleNode output;
52  
53      /**</package-summary/html">The package name in which the module sits *//package-summary.html">em>* The package name in which the module sits */
54      privateong> String packageName;   //
55  
56      // TODO should this really be static???
57      protected static HashMap resolutions = new HashMap();  // cleared on build(), to be safe
58  
59      private static String NOT_RESOLVED = new String();
60  
61      /*** temporarily store the class names that the current modulenode contains */
62      private List newClasses = new ArrayList();
63  
64      public ASTHelper(SourceUnit controller, ClassLoader classLoader) {
65          this();
66          this.controller = controller;
67          this.classLoader = classLoader;
68      }
69  
70      public ASTHelper() {
71          imports = new HashMap();
72      }
73  
74      public String getPackageName() {
75          return</strong> packageName;
76      }
77  
78      publicong> void setPackageName(String packageName) {
79          this.packageName = packageName;
80  
81          output.setPackageName(packageName);
82      }
83  
84  
85      /***
86       * Returns our class loader (as supplied on construction).
87       */
88      public ClassLoader getClassLoader() {
89          return classLoader;
90      }
91  
92      public void setClassLoader(ClassLoader classLoader) {
93          this.classLoader = classLoader;
94      }
95  
96      public SourceUnit getController() {
97          return controller;
98      }
99  
100     public void setController(SourceUnit controller) {
101         this.controller = controller;
102     }
103 
104     /***
105      * Returns a fully qualified name for any given potential type
106      * name.  Returns null if no qualified name could be determined.
107      */
108 
109     protected String resolveName(String name, boolean safe) {
110         //
111         // Use our cache of resolutions, if possible
112 
113         String resolution = (String) resolutions.get(name);
114         if (NOT_RESOLVED.equals(resolution)) {
115             return (safe ? name : null);
116         }
117         else if (resolution != null) {
118             return (String) resolution;
119         }
120 
121 
122         do {
123             //
124             // If the type name contains a ".", it's probably fully
125             // qualified, and we don't take it to verification here.
126 
127             if (name.indexOf(".") >= 0) {
128                 resolution = name;
129                 break;                                            // <<< FLOW CONTROL <<<<<<<<<
130             }
131 
132 
133             //
134             // Otherwise, we'll need the scalar type for checking, and
135             // the postfix for reassembly.
136 
137             String scalar = name, postfix = "";
138             while (scalar.endsWith("[]")) {
139                 scalar = scalar.substring(0, scalar.length() - 2);
140                 postfix += "[]";
141             }
142 
143 
144             //
145             // Primitive types are all valid...
146 
147             if (Types.ofType(Types.lookupKeyword(scalar), Types.PRIMITIVE_TYPE)) {
148                 resolution = name;
149                 break;                                            // <<< FLOW CONTROL <<<<<<<<<
150             }
151 
152 
153             //
154             // Next, check our imports and return the qualified name,
155             // if available.
156 
157             if (this.imports.containsKey(scalar)) {
158                 resolution = ((String) this.imports.get(scalar)) + postfix;
159                 break;                                            // <<< FLOW CONTROL <<<<<<<<<
160             }
161 
162 
163             //
164             // Next, see if our class loader can resolve it in the current package.
165 
166             if (packageName != null && packageName.length() > 0) {
167                 try {
168                     getClassLoader().loadClass(dot(packageName, scalar));
169                     resolution = dot(packageName, name);
170 
171                     break;                                        // <<< FLOW CONTROL <<<<<<<<<
172                 }
173                 catch (Throwable e) {
174                     /* ignore */
175                 }
176             }
177 
178             // search the package imports path
179             List packageImports = output.getImportPackages();
180             for (int i = 0; i < packageImports.size(); i++) {
181                 String pack = (String) packageImports.get(i);
182                 String clsName = pack + name;
183                 try {
184                     getClassLoader().loadClass(clsName);
185                     resolution = clsName;
186                     break;
187                 }
188                 catch (Throwable e) {
189                     //
190                 }
191             }
192             if (resolution != null) {
193                 break;
194             }
195 
196             //
197             // Last chance, check the default imports.
198 
199             for (int i = 0; i < DEFAULT_IMPORTS.length; i++) {
200                 try {
201                     String qualified = DEFAULT_IMPORTS[i] + scalar;
202                     getClassLoader().loadClass(qualified);
203 
204                     resolution = qualified + postfix;
205                     break;                                        // <<< FLOW CONTROL <<<<<<<<<
206                 }
207                 catch (Throwable e) {
208                     /* ignore */
209                 }
210             }
211 
212         }
213         while (false);
214 
215 
216         //
217         // Cache the solution and return it
218 
219         if (resolution == null) {
220             resolutions.put(name, NOT_RESOLVED);
221             return (safe ? name : null);
222         }
223         else {
224             resolutions.put(name, resolution);
225             return resolution;
226         }
227     }
228 
229     /***
230      * Returns two names joined by a dot.  If the base name is
231      * empty, returns the name unchanged.
232      */
233 
234     protected String dot(String base, String name) {
235         if (base != null && base.length() > 0) {
236             return base + "." + name;
237         }
238 
239         return name;
240     }
241 
242     protected void makeModule() {
243         this.newClasses.clear();
244         this.output = new ModuleNode(controller);
245         resolutions.clear();
246     }
247 
248     /***
249      * Returns true if the specified name is a known type name.
250      */
251 
252     protected boolean isDatatype(String name) {
253         return resolveName(name, false) != null;
254     }
255 
256     /***
257      * A synonym for <code>dot( base, "" )</code>.
258      */
259 
260     protected String dot(String base) {
261         return dot(base, "");
262     }
263 
264     protected String resolveNewClassOrName(String name, boolean safe) {
265         if (this.newClasses.contains(name)) {
266             return dot(packageName, name);
267         }
268         else {
269             return resolveName(name, safe);
270         }
271     }
272 
273     protected void addNewClassName(String name) {
274         this.newClasses.add(name);
275     }
276 
277     protected void importClass(String importPackage, String name, String as) {
278         //
279         // There appears to be a bug in the previous code for
280         // single imports, in that the old code passed unqualified
281         // class names to module.addImport().  This hasn't been a
282         // problem apparently because those names are resolved here.
283         // Passing module.addImport() a fully qualified name does
284         // currently causes problems with classgen, possibly because
285         // of name collisions.  So, for now, we use the old method...
286 
287         output.addImport( as, name );  // unqualified
288 
289         name = dot( importPackage, name );
290 
291         // module.addImport( as, name );  // qualified
292         imports.put( as, name );
293     }
294 
295     protected void importPackageWithStar(String importPackage) {
296         String[] classes = output.addImportPackage( dot(importPackage) );
297         for( int i = 0; i < classes.length; i++ )
298         {
299             imports.put( classes[i], dot(importPackage, classes[i]) );
300         }
301     }
302 }