/*
* Copyright (c) 2013, the Dart project authors.
*
* Licensed under the Eclipse Public License v1.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.eclipse.org/legal/epl-v10.html
*
* 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.google.dart.engine.internal.error;
import com.google.dart.engine.ast.AstNode;
import com.google.dart.engine.element.Element;
import com.google.dart.engine.error.AnalysisError;
import com.google.dart.engine.error.AnalysisErrorListener;
import com.google.dart.engine.error.AnalysisErrorWithProperties;
import com.google.dart.engine.error.ErrorCode;
import com.google.dart.engine.scanner.Token;
import com.google.dart.engine.source.Source;
import com.google.dart.engine.type.Type;
import java.util.HashSet;
/**
* Instances of the class {@code ErrorReporter} wrap an error listener with utility methods used to
* create the errors being reported.
*
* @coverage dart.engine.error
*/
public class ErrorReporter {
/**
* The error listener to which errors will be reported.
*/
private AnalysisErrorListener errorListener;
/**
* The default source to be used when reporting errors.
*/
private Source defaultSource;
/**
* The source to be used when reporting errors.
*/
private Source source;
/**
* Initialize a newly created error reporter that will report errors to the given listener.
*
* @param errorListener the error listener to which errors will be reported
* @param defaultSource the default source to be used when reporting errors
*/
public ErrorReporter(AnalysisErrorListener errorListener, Source defaultSource) {
if (errorListener == null) {
throw new IllegalArgumentException("An error listener must be provided");
} else if (defaultSource == null) {
throw new IllegalArgumentException("A default source must be provided");
}
this.errorListener = errorListener;
this.defaultSource = defaultSource;
this.source = defaultSource;
}
public Source getSource() {
return source;
}
/**
* Creates an error with properties with the given error code and arguments.
*
* @param errorCode the error code of the error to be reported
* @param node the node specifying the location of the error
* @param arguments the arguments to the error, used to compose the error message
*/
public AnalysisErrorWithProperties newErrorWithProperties(ErrorCode errorCode, AstNode node,
Object... arguments) {
return new AnalysisErrorWithProperties(
source,
node.getOffset(),
node.getLength(),
errorCode,
arguments);
}
/**
* Report a passed error.
*
* @param error the error to report
*/
public void reportError(AnalysisError error) {
errorListener.onError(error);
}
/**
* Report an error with the given error code and arguments.
*
* @param errorCode the error code of the error to be reported
* @param element the element which name should be used as the location of the error
* @param arguments the arguments to the error, used to compose the error message
*/
public void reportErrorForElement(ErrorCode errorCode, Element element, Object... arguments) {
reportErrorForOffset(
errorCode,
element.getNameOffset(),
element.getDisplayName().length(),
arguments);
}
/**
* Report an error with the given error code and arguments.
* <p>
* If the arguments contain the names of two or more types, the method
* {@link #reportTypeErrorForNode(ErrorCode, AstNode, Object...)} should be used and the types
* themselves (rather than their names) should be passed as arguments.
*
* @param errorCode the error code of the error to be reported
* @param node the node specifying the location of the error
* @param arguments the arguments to the error, used to compose the error message
*/
public void reportErrorForNode(ErrorCode errorCode, AstNode node, Object... arguments) {
reportErrorForOffset(errorCode, node.getOffset(), node.getLength(), arguments);
}
/**
* Report an error with the given error code and arguments.
*
* @param errorCode the error code of the error to be reported
* @param offset the offset of the location of the error
* @param length the length of the location of the error
* @param arguments the arguments to the error, used to compose the error message
*/
public void reportErrorForOffset(ErrorCode errorCode, int offset, int length, Object... arguments) {
errorListener.onError(new AnalysisError(source, offset, length, errorCode, arguments));
}
/**
* Report an error with the given error code and arguments.
*
* @param errorCode the error code of the error to be reported
* @param token the token specifying the location of the error
* @param arguments the arguments to the error, used to compose the error message
*/
public void reportErrorForToken(ErrorCode errorCode, Token token, Object... arguments) {
reportErrorForOffset(errorCode, token.getOffset(), token.getLength(), arguments);
}
/**
* Report an error with the given error code and arguments. The arguments are expected to contain
* two or more types. Convert the types into strings by using the display names of the types,
* unless there are two or more types with the same names, in which case the extended display
* names of the types will be used in order to clarify the message.
* <p>
* If there are not two or more types in the argument list, the method
* {@link #reportErrorForNode(ErrorCode, AstNode, Object...)} should be used instead.
*
* @param errorCode the error code of the error to be reported
* @param node the node specifying the location of the error
* @param arguments the arguments to the error, used to compose the error message
*/
public void reportTypeErrorForNode(ErrorCode errorCode, AstNode node, Object... arguments) {
convertTypeNames(arguments);
reportErrorForOffset(errorCode, node.getOffset(), node.getLength(), arguments);
}
/**
* Set the source to be used when reporting errors. Setting the source to {@code null} will cause
* the default source to be used.
*
* @param source the source to be used when reporting errors
*/
public void setSource(Source source) {
this.source = source == null ? defaultSource : source;
}
/**
* Given an array of arguments that is expected to contain two or more types, convert the types
* into strings by using the display names of the types, unless there are two or more types with
* the same names, in which case the extended display names of the types will be used in order to
* clarify the message.
*
* @param arguments the arguments that are to be converted
*/
private void convertTypeNames(Object[] arguments) {
if (hasEqualTypeNames(arguments)) {
int count = arguments.length;
for (int i = 0; i < count; i++) {
Object argument = arguments[i];
if (argument instanceof Type) {
Type type = (Type) argument;
Element element = type.getElement();
if (element == null) {
arguments[i] = type.getDisplayName();
} else {
arguments[i] = element.getExtendedDisplayName(type.getDisplayName());
}
}
}
} else {
int count = arguments.length;
for (int i = 0; i < count; i++) {
Object argument = arguments[i];
if (argument instanceof Type) {
arguments[i] = ((Type) argument).getDisplayName();
}
}
}
}
/**
* Return {@code true} if the given array of arguments contains two or more types with the same
* display name.
*
* @param arguments the arguments being tested
* @return {@code true} if the array of arguments contains two or more types with the same display
* name
*/
private boolean hasEqualTypeNames(Object[] arguments) {
int count = arguments.length;
HashSet<String> typeNames = new HashSet<String>(count);
for (int i = 0; i < count; i++) {
if (arguments[i] instanceof Type && !typeNames.add(((Type) arguments[i]).getDisplayName())) {
return true;
}
}
return false;
}
}