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 }