<?xml version="1.0" encoding="UTF-8"?>
<test-data
    xmlns="http://pmd.sourceforge.net/rule-tests"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://pmd.sourceforge.net/rule-tests http://pmd.sourceforge.net/rule-tests_1_0_0.xsd">

    <test-code>
        <description>null check after method invocation with conditional AND and !=</description>
        <expected-problems>4</expected-problems>
        <expected-linenumbers>3,4,6,7</expected-linenumbers>
        <expected-messages>
            <message>The null check here is misplaced; if the variable 'a' is null there will be a NullPointerException</message>
            <message>The null check here is misplaced; if the variable 'a' is null there will be a NullPointerException</message>
            <message>The null check here is misplaced; if the variable 'a' is null there will be a NullPointerException</message>
            <message>The null check here is misplaced; if the variable 'a' is null there will be a NullPointerException</message>
        </expected-messages>
        <code><![CDATA[
public class Foo {
    void bar(Object a, Object baz) {
        if (a.equals(baz) && a != null) {} // a could be null, misplaced null check
        if (a.equals(baz) || a == null) {} // a could be null, misplaced null check

        if (a.equals(baz) && null != a) {} // a could be null, misplaced null check
        if (a.equals(baz) || null == a) {} // a could be null, misplaced null check

        if (a != null && a.equals(baz)) {} // correct null check
        if (a == null || a.equals(baz)) {} // correct null check
    }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>null check after nested method invocation</description>
        <expected-problems>4</expected-problems>
        <expected-linenumbers>3,4,6,7</expected-linenumbers>
        <expected-messages>
            <message>The null check here is misplaced; if the variable 'baz' is null there will be a NullPointerException</message>
            <message>The null check here is misplaced; if the variable 'baz' is null there will be a NullPointerException</message>
            <message>The null check here is misplaced; if the variable 'baz' is null there will be a NullPointerException</message>
            <message>The null check here is misplaced; if the variable 'baz' is null there will be a NullPointerException</message>
        </expected-messages>
        <code><![CDATA[
public class Foo {
    void bar(Object a, Foo baz) {
        if (a.equals(baz.foo()) && baz != null) {} // baz could be null, misplaced null check
        if (a.equals(baz.foo()) || baz == null) {} // baz could be null, misplaced null check

        if (a.equals(baz.foo()) && null != baz) {} // baz could be null, misplaced null check
        if (a.equals(baz.foo()) || null == baz) {} // baz could be null, misplaced null check

        if (baz != null && a.equals(baz.foo())) {} // correct null check
        if (baz == null || a.equals(baz.foo())) {} // correct null check
    }

    public Object foo() { return null; }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>null check before nested method invocation</description>
        <expected-problems>0</expected-problems>
        <code><![CDATA[
public class Foo {
    void bar(Object a) {
        if (a != null && a.equals(foo())) {}
    }

    public Object foo() { return null; }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>1610730: null check after method invocation with conditional OR and ==</description>
        <expected-problems>1</expected-problems>
        <expected-linenumbers>3</expected-linenumbers>
        <expected-messages>
            <message>The null check here is misplaced; if the variable 'a' is null there will be a NullPointerException</message>
        </expected-messages>
        <code><![CDATA[
public class Foo {
    void bar(Object a, Object baz) {
        if (a.equals(baz) || a == null) {}
    }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>3372128: False positive: ArrayIsStoredDirectly</description>
        <expected-problems>0</expected-problems>
        <code><![CDATA[
import java.util.Arrays;
import java.util.List;
public class Foo {
    private List<String> excludeStatus;
    public final void setExcludeStatus(String[] excludeStatus) {
        if (excludeStatus != null) {
            this.excludeStatus = Arrays.asList(excludeStatus.clone());
        } else {
            this.excludeStatus = null;
        }
    }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>#977 MisplacedNullCheck makes false positives</description>
        <expected-problems>0</expected-problems>
        <code><![CDATA[
public class Test {
    public void method(final String value,final String oldValue) {
        if ((value != null && !value.equals(oldValue)) || value == null) {
            // Do something
        }

        if ((value == null || !value.equals(oldValue)) && value != null) {}
    }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>#2242 False-positive MisplacedNullCheck reported (1)</description>
        <expected-problems>0</expected-problems>
        <code><![CDATA[
public class Test {
    String field;

    Test(String f) { field = f; }

    public boolean method(final String value) {
        boolean c = value != null
             && ((field != null && !value.equals(field.toString())) || field == null);
        return c;
    }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>#2242 False-positive MisplacedNullCheck reported (2)</description>
        <expected-problems>0</expected-problems>
        <code><![CDATA[
public class Test {
    String field;

    Test(String f) { field = f; }

    public boolean method(final String value) {
        boolean c = value != null
             && (field == null || (field != null && !value.equals(field.toString())));
        return c;
    }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>False-positive/negative with multiple conditions</description>
        <expected-problems>2</expected-problems>
        <expected-linenumbers>8,9</expected-linenumbers>
        <expected-messages>
            <message>The null check here is misplaced; if the variable 'attributes' is null there will be a NullPointerException</message>
            <message>The null check here is misplaced; if the variable 'attributes' is null there will be a NullPointerException</message>
        </expected-messages>
        <code><![CDATA[
import java.util.Map;
public class Test {
    public void method(String annotationType, Map<String, Object> attributes) {
        boolean isStereotype = annotationType.equals("javax.inject.Named");
        if (isStereotype && attributes != null && attributes.containsKey("value")) {}
        if (isStereotype || attributes == null || attributes.containsKey("value")) {}

        if (isStereotype && attributes.containsKey("value") && attributes != null) {}
        if (isStereotype || attributes.containsKey("value") || attributes == null) {}
    }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>False positive with no dereferencing variable access</description>
        <expected-problems>0</expected-problems>
        <code><![CDATA[
public class Foo {
    void bar(Object a, Object b, boolean aMustBePresent) {
        if (a == b || a == null) { } // there is no NPE happening here
        int aUndefined = (a == b || a == null) ? 1 : 0;
        if (a != b && a != null) { }
        if (a == this || a == null) { }
        for (Object p = a; p != b && p != null; p = p.prev) {}
        if (a != this &&
            a != null && b.equals(a.toString())) { } // potential NPE for b.equals, but that's not the point here
        if (a == b || a == null || (b == null && !aMustBePresent)) { }
        if ((a instanceof Foo) || (a == null)) { }
        while (Object.class != a && a != null) { }
    }
}
        ]]></code>
    </test-code>
</test-data>
