/*
* Copyright 2014-2017 TWO SIGMA OPEN SOURCE, LLC
*
* 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.
*/
package com.twosigma.beaker.javash.evaluator;
import org.abstractmeta.toolbox.compilation.compiler.impl.JavaSourceCompilerImpl;
import org.abstractmeta.toolbox.compilation.compiler.impl.JavaSourceFileObject;
import org.abstractmeta.toolbox.compilation.compiler.registry.JavaFileObjectRegistry;
import javax.tools.Diagnostic;
import javax.tools.DiagnosticCollector;
import javax.tools.JavaFileObject;
import java.util.ArrayList;
import static java.lang.Math.*;
import static java.lang.String.format;
public class JavaSourceCompiler extends JavaSourceCompilerImpl {
private static final char POSITION_CHARACTER = '^';
@Override
protected boolean buildDiagnosticMessage(Diagnostic diagnostic, StringBuilder diagnosticBuilder, JavaFileObjectRegistry registry) {
diagnosticBuilder.append(diagnostic.getMessage(null));
diagnosticBuilder.append("\n");
diagnosticBuilder.append(getErrorDetails(diagnostic));
return diagnostic.getKind().equals(Diagnostic.Kind.ERROR);
}
@Override
protected IllegalStateException createCompilationErrorException(JavaFileObjectRegistry registry, DiagnosticCollector<JavaFileObject> diagnostics) {
final ArrayList<CompilationException.CompilationError> compilationErrors = new ArrayList<>();
for (Diagnostic diagnostic : diagnostics.getDiagnostics()) {
compilationErrors.add(new CompilationException.CompilationError(((int) diagnostic.getLineNumber()), diagnostic.getMessage(null), getErrorDetails(diagnostic)));
}
return new CompilationException(format("%d compilation error(s)\n", compilationErrors.size()), compilationErrors);
}
private String getErrorDetails(Diagnostic diagnostic) {
String sourceErrorDetails = "";
CharSequence sourceCode = getSourceContent(diagnostic);
if (sourceCode != null) {
String source = sourceCode.toString();
int startPosition = (int) diagnostic.getStartPosition();
int endPosition = (int) diagnostic.getEndPosition();
if (endPosition < 0 && startPosition < 0) {
return "";
}
if (startPosition < 0) {
startPosition = endPosition;
} else if (endPosition < 0) {
endPosition = startPosition;
}
final int snippetStart = getSnippetStart(source, startPosition);
final int snippetEnd = getSnippetEnd(source, endPosition);
final String errorLine = source.substring(snippetStart, snippetEnd).replaceAll("\n", " ");;
startPosition -= snippetStart;
endPosition -= snippetStart;
StringBuilder signStringBuilder = getSpacesStringBuilder(errorLine.length());
signStringBuilder.insert(startPosition, POSITION_CHARACTER);
if (startPosition != endPosition) {
signStringBuilder.insert(endPosition, POSITION_CHARACTER);
}
sourceErrorDetails = errorLine + "\n" + signStringBuilder.toString() + "\n\n";
}
return sourceErrorDetails;
}
private int getSnippetEnd(String source, int endPosition) {
final int lineEndPosition = getPositive(source.indexOf('\n', endPosition));
final int nextSemicolonPosition = getPositive(source.indexOf(';', endPosition));
return min(min(lineEndPosition, nextSemicolonPosition), min(source.length(), endPosition + 30));
}
private int getSnippetStart(String source, int startPosition) {
startPosition = abs(startPosition - 1);
final int lineStartPosition = source.lastIndexOf('\n', startPosition);
final int previousSemicolonPosition = source.lastIndexOf(';', startPosition) + 1;
return max(max(lineStartPosition, previousSemicolonPosition), abs(startPosition - 30));
}
private StringBuilder getSpacesStringBuilder(int length) {
final StringBuilder builder = new StringBuilder();
for(int i = 0; i < length; i++) {
builder.append(' ');
}
return builder;
}
private CharSequence getSourceContent(Diagnostic diagnostic) {
CharSequence sourceContent = null;
Object source = diagnostic.getSource();
if (source != null) {
JavaSourceFileObject sourceFile = JavaSourceFileObject.class.cast(source);
sourceContent = sourceFile.getCharContent(true);
}
return sourceContent;
}
private int getPositive(int number) {
return number >= 0 ? number : Integer.MAX_VALUE;
}
}