package checkers.source;
import checkers.compilermsgs.quals.CompilerMessageKey;
import checkers.nullness.quals.*;
import java.util.*;
/**
* Represents the outcome of a typechecking operation (success,
* warning, or failure, plus a list of explanatory messages).
* {@link Result}s created during typechecking can be reported using
* {@link SourceChecker#report}, which ultimately delivers an error
* or warning message via the JSR 199 compiler interface.
*
* @see SourceChecker#report
*/
public final class Result {
private static enum Type {
SUCCESS, FAILURE, WARNING;
/**
* @return whichever of the given types is most serious
*/
public static final Type merge(Type a, Type b) {
if (a == FAILURE || b == FAILURE)
return FAILURE;
else if (a == WARNING || b == WARNING)
return WARNING;
else
return SUCCESS;
}
}
/** The type of result (success, warning, failure). */
private final Type type;
/** The messages for the results **/
private final List<DiagMessage> messages;
/** The success result. */
public static final Result SUCCESS = new Result(Type.SUCCESS, null);
/**
* Creates a new failure result with the given message key.
*
* @param messageKey
* the key representing the reason for failure
* @param args
* optional arguments to be included in the message
* @return the failure result
*/
public static Result failure(@CompilerMessageKey String messageKey,
/*@Nullable*/ Object... args) {
return new Result(Type.FAILURE, Collections
.singleton(new DiagMessage(messageKey, args)));
}
/**
* Creates a new warning result with the given message.
*
* @param messageKey
* the key for the warning message
* @param args
* optional arguments to be included in the message
* @return the warning result
*/
public static Result warning(@CompilerMessageKey String messageKey,
/*@Nullable*/ Object... args) {
return new Result(Type.WARNING, Collections
.singleton(new DiagMessage(messageKey, args)));
}
private Result(Type type, Collection<DiagMessage> messagePairs) {
this.type = type;
this.messages = new LinkedList<DiagMessage>();
if (messagePairs != null)
for (DiagMessage msg : messagePairs) {
String message = msg.getMessageKey();
/*@Nullable*/ Object[] args = msg.getArgs();
if (args != null)
args = Arrays.copyOf(msg.getArgs(), args.length);
this.messages.add(new DiagMessage(message, args));
}
}
/**
* Merges two results into one.
*
* @param r
* the result to merge with this result
* @return a result that is the success result if both this and
* {@code r} are success results, or a result that has the
* more significant type (failure > warning > success) and
* the message keys of both this result and {@code r}
*/
public Result merge(Result r) {
if (r == null)
return this;
if (r.isSuccess() && this.isSuccess())
return SUCCESS;
List<DiagMessage> messages = new LinkedList<DiagMessage>();
messages.addAll(this.messages);
messages.addAll(r.messages);
return new Result(Type.merge(r.type, this.type), messages);
}
/**
* @return true if the result is success (not a failure or
* warning)
*/
public boolean isSuccess() {
return type == Type.SUCCESS;
}
/**
* @return true if the result is a failure
*/
public boolean isFailure() {
return type == Type.FAILURE;
}
/**
* @return true if the result is a warning
*/
public boolean isWarning() {
return type == Type.WARNING;
}
/**
* @return the message keys associated with the result
*/
public List<String> getMessageKeys() {
List<String> msgKeys = new LinkedList<String>();
for (DiagMessage msg : getDiagMessages())
msgKeys.add(msg.getMessageKey());
return Collections.</*@NonNull*/ String>unmodifiableList(msgKeys);
}
/**
* @return an unmodifiable list of the message pairs
*/
public List<DiagMessage> getDiagMessages() {
return Collections.</*@NonNull*/ DiagMessage>unmodifiableList(messages);
}
@Override
public String toString() {
switch (type) {
case FAILURE:
return "FAILURE: " + messages;
case WARNING:
return "WARNING: " + messages;
case SUCCESS:
default:
return "SUCCESS";
}
}
/**
* A class that represents the diangosis messages.
*
* {@code DiagMessage} encapsulate the message key which would identify
* the relevant standard error message according to the user locale.
*
* The optional arguments are possible custom strings for the error
* message.
*
*/
public static class DiagMessage {
private @CompilerMessageKey String message;
private Object[] args;
protected DiagMessage(@CompilerMessageKey String message, Object[] args) {
this.message = message;
if (args == null) {
this.args = new Object[0]; /*null->nn*/
} else {
this.args = Arrays.copyOf(args, args.length);
}
}
/**
* @return the message key of this DiagMessage
*/
public @CompilerMessageKey String getMessageKey() {
return this.message;
}
/**
* @return the customized optional arguments for the message
*/
public Object[] getArgs() {
return this.args;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof DiagMessage))
return false;
DiagMessage other = (DiagMessage) obj;
return (message.equals(other.message) && Arrays.equals(args,
other.args));
}
@Override
public String toString() {
if (args.length == 0)
return message;
return message + " : " + Arrays.toString(args);
}
}
}