001    /*
002     * $Id: CompileUnit.java 4295 2006-12-02 21:15:54Z blackdrag $
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.control.CompilerConfiguration;
047    import org.codehaus.groovy.control.SourceUnit;
048    import org.codehaus.groovy.control.messages.SyntaxErrorMessage;
049    import org.codehaus.groovy.syntax.SyntaxException;
050    
051    /**
052     * Represents the entire contents of a compilation step which consists of one
053     * or more {@link ModuleNode}instances
054     * 
055     * @author <a href="mailto:james@coredevelopers.net">James Strachan </a>
056     * @version $Revision: 4295 $
057     */
058    public class CompileUnit {
059    
060        private List modules = new ArrayList();
061        private Map classes = new HashMap();
062        private CompilerConfiguration config;
063        private GroovyClassLoader classLoader;
064        private CodeSource codeSource;
065        private Map classesToCompile = new HashMap();
066        private Map classNameToSource = new HashMap();
067        
068        public CompileUnit(GroovyClassLoader classLoader, CompilerConfiguration config) {
069            this(classLoader, null, config);
070        }
071        
072        public CompileUnit(GroovyClassLoader classLoader, CodeSource codeSource, CompilerConfiguration config) {
073            this.classLoader = classLoader;
074            this.config = config;
075            this.codeSource = codeSource;
076        }
077    
078        public List getModules() {
079            return modules;
080        }
081    
082        public void addModule(ModuleNode node) {
083            // node==null means a compilation error prevented
084            // groovy from building an ast
085            if (node==null) return;
086            modules.add(node);
087            node.setUnit(this);
088            addClasses(node.getClasses());
089        }
090    
091        /**
092         * @return the ClassNode for the given qualified name or returns null if
093         *         the name does not exist in the current compilation unit
094         *         (ignoring the .class files on the classpath)
095         */
096        public ClassNode getClass(String name) {
097            ClassNode cn = (ClassNode) classes.get(name);
098            if (cn!=null) return cn;
099            return (ClassNode) classesToCompile.get(name);
100        }
101    
102        /**
103         * @return a list of all the classes in each module in the compilation unit
104         */
105        public List getClasses() {
106            List answer = new ArrayList();
107            for (Iterator iter = modules.iterator(); iter.hasNext();) {
108                ModuleNode module = (ModuleNode) iter.next();
109                answer.addAll(module.getClasses());
110            }
111            return answer;
112        }
113    
114        public CompilerConfiguration getConfig() {
115            return config;
116        }
117    
118        public GroovyClassLoader getClassLoader() {
119            return classLoader;
120        }
121        
122        public CodeSource getCodeSource() {
123            return codeSource;
124        }
125    
126        /**
127         * Appends all of the fully qualified class names in this
128         * module into the given map
129         */
130        void addClasses(List classList) {
131            for (Iterator iter = classList.iterator(); iter.hasNext();) {
132                addClass((ClassNode) iter.next());
133            }
134        }
135        
136        /**
137         *  Adds a class to the unit.
138         */
139        public void addClass(ClassNode node) {
140            node = node.redirect();
141            String name = node.getName();
142            ClassNode stored = (ClassNode) classes.get(name);
143            if (stored != null && stored != node) {
144                // we have a duplicate class!
145                // One possibility for this is, that we delcared a script and a 
146                // class in the same file and named the class like the file
147                SourceUnit nodeSource = node.getModule().getContext();
148                SourceUnit storedSource = stored.getModule().getContext();
149                String txt = "Invalid duplicate class definition of class "+node.getName()+" : ";
150                if (nodeSource==storedSource) {
151                    // same class in same source
152                    txt += "The source "+nodeSource.getName()+" contains at last two defintions of the class "+node.getName()+".\n";
153                    if (node.isScriptBody() || stored.isScriptBody()) {
154                        txt += "One of the classes is a explicit generated class using the class statement, the other is a class generated from"+
155                               " the script body based on the file name. Solutions are to change the file name or to change the class name.\n";
156                    }
157                } else {
158                    txt += "The sources "+nodeSource.getName()+" and "+storedSource.getName()+" are containing both a class of the name "+node.getName()+".\n";
159                }
160                nodeSource.getErrorCollector().addErrorAndContinue(
161                        new SyntaxErrorMessage(new SyntaxException(txt, node.getLineNumber(), node.getColumnNumber()), nodeSource)
162                );
163            }
164            classes.put(name, node);
165            
166            if (classesToCompile.containsKey(name)) {
167                ClassNode cn = (ClassNode) classesToCompile.get(name);
168                cn.setRedirect(node);
169                classesToCompile.remove(name);
170            }        
171        }
172         
173        /**
174         * this emthod actually does not compile a class. It's only
175         * a marker that this type has to be compiled by the CompilationUnit
176         * at the end of a parse step no node should be be left.
177         */
178        public void addClassNodeToCompile(ClassNode node, SourceUnit location) {
179            classesToCompile.put(node.getName(),node);
180            classNameToSource.put(node.getName(),location);
181        }
182        
183        public SourceUnit getScriptSourceLocation(String className) {
184            return (SourceUnit) classNameToSource.get(className);
185        }
186    
187        public boolean hasClassNodeToCompile(){
188            return classesToCompile.size()!=0;
189        }
190        
191        public Iterator iterateClassNodeToCompile(){
192            return classesToCompile.keySet().iterator();
193        }
194    }