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.parser;
40
41 import java.io.IOException;
42 import java.text.ParseException;
43 import java.util.Calendar;
44 import java.util.Date;
45
46 import javax.xml.parsers.ParserConfigurationException;
47 import javax.xml.parsers.SAXParser;
48 import javax.xml.parsers.SAXParserFactory;
49
50 import net.objectlab.qalab.interfaces.QALabExporter;
51 import net.objectlab.qalab.util.QALabTags;
52 import net.objectlab.qalab.util.TaskLogger;
53
54 import org.xml.sax.EntityResolver;
55 import org.xml.sax.InputSource;
56 import org.xml.sax.SAXException;
57 import org.xml.sax.helpers.DefaultHandler;
58
59 /**
60 * This class abstract some methods used by all Stat Mergers being for
61 * Checkstyle, pmd, findbugs or Simian. This class implements some SAX callbacks
62 * for parsing the input statistics.
63 *
64 * @author Benoit Xhenseval
65 * @version $Revision: 187 $
66 */
67 public abstract class BaseStatMerge extends DefaultHandler implements
68 StatMerger {
69
70
71
72 /** total Statistics. * */
73 private int totalStatistics = 0;
74
75 /** count the number of files. * */
76 private int fileCount = 0;
77
78 /** statistics for given file. * */
79 private int fileStatistics = 0;
80
81 /** keep the current file name. * */
82 private String currentFile = null;
83
84 /** if true, no debug information is logged. * */
85 private boolean quiet = true;
86
87 /** given implementation for the logger. * */
88 private TaskLogger taskLogger = null;
89
90 /** The directory for source code. * */
91 private String srcDir;
92
93 /** exporter for the statistics. */
94 private QALabExporter exporter = null;
95
96 /** timestamp to use for Merge. */
97 private Date mergerTimeStamp;
98
99 /** timestamp as date only. */
100 private boolean timeStampAsDateOnly = true;
101
102
103
104
105 /**
106 * @param theFile
107 * the current file for which the stats are processed.
108 */
109 public final void setCurrentFile(final String theFile) {
110 this.currentFile = theFile;
111 }
112
113 /**
114 * @return true means that no debug info is logged.
115 */
116 public final boolean isQuiet() {
117 return quiet;
118 }
119
120 /**
121 * @param noLog
122 * true if no log required.
123 */
124 public final void setQuiet(final boolean noLog) {
125 this.quiet = noLog;
126 }
127
128 /**
129 * set the logger to use.
130 *
131 * @param task
132 * the logger.
133 */
134 public final void setTaskLogger(final TaskLogger task) {
135 this.taskLogger = task;
136 }
137
138 /**
139 * @return logger to use if not quiet.
140 */
141 protected final TaskLogger getTaskLogger() {
142 return taskLogger;
143 }
144
145 /**
146 * this will be used to truncate machine specific path.
147 *
148 * @param directory
149 * the source directory.
150 */
151 public final void setSrcDir(final String directory) {
152 srcDir = directory.replace('\\', '/');
153 }
154
155 /**
156 * @return the source directory
157 */
158 public final String getSrcDir() {
159 return srcDir;
160 }
161
162 /**
163 * Add new results to the compiled statistics. This could will generate a
164 * new <result> element and may be a new <file> element.
165 */
166 protected final void addNewResults() {
167 if (getFileStatistics() != 0) {
168 exporter.addFileResult(getFileStatistics(), currentFile);
169 } else {
170 if (!isQuiet()) {
171 taskLogger.log("No violation for " + currentFile);
172 }
173 }
174 }
175
176 /**
177 * @return give the total number of violations.
178 */
179 public int getTotalStatistics() {
180 return totalStatistics;
181 }
182
183 /**
184 * @param increment
185 * add increment to total number of violations.
186 */
187 protected final void addTotalStatistics(final int increment) {
188 totalStatistics += increment;
189 }
190
191 /**
192 * Directly set the total statistics.
193 *
194 * @param value
195 */
196 protected final void setTotalStatistics(final int value) {
197 totalStatistics = value;
198 }
199
200 /**
201 * @return number of file counted.
202 */
203 public final int getFileCount() {
204 return fileCount;
205 }
206
207 /**
208 * Increment file count by increment.
209 *
210 * @param increment
211 * increase file count by this value.
212 */
213 protected final void incrementFileCount(final int increment) {
214 fileCount += increment;
215 }
216
217 /**
218 * @return number of violation for current file.
219 */
220 protected final int getFileStatistics() {
221 return fileStatistics;
222 }
223
224 /**
225 * add a increment of file violation and also add it to the total violation
226 * count.
227 *
228 * @param increment
229 * value to add to the number of fileStatistics.
230 */
231 protected final void addFileStatistics(final int increment) {
232 fileStatistics += increment;
233 addTotalStatistics(increment);
234 }
235
236 /**
237 * reset the fileStatistics to 0, typically when handling a new file.
238 */
239 protected final void resetFileStatistics() {
240 fileStatistics = 0;
241 }
242
243 /**
244 * called by the ant task or the maven plug in.
245 *
246 * @param source
247 * the Reader from the statistics file.
248 * @param qalabExporter
249 * the QALabExporter to use for saving the stats.
250 */
251 public final void mergeStats(final InputSource source,
252 final QALabExporter qalabExporter) {
253 exporter = qalabExporter;
254
255 try {
256 final SAXParserFactory factory = SAXParserFactory.newInstance();
257 final SAXParser saxParser = factory.newSAXParser();
258
259 saxParser.getXMLReader().setEntityResolver(new EntityResolver() {
260 public InputSource resolveEntity(String publicId,
261 String systemId) throws SAXException, IOException {
262
263 return new InputSource();
264 }
265 });
266
267
268
269
270 saxParser.getXMLReader().setFeature(
271 "http://xml.org/sax/features/external-general-entities",
272 false);
273 saxParser.getXMLReader().setFeature(
274 "http://xml.org/sax/features/external-parameter-entities",
275 false);
276
277 InputSource src = preProcessSource(source);
278
279 saxParser.parse(src, this);
280 } catch (ParserConfigurationException e) {
281 getTaskLogger().log(e.toString());
282 } catch (SAXException e) {
283 getTaskLogger().log(e.toString());
284 } catch (IOException e) {
285 e.printStackTrace();
286 getTaskLogger().log(e.toString());
287 } finally {
288 if (!isQuiet()) {
289 getTaskLogger().log("Finished Parsing Source XML.");
290 }
291 }
292
293 if (getTotalStatistics() != 0) {
294 exporter.addSummary(getTotalStatistics(), getFileCount());
295 }
296 }
297
298 protected InputSource preProcessSource(InputSource source) {
299
300 return source;
301 }
302
303 /**
304 * set the merger time stamp to use for all stats.
305 *
306 * @param timestamp
307 * the timestamp "YYYY-MM-dd HH:mm:ss"
308 * @param timeStampAsDate
309 * The timeStampAsDateOnly to set.
310 */
311 public final void setMergerTimeStamp(final String timestamp,
312 final boolean timeStampAsDate) {
313 this.timeStampAsDateOnly = timeStampAsDate;
314 if (timestamp != null) {
315 try {
316 mergerTimeStamp = QALabTags.DEFAULT_DATETIME_FORMAT
317 .parse(timestamp);
318 } catch (ParseException e) {
319 throw new IllegalArgumentException(
320 "Wrong format for mergerTimeStamp. " + e.getMessage());
321 }
322 } else {
323 mergerTimeStamp = new Date();
324 }
325
326 if (timeStampAsDate) {
327 Calendar cal = Calendar.getInstance();
328 cal.setTime(mergerTimeStamp);
329 cal.set(Calendar.HOUR_OF_DAY, 0);
330 cal.set(Calendar.MINUTE, 0);
331 cal.set(Calendar.SECOND, 0);
332 cal.set(Calendar.MILLISECOND, 0);
333 mergerTimeStamp = cal.getTime();
334 }
335 }
336
337 /**
338 * This may have been changed to "now".
339 *
340 * @return timestamp the time stamp in format YYYY-MM-dd HH:mm:ss
341 */
342 public final String getMergerTimeStamp() {
343 if (mergerTimeStamp == null) {
344 setMergerTimeStamp(null, timeStampAsDateOnly);
345 }
346
347 if (timeStampAsDateOnly) {
348 return QALabTags.DEFAULT_DATE_FORMAT.format(mergerTimeStamp);
349 } else {
350 return QALabTags.DEFAULT_DATETIME_FORMAT.format(mergerTimeStamp);
351 }
352 }
353 }
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369