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); } } }