001 /* 002 * $Id: CompileUnit.java,v 1.14 2005/08/15 09:55:37 jez Exp $ 003 * 004 * Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved. 005 * 006 * Redistribution and use of this software and associated documentation 007 * ("Software"), with or without modification, are permitted provided that the 008 * following conditions are met: 009 * 1. Redistributions of source code must retain copyright statements and 010 * notices. Redistributions must also contain a copy of this document. 011 * 2. Redistributions in binary form must reproduce the above copyright 012 * notice, this list of conditions and the following disclaimer in the 013 * documentation and/or other materials provided with the distribution. 014 * 3. The name "groovy" must not be used to endorse or promote products 015 * derived from this Software without prior written permission of The Codehaus. 016 * For written permission, please contact info@codehaus.org. 017 * 4. Products derived from this Software may not be called "groovy" nor may 018 * "groovy" appear in their names without prior written permission of The 019 * Codehaus. "groovy" is a registered trademark of The Codehaus. 020 * 5. Due credit should be given to The Codehaus - http://groovy.codehaus.org/ 021 * 022 * THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS ``AS IS'' AND ANY 023 * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 024 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 025 * DISCLAIMED. IN NO EVENT SHALL THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR 026 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 027 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 028 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 029 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 030 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 031 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 032 * DAMAGE. 033 * 034 */ 035 package org.codehaus.groovy.ast; 036 037 import groovy.lang.GroovyClassLoader; 038 039 import java.security.CodeSource; 040 import java.util.ArrayList; 041 import java.util.HashMap; 042 import java.util.Iterator; 043 import java.util.List; 044 import java.util.Map; 045 046 import org.codehaus.groovy.classgen.ClassGeneratorException; 047 import org.codehaus.groovy.control.CompilationFailedException; 048 import org.codehaus.groovy.control.CompilerConfiguration; 049 050 /** 051 * Represents the entire contents of a compilation step which consists of one 052 * or more {@link ModuleNode}instances 053 * 054 * @author <a href="mailto:james@coredevelopers.net">James Strachan </a> 055 * @version $Revision: 1.14 $ 056 */ 057 public class CompileUnit { 058 059 private List modules = new ArrayList(); 060 private Map classes = new HashMap(); 061 private CompilerConfiguration config; 062 private ClassLoader classLoader; 063 private CodeSource codeSource; 064 private Map cachedClasses = new HashMap(); 065 066 public static final Object NO_CLASS = new Object(); 067 068 069 public CompileUnit(ClassLoader classLoader, CompilerConfiguration config) { 070 this(classLoader, null, config); 071 } 072 073 public CompileUnit(ClassLoader classLoader, CodeSource codeSource, CompilerConfiguration config) { 074 this.classLoader = classLoader; 075 this.config = config; 076 this.codeSource = codeSource; 077 } 078 079 public List getModules() { 080 return modules; 081 } 082 083 public void addModule(ModuleNode node) { 084 modules.add(node); 085 node.setUnit(this); 086 addClasses(node.classes); 087 } 088 089 /** 090 * @return the ClassNode for the given qualified name or returns null if 091 * the name does not exist in the current compilation unit 092 * (ignoring the .class files on the classpath) 093 */ 094 public ClassNode getClass(String name) { 095 return (ClassNode) classes.get(name); 096 } 097 098 /** 099 * @return a list of all the classes in each module in the compilation unit 100 */ 101 public List getClasses() { 102 List answer = new ArrayList(); 103 for (Iterator iter = modules.iterator(); iter.hasNext();) { 104 ModuleNode module = (ModuleNode) iter.next(); 105 answer.addAll(module.getClasses()); 106 } 107 return answer; 108 } 109 110 public CompilerConfiguration getConfig() { 111 return config; 112 } 113 114 public ClassLoader getClassLoader() { 115 return classLoader; 116 } 117 118 public CodeSource getCodeSource() { 119 return codeSource; 120 } 121 122 123 /** 124 * Loads a class on the compile classpath so that it can be introspected 125 * 126 * @param type 127 * @return @throws 128 * ClassNotFoundException 129 */ 130 public Class loadClass(String type) throws ClassNotFoundException { 131 Object obj = cachedClasses.get(type); 132 if ( obj == NO_CLASS ) { 133 throw new ClassNotFoundException(type); 134 } 135 if ( obj != null) { 136 return (Class)obj; 137 } 138 139 Class answer = null; 140 ClassLoader lastLoader = getClassLoader(); 141 try { 142 answer = lastLoader.loadClass(type); 143 } catch (ClassNotFoundException e) { 144 Throwable cause = e.getCause(); 145 if (cause !=null && cause instanceof CompilationFailedException){ 146 throw new ClassGeneratorException("Error when compiling class: " + type + ". Reason: " + cause, cause); 147 } 148 } catch (NoClassDefFoundError e) { 149 // fall through 150 } 151 152 try { 153 ClassLoader loader = Thread.currentThread().getContextClassLoader(); 154 if ( answer == null && loader != lastLoader /*&& loader != null*/) { 155 lastLoader = loader; 156 answer = loader.loadClass(type); 157 } 158 } 159 catch (ClassNotFoundException e1) { 160 Throwable cause = e1.getCause(); 161 if (cause !=null && cause instanceof CompilationFailedException){ 162 throw new ClassGeneratorException("Error when compiling class: " + type + ". Reason: " + cause, cause); 163 } 164 } catch (NoClassDefFoundError e) { 165 // fall through 166 } 167 168 // lets try our class loader 169 try { 170 ClassLoader loader = getClass().getClassLoader(); 171 if ( answer == null && loader != lastLoader) { 172 lastLoader = loader; 173 answer = loader.loadClass(type); 174 } 175 } 176 catch (ClassNotFoundException e2) { 177 Throwable cause = e2.getCause(); 178 if (cause !=null && cause instanceof CompilationFailedException){ 179 throw new ClassGeneratorException("Error when compiling class: " + type + ". Reason: " + cause, cause); 180 } 181 } 182 catch (NoClassDefFoundError e) { 183 // fall through 184 } 185 186 try { 187 if (answer == null ) { 188 answer = Class.forName(type); 189 } 190 } 191 catch (ClassNotFoundException e2) { 192 Throwable cause = e2.getCause(); 193 if (cause !=null && cause instanceof CompilationFailedException){ 194 throw new ClassGeneratorException("Error when compiling class: " + type + ". Reason: " + cause, cause); 195 } 196 } 197 catch (NoClassDefFoundError e) { 198 // fall through 199 } 200 201 if ( answer == null ) { 202 cachedClasses.put(type,NO_CLASS); 203 throw new ClassNotFoundException(type); 204 } else if (answer==GroovyClassLoader.PARSING.class){ 205 //no chaching 206 } else { 207 if (!type.equals(answer.getName())) { // br case sensitive match 208 cachedClasses.put(type,NO_CLASS); 209 System.out.println("Mismatch: answer.getName() = " + answer.getName() + ", type = " + type); 210 throw new ClassNotFoundException(type); 211 } 212 cachedClasses.put(type,answer); 213 } 214 215 return answer; 216 } 217 218 219 /** 220 * Appends all of the fully qualified class names in this 221 * module into the given map 222 */ 223 void addClasses(List classList) { 224 for (Iterator iter = classList.iterator(); iter.hasNext();) { 225 addClass((ClassNode) iter.next()); 226 } 227 } 228 229 /** 230 * Adds a class to the unit. 231 */ 232 public void addClass(ClassNode node) { 233 String name = node.getName(); 234 if (classes.containsKey(name)) { 235 throw new RuntimeException( 236 "Error: duplicate class declaration for name: " + name + " and class: " + node); 237 } 238 classes.put(name, node); 239 } 240 241 }