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    }