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.ClassNode; 021 import org.codehaus.groovy.ast.ModuleNode; 022 import org.codehaus.groovy.control.SourceUnit; 023 024 import java.util.ArrayList; 025 import java.util.HashMap; 026 import java.util.List; 027 import java.util.Map; 028 029 /** 030 * A common base class of AST helper methods which can be shared across the classic and new parsers 031 * 032 * @author Jochen Theodorou 033 * @author James Strachan 034 * @author Bob McWhirter 035 * @author Sam Pullara 036 * @author Chris Poirier 037 * @version $Revision: 4032 $ 038 */ 039 public class ASTHelper { 040 041 private static final String[] EMPTY_STRING_ARRAY = new String[0]; 042 043 /** The SourceUnit controlling us */ 044 private SourceUnit controller; 045 046 /** Our ClassLoader, which provides information on external types */ 047 private ClassLoader classLoader; 048 049 /** Our imports, simple name => fully qualified name */ 050 private Map imports; 051 protected ModuleNode output; 052 053 /** The package name in which the module sits */ 054 private String packageName; // 055 056 // TODO should this really be static??? 057 protected static HashMap resolutions = new HashMap(); // cleared on build(), to be safe 058 059 private static String NOT_RESOLVED = new String(); 060 061 /** temporarily store the class names that the current modulenode contains */ 062 private List newClasses = new ArrayList(); 063 064 public ASTHelper(SourceUnit controller, ClassLoader classLoader) { 065 this(); 066 this.controller = controller; 067 this.classLoader = classLoader; 068 } 069 070 public ASTHelper() { 071 imports = new HashMap(); 072 } 073 074 public String getPackageName() { 075 return packageName; 076 } 077 078 public void setPackageName(String packageName) { 079 this.packageName = packageName; 080 if (packageName!=null && packageName.length()>0){ 081 packageName+='.'; 082 } 083 output.setPackageName(packageName); 084 } 085 086 087 /** 088 * Returns our class loader (as supplied on construction). 089 */ 090 public ClassLoader getClassLoader() { 091 return classLoader; 092 } 093 094 public void setClassLoader(ClassLoader classLoader) { 095 this.classLoader = classLoader; 096 } 097 098 public SourceUnit getController() { 099 return controller; 100 } 101 102 public void setController(SourceUnit controller) { 103 this.controller = controller; 104 } 105 106 /** 107 * Returns a fully qualified name for any given potential type 108 * name. Returns null if no qualified name could be determined. 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 MultipleCompilationErrorsException) { 128 MultipleCompilationErrorsException mcee = (MultipleCompilationErrorsException) cnfe.getCause(); 129 controller.getErrorCollector().addCollectorContents(mcee.getErrorCollector()); 130 resolutions.put(name,name); 131 return name; 132 } 133 } catch (NoClassDefFoundError ncdfe) { 134 //fall through 135 } 136 137 do { 138 // 139 // If the type name contains a ".", it's probably fully 140 // qualified, and we don't take it to verification here. 141 142 if (name.indexOf(".") >= 0) { 143 resolution = name; 144 break; // <<< FLOW CONTROL <<<<<<<<< 145 } 146 147 148 // 149 // Otherwise, we'll need the scalar type for checking, and 150 // the postfix for reassembly. 151 152 String scalar = name, postfix = ""; 153 while (scalar.endsWith("[]")) { 154 scalar = scalar.substring(0, scalar.length() - 2); 155 postfix += "[]"; 156 } 157 158 159 // 160 // Primitive types are all valid... 161 162 if (Types.ofType(Types.lookupKeyword(scalar), Types.PRIMITIVE_TYPE)) { 163 resolution = name; 164 break; // <<< FLOW CONTROL <<<<<<<<< 165 } 166 167 168 // 169 // Next, check our imports and return the qualified name, 170 // if available. 171 172 if (this.imports.containsKey(scalar)) { 173 resolution = ((String) this.imports.get(scalar)) + postfix; 174 break; // <<< FLOW CONTROL <<<<<<<<< 175 } 176 177 178 // 179 // Next, see if our class loader can resolve it in the current package. 180 181 if (packageName != null && packageName.length() > 0) { 182 try { 183 getClassLoader().loadClass(dot(packageName, scalar)); 184 resolution = dot(packageName, name); 185 186 break; // <<< FLOW CONTROL <<<<<<<<< 187 } catch (ClassNotFoundException cnfe){ 188 if (cnfe.getCause() instanceof CompilationFailedException) { 189 resolution = dot(packageName, name); 190 break; 191 } 192 } catch (NoClassDefFoundError ncdfe) { 193 //fall through 194 } 195 } 196 197 // search the package imports path 198 List packageImports = output.getImportPackages(); 199 for (int i = 0; i < packageImports.size(); i++) { 200 String pack = (String) packageImports.get(i); 201 String clsName = pack + name; 202 try { 203 getClassLoader().loadClass(clsName); 204 resolution = clsName; 205 break; 206 } catch (ClassNotFoundException cnfe){ 207 if (cnfe.getCause() instanceof CompilationFailedException) { 208 resolution = clsName; 209 break; 210 } 211 } catch (NoClassDefFoundError ncdfe) { 212 //fall through 213 } 214 } 215 if (resolution != null) { 216 break; 217 } 218 219 // 220 // Last chance, check the default imports. 221 222 for (int i = 0; i < DEFAULT_IMPORTS.length; i++) { 223 String qualified = DEFAULT_IMPORTS[i] + scalar; 224 try { 225 getClassLoader().loadClass(qualified); 226 227 resolution = qualified + postfix; 228 break; // <<< FLOW CONTROL <<<<<<<<< 229 } catch (ClassNotFoundException cnfe){ 230 if (cnfe.getCause() instanceof CompilationFailedException) { 231 resolution = qualified + postfix; 232 break; 233 } 234 } catch (NoClassDefFoundError ncdfee) { 235 // fall through 236 } 237 } 238 239 } 240 while (false); 241 242 243 // 244 // Cache the solution and return it 245 246 if (resolution == null) { 247 resolutions.put(name, NOT_RESOLVED); 248 return (safe ? name : null); 249 } 250 else { 251 resolutions.put(name, resolution); 252 return resolution; 253 } 254 } 255 */ 256 257 /** 258 * Returns two names joined by a dot. If the base name is 259 * empty, returns the name unchanged. 260 */ 261 public static String dot(String base, String name) { 262 if (base != null && base.length() > 0) { 263 return base + "." + name; 264 } 265 266 return name; 267 } 268 269 protected void makeModule() { 270 this.newClasses.clear(); 271 this.output = new ModuleNode(controller); 272 resolutions.clear(); 273 } 274 275 /** 276 * A synonym for <code>dot( base, "" )</code>. 277 */ 278 protected String dot(String base) { 279 return dot(base, ""); 280 } 281 282 /*protected String resolveNewClassOrName(String name, boolean safe) { 283 if (this.newClasses.contains(name)) { 284 return dot(packageName, name); 285 } 286 else { 287 return resolveName(name, safe); 288 } 289 }*/ 290 291 protected void addNewClassName(String name) { 292 this.newClasses.add(name); 293 } 294 295 protected void importClass(ClassNode type, String name, String as) { 296 if (as==null) as=name; 297 298 output.addImport(as, type); 299 imports.put(as, type); 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 }