/*
* This file is part of the X10 project (http://x10-lang.org).
*
* This file is licensed to You under the Eclipse Public License (EPL);
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.opensource.org/licenses/eclipse-1.0.php
*
* This file was originally derived from the Polyglot extensible compiler framework.
*
* (C) Copyright 2000-2007 Polyglot project group, Cornell University
* (C) Copyright IBM Corporation 2007-2012.
*/
package polyglot.util;
import java.io.*;
import java.util.StringTokenizer;
import java.util.ArrayList;
import java.util.List;
/**
* A <code>StdErrorQueue</code> handles outputing error messages.
* We buffer the error messages because future phases might delete errors (e.g., see ErrChecker)
*/
public class StdErrorQueue extends SilentErrorQueue
{
private PrintStream err;
public StdErrorQueue(PrintStream err, int limit, String name) {
super(limit, name);
this.err = err;
}
private void println(ErrorInfo e) {
String message = e.getErrorKind() != ErrorInfo.DEBUG
? e.getMessage()
: e.getErrorString() + " -- " + e.getMessage();
Position position = e.getPosition();
String prefix = position != null
? position.nameAndLineString()
: name;
if (prefix == null) {
prefix = "jlc";
}
/*
// This doesn't work: it breaks the error message into one word
// per line.
CodeWriter w = new CodeWriter(err, 78);
w.begin(0);
w.write(prefix + ":");
w.write(" ");
StringTokenizer st = new StringTokenizer(message, " ");
while (st.hasMoreTokens()) {
String s = st.nextToken();
w.write(s);
if (st.hasMoreTokens()) {
w.allowBreak(0, " ");
}
}
w.end();
w.newline(0);
try {
w.flush();
}
catch (IOException ex) {
}
*/
// I (Nate) tried without success to get CodeWriter to do this.
// It would be nice if we could specify where breaks are allowed
// when generating the error. We don't want to break Jif labels,
// for instance.
int width = 0;
err.print(prefix + ":");
width += prefix.length() + 1;
int lmargin = 4;
int rmargin = Integer.MAX_VALUE; // 78;
StringTokenizer lines = new StringTokenizer(message, "\n", true);
boolean needNewline = false;
while (lines.hasMoreTokens()) {
String line = lines.nextToken();
if (line.indexOf("\n") < 0) {
StringTokenizer st = new StringTokenizer(line, " ");
while (st.hasMoreTokens()) {
String s = st.nextToken();
if (width + s.length() + 1 > rmargin) {
err.println();
for (int i = 0; i < lmargin; i++) err.print(" ");
width = lmargin;
}
else {
err.print(" ");
width++;
}
err.print(s);
width += s.length();
}
needNewline = true;
}
else {
err.println();
needNewline = false;
}
width = lmargin;
if (lines.hasMoreTokens()) {
for (int i = 0; i < lmargin; i++) err.print(" ");
}
else if (needNewline) {
err.println();
}
}
if (position != null) {
showError(position);
}
}
ArrayList<String> tooManyErrorsMsgs = new ArrayList<String>();
protected void tooManyErrors(ErrorInfo lastError) {
Position position = lastError.getPosition();
String prefix = position != null ? (position.file() + ": ") : "";
tooManyErrorsMsgs.add(prefix + "Too many errors. Aborting compilation.");
}
protected Reader reader(Position pos) throws IOException {
if (pos.path() != null && pos.line() != Position.UNKNOWN) {
return new FileReader(pos.path());
}
return null;
}
private void showError(Position pos) {
try {
Reader r = reader(pos);
if (r == null) {
return;
}
LineNumberReader reader = new LineNumberReader(r);
String s = null;
while (reader.getLineNumber() < pos.line()) {
s = reader.readLine();
}
if (s != null) {
err.println(s);
showErrorIndicator(pos, reader.getLineNumber(), s);
if (pos.endLine() != pos.line() &&
pos.endLine() != Position.UNKNOWN &&
pos.endLine() != Position.END_UNUSED) {
// if there is more than two lines,
// print some ellipsis.
if (pos.endLine() - pos.line() > 1) {
// add some whitespace first
for (int j = 0; j < s.length() && Character.isWhitespace(s.charAt(j)); j++) {
err.print(s.charAt(j));
}
err.println("...");
}
while (reader.getLineNumber() < pos.endLine()) {
s = reader.readLine();
}
// s is now the last line of the error.
if (s != null) {
err.println(s);
showErrorIndicator(pos, reader.getLineNumber(), s);
}
}
}
reader.close();
err.println();
}
catch (IOException e) {
}
}
protected void showErrorIndicator(Position pos, int lineNum, String s) {
if (pos.column() == Position.UNKNOWN) {
return;
}
int i = 0;
while (i < s.length() && Character.isWhitespace(s.charAt(i))) {
err.print(s.charAt(i++));
}
int startIndAt = i; // column position to start showing the indicator marks
int stopIndAt = s.length() - 1; // column position to stop showing the indicator marks
if (pos.line() == lineNum) {
startIndAt = pos.column();
}
if (pos.endLine() == lineNum) {
stopIndAt = pos.endColumn() - 1;
}
if (stopIndAt < startIndAt) {
stopIndAt = startIndAt;
}
if (pos.endColumn() == Position.UNKNOWN ||
pos.endColumn() == Position.END_UNUSED) {
stopIndAt = startIndAt;
}
for (;i <= stopIndAt; i++) {
char c = '-';
if (i < startIndAt) {
c = ' ';
}
if (i < s.length() && s.charAt(i) == '\t') {
c = '\t';
}
if (i == startIndAt && pos.line() == lineNum) {
c = '^';
}
if (i == stopIndAt && pos.endLine() == lineNum) {
c = '^';
}
err.print(c);
}
err.println();
}
public void flush() {
if (! flushed) {
final List<ErrorInfo> errList = getErrors();
int count = 0;
for (ErrorInfo info : errList) {
println(info);
if (info.getErrorKind() != ErrorInfo.WARNING &&
info.getErrorKind() != ErrorInfo.DEBUG) {
count++;
}
}
for (String msg : tooManyErrorsMsgs)
err.println(msg);
if (count > 0) {
err.println(count + " error" +
(count > 1 ? "s." : "."));
}
}
super.flush();
}
}