• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

pmd / pmd / 43

20 Jun 2025 06:39PM UTC coverage: 78.375% (-0.002%) from 78.377%
43

push

github

adangel
Fix #1639 #5832: Use filtered comment text for UnnecessaryImport (#5833)

Merged pull request #5833 from adangel:java/issue-5832-unnecessaryimport

17714 of 23438 branches covered (75.58%)

Branch coverage included in aggregate %.

3 of 3 new or added lines in 1 file covered. (100.0%)

109 existing lines in 17 files now uncovered.

38908 of 48807 relevant lines covered (79.72%)

0.81 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

56.07
/pmd-ant/src/main/java/net/sourceforge/pmd/ant/internal/Slf4jSimpleConfigurationForAnt.java
1
/*
2
 * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3
 */
4

5
package net.sourceforge.pmd.ant.internal;
6

7
import java.io.PrintStream;
8
import java.lang.reflect.Field;
9
import java.lang.reflect.Method;
10
import java.util.HashMap;
11
import java.util.Map;
12

13
import org.apache.tools.ant.BuildListener;
14
import org.apache.tools.ant.DefaultLogger;
15
import org.apache.tools.ant.Project;
16
import org.apache.tools.ant.XmlLogger;
17
import org.apache.tools.ant.taskdefs.RecorderEntry;
18
import org.slf4j.event.Level;
19

20
import net.sourceforge.pmd.internal.Slf4jSimpleConfiguration;
21

22
public final class Slf4jSimpleConfigurationForAnt {
23
    private Slf4jSimpleConfigurationForAnt() { }
24

25
    private static final Level DEFAULT_LEVEL = Level.INFO;
1✔
26

27
    // Maps from ant's Project.MSG_* to org.slf4j.event.Level
28
    private static final Level[] LOG_LEVELS = {
1✔
29
        Level.ERROR,   // Project.MSG_ERR=0
30
        Level.WARN,    // Project.MSG_WARN=1
31
        Level.INFO,    // Project.MSG_INFO=2
32
        Level.DEBUG,   // Project.MSG_VERBOSE=3
33
        Level.TRACE,   // Project.MSG_DEBUG=4
34
    };
35

36
    @SuppressWarnings("PMD.CloseResource")
37
    public static Level reconfigureLoggingForAnt(Project antProject) {
38
        if (!Slf4jSimpleConfiguration.isSimpleLogger()) {
1!
39
            // do nothing, not even set system properties, if not Simple Logger is in use
40
            return DEFAULT_LEVEL;
×
41
        }
42

43
        PrintStream original = System.err;
1✔
44
        try {
45
            System.setErr(new SimpleLoggerToAntBridge(antProject, original));
1✔
46

47
            // configuring the format so that the log level appears at the beginning of the printed line
48
            System.setProperty("org.slf4j.simpleLogger.showDateTime", "false");
1✔
49
            System.setProperty("org.slf4j.simpleLogger.showThreadName", "false");
1✔
50
            System.setProperty("org.slf4j.simpleLogger.showThreadId", "false");
1✔
51
            System.setProperty("org.slf4j.simpleLogger.levelInBrackets", "false");
1✔
52

53
            // using cacheOutputStream so that we can restore System.err after SimpleLogger has been initialized
54
            System.setProperty("org.slf4j.simpleLogger.cacheOutputStream", "true");
1✔
55
            System.setProperty("org.slf4j.simpleLogger.logFile", "System.err");
1✔
56

57
            Level level = getAntLogLevel(antProject);
1✔
58
            Slf4jSimpleConfiguration.reconfigureDefaultLogLevel(level);
1✔
59

60
            return level;
1✔
61
        } finally {
62
            System.setErr(original);
1✔
63
        }
64
    }
65

66
    private static final class SimpleLoggerToAntBridge extends PrintStream {
67
        private static final Map<String, Integer> ANT_LOG_LEVELS;
68

69
        static {
70
            ANT_LOG_LEVELS = new HashMap<>();
1✔
71
            ANT_LOG_LEVELS.put(Level.ERROR.name(), Project.MSG_ERR);
1✔
72
            ANT_LOG_LEVELS.put(Level.WARN.name(), Project.MSG_WARN);
1✔
73
            ANT_LOG_LEVELS.put(Level.INFO.name(), Project.MSG_INFO);
1✔
74
            ANT_LOG_LEVELS.put(Level.DEBUG.name(), Project.MSG_VERBOSE);
1✔
75
            ANT_LOG_LEVELS.put(Level.TRACE.name(), Project.MSG_DEBUG);
1✔
76
        }
1✔
77

78
        private final StringBuilder buffer = new StringBuilder(100);
1✔
79
        private final Project antProject;
80

81
        SimpleLoggerToAntBridge(Project antProject, PrintStream original) {
82
            super(original);
1✔
83
            this.antProject = antProject;
1✔
84
        }
1✔
85

86
        @Override
87
        public void println(String x) {
88
            buffer.append(x).append(System.lineSeparator());
1✔
89
        }
1✔
90

91
        @Override
92
        public void flush() {
93
            String logLevel = determineLogLevel();
1✔
94
            int antLogLevel = ANT_LOG_LEVELS.getOrDefault(logLevel, Project.MSG_INFO);
1✔
95
            antProject.log(buffer.toString(), antLogLevel);
1✔
96
            buffer.setLength(0);
1✔
97
        }
1✔
98

99
        private String determineLogLevel() {
100
            int firstSpace = buffer.indexOf(" ");
1✔
101
            if (firstSpace != -1) {
1!
102
                String level = buffer.substring(0, firstSpace);
1✔
103
                buffer.delete(0, firstSpace + 1);
1✔
104
                return level;
1✔
105
            }
106
            return DEFAULT_LEVEL.name();
×
107
        }
108
    }
109

110
    private static Level getAntLogLevel(Project project) {
111
        for (final BuildListener l : project.getBuildListeners()) {
1!
112
            Field declaredField = null;
1✔
113
            try {
114
                if (l instanceof DefaultLogger) {
1!
UNCOV
115
                    declaredField = DefaultLogger.class.getDeclaredField("msgOutputLevel");
×
116
                } else if (l instanceof XmlLogger) {
1!
UNCOV
117
                    declaredField = XmlLogger.class.getDeclaredField("msgOutputLevel");
×
118
                } else if (l instanceof RecorderEntry) {
1!
UNCOV
119
                    declaredField = RecorderEntry.class.getDeclaredField("loglevel");
×
120
                } else if ("org.gradle.api.internal.project.ant.AntLoggingAdapter".equals(l.getClass().getName())) {
1!
UNCOV
121
                    return determineGradleLogLevel(project, l);
×
122
                } else {
123
                    try {
124
                        declaredField = l.getClass().getDeclaredField("logLevel");
1✔
125
                        if (declaredField.getType() != Integer.class && declaredField.getType() != int.class) {
1!
UNCOV
126
                            declaredField = null;
×
127
                            project.log("Unsupported build listener: " + l.getClass(), Project.MSG_DEBUG);
×
128
                        }
UNCOV
129
                    } catch (final NoSuchFieldException e) {
×
130
                        project.log("Unsupported build listener: " + l.getClass(), Project.MSG_DEBUG);
×
131
                    }
1✔
132
                }
133

134
                if (declaredField != null) {
1!
135
                    declaredField.setAccessible(true);
1✔
136
                    return LOG_LEVELS[declaredField.getInt(l)];
1✔
137
                }
138

UNCOV
139
            } catch (final ReflectiveOperationException ignored) {
×
140
                // Just ignore it
UNCOV
141
            }
×
142
        }
×
143

UNCOV
144
        project.log("Could not determine ant log level, no supported build listeners found. "
×
145
                + "Log level is set to " + DEFAULT_LEVEL, Project.MSG_WARN);
146

UNCOV
147
        return DEFAULT_LEVEL;
×
148
    }
149

150
    private static Level determineGradleLogLevel(Project project, BuildListener l) {
151
        try {
UNCOV
152
            project.log("Detected gradle AntLoggingAdapter", Project.MSG_DEBUG);
×
UNCOV
153
            Field loggerField = l.getClass().getDeclaredField("logger");
×
154
            loggerField.setAccessible(true);
×
155
            // org.gradle.internal.logging.slf4j.OutputEventListenerBackedLogger
156
            Object logger = loggerField.get(l);
×
157

158
            Class<?> gradleLogLevel = l.getClass().getClassLoader().loadClass("org.gradle.api.logging.LogLevel");
×
159

160
            Method isLevelAtMostMethod = logger.getClass().getDeclaredMethod("isLevelAtMost", gradleLogLevel);
×
UNCOV
161
            isLevelAtMostMethod.setAccessible(true);
×
162

163
            Object[] logLevels = gradleLogLevel.getEnumConstants();
×
164
            // the log levels in gradle are declared in the order DEBUG, INFO, LIFECYCLE, WARN, QUIET, ERROR
165
            Level[] mapping = new Level[] {
×
166
                Level.TRACE,   // DEBUG
167
                Level.DEBUG,   // INFO
168
                Level.INFO,     // LIFECYCLE
169
                Level.WARN,  // WARN
170
                Level.ERROR,   // QUIET
171
                Level.ERROR,   // ERROR
172
            };
173

UNCOV
174
            for (int i = 0; i < Math.min(logLevels.length, mapping.length); i++) {
×
UNCOV
175
                boolean enabled = (boolean) isLevelAtMostMethod.invoke(logger, logLevels[i]);
×
176
                if (enabled) {
×
177
                    project.log("Current log level: " + logLevels[i] + " -> " + mapping[i], Project.MSG_DEBUG);
×
178
                    return mapping[i];
×
179
                }
180
            }
UNCOV
181
        } catch (ReflectiveOperationException ignored) {
×
182
            // ignored
183
        }
×
UNCOV
184
        project.log("Could not determine log level, falling back to default: " + DEFAULT_LEVEL, Project.MSG_WARN);
×
185
        return DEFAULT_LEVEL;
×
186
    }
187

188
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc