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