blob: 60fb05ec93ebbffad42400ac406c45bf51bc63ae [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.codehaus.groovy.control;
import org.codehaus.groovy.control.messages.ExceptionMessage;
import org.codehaus.groovy.control.messages.LocatedMessage;
import org.codehaus.groovy.control.messages.Message;
import org.codehaus.groovy.control.messages.SyntaxErrorMessage;
import org.codehaus.groovy.control.messages.WarningMessage;
import org.codehaus.groovy.syntax.CSTNode;
import org.codehaus.groovy.syntax.SyntaxException;
import java.io.PrintWriter;
import java.io.Serializable;
import java.util.LinkedList;
import java.util.List;
/**
* A base class for collecting messages and errors during processing.
* Each CompilationUnit should have an ErrorCollector, and the SourceUnits
* should share their ErrorCollector with the CompilationUnit.
*/
public class ErrorCollector implements Serializable {
private static final long serialVersionUID = 2844774170905056755L;
/**
* ErrorMessages collected during processing
*/
protected LinkedList<Message> errors;
/**
* WarningMessages collected during processing
*/
protected LinkedList<WarningMessage> warnings;
/**
* Configuration and other settings that control processing
*/
protected final CompilerConfiguration configuration;
/**
* Initialize the ErrorReporter.
*/
public ErrorCollector(final CompilerConfiguration configuration) {
this.configuration = configuration;
}
public void addCollectorContents(final ErrorCollector that) {
if (that.errors != null) {
if (this.errors == null) {
this.errors = that.errors;
} else {
this.errors.addAll(that.errors);
}
}
if (that.warnings != null) {
if (this.warnings == null) {
this.warnings = that.warnings;
} else {
this.warnings.addAll(that.warnings);
}
}
}
public void addErrorAndContinue(final SyntaxException error, final SourceUnit source) throws CompilationFailedException {
addErrorAndContinue(Message.create(error, source));
}
/**
* Adds an error to the message set, but does not cause a failure. The message is not required to have a source
* line and column specified, but it is best practice to try and include that information.
*/
public void addErrorAndContinue(final Message message) {
if (errors == null) {
errors = new LinkedList<>();
}
errors.add(message);
}
/**
* Adds a non-fatal error to the message set, which may cause a failure if the error threshold is exceeded.
* The message is not required to have a source line and column specified, but it is best practice to try
* and include that information.
*/
public void addError(final Message message) throws CompilationFailedException {
addErrorAndContinue(message);
if (errors != null && errors.size() >= configuration.getTolerance()) {
failIfErrors();
}
}
/**
* Adds an optionally-fatal error to the message set.
* The message is not required to have a source line and column specified, but it is best practice to try
* and include that information.
* @param fatal
* if true then then processing will stop
*/
public void addError(final Message message, final boolean fatal) throws CompilationFailedException {
if (fatal) {
addFatalError(message);
} else {
addError(message);
}
}
public void addError(final SyntaxException error, final SourceUnit source) throws CompilationFailedException {
addError(Message.create(error, source), error.isFatal());
}
public void addError(final String error, final CSTNode context, final SourceUnit source) throws CompilationFailedException {
addError(new LocatedMessage(error, context, source));
}
public void addException(final Exception exception, final SourceUnit source) throws CompilationFailedException {
addError(new ExceptionMessage(exception, configuration.getDebug(), source));
failIfErrors();
}
/**
* Adds an error to the message set and throws CompilationFailedException.
*/
public void addFatalError(final Message message) throws CompilationFailedException {
addError(message);
failIfErrors();
}
/**
* Adds a warning to the message set.
*/
public void addWarning(final WarningMessage message) {
if (message.isRelevant(configuration.getWarningLevel())) {
if (warnings == null) {
warnings = new LinkedList<>();
}
warnings.add(message);
}
}
/**
* Adds a warning to the message set if it is relevant.
*/
public void addWarning(final int importance, final String text, final CSTNode context, final SourceUnit source) {
if (WarningMessage.isRelevant(importance, configuration.getWarningLevel())) {
addWarning(new WarningMessage(importance, text, context, source));
}
}
/**
* Adds a warning to the message set if it is relevant.
*/
public void addWarning(final int importance, final String text, final Object data, final CSTNode context, final SourceUnit source) {
if (WarningMessage.isRelevant(importance, configuration.getWarningLevel())) {
addWarning(new WarningMessage(importance, text, data, context, source));
}
}
/**
* @return the compiler configuration used to create this error collector
*/
public CompilerConfiguration getConfiguration() {
return configuration;
}
/**
* Returns the number of errors.
*/
public int getErrorCount() {
return (hasErrors() ? errors.size() : 0);
}
/**
* Returns the specified error message, or null.
*/
public Message getError(final int index) {
if (index < getErrorCount()) {
return errors.get(index);
}
return null;
}
/**
* Returns the list of errors, or null if there are none.
*/
public List<? extends Message> getErrors() {
return errors;
}
/**
* Returns true if there are any errors pending.
*/
public boolean hasErrors() {
return (errors != null);
}
/**
* Returns the number of warnings.
*/
public int getWarningCount() {
return (hasWarnings() ? warnings.size() : 0);
}
/**
* Returns the specified warning message, or null.
*/
public WarningMessage getWarning(final int index) {
if (index < getWarningCount()) {
return warnings.get(index);
}
return null;
}
/**
* Returns the list of warnings, or null if there are none.
*/
public List<WarningMessage> getWarnings() {
return warnings;
}
/**
* Returns true if there are any warnings pending.
*/
public boolean hasWarnings() {
return (warnings != null);
}
//
/**
* Returns the last error reported.
*/
public Message getLastError() {
return errors.getLast();
}
/**
* Returns the specified error's underlying Exception, or null if it isn't one.
*/
public Exception getException(final int index) {
Exception exception = null;
Message message = getError(index);
if (message != null) {
if (message instanceof ExceptionMessage) {
exception = ((ExceptionMessage) message).getCause();
} else if (message instanceof SyntaxErrorMessage) {
exception = ((SyntaxErrorMessage) message).getCause();
}
}
return exception;
}
/**
* Returns the specified error's underlying SyntaxException, or null if it isn't one.
*/
public SyntaxException getSyntaxError(final int index) {
SyntaxException exception = null;
Message message = getError(index);
if (message instanceof SyntaxErrorMessage) {
exception = ((SyntaxErrorMessage) message).getCause();
}
return exception;
}
/**
* Causes the current phase to fail by throwing a CompilationFailedException.
*/
protected void failIfErrors() throws CompilationFailedException {
if (hasErrors()) {
throw new MultipleCompilationErrorsException(this);
}
}
//---------------------------------------------------------------------------
// OUTPUT
private void write(final PrintWriter writer, final Janitor janitor, final List<? extends Message> messages, final String txt) {
if (messages == null || messages.isEmpty()) return;
for (Message message : messages) {
message.write(writer, janitor);
if (configuration.getDebug() && (message instanceof SyntaxErrorMessage)) {
((SyntaxErrorMessage) message).getCause().printStackTrace(writer);
}
writer.println();
}
writer.print(messages.size());
writer.print(" " + txt);
if (messages.size() > 1) {
writer.print("s");
}
writer.println();
}
/**
* Writes error messages to the specified PrintWriter.
*/
public void write(final PrintWriter writer, final Janitor janitor) {
write(writer, janitor, warnings, "warning");
write(writer, janitor, errors, "error");
}
}