<?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>Use Diamond</description>
        <expected-problems>3</expected-problems>
        <expected-linenumbers>6,9,10</expected-linenumbers>
        <code><![CDATA[
import java.util.ArrayList;
import java.util.List;
public class Foo {
    List<String> field;
    public void foo() {
       List<String> strings = new ArrayList<String>();
       List<String> strings2 = new ArrayList<>();
       List<List<String>> strings3 = new ArrayList<>();
       List<List<List<String>>> strings4 = new ArrayList<List<List<String>>>();
       this.field = new ArrayList<String>();
    }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>False positive cases</description>
        <expected-problems>2</expected-problems>
        <expected-linenumbers>10,15</expected-linenumbers>
        <code><![CDATA[
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

public class E {
    public void foo(List<E> files) {
        Iterator<E> EMPTY_ITERATOR = new ArrayList<E>().iterator(); // necessary
        ((ListNode<E>) null).list = new ArrayList<E>();             // violation
    }

    public Map<ListNode<?>, Object> getOverriddenPropertiesByPropertyDescriptor() {
        // assignment context
        return Integer.compare(1,2) == 0 ? new HashMap<ListNode<?>, Object>() // violation
                                         : new HashMap<>();
    }

    static class ListNode<E> {
        List<E> list;
    }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>Anonymous classes before java 9</description>
        <expected-problems>0</expected-problems>
        <code><![CDATA[
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class E {
    public void foo(List<E> files) {
        Collections.sort(files, new Comparator<E>() {
            @Override
            public int compare(E left, E right) {
                return left.toString().compareTo(right.toString());
            }
        });
        new ThreadLocal<List<E>>() {
            @Override
            protected List<E> initialValue() {
                return null;
            }
        };
    }
}
        ]]></code>
        <source-type>java 8</source-type>
    </test-code>

    <test-code>
        <description>Anonymous classes after java 9</description>
        <expected-problems>2</expected-problems>
        <expected-linenumbers>8,17</expected-linenumbers>
        <code><![CDATA[
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class E {
    public void foo(List<E> files) {
        Collections.sort(files, new Comparator<E>() {
            @Override
            public int compare(E left, E right) {
                return left.toString().compareTo(right.toString());
            }
        });
        new ThreadLocal<List<E>>() { // not this one, it has no context
            @Override
            protected List<E> initialValue() {
                return new ArrayList<E>();
            }
        };
    }
}
        ]]></code>
        <source-type>java 9</source-type>
    </test-code>

    <test-code>
        <description>#1624[java] UseDiamondOperator doesn't work with var</description>
        <expected-problems>1</expected-problems>
        <expected-linenumbers>6</expected-linenumbers>
        <code><![CDATA[
import java.util.ArrayList;
public class Buzz {
    public void buzz() {
        var f = new ArrayList<String>(); // ok
        f = new ArrayList<>(); // ok
        f = new ArrayList<String>(); // flagged by rule
    }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>Multiple initializations in a single declaration</description>
        <expected-problems>1</expected-problems>
        <expected-linenumbers>6</expected-linenumbers>
        <code><![CDATA[
import java.util.ArrayList;
import java.util.List;
public class Buzz {
    public void buzz() {
        List<String> foo,
            bar = new ArrayList<String>(), // flagged
            baz = new ArrayList<>(); // ok
    }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>#1723 FP with var inside lambda (declaration)</description>
        <expected-problems>0</expected-problems>
        <code><![CDATA[
import java.util.ArrayList;
class Foo {
    {
        Runnable someAction = () -> {
            var foo = new ArrayList<String>(5); // ok
            System.err.println(foo);
        };
    }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>#1723 FP with var inside lambda (assignment)</description>
        <expected-problems>0</expected-problems>
        <code><![CDATA[
import java.util.ArrayList;
class Foo {
    {
        Runnable someAction;
        someAction = () -> {
            var foo = new ArrayList<String>(5); // ok
            System.err.println(foo);
        };
    }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>FP with array creation</description>
        <expected-problems>0</expected-problems>
        <code><![CDATA[
class Foo {
    {
        Class<?> c = new Class<?>[0];
    }
}
        ]]></code>
    </test-code>

    <!-- These tests depend on the Java version used -->
    <!-- For now we keep the old behaviour of Java 7 and ignoring type
     arguments which have wildcards involved, ie we have
     false negatives for Java8+. We can improve that with better type resolution
     in PMD 7. -->

    <test-code>
        <description>(J7) Version sensitive tests - avoid possible false positives on Java7</description>
        <expected-problems>0</expected-problems>
        <code><![CDATA[
import java.lang.ref.WeakReference;

public class Foo {
    private WeakReference<Class<?>> typeReference;
    public void foo() {
        // this should be positive in Java 8, negative in Java 7
        // in java 7: no violation, in java 8 violation
        typeReference = new WeakReference<Class<?>>(String.class);
        // this is because in java 7, new WeakReference<>(String.class) types as WeakReference<Class<String>>
        // which is incompatible with WeakReference<Class<?>>, whereas Java 8's type inference is better.

        // the following is the same:
        // in java 7: no violation, in java 8 violation
        Class<?> type = null;
        typeReference = new WeakReference<Class<?>>(type);
    }
}
        ]]></code>
        <source-type>java 7</source-type>
    </test-code>

    <test-code>
        <description>(J8) Version sensitive tests - known false negatives on Java8+</description>
        <expected-problems>2</expected-problems>
        <expected-linenumbers>6,8</expected-linenumbers>
        <code><![CDATA[
import java.lang.ref.WeakReference;

public class Foo {
    private WeakReference<Class<?>> typeReference;
    public void foo() {
        typeReference = new WeakReference<Class<?>>(String.class); // pos
        Class<?> type = null;
        typeReference = new WeakReference<Class<?>>(type); // pos
    }
}
        ]]></code>
        <source-type>java 8</source-type>
    </test-code>

    <test-code>
        <description>False negative for nested type parameters (#2545)</description>
        <expected-problems>3</expected-problems>
        <expected-linenumbers>8,9,19</expected-linenumbers>
        <code><![CDATA[
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.lang.ref.WeakReference;

public class UseDiamondOperatorFalseNegative {

    static List<Map<String, Object>> l = new ArrayList<Map<String, Object>>(); // FN
    static WeakReference<Class<String>> typeReference = new WeakReference<Class<String>>(String.class); // FN
    static WeakReference<Class<?>> typeReference2 = new WeakReference<Class<?>>(String.class); // FP

    public void test() {
        final List<String> l2;
        l2 = true ? new ArrayList<String>()
                  : new ArrayList<String>(); // FN twice for java8+, but for java7, this is ok!
    }

    static {
        l = new ArrayList<Map<String, Object>>(); // FN
    }
}
        ]]></code>
        <source-type>java 7</source-type>
    </test-code>

    <test-code>
        <description>(J8+) False negative for Java8+ and ternary operator (#2545)</description>
        <expected-problems>2</expected-problems>
        <expected-linenumbers>7,7</expected-linenumbers>
        <code><![CDATA[
import java.util.List;
import java.util.ArrayList;

public class UseDiamondOperatorFalseNegative {
    public void test() {
        final List<String> l2;
        l2 = true ? new ArrayList<String>() : new ArrayList<String>(); // FN twice for java8+, but for java7, this is ok!
    }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>Java 7 invocation context</description>
        <expected-problems>0</expected-problems>
        <code><![CDATA[
public class Generic<T> {
    <E> Generic<E> method(Generic<E> e) { return e; }
    public Generic<T> test() {
        return method(new Generic<T>());
    }
}
        ]]></code>
        <source-type>java 7</source-type>
    </test-code>
    <test-code>
        <description>Java 8+ invocation context</description>
        <expected-problems>1</expected-problems>
        <code><![CDATA[
public class Generic<T> {
    <E> Generic<E> method(Generic<E> e) { return e; }
    public Generic<T> test() {
        return method(new Generic<T>());
    }
}
        ]]></code>
    </test-code>
    <test-code>
        <description>Java 8+ invocation context, no target</description>
        <expected-problems>0</expected-problems>
        <code><![CDATA[
public class Generic<T> {
    <E> Generic<E> method(Generic<E> e) { return e; }
    public Generic<T> test() {
        var m = method(new Generic<T>());
    }
}
        ]]></code>
    </test-code>
    <test-code>
        <description>Test message with fqcn</description>
        <expected-problems>1</expected-problems>
        <expected-messages>
            <message>Explicit type arguments can be replaced by a diamond: `new p.Generic&lt;&gt;()`</message>
        </expected-messages>
        <code><![CDATA[
package p;

public class Generic<T> {
    <E> Generic<E> method(Generic<E> e) { return e; }
    public Generic<T> test() {

        return method(new p.Generic<T>());
    }
}
        ]]></code>
    </test-code>
    <test-code>
        <description>Test message with inner class</description>
        <expected-problems>1</expected-problems>
        <expected-messages>
            <message>Explicit type arguments can be replaced by a diamond: `new Generic.Inner&lt;&gt;()`</message>
        </expected-messages>
        <code><![CDATA[
package p;

public class Generic<T> {
    static class Inner<K> {}
    <E> Generic<E> method(Generic<E> e) { return e; }
    public Generic<T> test() {

        return method(new Generic.Inner<T>());
    }
}
        ]]></code>
    </test-code>
    <test-code>
        <description>Test message with raw type</description>
        <expected-problems>2</expected-problems>
        <expected-linenumbers>6,9</expected-linenumbers>
        <expected-messages>
            <message>Explicit type arguments can be replaced by a diamond: `new ArrayList&lt;&gt;(5)`</message>
            <message>Raw type use may be avoided by using a diamond: `new ArrayList&lt;&gt;()`</message>
        </expected-messages>
        <code><![CDATA[
            import java.util.List;
            import java.util.ArrayList;
            class Foo {
                static {
                    List<String> strings;
                    strings = new ArrayList<String>(5); // unnecessary duplication of type parameters
                    strings = new ArrayList<>();       // using diamond type arguments is more concise

                    strings = new ArrayList(); // accidental use of a raw type, you can use ArrayList<> instead

                    strings = new ArrayList<>() {
                        // for anonymous classes, this is possible since Java 9 only
                    };
                }
            }
            ]]></code>
    </test-code>
    <test-code>
        <description>[Java] UseDiamondOperator FP when expected type and constructed type have a different parameterization #2391</description>
        <expected-problems>0</expected-problems>
        <code><![CDATA[
            import java.util.List;
            interface LambdaKey<T> { String key(T value);}
            class LambdaKeyConverter<T> {
                public LambdaKeyConverter(final LambdaKey<T> lambdaKey) {}
            }

            public class SomeClass {
                private final LambdaKeyConverter converter =
                        new LambdaKeyConverter<List>(list -> String.valueOf(list.size()));
            }
            ]]></code>
    </test-code>
    
    <test-code>
        <description>IllegalStateException overload resolution not complete</description>
        <expected-problems>0</expected-problems>
        <code><![CDATA[
            import non.existing.ForOverloadResolution;
            import java.util.HashMap;
            
            class OverloadFailure {
                public void foo() {
                    ForOverloadResolution.method(new HashMap<String, Object>());
                }
            }
            ]]></code>
    </test-code>
    <test-code>
        <description>#2710 FP with Lombok val initializer</description>
        <expected-problems>0</expected-problems>
        <code><![CDATA[
import lombok.val;
import java.util.ArrayList;

public class Foo {
    val foo = new ArrayList<String>(5);
}
            ]]></code>
    </test-code>
    <code-fragment id="generic-anon-class"><![CDATA[
        class Foo {

            static void foo(Generic<String> g) {
            }

            static {
                foo(new Generic<String>() {
                    @Override
                    public void accept(String s) {
                        // ...
                    }
                });
            }

            interface Generic<T> {

                void accept(T t);
            }
        }
        ]]></code-fragment>
    <test-code>
        <description>Diamond cannot be used for anon classes in Java 8 sources</description>
        <expected-problems>0</expected-problems>
        <code-ref id="generic-anon-class"/>
        <source-type>java 8</source-type>
    </test-code>
    <test-code>
        <description>Diamond cannot be used for anon classes in Java 7 sources</description>
        <expected-problems>0</expected-problems>
        <code-ref id="generic-anon-class"/>
        <source-type>java 7</source-type>
    </test-code>
    <test-code>
        <description>Diamond can be used for anon classes in Java 9+ sources</description>
        <expected-problems>1</expected-problems>
        <code-ref id="generic-anon-class"/>
        <source-type>java 9</source-type>
    </test-code>
    <test-code>
        <description>Diamond can be used for anon classes in Java 17+ sources</description>
        <expected-problems>1</expected-problems>
        <code-ref id="generic-anon-class"/>
        <source-type>java 17</source-type>
    </test-code>
    <test-code>
        <description>Issue #3353 - FNs in PMD 6</description>
        <expected-problems>3</expected-problems>
        <expected-messages>
            <message>Explicit type arguments can be replaced by a diamond: `new ArrayList&lt;&gt;()`</message>
            <message>Explicit type arguments can be replaced by a diamond: `new ArrayList&lt;&gt;(timestamps)`</message>
            <message>Explicit type arguments can be replaced by a diamond: `new MyConsumer&lt;&gt;()`</message>
        </expected-messages>
        <code><![CDATA[
            import java.sql.Timestamp;
            import java.util.ArrayList;
            import java.util.Arrays;
            import java.util.Date;
            import java.util.List;
            import java.util.function.Consumer;

            public class DiamondIssues {

                public static void main(final String[] args) {
                    // <String> is not necessary. IntelliJ does not catch this one either
                    doStuff(new ArrayList<String>());

                    // specifying <Date> is also not necessary. IntelliJ does not catch this one either
                    final List<Timestamp> timestamps = Arrays.asList(new Timestamp(new Date().getTime()));
                    doOtherStuff(new ArrayList<Date>(timestamps));

                    // <Integer> is not required. IntelliJ correctly reports this one as un-necessary
                    addConsumer(Integer.class, new MyConsumer<Integer>());
                }

                private static void doStuff(final Object object) {
                    System.out.println(object);
                }

                private static void doOtherStuff(final List<? extends Date> dates) {
                    System.out.println(dates);
                }

                private static <T> void addConsumer(final Class<? extends T> type, final Consumer<T> consumer) {
                    // do something useful here
                }

                private static class MyConsumer<T extends Number> implements Consumer<T> {

                    @Override
                    public void accept(final T t) {
                        System.out.println(t);
                    }
                }
            }]]>
        </code>
    </test-code>
</test-data>
