├── .gitignore
├── .idea
└── workspace.xml
├── LICENSE
├── META-INF
└── MANIFEST.MF
├── README.md
├── Report.pdf
├── pom.xml
└── src
└── main
├── java
├── Runner.java
└── analyzer
│ ├── AbstractVoidVisitorAdapter.java
│ ├── Analyzer.java
│ ├── ComplexityCounter.java
│ ├── Config.java
│ ├── collectors
│ ├── Collector.java
│ └── HashMapCollector.java
│ ├── printers
│ ├── AbstractPrinter.java
│ ├── ComplexityPrinter.java
│ ├── MetricPrinter.java
│ ├── Printable.java
│ └── WarningPrinter.java
│ └── visitors
│ ├── BooleanMethodVisitor.java
│ ├── ClassComplexityVisitor.java
│ ├── ClassLineCounterVisitor.java
│ ├── MetricVisitor.java
│ ├── TooLongVisitor.java
│ ├── TooSmallVisitor.java
│ └── VariableNamingConventionVisitor.java
└── resources
└── Test.java
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .idea
3 | target
--------------------------------------------------------------------------------
/.idea/workspace.xml:
--------------------------------------------------------------------------------
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 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 | true
165 | DEFINITION_ORDER
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 |
285 |
286 |
287 |
288 |
289 |
290 |
291 |
292 |
293 |
294 |
295 |
296 |
297 |
298 |
299 |
300 |
301 |
302 |
303 |
304 |
305 |
306 |
307 |
308 |
309 |
310 |
311 |
312 |
313 |
314 |
315 |
316 |
317 |
318 |
319 |
320 |
321 |
322 |
323 |
324 |
325 |
326 |
327 |
328 |
329 |
330 |
331 |
332 |
333 |
334 |
335 |
336 |
337 |
338 |
339 |
340 |
341 |
342 |
343 |
344 |
345 |
346 |
347 |
348 |
349 |
350 |
351 |
352 |
353 |
354 |
355 |
356 |
357 |
358 |
359 |
360 |
361 |
362 |
363 |
364 |
365 |
366 |
367 |
368 |
369 |
370 |
371 |
372 |
373 |
374 |
375 |
376 |
377 |
378 |
379 |
380 |
381 |
382 |
383 |
384 |
385 |
386 |
387 |
388 |
389 |
390 |
391 |
392 |
393 |
394 |
395 |
396 |
397 |
398 |
399 |
400 |
401 |
402 |
403 |
404 |
405 |
406 |
407 |
408 |
409 |
410 |
411 |
412 |
413 |
414 |
415 |
416 |
417 |
418 |
419 |
420 |
421 |
422 |
423 |
424 |
425 |
426 |
427 |
428 |
429 |
430 |
431 |
432 |
433 |
434 |
435 |
436 |
437 |
438 |
439 |
440 |
441 |
442 |
443 |
444 |
445 |
446 |
447 |
448 |
449 |
450 |
451 |
452 |
453 |
454 |
455 |
456 |
457 |
458 |
459 |
460 |
461 |
462 |
463 |
464 |
465 |
466 |
467 |
468 |
469 |
470 |
471 |
472 |
473 |
474 |
475 |
476 |
477 | project
478 |
479 |
480 |
481 |
482 |
483 |
484 |
485 |
486 |
487 |
488 |
489 |
490 |
491 |
492 |
493 |
494 |
495 |
496 |
497 |
498 |
499 |
500 |
501 |
502 |
503 |
504 |
505 |
506 |
507 |
508 |
509 |
510 |
511 |
512 |
513 |
514 |
515 |
516 |
517 |
518 |
519 |
520 |
521 |
522 |
523 |
524 |
525 |
526 |
527 |
528 |
529 |
530 |
531 |
532 |
533 |
534 |
535 |
536 |
537 |
538 |
539 |
540 |
541 |
542 |
543 |
544 |
545 |
546 |
547 |
548 |
549 |
550 |
551 |
552 |
553 |
554 |
555 |
556 |
557 |
558 |
559 |
560 |
561 |
562 |
563 |
564 |
565 |
566 |
567 |
568 |
569 |
570 |
571 |
572 |
573 |
574 |
575 |
576 |
577 |
578 |
579 |
580 |
581 |
582 |
583 |
584 |
585 |
586 |
587 |
588 |
589 |
590 |
591 |
592 |
593 |
594 |
595 |
596 |
597 |
598 |
599 |
600 |
601 |
602 |
603 |
604 |
605 |
606 |
607 |
608 |
609 |
610 |
611 |
612 |
613 |
614 |
615 |
616 |
617 |
618 |
619 |
620 |
621 |
622 |
623 |
624 |
625 |
626 |
627 |
628 |
629 |
630 |
631 |
632 |
633 |
634 |
635 |
636 |
637 |
638 |
639 |
640 |
641 |
642 |
643 |
644 |
645 |
646 |
647 |
648 |
649 |
650 |
651 |
652 |
653 |
654 |
655 |
656 |
657 |
658 |
659 |
660 |
661 |
662 |
663 |
664 |
665 |
666 |
667 |
668 |
669 |
670 |
671 |
672 |
673 |
674 |
675 |
676 |
677 |
678 |
679 |
680 |
681 |
682 |
683 |
684 |
685 |
686 |
687 |
688 |
689 |
690 |
691 |
692 |
693 |
694 |
695 |
696 |
697 |
698 |
699 |
700 |
701 |
702 |
703 |
704 |
705 |
706 |
707 |
708 |
709 |
710 |
711 |
712 |
713 |
714 |
715 |
716 |
717 |
718 |
719 |
720 |
721 |
722 |
723 |
724 |
725 |
726 |
727 |
728 |
729 |
730 |
731 |
732 |
733 |
734 |
735 |
736 |
737 |
738 |
739 |
740 |
741 |
742 |
743 |
744 |
745 |
746 |
747 |
748 |
749 |
750 |
751 |
752 |
753 |
754 |
755 |
756 |
757 |
758 |
759 |
760 |
761 |
762 |
763 |
764 | project
765 |
766 |
767 | true
768 |
769 | bdd
770 |
771 | DIRECTORY
772 |
773 | false
774 |
775 |
776 |
777 |
778 |
779 |
780 |
781 |
782 |
783 |
784 |
785 |
786 |
787 |
788 |
789 |
790 |
791 |
792 |
793 |
794 |
795 |
796 |
797 |
798 |
799 |
800 |
801 |
802 |
803 |
804 |
805 |
806 | 1475829258323
807 |
808 |
809 | 1475829258323
810 |
811 |
812 |
813 |
814 |
815 |
816 |
817 |
818 |
819 |
820 |
821 | 1476548407331
822 |
823 |
824 |
825 | 1476548407331
826 |
827 |
828 | 1476560176894
829 |
830 |
831 |
832 | 1476560176894
833 |
834 |
835 | 1477165686875
836 |
837 |
838 |
839 | 1477165686875
840 |
841 |
842 | 1477261703163
843 |
844 |
845 |
846 | 1477261703163
847 |
848 |
849 | 1477262386946
850 |
851 |
852 |
853 | 1477262386946
854 |
855 |
856 | 1507835142276
857 |
858 |
859 |
860 | 1507835142277
861 |
862 |
863 |
864 |
865 |
866 |
867 |
868 |
869 |
870 |
871 |
872 |
873 |
874 |
875 |
876 |
877 |
878 |
879 |
880 |
881 |
882 |
883 |
884 |
885 |
886 |
887 |
888 |
889 |
890 |
891 |
892 |
893 |
894 |
895 |
896 |
897 |
898 |
899 |
900 |
901 |
902 |
903 |
904 |
905 |
906 |
907 |
908 |
909 |
910 |
911 |
912 |
913 |
914 |
915 |
916 |
917 |
918 |
919 |
920 |
921 |
922 |
923 |
924 |
925 |
926 |
927 |
928 |
929 |
930 |
931 |
932 |
933 |
934 |
935 |
936 |
937 |
938 |
939 |
940 |
941 |
942 |
943 |
944 |
945 |
946 |
947 |
948 |
949 |
950 |
951 |
952 |
953 |
954 |
955 |
956 |
957 |
958 |
959 | file://$PROJECT_DIR$/src/main/java/analyzer/collectors/HashMapCollector.java
960 | 44
961 |
962 |
963 |
964 |
965 | file://$PROJECT_DIR$/src/main/java/analyzer/collectors/HashMapCollector.java
966 | 60
967 |
968 |
969 |
970 |
971 |
972 |
973 |
974 |
975 |
976 |
977 |
978 |
979 |
980 |
981 |
982 |
983 |
984 |
985 |
986 |
987 |
988 |
989 |
990 |
991 |
992 |
993 |
994 |
995 |
996 |
997 |
998 |
999 |
1000 |
1001 |
1002 |
1003 |
1004 |
1005 |
1006 |
1007 |
1008 |
1009 |
1010 |
1011 |
1012 |
1013 |
1014 |
1015 |
1016 |
1017 |
1018 |
1019 |
1020 |
1021 |
1022 |
1023 |
1024 |
1025 |
1026 |
1027 |
1028 |
1029 |
1030 |
1031 |
1032 |
1033 |
1034 |
1035 |
1036 |
1037 |
1038 |
1039 |
1040 |
1041 |
1042 |
1043 |
1044 |
1045 |
1046 |
1047 |
1048 |
1049 |
1050 |
1051 |
1052 |
1053 |
1054 |
1055 |
1056 |
1057 |
1058 |
1059 |
1060 |
1061 |
1062 |
1063 |
1064 |
1065 |
1066 |
1067 |
1068 |
1069 |
1070 |
1071 |
1072 |
1073 |
1074 |
1075 |
1076 |
1077 |
1078 |
1079 |
1080 |
1081 |
1082 |
1083 |
1084 |
1085 |
1086 |
1087 |
1088 |
1089 |
1090 |
1091 |
1092 |
1093 |
1094 |
1095 |
1096 |
1097 |
1098 |
1099 |
1100 |
1101 |
1102 |
1103 |
1104 |
1105 |
1106 |
1107 |
1108 |
1109 |
1110 |
1111 |
1112 |
1113 |
1114 |
1115 |
1116 |
1117 |
1118 |
1119 |
1120 |
1121 |
1122 |
1123 |
1124 |
1125 |
1126 |
1127 |
1128 |
1129 |
1130 |
1131 |
1132 |
1133 |
1134 |
1135 |
1136 |
1137 |
1138 |
1139 |
1140 |
1141 |
1142 |
1143 |
1144 |
1145 |
1146 |
1147 |
1148 |
1149 |
1150 |
1151 |
1152 |
1153 |
1154 |
1155 |
1156 |
1157 |
1158 |
1159 |
1160 |
1161 |
1162 |
1163 |
1164 |
1165 |
1166 |
1167 |
1168 |
1169 |
1170 |
1171 |
1172 |
1173 |
1174 |
1175 |
1176 |
1177 |
1178 |
1179 |
1180 |
1181 |
1182 |
1183 |
1184 |
1185 |
1186 |
1187 |
1188 |
1189 |
1190 |
1191 |
1192 |
1193 |
1194 |
1195 |
1196 |
1197 |
1198 |
1199 |
1200 |
1201 |
1202 |
1203 |
1204 |
1205 |
1206 |
1207 |
1208 |
1209 |
1210 |
1211 |
1212 |
1213 |
1214 |
1215 |
1216 |
1217 |
1218 |
1219 |
1220 |
1221 |
1222 |
1223 |
1224 |
1225 |
1226 |
1227 |
1228 |
1229 |
1230 |
1231 |
1232 |
1233 |
1234 |
1235 |
1236 |
1237 |
1238 |
1239 |
1240 |
1241 |
1242 |
1243 |
1244 |
1245 |
1246 |
1247 |
1248 |
1249 |
1250 |
1251 |
1252 |
1253 |
1254 |
1255 |
1256 |
1257 |
1258 |
1259 |
1260 |
1261 |
1262 |
1263 |
1264 |
1265 |
1266 |
1267 |
1268 |
1269 |
1270 |
1271 |
1272 |
1273 |
1274 |
1275 |
1276 |
1277 |
1278 |
1279 |
1280 |
1281 |
1282 |
1283 |
1284 |
1285 |
1286 |
1287 |
1288 |
1289 |
1290 |
1291 |
1292 |
1293 |
1294 |
1295 |
1296 |
1297 |
1298 |
1299 |
1300 |
1301 |
1302 |
1303 |
1304 |
1305 |
1306 |
1307 |
1308 |
1309 |
1310 |
1311 |
1312 |
1313 |
1314 |
1315 |
1316 |
1317 |
1318 |
1319 |
1320 |
1321 |
1322 |
1323 |
1324 |
1325 |
1326 |
1327 |
1328 |
1329 |
1330 |
1331 |
1332 |
1333 |
1334 |
1335 |
1336 |
1337 |
1338 |
1339 |
1340 |
1341 |
1342 |
1343 |
1344 |
1345 |
1346 |
1347 |
1348 |
1349 |
1350 |
1351 |
1352 |
1353 |
1354 |
1355 |
1356 |
1357 |
1358 |
1359 |
1360 |
1361 |
1362 |
1363 |
1364 |
1365 |
1366 |
1367 |
1368 |
1369 |
1370 |
1371 |
1372 |
1373 |
1374 |
1375 |
1376 |
1377 |
1378 |
1379 |
1380 |
1381 |
1382 |
1383 |
1384 |
1385 |
1386 | CS409:jar
1387 |
1388 |
1389 |
1390 |
1391 |
1392 |
1393 |
1394 |
1395 |
1396 |
1397 |
1398 |
1399 | No facets are configured
1400 |
1401 |
1402 |
1403 |
1404 |
1405 |
1406 |
1407 |
1408 |
1409 |
1410 |
1411 |
1412 |
1413 |
1414 |
1415 |
1416 |
1417 |
1418 |
1419 |
1420 |
1421 |
1422 | 1.8
1423 |
1424 |
1425 |
1426 |
1427 |
1428 |
1429 |
1430 |
1431 |
1432 |
1433 |
1434 | CS409
1435 |
1436 |
1437 |
1438 |
1439 |
1440 |
1441 |
1442 |
1443 |
1444 |
1445 |
1446 |
1447 | 1.8
1448 |
1449 |
1450 |
1451 |
1452 |
1453 |
1454 |
1455 |
1456 |
1457 |
1458 |
1459 | Maven: com.github.javaparser:javaparser-core:2.5.1
1460 |
1461 |
1462 |
1463 |
1464 |
1465 |
1466 |
1467 |
1468 |
1469 |
1470 |
1471 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2016 Laurynas Sakalauskas
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/META-INF/MANIFEST.MF:
--------------------------------------------------------------------------------
1 | Manifest-Version: 1.0
2 | Main-Class: Runner
3 |
4 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Java Static Code Analyzer
2 |
3 | The analyzer can detect & collect statistics and metrics about the project mentioned below:
4 |
5 | **Code smells**
6 |
7 | 1. Calculate Class Cyclomatic Complexity and limit (default: 6)
8 |
9 | 2. Calculate Weighted Method Count (WMC) and limit (default: 50)
10 |
11 | 3. Maximum number of lines per class (default: 1000)
12 |
13 | 4. Maximum number of lines per method (default: 50)
14 |
15 | 5. Maximum method name length (default: 50)
16 |
17 | 6. Maximum number of parameters in method (default: 8)
18 |
19 | 7. Maximum length of field/parameter (default: 20)
20 |
21 | 8. Maximum number of fields in the class (default: 25)
22 |
23 | 9. Maximum number of methods in the class (default: 25)
24 |
25 | 10. Minimum length of field/parameter (default: 3)
26 |
27 | 11. Minimum length of method (default: 3)
28 |
29 | **Naming Conventions**
30 |
31 | 1. Boolean names must start with is (e.g. isSomething()), unless it has parameters.
32 |
33 | 2. Class must be in CamelCase.
34 |
35 | 3. Method must be in camelCase().
36 |
37 | 4. Parameters must be in camelCase.
38 |
39 | 5. Fields must be in camelCase, constants can be in UPPER_CASE.
40 |
41 | **Metrics**
42 |
43 | 1. Total Code lines in project.
44 |
45 | 2. Total Classes in project.
46 |
47 | 3. Total Test/Abstract/Final classes.
48 |
49 | 4. Total Interfaces in project.
50 |
51 | 5. Total Overridden methods in project.
52 |
53 | 6. Total Methods in project.
54 |
55 | 7. Total Public/private/protected methods in project.
56 |
57 | 8. Total Methods without Javadoc
58 |
59 | 9. Total Parameters in project.
60 |
61 | 10. Total Fields in project.
62 |
63 | # Building the app
64 |
65 | 1. Install Maven dependencies: `mvn install`
66 | 2. Compile java file: `javac src/main/java/Runner.java`
67 | 2. Run java file `java src/main/java/Runner`
68 |
69 | ## Outline of design
70 |
71 | I have used JavaParser framework’s *VoidVisitorAdapter* to parse AST (Abstract syntax tree). Visitor pattern is being used heavily to walk through the AST and collect all the relevant statistics and warnings. There are 7 different visitor classes separated so they handle different responsibilities to make the code more maintainable, understandable and with SOLID principles in mind.
72 |
73 | All the metrics and errors are collected in *Collector* object which holds all the data collected and can be reused throughout the project. *Collector* is an interface and I have added an implementation of *HashMapCollector* to simply collect the data into *HashMap*. In the future, if we for example want to store data to MySQL database, we can add *MysqlCollector* implementation and we would only need to change one line of code where we set the implementation to use.
74 |
75 | I have separated different console printers to the individual classes as well, (just in case we want to skip printing some stats) namely: *ComplexityPrinter* which prints statistics about Class Cyclomatic Complexity and Weighted Method Counts. *MetricPrinter* which prints all the available metrics about the java project and finally a *WarningPrinter* which prints out all the warnings produced by running the analyzer.
76 |
77 | With the current design it is easy to adjust the project how we want to output statistics and what else we may want analyse without modifying any collector code. Therefore, if we would like to display the results in HTML file, we can just add new Printers with some other output strategy. However, since the assignment is purely based on content, not on the style, I decided to simply output the results into console, rather than producing a nice HTML report with some graphs.
78 |
79 | There is a *Config.java* class where all the constants are defined in one file for easy configuration change. This could be useful to adjust the limits or enable/disable some parts of the application without going and searching through the code.
80 |
81 | ## Results and evaluation
82 | The analyzer application produces the relevant results for any Java project you put in. As a test cases, I have included several java projects from github (that produced correct metrics and did not generate any false warnings).
83 |
--------------------------------------------------------------------------------
/Report.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sakalauskas/Java-Static-Analyzer/2fe7c225f6a878cff566129e3b31205e44568ec0/Report.pdf
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | cs409
8 | 1
9 | 0.1-FINAL
10 |
11 |
12 |
13 |
14 | com.github.javaparser
15 | javaparser-core
16 | 3.4.3
17 |
18 |
19 |
20 |
21 |
22 | org.apache.maven.plugins
23 | maven-compiler-plugin
24 |
25 | 1.8
26 | 1.8
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/src/main/java/Runner.java:
--------------------------------------------------------------------------------
1 | import analyzer.Analyzer;
2 | import analyzer.collectors.HashMapCollector;
3 | import analyzer.printers.ComplexityPrinter;
4 | import analyzer.printers.MetricPrinter;
5 | import analyzer.printers.WarningPrinter;
6 |
7 | import java.io.IOException;
8 | import java.nio.file.Files;
9 | import java.nio.file.Path;
10 | import java.nio.file.Paths;
11 | import java.util.Scanner;
12 |
13 | /**
14 | * Created by laurynassakalauskas on 15/10/2016.
15 | */
16 | public class Runner {
17 |
18 |
19 | /**
20 | * Simple Analyzer runner method
21 | *
22 | * @param args
23 | * @throws Exception
24 | */
25 | public static void main(String[] args) throws Exception {
26 |
27 | Scanner in = new Scanner(System.in);
28 |
29 | System.out.println("Please type full path to project directory you want to analyze.");
30 |
31 | String tempPath = in.nextLine();
32 | // String tempPath = "/Users/laurynassakalauskas/IdeaProjects/CS409/testcases/java-design-patterns";
33 | try {
34 | run(tempPath);
35 | } catch (Exception e) {
36 | System.out.println("Looks like you specified not existing directory. Exiting...");
37 | }
38 |
39 |
40 | // other way of using
41 | // if (args.length == 0) {
42 | // System.out.println("Usage: java -jar analyzer [path]");
43 | // } else {
44 | // String path = args[0];
45 | //
46 | // run(path);
47 | // }
48 |
49 | }
50 |
51 | /**
52 | * Convert String to path if needed
53 | *
54 | * @param path
55 | */
56 | private static void run(String path) {
57 | run(Paths.get(path));
58 | }
59 |
60 | /**
61 | * Walk through the files and collect stats and then print them out
62 | *
63 | * @param p
64 | */
65 | private static void run(Path p) {
66 | try {
67 | HashMapCollector collector = new HashMapCollector();
68 |
69 | Files.walkFileTree(p, new Analyzer(collector));
70 |
71 | new WarningPrinter(collector).print();
72 | new MetricPrinter(collector).print();
73 | new ComplexityPrinter(collector).print();
74 |
75 | } catch (IOException e) {
76 | e.printStackTrace();
77 | }
78 | }
79 |
80 |
81 | }
82 |
--------------------------------------------------------------------------------
/src/main/java/analyzer/AbstractVoidVisitorAdapter.java:
--------------------------------------------------------------------------------
1 | package analyzer;
2 |
3 | import com.github.javaparser.ast.CompilationUnit;
4 | import com.github.javaparser.ast.visitor.VoidVisitorAdapter;
5 |
6 | /**
7 | * Wrapper class for automatically resolving classname and reusing throughout the visitor
8 | *
9 | * Created by laurynassakalauskas on 15/10/2016.
10 | */
11 | public abstract class AbstractVoidVisitorAdapter extends VoidVisitorAdapter {
12 |
13 | protected String className;
14 |
15 | @Override
16 | public void visit(CompilationUnit cu, A arg) {
17 |
18 | if (cu.getTypes().size() > 0) {
19 | className = cu.getTypes().get(0).getNameAsString();
20 | } else {
21 | className = "Unknown class";
22 | }
23 |
24 |
25 | super.visit(cu, arg);
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/main/java/analyzer/Analyzer.java:
--------------------------------------------------------------------------------
1 | package analyzer;
2 |
3 | import analyzer.collectors.Collector;
4 | import analyzer.visitors.*;
5 | import com.github.javaparser.JavaParser;
6 | import com.github.javaparser.ParseException;
7 | import com.github.javaparser.ast.CompilationUnit;
8 |
9 | import java.io.IOException;
10 | import java.nio.file.FileVisitResult;
11 | import java.nio.file.Path;
12 | import java.nio.file.SimpleFileVisitor;
13 | import java.nio.file.attribute.BasicFileAttributes;
14 |
15 | /**
16 | * Created by laurynassakalauskas on 15/10/2016.
17 | */
18 | public class Analyzer extends SimpleFileVisitor {
19 |
20 | private Collector collector;
21 |
22 | public Analyzer(Collector collector) {
23 |
24 | this.collector = collector;
25 | }
26 |
27 | @Override
28 | public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
29 | throws IOException {
30 |
31 | if (isNotJava(file))
32 | return FileVisitResult.CONTINUE;
33 |
34 | // initialize collector
35 | ComplexityCounter complexityCounter = new ComplexityCounter(getClassName(file), collector);
36 |
37 | // initialize compilation unit
38 | CompilationUnit unit = JavaParser.parse(file.toFile());
39 |
40 | // collect all the stats
41 | new BooleanMethodVisitor().visit(unit, collector);
42 | new TooLongVisitor().visit(unit, collector);
43 | new TooSmallVisitor().visit(unit, collector);
44 | new VariableNamingConventionVisitor().visit(unit, collector);
45 | new ClassLineCounterVisitor().visit(unit, collector);
46 | new ClassComplexityVisitor().visit(unit, complexityCounter);
47 | new MetricVisitor().visit(unit, collector);
48 |
49 | // print analysis
50 | complexityCounter.analyze();
51 | collector.addComplexityResults(getClassName(file), complexityCounter);
52 |
53 | return FileVisitResult.CONTINUE;
54 | }
55 |
56 | /**
57 | * Extract class name from the path
58 | *
59 | * @param file
60 | * @return
61 | */
62 | private String getClassName(Path file) {
63 |
64 | String filename = file.getFileName().toString();
65 |
66 | if (filename.indexOf(".") > 0) {
67 | filename = filename.substring(0, filename.lastIndexOf("."));
68 | }
69 | return filename;
70 | }
71 |
72 | /**
73 | * We only need to analyze the Java files
74 | *
75 | * @param file
76 | * @return
77 | */
78 | private boolean isNotJava(Path file) {
79 |
80 | return !file.toString().endsWith("java");
81 | }
82 |
83 | }
84 |
--------------------------------------------------------------------------------
/src/main/java/analyzer/ComplexityCounter.java:
--------------------------------------------------------------------------------
1 | package analyzer;
2 |
3 | import analyzer.collectors.Collector;
4 |
5 | import java.util.HashMap;
6 | import java.util.Map;
7 |
8 | /**
9 | * Created by laurynassakalauskas on 15/10/2016.
10 | */
11 | public class ComplexityCounter {
12 |
13 |
14 | private static final int MAX_CYCLOMATIC_COMPLEXITY = Config.MAX_CYCLOMATIC_COMPLEXITY;
15 |
16 | private static final int MAX_WEIGHTED_COUNT = Config.MAX_WEIGHTED_COUNT;
17 |
18 | private final String classname;
19 |
20 | private Collector collector;
21 |
22 | private HashMap map = new HashMap<>();
23 |
24 | private HashMap usage = new HashMap<>();
25 |
26 | public ComplexityCounter(String classname, Collector collector) {
27 | this.classname = classname;
28 | this.collector = collector;
29 | }
30 |
31 |
32 | /**
33 | * Collect information about each method
34 | */
35 | public void add(String methodName, String type) {
36 |
37 | // check if such method was ever updated
38 | if (map.containsKey(methodName)) {
39 |
40 | map.put(methodName, map.get(methodName) + 1);
41 | } else {
42 |
43 | // method name was never queried. create a new HashMap
44 | map.put(methodName, 1);
45 | }
46 |
47 | // put information about different stats
48 | if (usage.containsKey(type)) {
49 |
50 | usage.put(type, usage.get(type) + 1);
51 | } else {
52 | usage.put(type, 1);
53 | }
54 | }
55 |
56 | public void analyze() {
57 | cyclomaticComplexity();
58 | weightedMethodCount();
59 | }
60 |
61 | /**
62 | * Retrieves total usage of IF, While, Foreach, bitwise and etc
63 | *
64 | * @return
65 | */
66 | public HashMap getUsage() {
67 | return usage;
68 | }
69 |
70 | /**
71 | * Calculate cyclomatic complexity for all methods
72 | */
73 | protected void cyclomaticComplexity() {
74 | for (Map.Entry entry: map.entrySet()) {
75 |
76 | cyclomaticComplexity(entry.getKey());
77 |
78 | }
79 | }
80 |
81 | /**
82 | * Calculate cyclomatic complexity for selected method
83 | *
84 | * @param method
85 | * @return
86 | */
87 | public int cyclomaticComplexity(String method) {
88 |
89 | int sum = map.get(method);
90 |
91 | if (sum > MAX_CYCLOMATIC_COMPLEXITY) {
92 | collector.addWarning(classname, "\"" + method + "\" method has Cyclomatic complexity of more than " + MAX_CYCLOMATIC_COMPLEXITY + ". Consider minimizing class complexity.");
93 | }
94 |
95 | return sum;
96 | }
97 |
98 |
99 | /**
100 | * Calculate total weighted method count
101 | *
102 | * @return
103 | */
104 | public int weightedMethodCount() {
105 |
106 | int sum = 0;
107 |
108 | for (Map.Entry entry: map.entrySet()) {
109 | sum += entry.getValue();
110 | }
111 |
112 | // check if there are any methods
113 | if (map.entrySet().size() > 0) {
114 | sum = sum / map.entrySet().size();
115 | }
116 |
117 | if (sum > MAX_WEIGHTED_COUNT) {
118 | collector.addWarning(classname, "Class has Weighted Method Count more than " + MAX_WEIGHTED_COUNT + ". Consider minimizing class complexity.");
119 | }
120 |
121 | return sum;
122 | }
123 |
124 |
125 |
126 | }
127 |
--------------------------------------------------------------------------------
/src/main/java/analyzer/Config.java:
--------------------------------------------------------------------------------
1 | package analyzer;
2 |
3 | /**
4 | * Created by laurynassakalauskas on 15/10/2016.
5 | */
6 | public final class Config {
7 |
8 |
9 | /**
10 | * Max count for calculating "Cyclomatic Complexity"
11 | */
12 | public static final int MAX_CYCLOMATIC_COMPLEXITY = 50;
13 |
14 | /**
15 | * Max count for calculating "Weighted Method Count (WMC)"
16 | */
17 | public static final int MAX_WEIGHTED_COUNT = 50;
18 |
19 | /**
20 | * Maximum length of class file
21 | */
22 | public static final int MAX_CLASS_LENGTH = 1000;
23 |
24 | /**
25 | * Maximum number of lines per method
26 | */
27 | public static final int MAX_BODY_LENGTH = 50;
28 |
29 | /**
30 | * Maximum length per method name
31 | */
32 | public static final int MAX_METHOD_NAME_LENGTH = 50;
33 |
34 | /**
35 | * Maximum number of parameters per method
36 | */
37 | public static final int MAX_PARAM_COUNT = 8;
38 |
39 | /**
40 | * Maximum length of field/parameter
41 | */
42 | public static final int MAX_VARIABLE_LENGTH = 20;
43 |
44 | /**
45 | * Maximum number of variables declared per class
46 | */
47 | public static final int MAX_VARIABLE_COUNT = 15;
48 |
49 | /**
50 | * Maximum number of methods per class
51 | */
52 | public static final int MAX_METHODS_COUNT = 25;
53 |
54 | /**
55 | * Minimum length of variable
56 | */
57 | public static final int MIN_VARIABLE_LENGTH = 3;
58 |
59 | /**
60 | * Minimum length of method name
61 | */
62 | public static final int MIN_METHOD_NAME = 3;
63 |
64 | /**
65 | * Boolean method must start with is (e.g. isSomething()) when there are no parameters
66 | */
67 | public static final boolean BOOLEAN_STARTS_WITH_IS = true;
68 |
69 | /**
70 | * Class name must be in CamelCase
71 | */
72 | public static final boolean CAMEL_CASE_CLASS_NAME = true;
73 |
74 | /**
75 | * Method must be in camelCase
76 | */
77 | public static final boolean METHOD_IN_CAMEL_CASE = true;
78 | /**
79 | * Fields/parameters must be in camelCase
80 | */
81 | public static final boolean PARAM_IN_CAMEL_CASE = true;
82 | }
83 |
--------------------------------------------------------------------------------
/src/main/java/analyzer/collectors/Collector.java:
--------------------------------------------------------------------------------
1 | package analyzer.collectors;
2 |
3 | import analyzer.ComplexityCounter;
4 |
5 | /**
6 | * Created by laurynassakalauskas on 23/10/2016.
7 | */
8 | public interface Collector {
9 |
10 | /**
11 | * Add warnings to queue
12 | *
13 | * @param className
14 | * @param warning
15 | */
16 | void addWarning(String className, String warning);
17 |
18 | /**
19 | * Add complexity results for the class
20 | *
21 | * @param className
22 | * @param counter
23 | */
24 | void addComplexityResults(String className, ComplexityCounter counter);
25 |
26 | /**
27 | * Increment some metric by +1
28 | *
29 | * @param metricName
30 | */
31 | void incrementMetric(String metricName);
32 |
33 | /**
34 | * Increment some metric by specified count
35 | *
36 | * @param metricName
37 | * @param count
38 | */
39 | void incrementMetric(String metricName, int count);
40 | }
41 |
--------------------------------------------------------------------------------
/src/main/java/analyzer/collectors/HashMapCollector.java:
--------------------------------------------------------------------------------
1 | package analyzer.collectors;
2 |
3 | import analyzer.ComplexityCounter;
4 |
5 | import java.util.ArrayList;
6 | import java.util.HashMap;
7 | import java.util.List;
8 |
9 | /**
10 | * Collect all stats to hashmap
11 | *
12 | * Created by laurynassakalauskas on 15/10/2016.
13 | */
14 | public class HashMapCollector implements Collector {
15 |
16 |
17 | protected static HashMap> warnings;
18 |
19 | protected static HashMap stats;
20 |
21 | protected static HashMap complexity;
22 |
23 | public static HashMap> getWarnings() {
24 | return warnings;
25 | }
26 |
27 | public static HashMap getStats() {
28 | return stats;
29 | }
30 |
31 | public static HashMap getComplexity() {
32 | return complexity;
33 | }
34 |
35 | public HashMapCollector() {
36 | warnings = new HashMap<>();
37 | stats = new HashMap<>();
38 | complexity = new HashMap<>();
39 | }
40 |
41 |
42 | /**
43 | * {@inheritDoc}
44 | */
45 | public void addWarning(String className, String warning) {
46 |
47 | if (warnings.containsKey(className)) {
48 | warnings.get(className).add(warning);
49 | } else {
50 |
51 | warnings.put(className, new ArrayList() {{
52 | add(warning);
53 | }});
54 | }
55 |
56 | }
57 |
58 | /**
59 | * {@inheritDoc}
60 | */
61 | public void addComplexityResults(String className, ComplexityCounter counter) {
62 | complexity.put(className, counter);
63 | }
64 |
65 | /**
66 | * {@inheritDoc}
67 | */
68 | public void incrementMetric(String metricName) {
69 | if (stats.containsKey(metricName)) {
70 | stats.put(metricName, stats.get(metricName) + 1);
71 | } else {
72 | stats.put(metricName, 1);
73 | }
74 | }
75 |
76 |
77 | /**
78 | * {@inheritDoc}
79 | */
80 | public void incrementMetric(String metricName, int count) {
81 | if (stats.containsKey(metricName)) {
82 | stats.put(metricName, stats.get(metricName) + count);
83 | } else {
84 | stats.put(metricName, count);
85 | }
86 | }
87 |
88 |
89 | }
90 |
--------------------------------------------------------------------------------
/src/main/java/analyzer/printers/AbstractPrinter.java:
--------------------------------------------------------------------------------
1 | package analyzer.printers;
2 |
3 | import analyzer.collectors.HashMapCollector;
4 |
5 | /**
6 | * Created by laurynassakalauskas on 22/10/2016.
7 | */
8 | public abstract class AbstractPrinter {
9 |
10 | protected HashMapCollector collector;
11 |
12 | public AbstractPrinter(HashMapCollector collector) {
13 |
14 | this.collector = collector;
15 | }
16 |
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/src/main/java/analyzer/printers/ComplexityPrinter.java:
--------------------------------------------------------------------------------
1 | package analyzer.printers;
2 |
3 | import analyzer.ComplexityCounter;
4 | import analyzer.collectors.HashMapCollector;
5 |
6 | import java.util.HashMap;
7 | import java.util.Map;
8 |
9 | /**
10 | * Created by laurynassakalauskas on 15/10/2016.
11 | */
12 | public class ComplexityPrinter extends AbstractPrinter implements Printable{
13 |
14 |
15 | public ComplexityPrinter(HashMapCollector collector) {
16 | super(collector);
17 | }
18 |
19 | /**
20 | * Print out complexity stats to the console
21 | */
22 | @Override
23 | public void print() {
24 | HashMap totalUsage = new HashMap<>();
25 |
26 | int totalWCM = 0;
27 |
28 | for (Map.Entry entry: collector.getComplexity().entrySet()) {
29 |
30 | totalWCM += entry.getValue().weightedMethodCount();
31 | for (Map.Entry e: entry.getValue().getUsage().entrySet()) {
32 | totalUsage.merge(e.getKey(), e.getValue(), Integer::sum);
33 | }
34 |
35 | }
36 |
37 | totalWCM /= collector.getComplexity().entrySet().size();
38 |
39 | System.out.println("|==================================================================|");
40 | System.out.println("|----------------------Complexity Stats----------------------------|");
41 | System.out.println("|==================================================================|");
42 | System.out.format("|%60s|%5s|\n", "Type", "Count");
43 | System.out.println("|==================================================================|");
44 |
45 | for (Map.Entry entry: totalUsage.entrySet()) {
46 |
47 | System.out.format("|%60s|%5s|\n", entry.getKey(), entry.getValue());
48 |
49 | }
50 | System.out.println("|==================================================================|");
51 | System.out.println("|---------------------Weighted method count------------------------|");
52 | System.out.println("|==================================================================|");
53 | System.out.format("|%60s|%5s|\n", "Class", "Count");
54 | System.out.println("|==================================================================|");
55 | for (Map.Entry entry: collector.getComplexity().entrySet()) {
56 |
57 | System.out.format("|%60s|%5s|\n", entry.getKey(), entry.getValue().weightedMethodCount());
58 |
59 |
60 | }
61 | System.out.println("|==================================================================|");
62 | System.out.format("|%60s|%5s|\n", "Average weighted method count: ", totalWCM);
63 | System.out.println("|==================================================================|");
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/main/java/analyzer/printers/MetricPrinter.java:
--------------------------------------------------------------------------------
1 | package analyzer.printers;
2 |
3 | import analyzer.collectors.HashMapCollector;
4 |
5 | import java.util.Map;
6 |
7 | /**
8 | * Created by laurynassakalauskas on 15/10/2016.
9 | */
10 | public class MetricPrinter extends AbstractPrinter implements Printable {
11 |
12 | public MetricPrinter(HashMapCollector collector) {
13 | super(collector);
14 | }
15 |
16 | /**
17 | * Print out metrics to the console
18 | */
19 | @Override
20 | public void print() {
21 | System.out.println("____________________________________________________________________");
22 | System.out.println("|--------------------------METRICS---------------------------------|");
23 | System.out.println("|==================================================================|");
24 | System.out.format("|%60s|%5s|\n", "Type", "Count");
25 | System.out.println("|==================================================================|");
26 |
27 | for (Map.Entry entry: collector.getStats().entrySet()) {
28 |
29 | System.out.format("|%60s|%5s|\n", entry.getKey(), entry.getValue());
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/main/java/analyzer/printers/Printable.java:
--------------------------------------------------------------------------------
1 | package analyzer.printers;
2 |
3 | /**
4 | * Created by laurynassakalauskas on 15/10/2016.
5 | */
6 | public interface Printable {
7 |
8 | void print();
9 | }
10 |
--------------------------------------------------------------------------------
/src/main/java/analyzer/printers/WarningPrinter.java:
--------------------------------------------------------------------------------
1 | package analyzer.printers;
2 |
3 | import analyzer.collectors.HashMapCollector;
4 |
5 | import java.util.List;
6 | import java.util.Map;
7 |
8 | /**
9 | * Created by laurynassakalauskas on 15/10/2016.
10 | */
11 | public class WarningPrinter extends AbstractPrinter implements Printable {
12 |
13 | public WarningPrinter(HashMapCollector collector) {
14 | super(collector);
15 | }
16 |
17 |
18 | /**
19 | * Print out warnings to the console
20 | */
21 | @Override
22 | public void print() {
23 | System.out.println("____________________________________________________________________");
24 | System.out.println("|---------------------------WARNINGS-------------------------------|");
25 | System.out.println("|==================================================================|");
26 |
27 | if (collector.getWarnings().entrySet().size() == 0) {
28 |
29 | System.out.println("|No warnings were found. That's awesome! |");
30 |
31 | } else {
32 | System.out.println("|Oh no, there are " + collector.getWarnings().entrySet().size()+ " warnings you need to fix |");
33 |
34 | }
35 | System.out.println("|==================================================================|");
36 |
37 | for (Map.Entry> entry: collector.getWarnings().entrySet()) {
38 |
39 | System.out.println(entry.getKey() + ": " + entry.getValue().size() + " warnings");
40 |
41 | for (String warning: entry.getValue()) {
42 |
43 | System.out.println("[WARNING] " + warning);
44 | }
45 | System.out.println();
46 | }
47 |
48 |
49 |
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/main/java/analyzer/visitors/BooleanMethodVisitor.java:
--------------------------------------------------------------------------------
1 | package analyzer.visitors;
2 |
3 | import analyzer.AbstractVoidVisitorAdapter;
4 | import analyzer.Config;
5 | import analyzer.collectors.Collector;
6 | import com.github.javaparser.ast.body.MethodDeclaration;
7 |
8 | public class BooleanMethodVisitor extends AbstractVoidVisitorAdapter {
9 |
10 |
11 | /**
12 | * Boolean method should start isSomething(), not getSomething()
13 | *
14 | * getSomething(someVar) is OK, though
15 | *
16 | * @param declaration
17 | * @param collector
18 | */
19 | @Override
20 | public void visit(MethodDeclaration declaration, Collector collector) {
21 |
22 | if (Config.BOOLEAN_STARTS_WITH_IS && declaration.getType().toString().equals("boolean") && declaration.getParameters().size() == 0) {
23 |
24 | if (!declaration.getNameAsString().startsWith("is")) {
25 |
26 | collector.addWarning(className, "Method name \"" + declaration.getType().toString() + "\" should start with is, e.g.\"isSomething()\"");
27 |
28 | }
29 | }
30 |
31 | }
32 |
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/src/main/java/analyzer/visitors/ClassComplexityVisitor.java:
--------------------------------------------------------------------------------
1 | package analyzer.visitors;
2 |
3 | import analyzer.AbstractVoidVisitorAdapter;
4 | import analyzer.ComplexityCounter;
5 | import com.github.javaparser.ast.body.MethodDeclaration;
6 | import com.github.javaparser.ast.stmt.*;
7 |
8 | import java.util.regex.Matcher;
9 | import java.util.regex.Pattern;
10 |
11 | /**
12 | * Created by laurynassakalauskas on 15/10/2016.
13 | */
14 | public class ClassComplexityVisitor extends AbstractVoidVisitorAdapter {
15 |
16 | protected String methodName;
17 |
18 | /**
19 | * Before visiting any method, mark the method name, so we could collect it later.
20 | *
21 | * @param declaration
22 | * @param counter
23 | */
24 | public void visit(MethodDeclaration declaration, ComplexityCounter counter) {
25 |
26 | methodName = declaration.getNameAsString();
27 |
28 | super.visit(declaration, counter);
29 | }
30 |
31 |
32 | /**
33 | * Count foreach statements
34 | *
35 | * @param statement
36 | * @param counter
37 | */
38 | @Override
39 | public void visit(ForeachStmt statement, ComplexityCounter counter) {
40 | counter.add(methodName, "FOREACH");
41 |
42 | super.visit(statement, counter);
43 | }
44 |
45 | /**
46 | * Count for statements
47 | *
48 | * @param statement
49 | * @param counter
50 | */
51 | @Override
52 | public void visit(ForStmt statement, ComplexityCounter counter) {
53 | counter.add(methodName, "FOR");
54 |
55 | super.visit(statement, counter);
56 | }
57 |
58 | /**
59 | * Count if/else statements
60 | *
61 | * @param statement
62 | * @param counter
63 | */
64 | @Override
65 | public void visit(IfStmt statement, ComplexityCounter counter) {
66 | counter.add(methodName, "IF");
67 |
68 | if(statement.getElseStmt() != null) {
69 | counter.add(methodName, "ELSE");
70 | }
71 |
72 |
73 | String condition = statement.getCondition().toString();
74 |
75 | regexCheck(condition, Pattern.compile("/(\\s|\\w|\\d)&(\\s|\\w|\\d)/xg"), "BITWISE_AND_OPERATOR", counter);
76 | regexCheck(condition, Pattern.compile("/(\\s|\\w|\\d)\\|(\\s|\\w|\\d)/xg"), "BITWISE_OR_OPERATOR", counter);
77 | regexCheck(condition, Pattern.compile("/(\\s|\\w|\\d)&&(\\s|\\w|\\d)/xg"), "AND_OPERATOR", counter);
78 | regexCheck(condition, Pattern.compile("/(\\s|\\w|\\d)\\|\\|(\\s|\\w|\\d)/xg"), "OR_OPERATOR", counter);
79 |
80 |
81 | super.visit(statement, counter);
82 | }
83 |
84 |
85 | private void regexCheck(String haystack, Pattern pattern, String type, ComplexityCounter counter) {
86 | Matcher matcher = pattern.matcher(haystack);
87 |
88 | while (matcher.find()) {
89 | counter.add(methodName, type);
90 | }
91 | }
92 |
93 | /**
94 | * Count switch statements
95 | *
96 | * @param statement
97 | * @param counter
98 | */
99 | @Override
100 | public void visit(SwitchEntryStmt statement, ComplexityCounter counter) {
101 |
102 |
103 | for (Statement st :statement.getStatements()) {
104 |
105 | counter.add(methodName, "SWITCH");
106 |
107 | }
108 |
109 | super.visit(statement, counter);
110 | }
111 |
112 | /**
113 | * Count throw statements
114 | *
115 | * @param statement
116 | * @param counter
117 | */
118 | @Override
119 | public void visit(ThrowStmt statement, ComplexityCounter counter) {
120 |
121 | counter.add(methodName, "THROW");
122 |
123 | super.visit(statement, counter);
124 | }
125 |
126 | /**
127 | * Count try statements
128 | *
129 | * @param statement
130 | * @param counter
131 | */
132 | @Override
133 | public void visit(TryStmt statement, ComplexityCounter counter) {
134 |
135 | counter.add(methodName, "TRY");
136 |
137 | super.visit(statement, counter);
138 | }
139 |
140 | /**
141 | * Count catch statements
142 | *
143 | * @param statement
144 | * @param counter
145 | */
146 | @Override
147 | public void visit(CatchClause statement, ComplexityCounter counter) {
148 |
149 | counter.add(methodName, "CATCH");
150 |
151 | super.visit(statement, counter);
152 | }
153 |
154 | /**
155 | * Count while statements
156 | *
157 | * @param statement
158 | * @param counter
159 | */
160 | @Override
161 | public void visit(WhileStmt statement, ComplexityCounter counter) {
162 |
163 | counter.add(methodName, "WHILE");
164 |
165 | super.visit(statement, counter);
166 | }
167 | }
168 |
--------------------------------------------------------------------------------
/src/main/java/analyzer/visitors/ClassLineCounterVisitor.java:
--------------------------------------------------------------------------------
1 | package analyzer.visitors;
2 |
3 | import analyzer.AbstractVoidVisitorAdapter;
4 | import analyzer.Config;
5 | import analyzer.collectors.Collector;
6 | import com.github.javaparser.ast.CompilationUnit;
7 |
8 | /**
9 | * Created by laurynassakalauskas on 15/10/2016.
10 | */
11 | public class ClassLineCounterVisitor extends AbstractVoidVisitorAdapter {
12 |
13 | private static final int MAX_CLASS_LENGTH = Config.MAX_CLASS_LENGTH;
14 |
15 | /**
16 | * Calculate number of lines in the class
17 | *
18 | */
19 | public void visit(CompilationUnit cu, Collector collector) {
20 |
21 | super.visit(cu, collector);
22 |
23 | int count = cu.toString().split("\n").length;
24 |
25 | if (count > MAX_CLASS_LENGTH) {
26 | collector.addWarning(className, "Class has more than " + MAX_CLASS_LENGTH + " lines");
27 | }
28 |
29 | collector.incrementMetric("Code Lines", count);
30 | }
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/src/main/java/analyzer/visitors/MetricVisitor.java:
--------------------------------------------------------------------------------
1 | package analyzer.visitors;
2 |
3 | import analyzer.AbstractVoidVisitorAdapter;
4 | import analyzer.collectors.Collector;
5 | import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
6 | import com.github.javaparser.ast.body.MethodDeclaration;
7 | import com.github.javaparser.ast.expr.AnnotationExpr;
8 | import com.github.javaparser.ast.expr.VariableDeclarationExpr;
9 |
10 | public class MetricVisitor extends AbstractVoidVisitorAdapter {
11 |
12 |
13 | /**
14 | * Increment classes metric
15 | *
16 | * @param declaration
17 | * @param collector
18 | */
19 | @Override
20 | public void visit(ClassOrInterfaceDeclaration declaration, Collector collector) {
21 |
22 | if (declaration.isInterface()) {
23 | collector.incrementMetric("Interfaces");
24 | } else {
25 | collector.incrementMetric("Classes");
26 |
27 |
28 | if (declaration.isFinal()) {
29 | collector.incrementMetric("Final Classes");
30 | }
31 | if (declaration.isAbstract()) {
32 | collector.incrementMetric("Abstract Classes");
33 | }
34 |
35 | }
36 |
37 | if (className.endsWith("Test")) {
38 | collector.incrementMetric("Test Classes");
39 |
40 | }
41 |
42 |
43 |
44 | super.visit(declaration, collector);
45 | }
46 |
47 |
48 | /**
49 | * Increment method and parameter count
50 | *
51 | * @param declaration
52 | * @param collector
53 | */
54 | @Override
55 | public void visit(MethodDeclaration declaration, Collector collector) {
56 |
57 | for (AnnotationExpr annotation: declaration.getAnnotations()) {
58 | if (annotation.getName().equals("Override"))
59 | collector.incrementMetric("Overridden Methods");
60 |
61 | }
62 |
63 | if (declaration.toString().startsWith("public")) {
64 | collector.incrementMetric("Public Methods");
65 | }
66 |
67 | if (declaration.toString().startsWith("private")) {
68 | collector.incrementMetric("Private Methods");
69 | }
70 |
71 | if (declaration.toString().startsWith("protected")) {
72 | collector.incrementMetric("Protected Methods");
73 | }
74 |
75 | if (!declaration.hasComment()) {
76 | collector.incrementMetric("Methods without Javadoc");
77 | }
78 |
79 | collector.incrementMetric("Methods");
80 |
81 | collector.incrementMetric("Parameters", declaration.getParameters().size());
82 |
83 | super.visit(declaration, collector);
84 |
85 | }
86 |
87 |
88 | /**
89 | * Increment field count
90 | *
91 | * @param declaration
92 | * @param collector
93 | */
94 | @Override
95 | public void visit(VariableDeclarationExpr declaration, Collector collector) {
96 |
97 | collector.incrementMetric("Fields", declaration.getVariables().size());
98 |
99 | }
100 |
101 |
102 | }
103 |
--------------------------------------------------------------------------------
/src/main/java/analyzer/visitors/TooLongVisitor.java:
--------------------------------------------------------------------------------
1 | package analyzer.visitors;
2 |
3 | import analyzer.AbstractVoidVisitorAdapter;
4 | import analyzer.Config;
5 | import analyzer.collectors.Collector;
6 | import com.github.javaparser.ast.body.MethodDeclaration;
7 | import com.github.javaparser.ast.body.Parameter;
8 | import com.github.javaparser.ast.body.VariableDeclarator;
9 | import com.github.javaparser.ast.expr.MethodCallExpr;
10 | import com.github.javaparser.ast.expr.VariableDeclarationExpr;
11 |
12 | public class TooLongVisitor extends AbstractVoidVisitorAdapter {
13 |
14 |
15 | public static final int MAX_BODY_LENGTH = Config.MAX_BODY_LENGTH;
16 |
17 | private static final int MAX_METHOD_NAME_LENGTH = Config.MAX_METHOD_NAME_LENGTH;
18 |
19 | private static final int MAX_PARAM_COUNT = Config.MAX_PARAM_COUNT;
20 |
21 | private static final int MAX_VARIABLE_LENGTH = Config.MAX_VARIABLE_LENGTH;
22 |
23 | private static final int MAX_VARIABLE_COUNT = Config.MAX_VARIABLE_COUNT;
24 |
25 | private static final int MAX_METHODS_COUNT = Config.MAX_METHODS_COUNT;
26 |
27 |
28 |
29 | /**
30 | * Check for method count
31 | *
32 | * @param declaration
33 | * @param collector
34 | */
35 | @Override
36 | public void visit(MethodCallExpr declaration, Collector collector) {
37 |
38 | if (declaration.getArguments().size() > MAX_METHODS_COUNT) {
39 |
40 | collector.addWarning(className, "Class has more than " + MAX_METHODS_COUNT + " methods");
41 |
42 | }
43 |
44 | super.visit(declaration, collector);
45 | }
46 |
47 | /**
48 | * Check for too long method names, body, arguments
49 | *
50 | * @param declaration
51 | * @param collector
52 | */
53 | @Override
54 | public void visit(MethodDeclaration declaration, Collector collector) {
55 | int methodBodyLength = declaration.getRange().map(range -> range.begin.line - range.end.line).orElse(0);
56 |
57 | int methodNameLength = declaration.getNameAsString().length();
58 |
59 | int parametersCount = declaration.getParameters().size();
60 |
61 |
62 | if (methodBodyLength > MAX_BODY_LENGTH) {
63 | collector.addWarning(className, "Method \""+ declaration.getName() +"\" body has more than " + MAX_BODY_LENGTH + " lines");
64 | }
65 |
66 | if (methodNameLength > MAX_METHOD_NAME_LENGTH) {
67 | collector.addWarning(className, "Method \"" + declaration.getName() + "\" name is too long, it has more than " + MAX_METHOD_NAME_LENGTH + " characters");
68 | }
69 |
70 | if (parametersCount > MAX_PARAM_COUNT) {
71 | collector.addWarning(className, "Method \"" + declaration.getName() + "\" has more than " + MAX_METHOD_NAME_LENGTH + " parameters");
72 | }
73 |
74 | for (Parameter param: declaration.getParameters()) {
75 |
76 | if (param.getNameAsString().length() > MAX_VARIABLE_LENGTH) {
77 |
78 | collector.addWarning(className, "Method \"" + declaration.getName() + "\" variable \"" + param.getName() +"\" is way too long!");
79 |
80 | }
81 |
82 | }
83 |
84 | }
85 |
86 |
87 | /**
88 | * Check for too many variables & for too long ones
89 | *
90 | * @param declaration
91 | * @param collector
92 | */
93 | @Override
94 | public void visit(VariableDeclarationExpr declaration, Collector collector) {
95 |
96 | if (declaration.getVariables().size() > MAX_VARIABLE_COUNT) {
97 | collector.addWarning(className, "Class has more than " + MAX_VARIABLE_COUNT + " variables");
98 | }
99 |
100 | for (VariableDeclarator variable: declaration.getVariables()) {
101 |
102 | if (variable.getNameAsString().length() > MAX_VARIABLE_LENGTH) {
103 |
104 | collector.addWarning(className, "Field variable \"" + variable.getNameAsString() +"\" is way too long!");
105 |
106 | }
107 |
108 | }
109 |
110 |
111 | }
112 |
113 | }
114 |
--------------------------------------------------------------------------------
/src/main/java/analyzer/visitors/TooSmallVisitor.java:
--------------------------------------------------------------------------------
1 | package analyzer.visitors;
2 |
3 | import analyzer.AbstractVoidVisitorAdapter;
4 | import analyzer.Config;
5 | import analyzer.collectors.Collector;
6 | import com.github.javaparser.ast.body.MethodDeclaration;
7 | import com.github.javaparser.ast.body.VariableDeclarator;
8 | import com.github.javaparser.ast.expr.VariableDeclarationExpr;
9 |
10 | public class TooSmallVisitor extends AbstractVoidVisitorAdapter {
11 |
12 |
13 |
14 | private static final int MIN_VARIABLE_LENGTH = Config.MIN_VARIABLE_LENGTH;
15 | private static final int MIN_METHOD_NAME = Config.MIN_METHOD_NAME;
16 |
17 |
18 | /**
19 | * Check for too short method names
20 | *
21 | * @param declaration
22 | * @param collector
23 | */
24 | @Override
25 | public void visit(MethodDeclaration declaration, Collector collector) {
26 |
27 |
28 | if (declaration.getName().toString().length() < MIN_METHOD_NAME)
29 |
30 | super.visit(declaration, collector);
31 |
32 | }
33 |
34 |
35 | /**
36 | * Check for too short variable names
37 | *
38 | * @param declaration
39 | * @param collector
40 | */
41 | @Override
42 | public void visit(VariableDeclarationExpr declaration, Collector collector) {
43 |
44 | for (VariableDeclarator variable: declaration.getVariables()) {
45 |
46 | if (variable.getNameAsString().length() < MIN_VARIABLE_LENGTH) {
47 | collector.addWarning(className, "Variable \"" + variable.getNameAsString() + "\" length is too small");
48 | }
49 |
50 | }
51 |
52 | }
53 |
54 |
55 | }
56 |
--------------------------------------------------------------------------------
/src/main/java/analyzer/visitors/VariableNamingConventionVisitor.java:
--------------------------------------------------------------------------------
1 | package analyzer.visitors;
2 |
3 | import analyzer.AbstractVoidVisitorAdapter;
4 | import analyzer.Config;
5 | import analyzer.collectors.Collector;
6 | import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
7 | import com.github.javaparser.ast.body.MethodDeclaration;
8 | import com.github.javaparser.ast.body.Parameter;
9 | import com.github.javaparser.ast.body.VariableDeclarator;
10 | import com.github.javaparser.ast.expr.VariableDeclarationExpr;
11 |
12 | public class VariableNamingConventionVisitor extends AbstractVoidVisitorAdapter {
13 |
14 |
15 | public static final String CLASS_NAME_REGEX = "([A-Z][a-z0-9]+)+";
16 |
17 | public static final String OK_REGEX = "/(([A-Z]+_[A-Z]*)+)/gx";
18 |
19 | protected String methodName;
20 |
21 | /**
22 | * Check if class is in camel case
23 | *
24 | * @param declaration
25 | * @param collector
26 | */
27 | @Override
28 | public void visit(ClassOrInterfaceDeclaration declaration, Collector collector) {
29 |
30 |
31 | if (Config.CAMEL_CASE_CLASS_NAME && declaration.getNameAsString().length() > 2 && !declaration.getNameAsString().matches(CLASS_NAME_REGEX)) {
32 |
33 | collector.addWarning(className, "Class name must be in CamelCase");
34 |
35 | }
36 |
37 | super.visit(declaration, collector);
38 | }
39 |
40 |
41 | /**
42 | * Check if method & method variables is in camelCase, not in underscore_case,
43 | * since in Java we use camelCase naming convention
44 | *
45 | * @param declaration
46 | * @param collector
47 | */
48 | @Override
49 | public void visit(MethodDeclaration declaration, Collector collector) {
50 |
51 | methodName = declaration.getNameAsString();
52 |
53 | if (Config.METHOD_IN_CAMEL_CASE) {
54 | if (methodName.contains("_")) {
55 | collector.addWarning(className, "Method \"" + methodName + "\" should be in 'camelCase', not in 'underscore_case'");
56 |
57 | } else if (!methodName.matches("^[a-z][a-zA-Z0-9]*$")) {
58 | collector.addWarning(className, "Method \"" + methodName + "\" should be in 'camelCase', not in 'underscore_case'");
59 | }
60 | }
61 |
62 | if (Config.PARAM_IN_CAMEL_CASE) {
63 | for (Parameter param: declaration.getParameters()) {
64 |
65 | if (param.getNameAsString().contains("_")) {
66 |
67 | collector.addWarning(className, "Method \"" + methodName + "\" variable \"" + param.getName() +"\" should be in 'camelCase', not in 'underscore_case'");
68 |
69 | }
70 |
71 | }
72 | }
73 |
74 |
75 | super.visit(declaration, collector);
76 |
77 | }
78 |
79 |
80 | /**
81 | * Check if class variables is in camelCase, not in underscore_case,
82 | * since in Java we use camelCase naming convention
83 | *
84 | * @param declaration
85 | * @param collector
86 | */
87 | @Override
88 | public void visit(VariableDeclarationExpr declaration, Collector collector) {
89 |
90 | if (Config.PARAM_IN_CAMEL_CASE) {
91 | for (VariableDeclarator variable: declaration.getVariables()) {
92 |
93 | String name = variable.getNameAsString();
94 |
95 | if (name.matches(OK_REGEX)) // e.x. SOME_VARIABLE is OKAY
96 | continue;
97 |
98 |
99 | if (name.contains("_")) {
100 |
101 | collector.addWarning(className, "Method \"" + methodName + "\" variable \"" + name +"\" should be in 'camelCase', not in 'underscore_case'");
102 |
103 | }
104 |
105 | }
106 | }
107 |
108 | }
109 |
110 |
111 | }
112 |
--------------------------------------------------------------------------------
/src/main/resources/Test.java:
--------------------------------------------------------------------------------
1 | import com.github.javaparser.JavaParser;
2 | import com.github.javaparser.ast.CompilationUnit;
3 | import com.github.javaparser.ast.body.MethodDeclaration;
4 | import com.github.javaparser.ast.visitor.VoidVisitorAdapter;
5 |
6 | import java.io.FileInputStream;
7 |
8 | /**
9 | * Created by laurynassakalauskas on 07/10/2016.
10 | */
11 | public class Test {
12 |
13 | public static void main(String[] args) throws Exception {
14 | // creates an input stream for the file to be parsed
15 | FileInputStream in = new FileInputStream("Test.java");
16 |
17 | CompilationUnit cu;
18 | try {
19 | // parse the file
20 | cu = JavaParser.parse(in);
21 | } finally {
22 | in.close();
23 | }
24 |
25 | // visit and print the methods names
26 | new MethodVisitor().visit(cu, null);
27 | }
28 |
29 | /**
30 | * Simple visitor implementation for visiting MethodDeclaration nodes.
31 | */
32 | private static class MethodVisitor extends VoidVisitorAdapter {
33 |
34 | @Override
35 | public void visit(MethodDeclaration n, Object arg) {
36 | // here you can access the attributes of the method.
37 | // this method will be called for all methods in this
38 | // CompilationUnit, including inner class methods
39 | System.out.println(n.getName());
40 | super.visit(n, arg);
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------