1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 package javancss;
24
25 import java.io.File;
26 import java.io.FileOutputStream;
27 import java.io.IOException;
28 import java.io.PrintWriter;
29 import java.util.ArrayList;
30 import java.util.Arrays;
31 import java.util.List;
32
33 import org.apache.tools.ant.BuildException;
34 import org.apache.tools.ant.DirectoryScanner;
35 import org.apache.tools.ant.Project;
36 import org.apache.tools.ant.taskdefs.MatchingTask;
37 import org.apache.tools.ant.types.CommandlineJava;
38 import org.apache.tools.ant.types.Path;
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62 public class JavancssAntTask extends MatchingTask {
63
64
65
66 private CommandlineJava commandline = new CommandlineJava();
67
68
69
70
71 private boolean abortOnFail = false;
72
73
74
75 private File srcdir;
76
77
78
79 private Path classpath;
80
81
82
83 private File outputfile;
84
85
86
87 private String format = "plain";
88
89
90
91 private static final int FAILURE = 1;
92
93
94
95 private static final int SUCCESS = 0;
96
97
98
99 private int classPerPkgMax = Integer.MAX_VALUE;
100
101
102
103 private int classPerPkgMin = -1;
104
105
106
107 private int funcPerPkgMax = Integer.MAX_VALUE;
108
109
110
111 private int funcPerPkgMin = -1;
112
113
114
115 private int ncssPerPkgMax = Integer.MAX_VALUE;
116
117
118
119 private int ncssPerPkgMin = -1;
120
121
122
123 private int classPerClassMax = Integer.MAX_VALUE;
124
125
126
127 private int classPerClassMin = -1;
128
129
130
131 private int funcPerClassMax = Integer.MAX_VALUE;
132
133
134
135 private int funcPerClassMin = -1;
136
137
138
139 private int ncssPerClassMax = Integer.MAX_VALUE;
140
141
142
143 private int ncssPerClassMin = -1;
144
145
146
147 private int jvdcPerClassMax = Integer.MAX_VALUE;
148
149
150
151 private int jvdcPerClassMin = -1;
152
153
154
155 private int jvdcPerFuncMax = Integer.MAX_VALUE;
156
157
158
159 private int jvdcPerFuncMin = -1;
160
161
162
163 private int ccnPerFuncMax = Integer.MAX_VALUE;
164
165
166
167 private int ccnPerFuncMin = -1;
168
169
170
171 private int ncssPerFuncMax = Integer.MAX_VALUE;
172
173
174
175 private int ncssPerFuncMin = -1;
176
177
178
179 private boolean packageMetrics = true;
180
181
182
183 private boolean classMetrics = true;
184
185
186
187 private boolean functionMetrics = true;
188
189
190
191 private boolean generateReport = false;
192
193
194
195
196 private Javancss javancss;
197
198
199
200
201 public JavancssAntTask() {
202 commandline.setClassname("javancss.Main");
203 }
204
205
206
207
208
209
210 public void setFormat(String format) {
211 this.format = format;
212 }
213
214
215
216
217
218 public void setPackageMetrics(boolean packageMetrics) {
219 this.packageMetrics = packageMetrics;
220 }
221
222
223
224
225
226 public void setClassMetrics(boolean classMetrics) {
227 this.classMetrics = classMetrics;
228 }
229
230
231
232
233
234 public void setFunctionMetrics(boolean functionMetrics) {
235 this.functionMetrics = functionMetrics;
236 }
237
238
239
240
241
242 public void setGenerateReport(boolean generateReport) {
243 this.generateReport = generateReport;
244 }
245
246
247
248
249
250
251
252 public void setSrcdir(File srcdir) {
253 this.srcdir = srcdir;
254 }
255
256
257
258
259
260 public void setOutputfile(File outputfile) {
261 this.outputfile = outputfile;
262 }
263
264
265
266
267
268 public void setClasspath(Path classpath) {
269 if (this.classpath == null) {
270 this.classpath = classpath;
271 } else {
272 this.classpath.append(classpath);
273 }
274 }
275
276
277
278
279
280
281 public void setAbortOnFail(boolean abortOnFail) {
282 this.abortOnFail = abortOnFail;
283 }
284
285
286
287
288
289 @Override
290 public void execute() throws BuildException {
291 if (srcdir == null) {
292 throw new BuildException("srcdir attribute must be set!");
293 }
294 if (!srcdir.exists()) {
295 throw new BuildException("srcdir does not exist!");
296 }
297 if (!srcdir.isDirectory()) {
298 throw new BuildException("srcdir is not a directory!");
299 }
300
301 List<File> fileList = findFilesToAnalyse();
302
303
304 if (thresholdsExceeded(fileList) && abortOnFail) {
305 throw new BuildException("Metric threshold value(s) surpassed");
306 }
307
308
309 int exitValue = generateReport(fileList);
310 if (exitValue == FAILURE) {
311 if (abortOnFail) {
312 throw new BuildException("JavaNcss failed", getLocation());
313 } else {
314 log("JavaNcss failed", Project.MSG_ERR);
315 }
316 }
317 }
318
319
320
321
322
323
324
325 private int generateReport(List<File> fileList) {
326
327
328 if (!generateReport) {
329 return SUCCESS;
330 }
331
332
333 log("Generating report");
334 if (outputfile != null) {
335 log("Report to be stored in " + outputfile.getPath(), Project.MSG_VERBOSE);
336 } else {
337 log("Report to be sent to standard output", Project.MSG_VERBOSE);
338 }
339 String[] javancssArguments = getCommandLineArguments(fileList);
340 log("Executing: javancss " + Arrays.toString( javancssArguments ), Project.MSG_VERBOSE);
341
342 try {
343 Javancss javancss = new Javancss(javancssArguments);
344
345 if (javancss.getLastError() == null) {
346 return SUCCESS;
347 }
348 }
349 catch (IOException ioe)
350 {
351 log("IO exception while executing JavaNCSS: " + ioe.getMessage(), Project.MSG_ERR);
352 }
353
354 return FAILURE;
355 }
356
357
358
359
360
361
362
363 private boolean thresholdsExceeded(List<File> fileList) {
364 return packageThresholdsExceeded(fileList) ||
365 classThresholdsExceeded(fileList) ||
366 functionThresholdsExceeded(fileList);
367 }
368
369
370
371
372
373
374
375 private List<File> findFilesToAnalyse() {
376 DirectoryScanner ds = super.getDirectoryScanner(srcdir);
377 String[] files = ds.getIncludedFiles();
378 if (files.length == 0) {
379 log("No files in specified directory " + srcdir, 3);
380 }
381 return copyFiles(files);
382 }
383
384
385
386
387
388
389
390 private List<File> copyFiles(String[] filenames) {
391 List<File> returnVector = new ArrayList<File>(filenames.length);
392 for (String filename : filenames) {
393 returnVector.add(new File(srcdir, filename));
394 }
395 return returnVector;
396 }
397
398
399
400
401 public Path createClasspath() {
402 if (classpath == null) {
403 classpath = new Path(getProject());
404 }
405 return classpath.createPath();
406 }
407
408
409
410
411
412
413 private String[] getCommandLineArguments(List<File> fileList) {
414 List<String> arguments = new ArrayList<String>();
415
416
417 if (packageMetrics) {
418 arguments.add("-package");
419 }
420 if (classMetrics) {
421 arguments.add("-object");
422 }
423 if (functionMetrics) {
424 arguments.add("-function");
425 }
426
427
428 if (format.equals("xml")) {
429 arguments.add("-xml");
430 }
431
432
433 if (outputfile != null) {
434 arguments.add("-out");
435 arguments.add(outputfile.getPath());
436 }
437
438
439 arguments.add("@" + createSourceListFile(fileList).getPath());
440
441 String[] javancssArguments = new String[arguments.size()];
442 for (int argument = 0; argument < arguments.size(); argument++) {
443 javancssArguments[argument] = arguments.get(argument);
444 }
445 return javancssArguments;
446 }
447
448
449
450
451
452
453
454 private File createSourceListFile(List<File> fileList) {
455 File srcListFile;
456 try {
457 srcListFile = File.createTempFile("srcList", null);
458 srcListFile.deleteOnExit();
459 FileOutputStream fos = new FileOutputStream(srcListFile);
460 PrintWriter pw = new PrintWriter(fos);
461 for (File file : fileList) {
462 log(file.toString(), 3);
463 pw.println(file.toString());
464 }
465 pw.close();
466 fos.close();
467 } catch (IOException e) {
468 throw new BuildException(e, getLocation());
469 }
470 return srcListFile;
471 }
472
473
474
475
476
477
478
479
480 private Javancss getJavaNcss(List<File> fileList)
481 {
482 if (javancss == null)
483 {
484 log("Checking metrics on " + fileList.size() + " files");
485 javancss = new Javancss(fileList);
486 }
487 return javancss;
488 }
489
490
491
492
493
494
495 private boolean packageThresholdsExceeded(List<File> fileList) {
496 boolean failed = false;
497 if (!(classPerPkgMax == Integer.MAX_VALUE &&
498 classPerPkgMin == -1 &&
499 funcPerPkgMax == Integer.MAX_VALUE &&
500 funcPerPkgMin == -1 &&
501 ncssPerPkgMax == Integer.MAX_VALUE &&
502 ncssPerPkgMin == -1)) {
503 List<PackageMetric> pkgMetrics = getJavaNcss(fileList).getPackageMetrics();
504 for (PackageMetric pkgMetric : pkgMetrics) {
505 failed = packageThresholdExceeded(pkgMetric) || failed;
506 }
507 }
508 return failed;
509 }
510
511
512
513
514
515
516 private boolean packageThresholdExceeded(PackageMetric packageMetrics) {
517 boolean failed = false;
518 String errorMsg = "";
519 if (classPerPkgMax < packageMetrics.classes) {
520 failed = true;
521 errorMsg = packageMetrics.classes + " classes exceeds maximum per package";
522 } else if (classPerPkgMin > packageMetrics.classes) {
523 failed = true;
524 errorMsg = packageMetrics.classes + " classes does not meet minimum per package";
525 }
526 if (funcPerPkgMax < packageMetrics.functions) {
527 failed = true;
528 errorMsg = packageMetrics.functions + " functions exceeds maximum pre package";
529 } else if (funcPerPkgMin > packageMetrics.functions) {
530 failed = true;
531 errorMsg = packageMetrics.functions + " functions does not meet minimum per package";
532 }
533 if (ncssPerPkgMax < packageMetrics.ncss) {
534 failed = true;
535 errorMsg = packageMetrics.ncss + " NCSS exceeds maximum per package";
536 } else if (ncssPerPkgMin > packageMetrics.ncss) {
537 failed = true;
538 errorMsg = packageMetrics.ncss + " NCSS does not meet minimum per package";
539 }
540
541 if (failed) {
542 log(packageMetrics.name + " - " + errorMsg, Project.MSG_INFO);
543 }
544 return failed;
545 }
546
547
548
549
550
551
552 private boolean classThresholdsExceeded(List<File> fileList) {
553 boolean failed = false;
554 if (!(classPerClassMax == Integer.MAX_VALUE &&
555 classPerClassMin == -1 &&
556 funcPerClassMax == Integer.MAX_VALUE &&
557 funcPerClassMin == -1 &&
558 jvdcPerClassMax == Integer.MAX_VALUE &&
559 jvdcPerClassMin == -1 &&
560 ncssPerClassMax == Integer.MAX_VALUE &&
561 ncssPerClassMin == -1)) {
562 List<ObjectMetric> objMetrics = getJavaNcss(fileList).getObjectMetrics();
563 for (ObjectMetric objMetric : objMetrics) {
564 failed = classThresholdExceeded(objMetric) || failed;
565 }
566 }
567 return failed;
568 }
569
570
571
572
573
574
575 private boolean classThresholdExceeded(ObjectMetric classMetrics) {
576 boolean failed = false;
577 String errorMsg = "";
578
579 int classPerClass = classMetrics.classes;
580 int funcPerClass = classMetrics.functions;
581 int ncssPerClass = classMetrics.ncss;
582 int jvdcPerClass = classMetrics.javadocs;
583
584 if (classPerClassMax < classPerClass) {
585 failed = true;
586 errorMsg = classPerClass + " inner classes exceeds maximum per class";
587 } else if (classPerClassMin > classPerClass) {
588 failed = true;
589 errorMsg = classPerClass + " inner classes does not meet minimum per class";
590 }
591 if (funcPerClassMax < funcPerClass) {
592 failed = true;
593 errorMsg = funcPerClass + " functions exceeds maximum pre class";
594 } else if (funcPerClassMin > funcPerClass) {
595 failed = true;
596 errorMsg = funcPerClass + " functions does not meet minimum per class";
597 }
598 if (ncssPerClassMax < ncssPerClass) {
599 failed = true;
600 errorMsg = ncssPerClass + " NCSS exceeds maximum per class";
601 } else if (ncssPerClassMin > ncssPerClass) {
602 failed = true;
603 errorMsg = ncssPerClass + " NCSS does not meet minimum per class";
604 }
605 if (jvdcPerClassMax < jvdcPerClass) {
606 failed = true;
607 errorMsg = jvdcPerClass + " javadoc statements exceeds maximum per class";
608 } else if (jvdcPerClassMin > jvdcPerClass) {
609 failed = true;
610 errorMsg = jvdcPerClass + " javadoc statements does not meet minimum per class";
611 }
612
613 if (failed) {
614 log(classMetrics.name + " - " + errorMsg, Project.MSG_INFO);
615 }
616 return failed;
617 }
618
619
620
621
622
623
624 private boolean functionThresholdsExceeded(List<File> fileList) {
625 boolean failed = false;
626
627 if (!(jvdcPerFuncMax == Integer.MAX_VALUE &&
628 jvdcPerFuncMin == -1 &&
629 ccnPerFuncMax == Integer.MAX_VALUE &&
630 ccnPerFuncMin == -1 &&
631 ncssPerFuncMax == Integer.MAX_VALUE &&
632 ncssPerFuncMin == -1)) {
633
634 List<FunctionMetric> funcMetrics = getJavaNcss(fileList).getFunctionMetrics();
635 for (FunctionMetric funcMetric : funcMetrics) {
636 failed = functionThresholdExceeded(funcMetric) || failed;
637 }
638 }
639 return failed;
640 }
641
642
643
644
645
646
647 private boolean functionThresholdExceeded(FunctionMetric functionMetrics) {
648 boolean failed = false;
649 String errorMsg = "";
650
651 int ccnPerFunc = functionMetrics.ccn;
652 int ncssPerFunc = functionMetrics.ncss;
653 int jvdcPerFunc = functionMetrics.javadocs;
654
655 if (ccnPerFuncMax < ccnPerFunc) {
656 failed = true;
657 errorMsg = ccnPerFunc + " CCN exceeds maximum per function";
658 } else if (ccnPerFuncMin > ccnPerFunc) {
659 failed = true;
660 errorMsg = ccnPerFunc + " CCN does not meet minimum per function";
661 }
662 if (ncssPerFuncMax < ncssPerFunc) {
663 failed = true;
664 errorMsg = ncssPerFunc + " NCSS exceeds maximum per function";
665 } else if (ncssPerFuncMin > ncssPerFunc) {
666 failed = true;
667 errorMsg = ncssPerFunc + " NCSS does not meet minimum per function";
668 }
669 if (jvdcPerFuncMax < jvdcPerFunc) {
670 failed = true;
671 errorMsg = jvdcPerFunc + " javadoc statements exceeds maximum per function";
672 } else if (jvdcPerFuncMin > jvdcPerFunc) {
673 failed = true;
674 errorMsg = jvdcPerFunc + " javadoc statements does not meet minimum per function";
675 }
676
677 if (failed) {
678 log(functionMetrics.name + " - " + errorMsg, Project.MSG_INFO);
679 }
680 return failed;
681 }
682
683
684
685
686
687 public void setClassPerPkgMax(int classPerPkgMax) {
688 this.classPerPkgMax = classPerPkgMax;
689 }
690
691
692
693
694
695 public void setClassPerPkgMin(int classPerPkgMin) {
696 this.classPerPkgMin = classPerPkgMin;
697 }
698
699
700
701
702
703 public void setFuncPerPkgMax(int funcPerPkgMax) {
704 this.funcPerPkgMax = funcPerPkgMax;
705 }
706
707
708
709
710
711 public void setFuncPerPkgMin(int funcPerPkgMin) {
712 this.funcPerPkgMin = funcPerPkgMin;
713 }
714
715
716
717
718
719
720 public void setNcssPerPkgMax(int ncssPerPkgMax) {
721 this.ncssPerPkgMax = ncssPerPkgMax;
722 }
723
724
725
726
727
728
729 public void setNcssPerPkgMin(int ncssPerPkgMin) {
730 this.ncssPerPkgMin = ncssPerPkgMin;
731 }
732
733
734
735
736
737 public void setClassPerClassMax(int classPerClassMax) {
738 this.classPerClassMax = classPerClassMax;
739 }
740
741
742
743
744
745 public void setClassPerClassMin(int classPerClassMin) {
746 this.classPerClassMin = classPerClassMin;
747 }
748
749
750
751
752
753 public void setFuncPerClassMax(int funcPerClassMax) {
754 this.funcPerClassMax = funcPerClassMax;
755 }
756
757
758
759
760
761 public void setFuncPerClassMin(int funcPerClassMin) {
762 this.funcPerClassMin = funcPerClassMin;
763 }
764
765
766
767
768
769
770 public void setNcssPerClassMax(int ncssPerClassMax) {
771 this.ncssPerClassMax = ncssPerClassMax;
772 }
773
774
775
776
777
778
779 public void setNcssPerClassMin(int ncssPerClassMin) {
780 this.ncssPerClassMin = ncssPerClassMin;
781 }
782
783
784
785
786
787 public void setJvdcPerClassMax(int jvdcPerClassMax) {
788 this.jvdcPerClassMax = jvdcPerClassMax;
789 }
790
791
792
793
794
795 public void setJvdcPerClassMin(int jvdcPerClassMin) {
796 this.jvdcPerClassMin = jvdcPerClassMin;
797 }
798
799
800
801
802
803
804 public void setCcnPerFuncMax(int ccnPerFuncMax) {
805 this.ccnPerFuncMax = ccnPerFuncMax;
806 }
807
808
809
810
811
812
813 public void setCcnPerFuncMin(int ccnPerFuncMin) {
814 this.ccnPerFuncMin = ccnPerFuncMin;
815 }
816
817
818
819
820
821
822 public void setNcssPerFuncMax(int ncssPerFuncMax) {
823 this.ncssPerFuncMax = ncssPerFuncMax;
824 }
825
826
827
828
829
830
831 public void setNcssPerFuncMin(int ncssPerFuncMin) {
832 this.ncssPerFuncMin = ncssPerFuncMin;
833 }
834
835
836
837
838
839 public void setJvdcPerFuncMax(int jvdcPerFuncMax) {
840 this.jvdcPerFuncMax = jvdcPerFuncMax;
841 }
842
843
844
845
846
847 public void setJvdcPerFuncMin(int jvdcPerFuncMin) {
848 this.jvdcPerFuncMin = jvdcPerFuncMin;
849 }
850 }