org.checkerframework.framework.source.Result Maven / Gradle / Ivy
Show all versions of framework-all Show documentation
package org.checkerframework.framework.source;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import org.checkerframework.checker.compilermsgs.qual.CompilerMessageKey;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.checkerframework.dataflow.qual.Pure;
import org.checkerframework.dataflow.qual.SideEffectFree;
/**
* Represents the outcome of a type-checking operation (success, warning, or failure, plus a list of
* explanatory messages). {@link Result}s created during type-checking 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 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 messagePairs) {
this.type = type;
this.messages = new ArrayList<>();
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 messages = new ArrayList<>(this.messages.size() + r.messages.size());
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 getMessageKeys() {
List msgKeys = new ArrayList<>(getDiagMessages().size());
for (DiagMessage msg : getDiagMessages()) {
msgKeys.add(msg.getMessageKey());
}
return Collections.unmodifiableList(msgKeys);
}
/** @return an unmodifiable list of the message pairs */
public List getDiagMessages() {
return Collections.unmodifiableList(messages);
}
@SideEffectFree
@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 diagnostic messages.
*
* {@code DiagMessage} encapsulates 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 final @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));
}
@Pure
@Override
public int hashCode() {
return Objects.hash(this.message, Arrays.hashCode(this.args));
}
@SideEffectFree
@Override
public String toString() {
if (args.length == 0) {
return message;
}
return message + " : " + Arrays.toString(args);
}
}
}