<?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>missing argument</description>
        <expected-problems>1</expected-problems>
        <expected-linenumbers>9</expected-linenumbers>
        <code><![CDATA[
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Foo {
    private static final Logger LOGGER = LoggerFactory.getLogger(Foo.class);

    public void call() {
        final String oneArg = "one argument";
        LOGGER.error("forget the arg {} and {}", oneArg);
    }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>too many arguments</description>
        <expected-problems>1</expected-problems>
        <expected-linenumbers>9</expected-linenumbers>
        <code><![CDATA[
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Foo {
    private static final Logger LOGGER = LoggerFactory.getLogger(Foo.class);
    private final String MESSAGE = "too many args {}";

    public void call() {
        LOGGER.error(MESSAGE, "arg1", "arg2");
    }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>Logger without problems</description>
        <expected-problems>0</expected-problems>
        <code><![CDATA[
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Foo {
    private static final Logger LOGGER = LoggerFactory.getLogger(Foo.class);

    public void call() {
        final String message = "expected {} argument";
        LOGGER.error(message, 1);
    }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>ignore the exception param</description>
        <expected-problems>0</expected-problems>
        <code><![CDATA[
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Foo {
    private static final Logger LOGGER = LoggerFactory.getLogger(Foo.class);

    public void call() {
        LOGGER.error("params {} and {}", "arg1", "arg2", new IllegalStateException("Extra arg"));
    }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>Class cast exception with Method calls</description>
        <expected-problems>0</expected-problems>
        <code><![CDATA[
public class MethodCallClassCastExceptionProblem {
    public void foo() {
        // a method call
        otherMethod();
    }

    private void otherMethod() {
    }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>Null pointer with VariableNameDeclaration / VariableId</description>
        <expected-problems>0</expected-problems>
        <code><![CDATA[
public class NullPointerTypeProblem {
    public void foo() {
        java.util.StringTokenizer st = new java.util.StringTokenizer("a.b.c.d", ".");
        while (st.hasMoreTokens()) {
            System.out.println(st.nextToken());
        }
    }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>#1509 [java] InvalidSlf4jMessageFormat NPE</description>
        <expected-problems>0</expected-problems>
        <code><![CDATA[
package my.test;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Test {

    private static final Logger log = LoggerFactory.getLogger(Test.class);

    public static void main(String[] args) {
        log.info("1" + "2");
    }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>#1500 [java] InvalidSlf4jMessageFormat: doesn't ignore exception param</description>
        <expected-problems>0</expected-problems>
        <code><![CDATA[
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;

public class InvalidSlf4jExceptionTest {

    private static final Logger LOGGER = LoggerFactory.getLogger(InvalidSlf4jExceptionTest.class);

    public void foo(int tcmport) {
        try {
            throw new RuntimeException();
        } catch (RuntimeException e) {
            try {
                LOGGER.error("Exception was thrown during conversion from tc model: {}.", marshall(tcmport));
            } catch (IOException e1) {
                LOGGER.error("Problem to marshall to json tcImport: {}", tcsTripImport, e1);
            }
            throw e;
        }
    }

    Object marshall(int i) {}
}
        ]]></code>
    </test-code>

    <test-code>
        <description>#1541 [java] InvalidSlf4jMessageFormat: False positive with placeholder and exception</description>
        <expected-problems>0</expected-problems>
        <code><![CDATA[
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class InvalidSl4jExceptionBug1541 {
    private static final Logger log = LoggerFactory.getLogger(InvalidSl4jExceptionBug1541.class);

    public static void main(String[] args) {
        try {
            // ...
        } catch (Exception e) {
            log.error("Arg1 = {}. Exception: {}", "arg1Value", e);
        }
    }
}
        ]]></code>
    </test-code>
    <test-code>
        <description>#3560 [java] InvalidLogMessageFormat: False positive with message and exception in a block inside a lambda</description>
        <expected-problems>0</expected-problems>
        <code><![CDATA[
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.function.Consumer;

public class InvalidSl4jExceptionBug3560 {
    private static final Logger LOGGER = LoggerFactory.getLogger(InvalidSl4jExceptionBug3560.class);

    public static Consumer<Throwable> build() {
        return e -> {
            if (e instanceof RuntimeException) {
                LOGGER.warn("Unexpected RuntimeException", e);
            }
        };
    }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>#1551 [java] InvalidSlf4jMessageFormat: fails with NPE</description>
        <expected-problems>0</expected-problems>
        <code><![CDATA[
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class TestBug1551
{
    private static final Logger LOGGER = LoggerFactory.getLogger(TestBug1551.class);

    public void test()
    {
        String message = generateMessage();
        LOGGER.info(message);
    }

    private String generateMessage()
    {
        return "message";
    }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>#365 [java] InvalidSlf4jMessageFormat: false positive with pre-incremented variable</description>
        <expected-problems>0</expected-problems>
        <code><![CDATA[
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Foo
{
    private static final Logger LOGGER = LoggerFactory.getLogger(Foo.class);

    public void test()
    {
        int attempt = 0;
        LOGGER.info("test (attempt #{})", ++attempt);
    }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>#721 NPE in PMD 5.8.1 InvalidSlf4jMessageFormat</description>
        <expected-problems>0</expected-problems>
        <code><![CDATA[
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class LoggerHelper {
    public LoggerHelper(String loggerName) {
        Logger logger = LoggerFactory.getLogger(loggerName);
        logger.info(message);
    }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>#1291 [java] InvalidSlf4jMessageFormat false positive: too many arguments with string concatenation operator</description>
        <expected-problems>0</expected-problems>
        <code><![CDATA[
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public final class Main {
    private static final Logger LOGGER = LoggerFactory.getLogger(Main.class);

    private Main() {
    }

    public static void main(String[] args) {
        String string0 = "a";
        String string1 = "b";
        LOGGER.trace("first line {}"
                + "second line {}",
                string0,
                string1);
        String string2 = "c";
        LOGGER.debug("first line {} "
                + "second line {} and "
                + "the third line {}.",
                string0, string1, string2);
    }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>NPE in static block (see #1512)</description>
        <expected-problems>0</expected-problems>
        <code><![CDATA[
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class LoggerHelper {
    static {
        Logger logger = LoggerFactory.getLogger(loggerName);
        logger.info(message);
    }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>missing argument in static block</description>
        <expected-problems>1</expected-problems>
        <expected-linenumbers>9</expected-linenumbers>
        <code><![CDATA[
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class LoggerHelper {
    static {
        final String pattern = "log: {}";

        Logger logger = LoggerFactory.getLogger(loggerName);
        logger.info(pattern, 1, 2);
    }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>NPE in lambda call (see #1512)</description>
        <expected-problems>0</expected-problems>
        <code><![CDATA[
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;

public class LoggerHelper {
    final Logger logger = LoggerFactory.getLogger(loggerName);

    final List<String> list = someMethod(message -> logger.info(message));
}
        ]]></code>
    </test-code>

    <test-code>
        <description>missing argument in lambda call</description>
        <expected-problems>1</expected-problems>
        <expected-linenumbers>11</expected-linenumbers>
        <code><![CDATA[
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;

public class LoggerHelper {
    final Logger logger = LoggerFactory.getLogger(loggerName);

    final List<String> list = someMethod(message -> {
        final String pattern = "log: {}";

        logger.info(pattern, 1, 2);
    });
}
        ]]></code>
    </test-code>

    <test-code>
        <description>NPE in enums (see #1549)</description>
        <expected-problems>0</expected-problems>
        <code><![CDATA[
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public enum LoggerHelper {
    INSTANCE;

    private final Logger log = LoggerFactory.getLogger(LoggerHelper.class);

    public void sendMessage(String message) {
        log.info(message);
    }

    public static void main(String[] args) {
        LoggerHelper.INSTANCE.sendMessage("A message");
    }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>missing argument in enum</description>
        <expected-problems>1</expected-problems>
        <expected-linenumbers>12</expected-linenumbers>
        <code><![CDATA[
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public enum LoggerHelper {
    INSTANCE;

    private static final String pattern = "log: {}";

    public static void main(String[] args) {
        final Logger logger = LoggerFactory.getLogger(LoggerHelper.class);

        logger.info(pattern, 1, 2);
    }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>log4j2: missing argument</description>
        <expected-problems>1</expected-problems>
        <expected-linenumbers>9</expected-linenumbers>
        <code><![CDATA[
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;

public class Foo {
    private static final Logger LOGGER = LogManager.getLogger(Foo.class);

    public void call() {
        final String oneArg = "one argument";
        LOGGER.error("forget the arg {} and {}", oneArg);
    }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>log4j2: too many arguments</description>
        <expected-problems>1</expected-problems>
        <expected-linenumbers>9</expected-linenumbers>
        <code><![CDATA[
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;

public class Foo {
    private static final Logger LOGGER = LogManager.getLogger(Foo.class);
    private final String MESSAGE = "too many args {}";

    public void call() {
        LOGGER.error(MESSAGE, "arg1", "arg2");
    }
}
         ]]></code>
    </test-code>

    <test-code>
        <description>log4j2: Logger without problems</description>
        <expected-problems>0</expected-problems>
        <code><![CDATA[
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;

public class Foo {
    private static final Logger LOGGER = LogManager.getLogger(Foo.class);

    public void call() {
        final String message = "expected {} argument";
        LOGGER.error(message, 1);
    }
}
        ]]></code>
    </test-code>

    <test-code>
         <description>log4j2: ignore the exception param</description>
        <expected-problems>0</expected-problems>
        <code><![CDATA[
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;

public class Foo {
    private static final Logger LOGGER = LogManager.getLogger(Foo.class);

    public void call() {
        LOGGER.error("params {} and {}", "arg1", "arg2", new IllegalStateException("Extra arg"));
    }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>log4j2: Class cast exception with Method calls</description>
        <expected-problems>0</expected-problems>
        <code><![CDATA[
public class MethodCallClassCastExceptionProblem {
    public void foo() {
        // a method call
        otherMethod();
    }

    private void otherMethod() {
    }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>log4j2: Null pointer with VariableNameDeclaration / VariableId</description>
        <expected-problems>0</expected-problems>
        <code><![CDATA[
public class NullPointerTypeProblem {
    public void foo() {
        java.util.StringTokenizer st = new java.util.StringTokenizer("a.b.c.d", ".");
        while (st.hasMoreTokens()) {
            System.out.println(st.nextToken());
        }
    }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>Log4j2 NPE</description>
        <expected-problems>0</expected-problems>
        <code><![CDATA[
package my.test;

import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;

public class Test {

    private static final Logger log = LogManager.getLogger(Test.class);

    public static void main(String[] args) {
        log.info("1" + "2");
    }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>Log4j2: doesn't ignore exception param</description>
        <expected-problems>0</expected-problems>
        <code><![CDATA[
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
import java.io.IOException;

public class InvalidLog4j2ExceptionTest {

    private static final Logger LOGGER = LogManager.getLogger(InvalidLog4j2ExceptionTest.class);

    public void foo(int tcmport) {
        try {
            throw new RuntimeException();
        } catch (RuntimeException e) {
            try {
                LOGGER.error("Exception was thrown during conversion from tc model: {}.", marshall(tcmport));
            } catch (IOException e1) {
                LOGGER.error("Problem to marshall to json tcImport: {}", tcsTripImport, e1);
            }
            throw e;
        }
    }
    Object marshall(int i) {}
}
        ]]></code>
    </test-code>

    <test-code>
        <description>Log4j2: False positive with placeholder and exception</description>
        <expected-problems>0</expected-problems>
        <code><![CDATA[
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;

public class InvalidSl4jExceptionBug1541 {
    private static final Logger log = LogManager.getLogger(InvalidSl4jExceptionBug1541.class);

    public static void main(String[] args) {
        try {
            // ...
        } catch (Exception e) {
            log.error("Arg1 = {}. Exception: {}", "arg1Value", e);
        }
    }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>Log4j2: fails with NPE</description>
        <expected-problems>0</expected-problems>
        <code><![CDATA[
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;

public class TestBug1551
{
    private static final Logger LOGGER = LogManager.getLogger(TestBug1551.class);

    public void test()
    {
        String message = generateMessage();
        LOGGER.info(message);
    }

    private String generateMessage()
    {
        return "message";
    }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>Log4j2: false positive with pre-incremented variable</description>
        <expected-problems>0</expected-problems>
        <code><![CDATA[
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;

public class Foo
{
    private static final Logger LOGGER = LogManager.getLogger(Foo.class);

    public void test()
    {
        int attempt = 0;
        LOGGER.info("test (attempt #{})", ++attempt);
    }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>NPE in PMD 5.8.1 Log4j2</description>
        <expected-problems>0</expected-problems>
        <code><![CDATA[
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;

public class LoggerHelper {
    public LoggerHelper(String loggerName) {
        Logger logger = LogManager.getLogger(loggerName);
        logger.info(message);
    }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>Log4j2 false positive: too many arguments with string concatenation operator</description>
        <expected-problems>0</expected-problems>
        <code><![CDATA[
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;

public final class Main {
    private static final Logger LOGGER = LogManager.getLogger(Main.class);

    private Main() {
    }

    public static void main(String[] args) {
        String string0 = "a";
        String string1 = "b";
        LOGGER.trace("first line {}"
                + "second line {}",
                string0,
                string1);
        String string2 = "c";
        LOGGER.debug("first line {} "
                + "second line {} and "
                + "the third line {}.",
                string0, string1, string2);
    }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>log4j2: NPE in static block (see #1512)</description>
        <expected-problems>0</expected-problems>
        <code><![CDATA[
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;

public class LoggerHelper {
    static {
        Logger logger = LogManager.getLogger(loggerName);
        logger.info(message);
    }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>log4j2: missing argument in static block</description>
        <expected-problems>1</expected-problems>
        <expected-linenumbers>9</expected-linenumbers>
        <code><![CDATA[
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;

public class LoggerHelper {
    static {
        final String pattern = "log: {}";

        Logger logger = LogManager.getLogger(loggerName);
        logger.info(pattern, 1, 2);
    }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>log4j2: NPE in lambda call (see #1512)</description>
        <expected-problems>0</expected-problems>
        <code><![CDATA[
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
import java.util.List;

public class LoggerHelper {
    final Logger logger = LogManager.getLogger(loggerName);

    final List<String> list = someMethod(message -> logger.info(message));
}
        ]]></code>
    </test-code>

    <test-code>
        <description>log4j2: missing argument in lambda call</description>
        <expected-problems>1</expected-problems>
        <expected-linenumbers>11</expected-linenumbers>
        <code><![CDATA[
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
import java.util.List;

public class LoggerHelper {
    final Logger logger = LogManager.getLogger(loggerName);

    final List<String> list = someMethod(message -> {
        final String pattern = "log: {}";

        logger.info(pattern, 1, 2);
    });
}
        ]]></code>
    </test-code>

    <test-code>
        <description>log4j2: NPE in enums (see #1549)</description>
        <expected-problems>0</expected-problems>
        <code><![CDATA[
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;

public enum LoggerHelper {
    INSTANCE;

    private final Logger log = LogManager.getLogger(LoggerHelper.class);

    public void sendMessage(String message) {
        log.info(message);
    }

    public static void main(String[] args) {
        LoggerHelper.INSTANCE.sendMessage("A message");
    }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>log4j2: missing argument in enum</description>
        <expected-problems>1</expected-problems>
        <expected-linenumbers>12</expected-linenumbers>
        <code><![CDATA[
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;

public enum LoggerHelper {
    INSTANCE;

    private static final String pattern = "log: {}";

    public static void main(String[] args) {
        final Logger logger = LogManager.getLogger(LoggerHelper.class);

        logger.info(pattern, 1, 2);
    }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>log4j2: #2196 [java] InvalidLogMessageFormat does not detect extra parameters when no placeholders</description>
        <expected-problems>2</expected-problems>
        <expected-linenumbers>8,9</expected-linenumbers>
        <code><![CDATA[
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;

public class InvalidLogMessageFormatTest {
    private static final Logger logger = LogManager.getLogger("MyLogger");

    public static void main(String[] args) {
        logger.warn("foo {}", "flibble", "moo", "blah", "blah"); // PMD flags this
        logger.warn("foo", "flibble", "moo", "blah", "blah"); // PMD doesn't flag this
    }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>slf4j: #2196 [java] InvalidLogMessageFormat does not detect extra parameters when no placeholders</description>
        <expected-problems>2</expected-problems>
        <expected-linenumbers>8,9</expected-linenumbers>
        <code><![CDATA[
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class InvalidLogMessageFormatTest {
    private static final Logger logger = LoggerFactory.getLogger("MyLogger");

    public static void main(String[] args) {
        logger.warn("foo {}", "flibble", "moo", "blah", "blah"); // PMD flags this
        logger.warn("foo", "flibble", "moo", "blah", "blah"); // PMD doesn't flag this
    }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>ignore slf4j-Markers when detecting the number of arguments #2250</description>
        <expected-problems>1</expected-problems>
        <expected-linenumbers>12</expected-linenumbers>
        <code><![CDATA[
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.Marker;
import org.slf4j.MarkerFactory;
import org.slf4j.helpers.BasicMarkerFactory;

public class InvalidLogMessageFormatTest {
    private static final Logger logger = LoggerFactory.getLogger("MyLogger");
    private static final Marker marker = BasicMarkerFactory.getMarker("MARKER");

    public static void main(String[] args) {
        logger.warn(marker, "foo {}", "flibble", "moo", "blah", "blah"); // wrong number of arguments
        logger.warn(marker, "foo"); // correct: marker and no arguments
        logger.warn(marker, "foo", new Exception()); // correct: marker and one exception parameter
        logger.warn(marker, "foo {}", "bar"); // correct: marker and one argument

        final var otherMarker = MarkerFactory.getMarker("OTHER_MARKER");
        // we can't statically determine the type of the "otherMarker" variable, so we assume it is not a string and ignore it
        logger.warn(otherMarker, "foo");

        final var message = "foo {} {}";
        logger.warn(message, "a", "b"); // correct: first var is the message with expects two parameters
    }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>[java] InvalidLogMessageFormat false-positive for a lambda argument #2255</description>
        <expected-problems>0</expected-problems>
        <code><![CDATA[
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.function.Consumer;

class InvalidLogMessageFormatTest {
    private static final Logger LOGGER = LoggerFactory.getLogger(InvalidLogMessageFormatTest.class);

    private InvalidLogMessageFormatTest() {
    }

    public static void main(String[] args) {
        foo(exception -> LOGGER.error("Foo", exception));
    }

    private static void foo(Consumer<Throwable> consumer) {
        consumer.accept(new IllegalArgumentException());
    }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>[java] InvalidLogMessageFormat false-negative for a lambda argument #2255</description>
        <expected-problems>3</expected-problems>
        <expected-linenumbers>12,14,17</expected-linenumbers>
        <code><![CDATA[
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.function.Consumer;

class InvalidLogMessageFormatTest {
    private static final Logger LOGGER = LoggerFactory.getLogger(InvalidLogMessageFormatTest.class);

    private InvalidLogMessageFormatTest() {
    }

    public static void main(String[] args) {
        foo(arg -> LOGGER.error("Foo", arg)); // missing violation: extra argument, that is not a exception
        // explicit cast helps type resolution
        foo((String arg) -> LOGGER.error("Foo", arg)); // violation: extra argument, that is not a exception
        foo((String arg) -> LOGGER.error("Foo {}", arg)); // no violation: correct number of arguments
        foo(arg -> LOGGER.error("Foo {}", arg)); // no violation: correct number of arguments
        foo(arg -> LOGGER.error("Foo {} {}", arg)); // violation: missing argument
    }

    private static void foo(Consumer<String> consumer) {
        consumer.accept("bar");
    }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>#2431 IndexOutOfBoundsException when only logging exception message</description>
        <expected-problems>0</expected-problems>
        <code><![CDATA[
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Foo {
    private static final Logger LOG = LoggerFactory.getLogger(Foo.class);
    public void bar() {
        try {
            new java.io.File("/text.txt");
        } catch (Exception e) {
            LOG.warn(e.getMessage());
        }
    }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>[java] InvalidLogMessageFormat FP with StringFormattedMessage and ParameterizedMessage #3133</description>
        <expected-problems>2</expected-problems>
        <expected-linenumbers>10,14</expected-linenumbers>
        <expected-messages>
            <message>Missing arguments, expected 2 arguments but found 1</message>
            <message>Too many arguments, expected 1 argument but found 2</message>
        </expected-messages>
        <code><![CDATA[
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

class TestInvalidLogMessageFormat {
    Logger log = LoggerFactory.getLogger(TestInvalidLogMessageFormat.class);

    public void testPMD() {
        /* whistled, fp*/ log.debug("param %10s %% %n %%", "1"); // string formatted message

        /*10:violation*/ log.debug("param %.2d%s", "expected 2 params, given 1");
        try {
            throw new Exception();
        } catch (Exception e) {
            /*14:violation*/ log.debug("param %s", "expected 1 params, given 2", "too many params", e);
        }
    }

    protected void logProblem(String type, Object val) {
        if (log.isDebugEnabled() && val instanceof Throwable) {
            /*whistled, fp*/ log.debug("Trace for "+type+" reading "+getBriefDescription()+": "+val, (Throwable)val);
        }

        if (log.isDebugEnabled()) {
            /*whistled, fp*/ log.debug("Recurring {} reading {} in {} (still in grace period): {}", new Object[] {type, this, getBriefDescription(), val});
        }
    }

    private String getBriefDescription() { return ""; }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>[java] InvalidLogMessageFormat detection failing when String.format used #3146</description>
        <expected-problems>0</expected-problems>
        <code><![CDATA[
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static java.lang.String.format;

class TestInvalidLogMessageFormat {
    private static final Logger LOGGER = LoggerFactory.getLogger(TestInvalidLogMessageFormat.class);
    public void testPMD() {
        LOGGER.info(String.format("Skipping file %s because no parser could be found", getName()));
        LOGGER.info(format("Skipping file %s", getName()));
    }

    private String getName() { return "the-name"; }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>[java] InvalidLogMessageFormat detection failing when String.format used in a variable</description>
        <expected-problems>0</expected-problems>
        <code><![CDATA[
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static java.lang.String.format;

class TestInvalidLogMessageFormat {
    private static final Logger LOGGER = LoggerFactory.getLogger(TestInvalidLogMessageFormat.class);
    public void testPMD() {
        String message = String.format("Skipping file %s because no parser could be found", getName());
        LOGGER.info(message);
    }

    private String getName() { return "the-name"; }
}
        ]]></code>
    </test-code>
    <test-code>
        <description>#2642 [java] InvalidLogMessageFormat throws IndexOutOfBoundsException with SLF4J and a StringBuilder variable set using var</description>
        <expected-problems>0</expected-problems>
        <code><![CDATA[
            import org.slf4j.Logger;
            import org.slf4j.LoggerFactory;

            public class Foo {

                private static final Logger LOGGER = LoggerFactory.getLogger(Foo.class);

                public void okStr() {
                    String message = "Test"; // No exception
                    LOGGER.info(message.toString());
                }
                public void okStrVar() {
                    var message = "Test"; // No exception
                    LOGGER.info(message.toString());
                }
                public void okSb() {
                    StringBuilder message = new StringBuilder("Test"); // No exception
                    LOGGER.info(message.toString());
                }
                public void exceptionOnThisOne() {
                    var message = new StringBuilder("Test"); // Throws IndexOutOfBoundsException
                    LOGGER.info(message.toString());
                }
            }
            ]]></code>
    </test-code>

    <test-code>
        <description>[java] InvalidLogMessageFormat may examine the value of a different but identically named String variable #3284</description>
        <expected-problems>0</expected-problems>
        <code><![CDATA[
import org.slf4j.Logger;
public class Foo {
    public void aTest() {
        Logger logger = null;

        if (true) {
            final String logMessage = "A message formatted with three parameters: {}, {}, {}";
        }

        if (true) {
            final String logMessage = "A message formatted with only one parameter: {}";
            final Object param = null;
            logger.trace(logMessage, param);
        }
    }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>[java] InvalidLogMessageFormat: false positives with logstash structured logging #3644</description>
        <expected-problems>2</expected-problems>
        <expected-linenumbers>27,29</expected-linenumbers>
        <code><![CDATA[
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.logstash.logback.argument.StructuredArguments;
import static net.logstash.logback.marker.Markers.*;
import static net.logstash.logback.argument.StructuredArguments.*;

public class Foo {
    private static final Logger LOGGER = LoggerFactory.getLogger(Foo.class);

    public void withMarkers() {
        // Add "foo:bar" to the JSON output
        LOGGER.info(append("foo", "bar"), "Some log message");
        // Add "foo:bar,name:value" to the JSON output
        LOGGER.info(append("foo", "bar").and(append("name", "value")), "Some log message");
    }

    public void withStructuredArguments() {
        // Add "foo:bar" to JSON output and "bar" to log message
        LOGGER.info("log message {}", value("foo", "bar"));
        // Add "foo:bar" to JSON output only
        LOGGER.info("log message", keyValue("foo", "bar"));
        // Add "foo:bar" to JSON output only
        LOGGER.info("log message", StructuredArguments.keyValue("foo", "bar"));
        // Add "foo:bar" to JSON output only, but one parameter
        LOGGER.info("log message: {}", "used parameter", keyValue("foo", "bar"));
        // Add "foo:bar" to JSON output only - too many arguments!
        LOGGER.info("log message", "unused parameter", keyValue("foo", "bar")); // line 27
        // Add "foo:bar" to JSON output only - too less arguments!
        LOGGER.info("log message {} {}", keyValue("foo", "bar")); // line 29
    }
}
        ]]></code>
    </test-code>

    <test-code>
        <description>[java] InvalidLogMessageFormat rule produces a NPE #4185</description>
        <expected-problems>0</expected-problems>
        <code><![CDATA[
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.logstash.logback.argument.StructuredArguments;
import static net.logstash.logback.marker.Markers.*;

public class Foo {
    private static final Logger LOGGER = LoggerFactory.getLogger(Foo.class);

    public void bar(Object span, long duration) {
        new Runnable() {
            public void run() {
                LOGGER.info(
                      LogMarker.OBSERVABILITY.getMarker(),
                      "End span {} in {}ms",
                      kindMessage(span),
                      duration,
                      StructuredArguments.keyValue("traceId", "Text"));
            }
        };
    }

    public enum LogMarker {
        OBSERVABILITY {
            public LogstashMarker getMarker() {
                return append("foo", "bar");
            }
        };
        public abstract LogstashMarker getMarker();
    }

    private String kindMessage(Object span) {
        return String.valueOf(span);
    }
}
]]></code>
    </test-code>

    <test-code>
        <description>[java] InvalidLogMessageFormat false positive on externally formatted strings #4172</description>
        <expected-problems>0</expected-problems>
        <code><![CDATA[
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Foo {
    private static final Logger LOGGER = LoggerFactory.getLogger(Foo.class);
    public void bar() {
        String msg = "Got kafka msg, headers=%s, body=%s".formatted(headers, new String(body, UTF_8));
        LOGGER.info(msg);
        return msg;
    }
}
]]></code>
    </test-code>

    <test-code>
        <description>#935 FP when exception is obtained from a method call</description>
        <expected-problems>0</expected-problems>
        <code><![CDATA[
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Foo {
    private static final Logger LOGGER = LoggerFactory.getLogger(Foo.class);
    public static IllegalStateException exception() { return new IllegalStateException();}

	public static void log() {
        // violation raised below, but code is fine
		LOGGER.warn("Exception {}", IllegalStateException.class.getName(), exception());
	}
}
]]></code>
    </test-code>

    <test-code>
        <description>slf4j: [java] InvalidLogMessageFormat doesn't handle formats assembled via multi-stage concatenation #253</description>
        <expected-problems>0</expected-problems>
        <code><![CDATA[
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Slf4JMessageFormatConcatenation {
    private static final Logger LOG = LoggerFactory.getLogger(Slf4JMessageFormatConcatenation.class);

    public void run() {
        String message = "Field: {}";
        // some code
        String error = message + ", error: {}";

        LOG.error(error, "fieldValue", "errorMessage"); // two placeholders, two args

        // string concat with 7 params
        int a, b, c, d, e, f, g;
        LOGGER.warn("blah blah a={}, b={}, c={}, " +
                            "d={}, e={}, f={}, g={}",
                    a, b, c, d, e, f, g);
    }
}
]]></code>
    </test-code>

    <test-code>
        <description>slf4j: [java] InvalidLogMessageFormat false positive with Throwable #939</description>
        <expected-problems>0</expected-problems>
        <code><![CDATA[
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class MessageFormatWithThrowable {
    private static final Logger LOGGER = LoggerFactory.getLogger(MessageFormatWithThrowable.class);

    public void run() {
        Throwable t = new Throwable();
        LOGGER.error("some text {}", 1, t);

        Thread.setDefaultUncaughtExceptionHandler((thread, exception) ->
            LOGGER.error("Uncaught exception - {}", thread.getName(), exception));
    }
}
]]></code>
    </test-code>
</test-data>
