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
32
33
34
35
36
37
38
39 package net.objectlab.qalab.m2;
40
41 import org.apache.maven.plugin.AbstractMojo;
42 import org.apache.maven.plugin.MojoExecutionException;
43
44 import net.objectlab.qalab.interfaces.QALabExporter;
45 import net.objectlab.qalab.parser.StatMerger;
46 import net.objectlab.qalab.util.TaskLogger;
47 import net.objectlab.qalab.m2.util.Utils;
48 import net.objectlab.qalab.m2.util.Maven2TaskLogger;
49
50 import org.xml.sax.InputSource;
51
52 import java.io.File;
53 import java.io.FileInputStream;
54 import java.io.IOException;
55 import java.io.FileNotFoundException;
56
57 import java.util.Iterator;
58 import java.util.Map;
59 import java.util.Properties;
60
61 /**
62 * Goal that handles the merge of statistics into qalab.xml.
63 *
64 * @author <a href="http://www.davesag.com">Dave Sag</a>.
65 * @goal merge
66 * @phase deploy
67 */
68 public class BuildStatMergeMojo extends AbstractMojo {
69
70
71 /**
72 * The file the whose values will be incorporated in the qalab.xml. This
73 * file will have been generated by checkstyle, or pmd etc.
74 * @parameter
75 */
76 private File inputFile = null;
77
78 /**
79 * The merged properties file. By default this is generated into the root of
80 * the project as it is often checked into source control.
81 *
82 * @parameter expression="${project.basedir}/qalab.xml"
83 */
84 private File outputFile = null;
85
86 /**
87 * If true then any debug logging output will be suppressed.
88 *
89 * @parameter default-value=false
90 */
91 private boolean quiet = false;
92
93 /**
94 * If true then use ONLY DATE for timestamp (use in conjunction action replace).
95 *
96 * @parameter default-value=true
97 */
98 private boolean dateOnly = true;
99
100 /**
101 * The directory where the source code is.
102 *
103 * @parameter expression="${project.build.sourceDirectory}"
104 */
105 private String srcDir;
106
107 /**
108 * The fully qualified class name for the handler for the given statistics.
109 * <ul>
110 * The built-in handlers are:
111 * <li>net.objectlab.qalab.parser.CheckstyleStatMerge</li>
112 * <li>net.objectlab.qalab.parser.PMDStatMerge</li>
113 * <li>net.objectlab.qalab.parser.FindBugsStatMerge</li>
114 * <li>net.objectlab.qalab.parser.SimianStatMerge</li>
115 * </ul>
116 *
117 * @parameter
118 */
119 private String handler;
120
121 /**
122 * The timestamp for the stats.
123 *
124 * @parameter
125 */
126 private String mergerTimeStamp;
127
128 /**
129 * An exporter class name.
130 *
131 * @parameter default-value="net.objectlab.qalab.exporter.QALabXMLExporter"
132 */
133 private String exporterClassName = "net.objectlab.qalab.exporter.QALabXMLExporter";
134
135 /**
136 * The properties file to use instead of setting them one by one.
137 *
138 * @parameter
139 */
140 private File propertiesFile;
141
142 /**
143 * the loaded properties from the properties file declared above.
144 */
145 private Properties theProperties = null;
146
147
148
149 /**
150 * Method invoked by the ant framework to execute the action associated with
151 * this task. This will validate the input parameters then merge the
152 * statistics.
153 *
154 * @throws MojoExecutionException
155 * if anything goes wrong.
156 */
157 public final void execute() throws MojoExecutionException {
158
159 theProperties = createOverridingProperties();
160
161 getLog().info("Starting QALab Merge");
162
163
164 if (!validate()) {
165 return;
166 }
167
168
169 if (!quiet) {
170 getLog().info("QALab Merge:");
171 getLog().info("inputFile='" + getInputFile().getPath());
172 getLog().info("outputFile='" + outputFile.getPath());
173 getLog().info("srcDir='" + srcDir + "', mergerTimeStamp=" + mergerTimeStamp);
174 String proppath = null;
175 if (propertiesFile != null) {
176 proppath = propertiesFile.getPath();
177 }
178 getLog().info("quiet='" + quiet + "', propertiesFile='" + proppath + "'.");
179 for (final Iterator it = theProperties.entrySet().iterator(); it.hasNext();) {
180
181 final Map.Entry entry = (Map.Entry) it.next();
182 final String key = entry.getKey().toString();
183 if (key.indexOf("qalab") >= 0) {
184 getLog().info(key + " = '" + entry.getValue() + "'");
185 }
186 }
187 }
188
189 mergeFiles();
190 }
191
192 /**
193 * Merge the statistics into the qalab.xml.
194 *
195 * @throws MojoExecutionException
196 * if anything goes wrong.
197 */
198 private void mergeFiles() throws MojoExecutionException {
199 try {
200 final TaskLogger logger = new Maven2TaskLogger(this);
201
202
203 final QALabExporter exporter = (QALabExporter) Class.forName(getExporterClassName()).newInstance();
204
205 getTheProperties().setProperty("qalab.merge.output.file", getOutputFile().getAbsolutePath());
206
207 exporter.setQuiet(isQuiet());
208 exporter.setTaskLogger(logger);
209
210 final StatMerger merger = (StatMerger) Class.forName(getHandler()).newInstance();
211
212 merger.setQuiet(isQuiet());
213 merger.setSrcDir(getSrcDir());
214 merger.setTaskLogger(logger);
215 merger.setMergerTimeStamp(getMergerTimeStamp(), dateOnly);
216
217 getTheProperties().setProperty("qalab.merge.output.timestamp", merger.getMergerTimeStamp());
218 getTheProperties().setProperty("qalab.merge.type", merger.getType());
219 exporter.configure(getTheProperties());
220
221 merger.mergeStats(new InputSource(new FileInputStream(getInputFile())), exporter);
222 getLog().info("Files: " + merger.getFileCount() + " Violations:" + merger.getTotalStatistics());
223
224 exporter.save();
225 } catch (IllegalAccessException e) {
226 throw new MojoExecutionException(e.toString());
227 } catch (FileNotFoundException e) {
228 throw new MojoExecutionException(e.toString());
229 } catch (IOException e) {
230 throw new MojoExecutionException(e.toString());
231 } catch (ClassNotFoundException e) {
232 throw new MojoExecutionException(e.toString());
233 } catch (InstantiationException e) {
234 throw new MojoExecutionException(e.toString());
235 }
236 }
237
238 /**
239 * Validates the parameters supplied by maven 2.
240 *
241 * @throws MojoExecutionException
242 * if anything goes wrong.
243 */
244 private boolean validate() throws MojoExecutionException {
245 try {
246 Utils.checkFile(getInputFile(), "inputFile");
247 return true;
248 } catch (IOException ioex) {
249 getLog().warn("\n\nQALab ==> The file " + getInputFile().getPath() + " cannot be accessed. SKIPPING....\n\n");
250
251 }
252 return false;
253 }
254
255 /**
256 * Create the Properties object based on the arguments specified to the Mojo
257 * in your <code>pom.xml</code> file.
258 *
259 * @return the properties for property expansion expansion
260 * @throws MojoExecutionException
261 * if anything goes wrong.
262 */
263 private Properties createOverridingProperties() throws MojoExecutionException {
264
265 final Properties result = new Properties();
266
267
268 if (propertiesFile != null && propertiesFile.canRead() && propertiesFile.isFile()) {
269 FileInputStream inStream = null;
270 if (!quiet) {
271 getLog().debug("loading " + propertiesFile);
272 }
273
274 try {
275 inStream = new FileInputStream(propertiesFile);
276 result.load(inStream);
277 } catch (FileNotFoundException fnfex) {
278 throw new MojoExecutionException("Could not find Properties file '" + propertiesFile + "'", fnfex);
279 } catch (IOException ioex) {
280 throw new MojoExecutionException("Error loading Properties file '" + propertiesFile + "'", ioex);
281 } finally {
282 try {
283 if (inStream != null) {
284 inStream.close();
285 }
286 } catch (IOException ioex) {
287 throw new MojoExecutionException("Error closing Properties file '" + propertiesFile + "'", ioex);
288 }
289 }
290 }
291
292
293 final Map projectContext = getPluginContext();
294
295 for (final Iterator it = projectContext.entrySet().iterator(); it.hasNext();) {
296
297 final Map.Entry entry = (Map.Entry) it.next();
298 final String value = String.valueOf(entry.getValue());
299 if (entry.getKey().toString().indexOf("qalab") >= 0) {
300 getLog().info("Adding " + entry.getKey() + " / " + value);
301 }
302 result.put(entry.getKey(), value);
303 }
304
305 return result;
306 }
307
308 public String getExporterClassName() {
309 return exporterClassName;
310 }
311
312 public void setExporterClassName(String exporterClassName) {
313 this.exporterClassName = exporterClassName;
314 }
315
316 public String getHandler() {
317 return handler;
318 }
319
320 public void setHandler(String handler) {
321 this.handler = handler;
322 }
323
324 public File getInputFile() {
325 return inputFile;
326 }
327
328 public void setInputFile(File inputFile) {
329 this.inputFile = inputFile;
330 }
331
332 public String getMergerTimeStamp() {
333 return mergerTimeStamp;
334 }
335
336 public void setMergerTimeStamp(String mergerTimeStamp) {
337 this.mergerTimeStamp = mergerTimeStamp;
338 }
339
340 public File getOutputFile() {
341 return outputFile;
342 }
343
344 public void setOutputFile(File outputFile) {
345 this.outputFile = outputFile;
346 }
347
348 public File getPropertiesFile() {
349 return propertiesFile;
350 }
351
352 public void setPropertiesFile(File propertiesFile) {
353 this.propertiesFile = propertiesFile;
354 }
355
356 public boolean isQuiet() {
357 return quiet;
358 }
359
360 public void setQuiet(boolean quiet) {
361 this.quiet = quiet;
362 }
363
364 public String getSrcDir() {
365 return srcDir;
366 }
367
368 public void setSrcDir(String srcDir) {
369 this.srcDir = srcDir;
370 }
371
372 public Properties getTheProperties() {
373 return theProperties;
374 }
375
376 public void setTheProperties(Properties theProperties) {
377 this.theProperties = theProperties;
378 }
379 }
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395