import org.checkerframework.checker.nullness.qual.*;
import org.checkerframework.checker.nullness.qual.EnsuresNonNullIf;

public class AssertIfChecked {

    boolean unknown = false;

    @Nullable Object value;

    @EnsuresNonNullIf(result = true, expression = "value")
    // :: error: (contracts.conditional.postcondition.invalid.returntype)
    public void badform1() {}

    @EnsuresNonNullIf(result = true, expression = "value")
    // :: error: (contracts.conditional.postcondition.invalid.returntype)
    public Object badform2() {
        return new Object();
    }

    @EnsuresNonNullIf(result = false, expression = "value")
    // :: error: (contracts.conditional.postcondition.invalid.returntype)
    public void badform3() {}

    @EnsuresNonNullIf(result = false, expression = "value")
    // :: error: (contracts.conditional.postcondition.invalid.returntype)
    public Object badform4() {
        return new Object();
    }

    @EnsuresNonNullIf(result = true, expression = "value")
    public boolean goodt1() {
        return value != null;
    }

    @EnsuresNonNullIf(result = true, expression = "value")
    public boolean badt1() {
        // :: error: (contracts.conditional.postcondition.not.satisfied)
        return value == null;
    }

    @EnsuresNonNullIf(result = false, expression = "value")
    public boolean goodf1() {
        return value == null;
    }

    @EnsuresNonNullIf(result = false, expression = "value")
    public boolean badf1() {
        // :: error: (contracts.conditional.postcondition.not.satisfied)
        return value != null;
    }

    @EnsuresNonNullIf(result = true, expression = "value")
    public boolean bad2() {
        // :: error: (contracts.conditional.postcondition.not.satisfied)
        return value == null || unknown;
    }

    @EnsuresNonNullIf(result = false, expression = "value")
    public boolean bad3() {
        // :: error: (contracts.conditional.postcondition.not.satisfied)
        return value == null && unknown;
    }

    @EnsuresNonNullIf(result = true, expression = "#1")
    boolean testParam(final @Nullable Object param) {
        return param != null;
    }

    @EnsuresNonNullIf(result = true, expression = "#1")
    boolean testLitTTgood1(final @Nullable Object param) {
        if (param == null) {
            return false;
        }
        return true;
    }

    @EnsuresNonNullIf(result = true, expression = "#1")
    boolean testLitTTbad1(final @Nullable Object param) {
        // :: error: (contracts.conditional.postcondition.not.satisfied)
        return true;
    }

    @EnsuresNonNullIf(result = false, expression = "#1")
    boolean testLitFFgood1(final @Nullable Object param) {
        return true;
    }

    @EnsuresNonNullIf(result = false, expression = "#1")
    boolean testLitFFgood2(final @Nullable Object param) {
        if (param == null) {
            return true;
        }
        return false;
    }

    @EnsuresNonNullIf(result = false, expression = "#1")
    boolean testLitFFbad1(final @Nullable Object param) {
        if (param == null) {
            // :: error: (contracts.conditional.postcondition.not.satisfied)
            return false;
        }
        return true;
    }

    @EnsuresNonNullIf(result = false, expression = "#1")
    boolean testLitFFbad2(final @Nullable Object param) {
        // :: error: (contracts.conditional.postcondition.not.satisfied)
        return false;
    }

    @Nullable Object getValueUnpure() {
        return value;
    }

    @org.checkerframework.dataflow.qual.Pure
    @Nullable Object getValuePure() {
        return value;
    }

    @EnsuresNonNullIf(result = true, expression = "getValuePure()")
    public boolean hasValuePure() {
        return getValuePure() != null;
    }

    @EnsuresNonNullIf(result = true, expression = "#1")
    public static final boolean isComment(@Nullable String s) {
        return s != null && (s.startsWith("//") || s.startsWith("#"));
    }

    @EnsuresNonNullIf(result = true, expression = "#1")
    public boolean myEquals(@Nullable Object o) {
        return (o instanceof String) && equals((String) o);
    }

    /*
     * The next two methods are from Daikon's class Quant. They verify that
     * EnsuresNonNullIf is correctly added to the assumptions after a check.
     */

    @EnsuresNonNullIf(
            result = true,
            expression = {"#1", "#2"})
    /* pure */ public static boolean sameLength(
            boolean @Nullable [] seq1, boolean @Nullable [] seq2) {
        return ((seq1 != null) && (seq2 != null) && seq1.length == seq2.length);
    }

    /* pure */ public static boolean isReverse(
            boolean @Nullable [] seq1, boolean @Nullable [] seq2) {
        if (!sameLength(seq1, seq2)) {
            return false;
        }
        // This assert is not needed for inference.
        // assert seq1 != null && seq2 != null; // because sameLength() = true

        int length = seq1.length;
        for (int i = 0; i < length; i++) {
            if (seq1[i] != seq2[length - i - 1]) {
                return false;
            }
        }
        return true;
    }
}
