/*
 * Copyright (c) 2013 - present Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 */

package codetoanalyze.java.eradicate;

import android.text.TextUtils;
import com.google.common.base.Preconditions;

import java.lang.System;
import javax.annotation.Nullable;
import com.facebook.infer.annotation.Assertions;
import com.facebook.infer.annotation.FalseOnNull;
import com.facebook.infer.annotation.TrueOnNull;


public class NullMethodCall {

  void callOnNull() {
    String s = null;
    int n = s.length();
  }

  void callOnEmptyString() {
    String s = "";
    int n = s.length();
  }

  void callAfterYodaCondition(@Nullable String s) {
    if (null != s) {
      int n = s.length();
    }
  }

  int objectLength(@Nullable Object o) {
    if (o instanceof String) {
      String s = (String) o;
      return s.length(); // OK: s cannot be null because of instanceof
    }
    return 0;
  }

  int testCheckState(@Nullable String s1, @Nullable String s2) {
    Preconditions.checkState(s1 != null && s2 != null, "bad");
    return s1.length() + s2.length();
  }

  int testPrivateStaticInnerClassField() {
    String s;
    S.sfld = "abc";
    s = S.sfld;
    return s.length();
  }

  private static class S {
    private static
    @Nullable
    String sfld;
  }

  @Nullable
  String fld;
  private
  @Nullable
  String pfld;

  public class Inner {
    int outerField() {
      String s = fld;
      return s.length();
    }

    int outerFieldInitialized() {
      fld = "abc";
      String s = fld;
      return s.length();
    }

    int outerPrivateField() {
      String s = pfld;
      return s.length();
    }

    int outerPrivateFieldInitialized() {
      pfld = "abc";
      String s = pfld;
      return s.length();
    }

    int outerPrivateFieldCheckNotNull() {
      Preconditions.checkNotNull(pfld);
      String s = pfld;
      return s.length();
    }

    int outerPrivateFieldCheckState() {
      Preconditions.checkState(pfld != null);
      String s = pfld;
      return s.length();
    }

    int outerPrivateFieldAssertNotNull() {
      Assertions.assertNotNull(pfld);
      String s = pfld;
      return s.length();
    }

    int outerPrivateFieldAssumeNotNull() {
      Assertions.assumeNotNull(pfld);
      String s = pfld;
      return s.length();
    }

    int outerPrivateFieldAssertCondition() {
      Assertions.assertCondition(pfld != null, "explanation");
      String s = pfld;
      return s.length();
    }

    int outerPrivateFieldAssumeCondition() {
      Assertions.assumeCondition(pfld != null, "explanation");
      String s = pfld;
      return s.length();
    }

    int outerPrivateFieldCheckStateYoda() {
      Preconditions.checkState(null != pfld);
      String s = pfld;
      return s.length();
    }

    String outerFieldGuardPrivate() {
      if (pfld != null) return pfld.toString();
      return "";
    }

    String outerFieldGuardPublic() {
      if (fld != null) return fld.toString();
      return "";
    }

    public class InnerInner {
      int outerouterPrivateFieldInitialized() {
        pfld = "abc";
        String s = pfld;
        return s.length();
      }
    }
  }

  @Nullable
  String getNullable() {
    return null;
  }

  void testVariableAssigmentInsideConditional() {
    String s = null;
    if ((s = getNullable()) != null) {
      int n = s.length();
    }
  }

  void testFieldAssigmentInsideConditional() {
    if ((fld = getNullable()) != null) {
      int n = fld.length();
    }
  }

  String abc = "abc";

  void testFieldAssignmentIfThenElse(String name) {
    String s = (name.length() == 0) ? null : abc;
    int n = s.length();
  }

  static String throwsExn() throws java.io.IOException {
    throw new java.io.IOException();
  }

  void testExceptionPerInstruction(int z) throws java.io.IOException {
    String s = null;

    try {
      s = throwsExn();
    } finally {
      int n = s.length();
    }
  }

  public class InitializeAndExceptions {
    String s;

    String bad() throws java.io.IOException {
      throw new java.io.IOException();
    }

    InitializeAndExceptions() throws java.io.IOException {
      s = bad(); // should not report field not initialized
    }
  }

  public class InitializeViaPrivateMethod {
    String name;

    private void reallyInitName(String s) {
      name = s;
    }

    private void initName(String s) {
      reallyInitName(s);
    }

    InitializeViaPrivateMethod() {
      initName("abc");
    }
  }

  class CheckNotNullVararg {
    void checkNotNull(String msg, Object ... objects) {
    }

    void testCheckNotNullVaratg(@Nullable String s1, @Nullable String s2) {
      checkNotNull("hello", s1, s2);
      s1.isEmpty();
      s2.isEmpty();
    }

    void testRepeatedCheckNotNull(@Nullable String s) {
      checkNotNull("abc", s);
      checkNotNull("abc", s.toString());
      s.toString().isEmpty();
    }
  }

  public void testSystemGetPropertyReturn() {
    String s = System.getProperty("");
    int n = s.length();
  }


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