1
2
3
4
5 package com.qulice.checkstyle;
6
7 import com.jcabi.log.Logger;
8 import com.puppycrawl.tools.checkstyle.Checker;
9 import com.puppycrawl.tools.checkstyle.ConfigurationLoader;
10 import com.puppycrawl.tools.checkstyle.PropertiesExpander;
11 import com.puppycrawl.tools.checkstyle.api.AuditEvent;
12 import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
13 import com.puppycrawl.tools.checkstyle.api.Configuration;
14 import com.qulice.spi.Environment;
15 import com.qulice.spi.ResourceValidator;
16 import com.qulice.spi.Violation;
17 import java.io.File;
18 import java.util.Collection;
19 import java.util.LinkedList;
20 import java.util.List;
21 import java.util.Properties;
22 import org.xml.sax.InputSource;
23
24
25
26
27
28
29
30 public final class CheckstyleValidator implements ResourceValidator {
31
32
33
34
35 private final Checker checker;
36
37
38
39
40 private final CheckstyleListener listener;
41
42
43
44
45 private final Environment env;
46
47
48
49
50
51 @SuppressWarnings("PMD.ConstructorOnlyInitializesOrCallOtherConstructors")
52 public CheckstyleValidator(final Environment env) {
53 this.env = env;
54 this.checker = new Checker();
55 this.checker.setModuleClassLoader(
56 Thread.currentThread().getContextClassLoader()
57 );
58 try {
59 this.checker.configure(this.configuration());
60 } catch (final CheckstyleException ex) {
61 throw new IllegalStateException("Failed to configure checker", ex);
62 }
63 this.listener = new CheckstyleListener(this.env);
64 this.checker.addListener(this.listener);
65 }
66
67 @Override
68 @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
69 public Collection<Violation> validate(final Collection<File> files) {
70 final List<File> sources = this.getNonExcludedFiles(files);
71 try {
72 Logger.debug(this, "Checkstyle processing %d files", sources.size());
73 final long start = System.currentTimeMillis();
74 this.checker.process(sources);
75 Logger.debug(
76 this,
77 "Checkstyle processed %d files in %[ms]s",
78 sources.size(),
79 System.currentTimeMillis() - start
80 );
81 } catch (final CheckstyleException ex) {
82 throw new IllegalStateException("Failed to process files", ex);
83 }
84 final List<AuditEvent> events = this.listener.events();
85 final Collection<Violation> results = new LinkedList<>();
86 for (final AuditEvent event : events) {
87 final String check = event.getSourceName();
88 results.add(
89 new Violation.Default(
90 this.name(),
91 check.substring(check.lastIndexOf('.') + 1),
92 event.getFileName(),
93 String.valueOf(event.getLine()),
94 event.getMessage()
95 )
96 );
97 }
98 return results;
99 }
100
101 @Override public String name() {
102 return "Checkstyle";
103 }
104
105
106
107
108
109
110 public List<File> getNonExcludedFiles(final Collection<File> files) {
111 final List<File> relevant = new LinkedList<>();
112 for (final File file : files) {
113 final String name = file.getPath().substring(
114 this.env.basedir().toString().length()
115 );
116 if (this.env.exclude("checkstyle", name)) {
117 continue;
118 }
119 if (!name.toLowerCase(java.util.Locale.ROOT).endsWith(".java")) {
120 continue;
121 }
122 relevant.add(file);
123 }
124 return relevant;
125 }
126
127
128
129
130
131
132 private Configuration configuration() {
133 final File cache =
134 new File(this.env.tempdir(), "checkstyle/checkstyle.cache");
135 final File parent = cache.getParentFile();
136 if (!parent.exists() && !parent.mkdirs()) {
137 throw new IllegalStateException(
138 String.format(
139 "Unable to create directories needed for %s",
140 cache.getPath()
141 )
142 );
143 }
144 final Properties props = new Properties();
145 props.setProperty("cache.file", cache.getPath());
146 final Configuration config;
147 try (java.io.InputStream stream = this.getClass().getResourceAsStream("checks.xml")) {
148 if (stream == null) {
149 throw new IllegalStateException(
150 "Checkstyle configuration file 'checks.xml' not found in classpath."
151 );
152 }
153 final InputSource src = new InputSource(stream);
154 config = ConfigurationLoader.loadConfiguration(
155 src,
156 new PropertiesExpander(props),
157 ConfigurationLoader.IgnoredModulesOptions.OMIT
158 );
159 } catch (final CheckstyleException | java.io.IOException ex) {
160 throw new IllegalStateException("Failed to load config", ex);
161 }
162 return config;
163 }
164 }