blob: 5c8231f6c98c6faf4d5e4bbc6217b7a6efd8c561 [file] [log] [blame]
package org.jetbrains.groovy.compiler.rt;
/*
* Copyright 2000-2007 JetBrains s.r.o.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import groovy.lang.GroovyRuntimeException;
import org.codehaus.groovy.GroovyBugError;
import org.codehaus.groovy.ast.ASTNode;
import org.codehaus.groovy.ast.AnnotatedNode;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.ModuleNode;
import org.codehaus.groovy.classgen.GeneratorContext;
import org.codehaus.groovy.control.*;
import org.codehaus.groovy.control.messages.*;
import org.codehaus.groovy.syntax.SyntaxException;
import org.codehaus.groovy.tools.GroovyClass;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.*;
public class GroovyCompilerWrapper {
private static final String LINKAGE_ERROR =
"A groovyc error occurred while trying to load one of the classes in project dependencies, please ensure it's present. " +
"See the message and the stack trace below for reference\n\n";
private final List<CompilerMessage> collector;
private final boolean forStubs;
public GroovyCompilerWrapper(List<CompilerMessage> collector, boolean forStubs) {
this.collector = collector;
this.forStubs = forStubs;
}
public List<OutputItem> compile(final CompilationUnit unit) {
List<OutputItem> compiledFiles = new ArrayList<OutputItem>();
try {
unit.compile(forStubs ? Phases.CONVERSION : Phases.ALL);
addCompiledFiles(unit, compiledFiles, forStubs);
}
catch (CompilationFailedException e) {
processCompilationException(e);
}
catch (IOException e) {
processException(e, "");
}
catch (GroovyBugError e) {
processException(e, "");
}
catch (NoClassDefFoundError e) {
final String className = e.getMessage();
if (className.startsWith("org/apache/ivy/")) {
addMessageWithoutLocation("Cannot @Grab without Ivy, please add it to your module dependencies (NoClassDefFoundError: " + className + ")", true);
} else {
throw e;
}
}
catch (TypeNotPresentException e) {
processException(e, LINKAGE_ERROR);
}
catch (LinkageError e) {
processException(e, LINKAGE_ERROR);
}
finally {
addWarnings(unit.getErrorCollector());
}
return compiledFiles;
}
private static void addCompiledFiles(CompilationUnit compilationUnit,
final List<OutputItem> compiledFiles,
final boolean forStubs) throws IOException {
File targetDirectory = compilationUnit.getConfiguration().getTargetDirectory();
final String outputPath = targetDirectory.getCanonicalPath().replace(File.separatorChar, '/');
if (forStubs) {
compilationUnit.applyToPrimaryClassNodes(new CompilationUnit.PrimaryClassNodeOperation() {
public void call(SourceUnit source, GeneratorContext context, ClassNode classNode) throws CompilationFailedException {
final String topLevel = classNode.getName();
final String stubPath = outputPath + "/" + topLevel.replace('.', '/') + ".java";
String fileName = source.getName();
if (fileName.startsWith("file:")) {
try {
fileName = new URL(fileName).getFile();
}
catch (MalformedURLException ignored) {
}
}
if (new File(stubPath).exists()) {
compiledFiles.add(new OutputItem(stubPath, fileName));
}
/*
else {
collector.add(new CompilerMessage(CompilerMessage.WARNING, "Groovyc didn't generate stub for " + topLevel, fileName,
classNode.getLineNumber(), classNode.getColumnNumber()));
}
*/
}
});
return;
}
final SortedSet<String> allClasses = new TreeSet<String>();
//noinspection unchecked
List<GroovyClass> listOfClasses = compilationUnit.getClasses();
for (GroovyClass listOfClass : listOfClasses) {
allClasses.add(listOfClass.getName());
}
for (Iterator iterator = compilationUnit.iterator(); iterator.hasNext();) {
SourceUnit sourceUnit = (SourceUnit) iterator.next();
String fileName = sourceUnit.getName();
//for debug purposes
//System.out.println("source: " + fileName);
//System.out.print("classes:");
final ModuleNode ast = sourceUnit.getAST();
final List<ClassNode> topLevelClasses = ast.getClasses();
for (ClassNode classNode : topLevelClasses) {
final String topLevel = classNode.getName();
final String nested = topLevel + "$";
final SortedSet<String> tail = allClasses.tailSet(topLevel);
for (Iterator<String> tailItr = tail.iterator(); tailItr.hasNext(); ) {
String className = tailItr.next();
if (className.equals(topLevel) || className.startsWith(nested)) {
tailItr.remove();
compiledFiles.add(new OutputItem(outputPath + "/" + className.replace('.', '/') + ".class", fileName));
}
else {
break;
}
}
}
}
}
private void addWarnings(ErrorCollector errorCollector) {
for (int i = 0; i < errorCollector.getWarningCount(); i++) {
WarningMessage warning = errorCollector.getWarning(i);
collector.add(new CompilerMessage(GroovyCompilerMessageCategories.WARNING, warning.getMessage(), null, -1, -1));
}
}
private void processCompilationException(Exception exception) {
if (exception instanceof MultipleCompilationErrorsException) {
MultipleCompilationErrorsException multipleCompilationErrorsException = (MultipleCompilationErrorsException) exception;
ErrorCollector errorCollector = multipleCompilationErrorsException.getErrorCollector();
for (int i = 0; i < errorCollector.getErrorCount(); i++) {
processException(errorCollector.getError(i));
}
} else {
processException(exception, "");
}
}
/** @noinspection ThrowableResultOfMethodCallIgnored*/
private void processException(Message message) {
if (message instanceof SyntaxErrorMessage) {
SyntaxErrorMessage syntaxErrorMessage = (SyntaxErrorMessage) message;
addErrorMessage(syntaxErrorMessage.getCause());
} else if (message instanceof ExceptionMessage) {
ExceptionMessage exceptionMessage = (ExceptionMessage) message;
processException(exceptionMessage.getCause(), "");
} else if (message instanceof SimpleMessage) {
addErrorMessage((SimpleMessage) message);
} else {
addMessageWithoutLocation("An unknown error occurred: " + message, true);
}
}
private void processException(Throwable exception, String prefix) {
if (exception instanceof GroovyRuntimeException) {
addErrorMessage((GroovyRuntimeException) exception);
return;
}
if (forStubs) {
collector.add(new CompilerMessage(GroovyCompilerMessageCategories.INFORMATION,
"Groovyc stub generation failed", null, -1, -1));
}
final StringWriter writer = new StringWriter();
writer.append(prefix);
//noinspection IOResourceOpenedButNotSafelyClosed
exception.printStackTrace(new PrintWriter(writer));
collector.add(new CompilerMessage(forStubs ? GroovyCompilerMessageCategories.INFORMATION : GroovyCompilerMessageCategories.ERROR, writer.toString(), null, -1, -1));
}
private void addMessageWithoutLocation(String message, boolean error) {
collector.add(new CompilerMessage(error ? GroovyCompilerMessageCategories.ERROR : GroovyCompilerMessageCategories.WARNING, message, null, -1, -1));
}
private static final String LINE_AT = " @ line ";
private void addErrorMessage(SyntaxException exception) {
String message = exception.getMessage();
String justMessage = message.substring(0, message.lastIndexOf(LINE_AT));
collector.add(new CompilerMessage(GroovyCompilerMessageCategories.ERROR, justMessage, exception.getSourceLocator(),
exception.getLine(), exception.getStartColumn()));
}
private void addErrorMessage(GroovyRuntimeException exception) {
ASTNode astNode = exception.getNode();
ModuleNode module = exception.getModule();
if (module == null) {
module = findModule(astNode);
}
String moduleName = module == null ? "<no module>" : module.getDescription();
int lineNumber = astNode == null ? -1 : astNode.getLineNumber();
int columnNumber = astNode == null ? -1 : astNode.getColumnNumber();
String message = exception.getMessageWithoutLocationText();
if (message == null) {
StringWriter stringWriter = new StringWriter();
//noinspection IOResourceOpenedButNotSafelyClosed
PrintWriter writer = new PrintWriter(stringWriter);
exception.printStackTrace(writer);
message = stringWriter.getBuffer().toString();
}
collector.add(new CompilerMessage(GroovyCompilerMessageCategories.ERROR, message, moduleName, lineNumber, columnNumber));
}
private static ModuleNode findModule(ASTNode node) {
if (node instanceof ModuleNode) {
return (ModuleNode)node;
}
if (node instanceof ClassNode) {
return ((ClassNode)node).getModule();
}
if (node instanceof AnnotatedNode) {
return ((AnnotatedNode)node).getDeclaringClass().getModule();
}
return null;
}
private void addErrorMessage(SimpleMessage message) {
addMessageWithoutLocation(message.getMessage(), true);
}
public static class OutputItem {
private final String myOutputPath;
private final String mySourceFileName;
public OutputItem(String outputPath, String sourceFileName) {
myOutputPath = outputPath;
mySourceFileName = sourceFileName;
}
public String getOutputPath() {
return myOutputPath;
}
public String getSourceFile() {
return mySourceFileName;
}
}
}