View Javadoc

1   /*
2    * $Id: CompileUnit.java,v 1.14 2005/08/15 09:55:37 jez Exp $
3    * 
4    * Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
5    * 
6    * Redistribution and use of this software and associated documentation
7    * ("Software"), with or without modification, are permitted provided that the
8    * following conditions are met:
9    *  1. Redistributions of source code must retain copyright statements and
10   * notices. Redistributions must also contain a copy of this document.
11   *  2. Redistributions in binary form must reproduce the above copyright
12   * notice, this list of conditions and the following disclaimer in the
13   * documentation and/or other materials provided with the distribution.
14   *  3. The name "groovy" must not be used to endorse or promote products
15   * derived from this Software without prior written permission of The Codehaus.
16   * For written permission, please contact info@codehaus.org.
17   *  4. Products derived from this Software may not be called "groovy" nor may
18   * "groovy" appear in their names without prior written permission of The
19   * Codehaus. "groovy" is a registered trademark of The Codehaus.
20   *  5. Due credit should be given to The Codehaus - http://groovy.codehaus.org/
21   * 
22   * THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS ``AS IS'' AND ANY
23   * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
24   * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
25   * DISCLAIMED. IN NO EVENT SHALL THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR
26   * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27   * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
28   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29   * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30   * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31   * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
32   * DAMAGE.
33   *  
34   */
35  package org.codehaus.groovy.ast;
36  
37  import groovy.lang.GroovyClassLoader;
38  
39  import java.security.CodeSource;
40  import java.util.ArrayList;
41  import java.util.HashMap;
42  import java.util.Iterator;
43  import java.util.List;
44  import java.util.Map;
45  
46  import org.codehaus.groovy.classgen.ClassGeneratorException;
47  import org.codehaus.groovy.control.CompilationFailedException;
48  import org.codehaus.groovy.control.CompilerConfiguration;
49  
50  /***
51   * Represents the entire contents of a compilation step which consists of one
52   * or more {@link ModuleNode}instances
53   * 
54   * @author <a href="mailto:james@coredevelopers.net">James Strachan </a>
55   * @version $Revision: 1.14 $
56   */
57  public class CompileUnit {
58  
59      private List modules = new ArrayList();
60      private Map classes = new HashMap();
61      private CompilerConfiguration config;
62      private ClassLoader classLoader;
63      private CodeSource codeSource;
64      private Map cachedClasses = new HashMap();
65      
66      public static final Object NO_CLASS = new Object();
67      
68  
69      public CompileUnit(ClassLoader classLoader, CompilerConfiguration config) {
70      	this(classLoader, null, config);
71      }
72      
73      public CompileUnit(ClassLoader classLoader, CodeSource codeSource, CompilerConfiguration config) {
74          this.classLoader = classLoader;
75          this.config = config;
76          this.codeSource = codeSource;
77      }
78  
79      public List getModules() {
80          return modules;
81      }
82  
83      public void addModule(ModuleNode node) {
84          modules.add(node);
85          node.setUnit(this);
86          addClasses(node.classes);
87      }
88  
89      /***
90       * @return the ClassNode for the given qualified name or returns null if
91       *         the name does not exist in the current compilation unit
92       *         (ignoring the .class files on the classpath)
93       */
94      public ClassNode getClass(String name) {
95          return (ClassNode) classes.get(name);
96      }
97  
98      /***
99       * @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 }