package com.redhat.ceylon.eclipse.core.builder;
import static com.redhat.ceylon.eclipse.core.builder.CeylonBuilder.PROBLEM_MARKER_ID;
import static com.redhat.ceylon.eclipse.ui.CeylonPlugin.PLUGIN_ID;
import static org.eclipse.core.resources.IResource.DEPTH_ZERO;
import static org.eclipse.core.resources.ResourcesPlugin.getWorkspace;
import static org.eclipse.jdt.core.IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.List;
import java.util.Locale;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import com.redhat.ceylon.compiler.java.launcher.Main.ExitState;
import com.redhat.ceylon.compiler.java.launcher.Main.ExitState.CeylonState;
import com.redhat.ceylon.javax.tools.Diagnostic;
import com.redhat.ceylon.javax.tools.DiagnosticListener;
import com.redhat.ceylon.javax.tools.JavaFileObject;
final class CompileErrorReporter implements
DiagnosticListener<JavaFileObject> {
private IProject project;
private boolean errorReported;
private List<IFolder> sourceDirectories;
public CompileErrorReporter(IProject project) {
this.project = project;
sourceDirectories = CeylonBuilder.getSourceFolders(project);
}
public void failed() {
if (!errorReported) {
setupMarker(project, null);
}
}
public void failed(final ExitState exitState) {
Diagnostic<? extends JavaFileObject> diagnostic = null;
if (exitState != null
&& exitState.ceylonState != null
&& (exitState.ceylonState.equals(CeylonState.BUG)
|| exitState.ceylonState.equals(CeylonState.SYS))) {
diagnostic = new Diagnostic<JavaFileObject>() {
@Override
public com.redhat.ceylon.javax.tools.Diagnostic.Kind getKind() {
return com.redhat.ceylon.javax.tools.Diagnostic.Kind.ERROR;
}
@Override
public JavaFileObject getSource() {
return null;
}
@Override
public long getPosition() {
return 0;
}
@Override
public long getStartPosition() {
return 0;
}
@Override
public long getEndPosition() {
return 0;
}
@Override
public long getLineNumber() {
return 0;
}
@Override
public long getColumnNumber() {
return 0;
}
@Override
public String getCode() {
return null;
}
@Override
public String getMessage(Locale locale) {
return exitState.ceylonState.equals(CeylonState.BUG) ?
"The Ceylon Java backend compilation failed" +
(exitState.ceylonCodegenExceptionCount > 0 ? "\n with " + exitState.ceylonCodegenExceptionCount + " code generation exceptions" : "") +
(exitState.ceylonCodegenErroneousCount > 0 ? "\n with " + exitState.ceylonCodegenErroneousCount + " erroneous code generations" : "") +
(exitState.ceylonCodegenGarbageCount > 0 ? "\n with " + exitState.ceylonCodegenGarbageCount + " malformed Javac tree cases" : "") +
(exitState.abortingException != null ? "\n with a throwable : " + exceptionToString(exitState.abortingException) : "")
:
"The Ceylon Java backend compilation was aborted" +
(exitState.abortingException != null ? "\n due to the following event : " + exceptionToString(exitState.abortingException) : ".");
}
};
}
if (!errorReported || diagnostic != null) {
setupMarker(project, diagnostic);
}
}
private String exceptionToString(Throwable x){
StringWriter w = new StringWriter();
x.printStackTrace(new PrintWriter(w));
return w.toString();
}
@Override
public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
errorReported = true;
JavaFileObject source = diagnostic.getSource();
if (Diagnostic.Kind.NOTE.equals(diagnostic.getKind())
&& diagnostic.toString().startsWith("Note: Created module")) {
return;
}
if (source == null) {
setupMarker(project, diagnostic);
}
else {
IPath absolutePath = new Path(source.getName());
IFile file = null;
for (IFolder sourceDirectory : sourceDirectories) {
IPath sourceDirPath = sourceDirectory.getLocation();
if (sourceDirPath.isPrefixOf(absolutePath)) {
IResource r = sourceDirectory.findMember(absolutePath.makeRelativeTo(sourceDirPath));
if (r instanceof IFile) {
file = (IFile) r;
}
}
}
if (file == null) {
file = getWorkspace().getRoot()
.getFileForLocation(new Path(source.getName()));
}
if(file != null) {
long diagnosticLineNumber = diagnostic.getLineNumber();
if (CeylonBuilder.isCeylon(file)){
int backendErrorSeverity = kindToSeverity(diagnostic.getKind());
try {
for (IMarker m: file.findMarkers(PROBLEM_MARKER_ID, true, DEPTH_ZERO)) {
if (m.getAttribute(IMarker.LINE_NUMBER, -1) == diagnosticLineNumber) {
int existingSeverity = ((Integer) m.getAttribute(IMarker.SEVERITY)).intValue();
if (existingSeverity >= backendErrorSeverity) {
return;
}
}
}
}
catch (CoreException e) {
e.printStackTrace();
}
setupMarker(file, diagnostic);
}
if (CeylonBuilder.isJava(file)){
try {
for (IMarker m: file.findMarkers(JAVA_MODEL_PROBLEM_MARKER, false, DEPTH_ZERO)) {
if (m.getAttribute(IMarker.LINE_NUMBER, -1) == diagnosticLineNumber) {
int sev = ((Integer) m.getAttribute(IMarker.SEVERITY)).intValue();
if (sev==IMarker.SEVERITY_ERROR) {
return;
}
}
}
}
catch (CoreException e) {
e.printStackTrace();
}
setupMarker(file, diagnostic);
}
}else{
setupMarker(project, diagnostic);
}
}
}
private int kindToSeverity(Diagnostic.Kind kind) {
switch (kind) {
case ERROR:
return IMarker.SEVERITY_ERROR;
case WARNING:
case MANDATORY_WARNING:
return IMarker.SEVERITY_WARNING;
default:
return IMarker.SEVERITY_INFO;
}
}
private void setupMarker(IResource resource, Diagnostic<? extends JavaFileObject> diagnostic) {
try {
long line = diagnostic==null ? -1 :
diagnostic.getLineNumber();
String markerId = PROBLEM_MARKER_ID + ".backend";
if (resource instanceof IFile) {
IFile file = (IFile) resource;
if (CeylonBuilder.isJava(file)) {
markerId = JAVA_MODEL_PROBLEM_MARKER;
}
else if (CeylonBuilder.isCeylon(file)) {
for (IMarker marker:
file.findMarkers(PROBLEM_MARKER_ID,
false, DEPTH_ZERO)) {
Integer severity = (Integer)
marker.getAttribute(IMarker.SEVERITY);
if (severity!=null &&
severity.intValue()
>= IMarker.SEVERITY_ERROR) {
return;
}
}
}
// if (line<0) {
//TODO: use the Symbol to get a location for the javac error
// String name = ((Symbol)((JCDiagnostic) diagnostic).getArgs()[0]).name.toString();
// Declaration member = CeylonBuilder.getPackage((IFile)resource).getDirectMember(name, null, false);
// }
}
IMarker marker = resource.createMarker(markerId);
if (line>=0) {
//Javac doesn't have line number info for certain errors
marker.setAttribute(IMarker.LINE_NUMBER, (int) line);
long startPosition = diagnostic.getStartPosition();
long endPosition = diagnostic.getEndPosition();
marker.setAttribute(IMarker.CHAR_START,
(int) startPosition);
marker.setAttribute(IMarker.CHAR_END,
(int) (endPosition<0 ? startPosition : endPosition)
+ 1);
}
if (markerId.equals(JAVA_MODEL_PROBLEM_MARKER)) {
marker.setAttribute(IMarker.SOURCE_ID, PLUGIN_ID);
}
String message = diagnostic==null ?
"unexplained compilation problem" :
diagnostic.getMessage(Locale.getDefault());
marker.setAttribute(IMarker.MESSAGE, message);
marker.setAttribute(IMarker.PRIORITY, IMarker.PRIORITY_HIGH);
int severity = kindToSeverity(diagnostic==null ? Diagnostic.Kind.ERROR : diagnostic.getKind());
marker.setAttribute(IMarker.SEVERITY, severity);
}
catch (CoreException ce) {
ce.printStackTrace();
}
}
}