/*
 * Copyright (c) 2013-present, Facebook, Inc.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

package codetoanalyze.java.eradicate;

import android.text.TextUtils;
import com.facebook.infer.annotation.FalseOnNull;
import com.facebook.infer.annotation.PropagatesNullable;
import com.facebook.infer.annotation.TrueOnNull;
import com.google.common.base.Strings;
import javax.annotation.Nullable;

public class CustomAnnotations {

  static class MyTextUtils {

    @TrueOnNull
    static boolean isEmpty(@Nullable java.lang.CharSequence s) {
      return s == null || s.equals("");
    }

    @FalseOnNull
    static boolean isNotEmpty(@Nullable java.lang.CharSequence s) {
      return s != null && s.length() > 0;
    }
  }

  class TestTextUtilsIsEmpty {
    void textUtilsNotIsEmpty(@Nullable CharSequence s) {
      if (!TextUtils.isEmpty(s)) {
        s.toString(); // OK
      }
    }

    void textUtilsIsEmpty(@Nullable CharSequence s) {
      if (TextUtils.isEmpty(s)) {
        s.toString(); // BAD
      }
    }

    void myTextUtilsNotIsEmpty(@Nullable CharSequence s) {
      if (!MyTextUtils.isEmpty(s)) {
        s.toString(); // OK
      }
    }

    void myTextUtilsIsEmpty(@Nullable CharSequence s) {
      if (MyTextUtils.isEmpty(s)) {
        s.toString(); // BAD
      }
    }

    void myTextUtilsIsNotEmpty(@Nullable CharSequence s) {
      if (MyTextUtils.isNotEmpty(s)) {
        s.toString(); // OK
      }
    }

    void myTextUtilsNotIsNotEmpty(@Nullable CharSequence s) {
      if (!MyTextUtils.isNotEmpty(s)) {
        s.toString(); // BAD
      }
    }
  }

  // Tests for the annotation @PropagatesNullable
  class TestPropagatesNullable {

    // one parameter: means return null iff s is null
    String m(@PropagatesNullable String s) {
      return s;
    }

    void mBad() {
      m(null).length(); // bad: m returns null
    }

    void mGood() {
      m("").length();
    }

    // limitation: we currently cannot check the body, and just trust the annotation
    String cannotCheckBody(@PropagatesNullable String s) {
      return null; // nothing is reported here
    }

    void illustrateFalseNegativeAsCannotCheckBody() {
      cannotCheckBody("").length(); // this is an NPE but is not found
    }

    // second parameter: means return null iff s2 is null
    String m2(@Nullable String s1, @PropagatesNullable String s2) {
      return s2;
    }

    void m2Bad() {
      m2(null, null).length(); // bad: m2 returns null
    }

    void m2Good() {
      m2(null, "").length();
    }

    // two parameters: means return null iff either s1 or s2 is null
    String m12(@PropagatesNullable String s1, @PropagatesNullable String s2) {
      return s1 == null ? s1 : s2;
    }

    void m12Bad1() {
      m12(null, "").length(); // bad: m12 returns null
    }

    void m12Bad2() {
      m12("", null).length(); // bad: m12 returns null
    }

    void m12Good() {
      m12("", "").length();
    }
  }

  class TestModeledTrueOnNull {

    void testIsEmptyOrNullOk(@Nullable String string) {
      if (!Strings.isNullOrEmpty(string)) {
        string.contains("Infer");
      }
    }

    void testIsEmptyOrNullBad(@Nullable String string) {
      if (Strings.isNullOrEmpty(string)) {
        string.contains("Infer");
      }
    }
  }
}
