001 /**
002 *
003 * Copyright 2004 James Strachan
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 *
017 **/
018 package org.codehaus.groovy.syntax;
019
020 import org.codehaus.groovy.ast.ModuleNode;
021 import org.codehaus.groovy.control.SourceUnit;
022
023 import java.util.ArrayList;
024 import java.util.HashMap;
025 import java.util.List;
026 import java.util.Map;
027
028 /**
029 * A common base class of AST helper methods which can be shared across the classic and new parsers
030 *
031 * @author James Strachan
032 * @author Bob McWhirter
033 * @author Sam Pullara
034 * @author Chris Poirier
035 * @version $Revision: 1.9 $
036 */
037 public class ASTHelper {
038
039 private static final String[] EMPTY_STRING_ARRAY = new String[0];
040
041 /** The SourceUnit controlling us */
042 private SourceUnit controller;
043
044 /** Our ClassLoader, which provides information on external types */
045 private ClassLoader classLoader;
046
047 /** Our imports, simple name => fully qualified name */
048 private Map imports;
049 protected ModuleNode output;
050
051 /** The package name in which the module sits */
052 private String packageName; //
053
054 // TODO should this really be static???
055 protected static HashMap resolutions = new HashMap(); // cleared on build(), to be safe
056
057 private static String NOT_RESOLVED = new String();
058
059 /** temporarily store the class names that the current modulenode contains */
060 private List newClasses = new ArrayList();
061
062 public ASTHelper(SourceUnit controller, ClassLoader classLoader) {
063 this();
064 this.controller = controller;
065 this.classLoader = classLoader;
066 }
067
068 public ASTHelper() {
069 imports = new HashMap();
070 }
071
072 public String getPackageName() {
073 return packageName;
074 }
075
076 public void setPackageName(String packageName) {
077 this.packageName = packageName;
078 if (packageName!=null && packageName.length()>0){
079 packageName+='.';
080 }
081 output.setPackageName(packageName);
082 }
083
084
085 /**
086 * Returns our class loader (as supplied on construction).
087 */
088 public ClassLoader getClassLoader() {
089 return classLoader;
090 }
091
092 public void setClassLoader(ClassLoader classLoader) {
093 this.classLoader = classLoader;
094 }
095
096 public SourceUnit getController() {
097 return controller;
098 }
099
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 /* protected String resolveName(String name, boolean safe) {
109 //
110 // Use our cache of resolutions, if possible
111
112 String resolution = (String) resolutions.get(name);
113 if (NOT_RESOLVED.equals(resolution)) {
114 return (safe ? name : null);
115 }
116 else if (resolution != null) {
117 return (String) resolution;
118 }
119
120 try {
121 getClassLoader().loadClass(name);
122 resolutions.put(name,name);
123 return name;
124 } catch (ClassNotFoundException cnfe){
125 if (cnfe.getCause() instanceof MultipleCompilationErrorsException) {
126 MultipleCompilationErrorsException mcee = (MultipleCompilationErrorsException) cnfe.getCause();
127 controller.getErrorCollector().addCollectorContents(mcee.getErrorCollector());
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 /**
256 * Returns two names joined by a dot. If the base name is
257 * empty, returns the name unchanged.
258 */
259 public static 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 * A synonym for <code>dot( base, "" )</code>.
275 */
276 protected String dot(String base) {
277 return dot(base, "");
278 }
279
280 /*protected String resolveNewClassOrName(String name, boolean safe) {
281 if (this.newClasses.contains(name)) {
282 return dot(packageName, name);
283 }
284 else {
285 return resolveName(name, safe);
286 }
287 }*/
288
289 protected void addNewClassName(String name) {
290 this.newClasses.add(name);
291 }
292
293 protected void importClass(String importPackage, String name, String as) {
294 if (as==null) as=name;
295
296 name = dot( importPackage, name );
297
298 output.addImport( as, name );
299 imports.put( as, name );
300 }
301
302 protected void importPackageWithStar(String importPackage) {
303 String[] classes = output.addImportPackage( dot(importPackage) );
304 for( int i = 0; i < classes.length; i++ )
305 {
306 imports.put( classes[i], dot(importPackage, classes[i]) );
307 }
308 }
309 }