1 package net.sf.jlayercheck.util;
2
3 import java.io.File;
4 import java.io.FileInputStream;
5 import java.io.FileNotFoundException;
6 import java.io.IOException;
7 import java.net.URL;
8 import java.util.ArrayList;
9 import java.util.HashSet;
10 import java.util.List;
11 import java.util.Map;
12 import java.util.Set;
13 import java.util.TreeMap;
14 import java.util.TreeSet;
15 import java.util.logging.Logger;
16
17 import javax.swing.tree.TreeNode;
18
19 import net.sf.jlayercheck.util.exceptions.OrphanedSearchException;
20 import net.sf.jlayercheck.util.exceptions.OverlappingModulesDefinitionException;
21 import net.sf.jlayercheck.util.model.ClassDependency;
22 import net.sf.jlayercheck.util.model.ClassSource;
23 import net.sf.jlayercheck.util.modeltree.ClassNode;
24 import net.sf.jlayercheck.util.modeltree.DefaultModelTree;
25 import net.sf.jlayercheck.util.modeltree.DefaultPackageNode;
26 import net.sf.jlayercheck.util.modeltree.DependenciesTreeModel;
27 import net.sf.jlayercheck.util.modeltree.DependentClassNode;
28 import net.sf.jlayercheck.util.modeltree.DependentModelTree;
29 import net.sf.jlayercheck.util.modeltree.DependentModuleNode;
30 import net.sf.jlayercheck.util.modeltree.DependentPackageNode;
31 import net.sf.jlayercheck.util.modeltree.ModelTree;
32 import net.sf.jlayercheck.util.modeltree.ModuleNode;
33 import net.sf.jlayercheck.util.modeltree.PackageNode;
34 import net.sf.jlayercheck.util.modeltree.UnallowedOrAllowedDependency;
35
36 import org.objectweb.asm.ClassReader;
37
38
39
40
41
42
43
44
45
46
47
48 public class XMLConfiguration {
49 protected static Logger logger = Logger.getLogger("JLayerCheck");
50
51
52
53
54 protected Map<String, Set<String>> modulePackages = new TreeMap<String, Set<String>>();
55
56
57
58
59 protected Map<String, Set<String>> moduleDependencies = new TreeMap<String, Set<String>>();
60
61
62
63
64 protected List<ClassSource> classSources = new ArrayList<ClassSource>();
65
66
67
68
69 protected Set<String> excludeList = new TreeSet<String>();
70
71
72
73
74
75 protected Set<String> entryClasses = new TreeSet<String>();
76
77 public XMLConfiguration() {
78 }
79
80 protected void addPackageToExcludeList(String packageName) {
81 excludeList.add(packageName);
82 }
83
84 protected void addDependencyToModule(String moduleName, String dependencyName) {
85 logger.finer("Add dependency "+dependencyName+" to module "+moduleName);
86
87 Set<String> deps = moduleDependencies.get(moduleName);
88
89 if (deps == null) {
90 deps = new TreeSet<String>();
91 moduleDependencies.put(moduleName, deps);
92 }
93
94 deps.add(dependencyName);
95 }
96
97 protected void addPackageToModule(String moduleName, String packageName) {
98 logger.finer("Add package "+packageName+" to module "+moduleName);
99 packageName = packageName.replaceAll("\\.", "/");
100
101 Set<String> packs = modulePackages.get(moduleName);
102
103 if (packs == null) {
104 packs = new TreeSet<String>();
105 modulePackages.put(moduleName, packs);
106 }
107
108 packs.add(packageName);
109 }
110
111
112
113
114
115
116
117
118
119
120 public Map<String, Set<String>> getModuleDependencies() {
121 return moduleDependencies;
122 }
123
124
125
126
127
128
129
130
131 public Map<String, Set<String>> getModulePackages() {
132 return modulePackages;
133 }
134
135
136
137
138
139
140
141
142
143
144
145 public String getMatchingModule(String classname) throws OverlappingModulesDefinitionException {
146 String result = null;
147
148 for(String modulename : getModulePackages().keySet()) {
149 for(String packagename : getModulePackages().get(modulename)) {
150 packagename = convertToRegularExpression(packagename);
151 packagename = packagename + "/[^/]*";
152
153 if (classname.matches(packagename)) {
154 if (result == null) {
155 result = modulename;
156 } else {
157 throw new OverlappingModulesDefinitionException("Class "+classname+" is matched by more than one module definition.");
158 }
159 }
160 }
161 }
162
163 return result;
164 }
165
166
167
168
169
170
171
172 public Map<String, String> getPackageModules() {
173 Map<String, String> result = new TreeMap<String, String>();
174
175 for(String modulename : getModulePackages().keySet()) {
176 for(String packagename : getModulePackages().get(modulename)) {
177 result.put(packagename, modulename);
178 }
179 }
180
181 return result;
182 }
183
184
185
186
187
188
189 public List<ClassSource> getClassSources() {
190 return classSources;
191 }
192
193
194
195
196
197
198
199 public Map<String, URL> getAllClassSources() {
200 Map<String, URL> result = new TreeMap<String, URL>();
201 for(ClassSource source : getClassSources()) {
202 result.putAll(source.getSourceFiles());
203 }
204 return result;
205 }
206
207
208
209
210
211
212
213 public Set<String> getExcludeList() {
214 return excludeList;
215 }
216
217
218
219
220
221
222
223 public boolean isExcluded(String classname) {
224 for(String exclude : getExcludeList()) {
225 exclude = convertToRegularExpression(exclude);
226
227 if (classname.matches(exclude)) {
228 return true;
229 }
230 }
231
232 return false;
233 }
234
235
236
237
238
239
240
241
242 protected String convertToRegularExpression(String wildcardstring) {
243 wildcardstring = wildcardstring.replaceAll("\\.", "/");
244
245 wildcardstring = wildcardstring.replaceAll("\\*", ".*");
246 return wildcardstring;
247 }
248
249
250
251
252
253
254
255
256
257
258 public Set<String> getEntryClasses() {
259 return entryClasses;
260 }
261
262
263
264
265
266
267
268
269 public Set<String> getOrphanedClasses(Map<String, Map<String, Set<Integer>>> dependencies) throws OrphanedSearchException {
270 Set<String> visitedClasses = new HashSet<String>();
271
272
273 visitedClasses.addAll(entryClasses);
274
275
276 Set<String> unvisited = null;
277 Map<String, URL> allClassSources = getAllClassSources();
278 do {
279 unvisited = getUnvisitedDependendClasses(visitedClasses, dependencies, allClassSources);
280 visitedClasses.addAll(unvisited);
281 } while(unvisited.size()>0);
282
283
284 Set<String> orphanedClasses = new TreeSet<String>();
285
286 for(String classname : dependencies.keySet()) {
287 if (!visitedClasses.contains(classname)) {
288 orphanedClasses.add(classname);
289 }
290 }
291
292 return orphanedClasses;
293 }
294
295
296
297
298
299
300
301
302
303
304
305 protected Set<String> getUnvisitedDependendClasses(Set<String> visitedClasses, Map<String, Map<String, Set<Integer>>> dependencies, Map<String, URL> allClassSources) throws OrphanedSearchException {
306 Set<String> result = new HashSet<String>();
307
308 for(String visitedClass : visitedClasses) {
309 if (dependencies.get(visitedClass) != null) {
310 for(String classname : dependencies.get(visitedClass).keySet()) {
311 if (!visitedClasses.contains(classname)) {
312 result.add(classname);
313 }
314 }
315 } else {
316 if (allClassSources.containsKey(visitedClass)) {
317
318 throw new OrphanedSearchException("Class file for "+visitedClass+" is not available! (Source file is "+allClassSources.get(visitedClass).toExternalForm());
319 } else {
320 logger.fine("Dependencies for "+visitedClass+" not found!");
321 }
322 }
323 }
324
325 return result;
326 }
327
328
329
330
331
332
333
334
335
336 public Map<String, Map<String, ClassDependency>> getUnallowedDependencies(Map<String, Map<String, Set<Integer>>> dependencies) throws OverlappingModulesDefinitionException {
337 Map<String, Map<String, ClassDependency>> unallowedDependencies = new TreeMap<String, Map<String, ClassDependency>>();
338
339 for(String classname : dependencies.keySet()) {
340 for(String dependency : dependencies.get(classname).keySet()) {
341
342 if (isUnallowedDependency(classname, dependency)) {
343 Map<String, ClassDependency> depList = unallowedDependencies.get(classname);
344 if (depList == null) {
345 depList = new TreeMap<String, ClassDependency>();
346 unallowedDependencies.put(classname, depList);
347 }
348 ClassDependency cd = depList.get(dependency);
349 if (cd == null) {
350 cd = new ClassDependency(dependency);
351 depList.put(dependency, cd);
352 }
353 for(Integer lineNumber : dependencies.get(classname).get(dependency)) {
354 cd.addLineNumber(lineNumber);
355 }
356 }
357 }
358 }
359
360 return unallowedDependencies;
361 }
362
363
364
365
366
367
368
369
370
371
372 public boolean isUnallowedDependency(String fromClass, String toClass) throws OverlappingModulesDefinitionException {
373 String classmodule = getMatchingModule(fromClass);
374 String dependencymodule = getMatchingModule(toClass);
375
376 if (classmodule == null) {
377
378 return false;
379 } else {
380 if (!classmodule.equals(dependencymodule)) {
381 if (!(isExcluded(fromClass) || isExcluded(toClass))) {
382 if (isUnallowedModuleDependency(classmodule, dependencymodule)) {
383 return true;
384 }
385 }
386 }
387 }
388
389 return false;
390 }
391
392
393
394
395
396
397
398
399
400 public boolean isUnallowedModuleDependency(String fromModule, String toModule) {
401 if (fromModule.equals(toModule)) return false;
402
403 if (getModuleDependencies().get(fromModule) == null || toModule == null ||
404 !getModuleDependencies().get(fromModule).contains(toModule)) {
405 return true;
406 }
407
408 return false;
409 }
410
411
412
413
414
415
416
417
418
419 public Set<String> getUnspecifiedPackages(Map<String, Map<String, Set<Integer>>> dependencies) throws OverlappingModulesDefinitionException {
420 Set<String> unspecifiedPackages = new TreeSet<String>();
421
422 for(String classname : dependencies.keySet()) {
423 String classPackageName = StringUtils.getPackageName(classname);
424
425
426 String classmodule = getMatchingModule(classPackageName+"/Dummy");
427
428 if (classmodule == null) {
429 unspecifiedPackages.add(classPackageName);
430 }
431 }
432
433 return unspecifiedPackages;
434 }
435
436
437
438
439
440
441
442
443
444
445 public ModelTree getModelTree(DependencyVisitor dv) throws OverlappingModulesDefinitionException {
446 DefaultModelTree result = new DefaultModelTree();
447
448
449 for(String packagename : dv.getPackages().keySet()) {
450
451
452 String packagemodule = getMatchingModule(packagename+"/Dummy");
453
454 boolean packageUnassigned = false;
455 if (packagemodule == null) {
456 packagemodule = "unassigned";
457 packageUnassigned = true;
458 }
459
460 ModuleNode module = result.getModule(packagemodule);
461
462 if (module == null) {
463 module = new DependentModuleNode(packagemodule, packageUnassigned);
464 result.add(module);
465 }
466
467
468 DefaultPackageNode packagenode = new DependentPackageNode(packagename);
469 module.add(packagenode);
470
471 for(String classname : dv.getPackages().get(packagename)) {
472
473 packagenode.add(new DependentClassNode(new ClassDependency(classname)));
474 }
475 }
476
477
478 for(String classname : dv.getDependencies().keySet()) {
479 for(String dep : dv.getDependencies().get(classname).keySet()) {
480
481 ClassDependency cd = new ClassDependency(dep);
482
483 for(Integer line : dv.getDependencies().get(classname).get(dep)) {
484 cd.addLineNumber(line);
485 }
486
487 cd.setUnallowedDependency(isUnallowedDependency(classname, dep));
488
489
490 ClassNode cn = result.getClassNode(classname);
491 if (cn != null) {
492 cn.addClassDependency(cd);
493 } else {
494 System.out.println("Class "+classname+" not found (to "+dep+")!");
495 }
496 }
497 }
498
499 cumulateDependencyViolations(result);
500
501 return result;
502 }
503
504
505
506
507
508
509
510
511
512
513
514
515 public void updateModelTree(ModelTree mt, File classFile) throws OverlappingModulesDefinitionException, FileNotFoundException, IOException {
516 DependencyVisitor dv = new DependencyVisitor();
517
518
519 new ClassReader(new FileInputStream(classFile)).accept(dv, 0);
520
521
522 for(Set<String> classes : dv.getPackages().values()) {
523 for(String clazz : classes) {
524 ClassNode cn = mt.getClassNode(clazz);
525 if (cn != null) {
526 cn.removeFromParent();
527 }
528 }
529 }
530
531 ModelTree additionalModelTree = getModelTree(dv);
532 mt.merge(additionalModelTree);
533 }
534
535
536
537
538
539
540
541
542 public boolean isUnallowedDependency(ModuleNode sourceModule, ModuleNode destModule) {
543 return isUnallowedModuleDependency(sourceModule.getModuleName(), destModule.getModuleName());
544 }
545
546
547
548
549
550
551 public void cumulateDependencyViolations(ModelTree mt) {
552
553
554 for(ModuleNode mn : mt.getModules()) {
555 boolean mnUnallowed = false;
556 for(PackageNode pn : mn.getPackages()) {
557 boolean pnUnallowed = false;
558 for (ClassNode cn : pn.getClasses()) {
559 if (cn instanceof DependentClassNode) {
560 DependentClassNode dcn = (DependentClassNode) cn;
561
562 DependenciesTreeModel dtm = new DependenciesTreeModel();
563 dtm.setRoot(createModel(dcn, mt, dtm));
564 dcn.getClassDependency().setUnallowedDependency(((UnallowedOrAllowedDependency) dtm.getRoot()).isUnallowedDependency());
565 dcn.setDependenciesTreeModel(dtm);
566
567 if (dcn.getClassDependency().isUnallowedDependency()) {
568 pnUnallowed = true;
569 mnUnallowed = true;
570 }
571
572 dtm = new DependenciesTreeModel();
573 dtm.setRoot(createIncomingModel(dcn, mt, dtm));
574 dcn.setIncomingDependenciesTreeModel(dtm);
575 }
576 }
577
578 if (pn instanceof DependentPackageNode) {
579 ((DependentPackageNode) pn).setUnallowedDependency(pnUnallowed);
580 }
581 }
582
583 if (mn instanceof DependentModuleNode) {
584 ((DependentModuleNode) mn).setUnallowedDependency(mnUnallowed);
585 }
586 }
587 }
588
589
590
591
592
593
594
595
596
597 public TreeNode createModel(ClassNode node, ModelTree treemodel, DependenciesTreeModel modelToUpdate) {
598 DependentModelTree depTree = new DependentModelTree();
599
600
601 for(ClassDependency cd : node.getClassDependencies()) {
602
603 ClassNode depClass = treemodel.getClassNode(cd.getDependency());
604
605 if (!cd.getDependency().equals(node.getName())) {
606 modelToUpdate.addClassNodeToDependentModelTree(depTree, cd, depClass);
607 }
608 }
609
610
611 boolean treeUnallowed = false;
612 for(ModuleNode mn : depTree.getModules()) {
613 boolean mnUnallowed = false;
614 for(PackageNode pn : mn.getPackages()) {
615 boolean pnUnallowed = false;
616 for (ClassNode cn : pn.getClasses()) {
617 if (cn instanceof DependentClassNode) {
618 DependentClassNode dcn = (DependentClassNode) cn;
619
620
621 boolean unallowedDependency = false;
622 if (!pn.isUnassignedPackage()) {
623 ModuleNode dModule = mn;
624 ClassNode sourceClass = node;
625
626 if (sourceClass !=null) {
627 PackageNode sourcePackage = (PackageNode) sourceClass.getParent();
628
629 if (!sourcePackage.isUnassignedPackage()) {
630 ModuleNode sourceModule = (ModuleNode) sourcePackage.getParent();
631
632 if (!dModule.isUnassignedModule() && !sourceModule.isUnassignedModule()) {
633 unallowedDependency = isUnallowedDependency(sourceModule, dModule);
634 }
635 }
636 } else {
637 unallowedDependency = true;
638 }
639 }
640 dcn.getClassDependency().setUnallowedDependency(unallowedDependency);
641
642 if (dcn.getClassDependency().isUnallowedDependency()) {
643 pnUnallowed = true;
644 mnUnallowed = true;
645 treeUnallowed = true;
646 }
647 }
648 }
649
650 if (pn instanceof DependentPackageNode) {
651 ((DependentPackageNode) pn).setUnallowedDependency(pnUnallowed);
652 }
653 }
654
655 if (mn instanceof DependentModuleNode) {
656 ((DependentModuleNode) mn).setUnallowedDependency(mnUnallowed);
657 }
658 }
659 depTree.setUnallowedDependency(treeUnallowed);
660
661
662 depTree.sortNodes();
663
664 return depTree;
665 }
666
667
668
669
670
671
672
673
674
675 public TreeNode createIncomingModel(ClassNode node, ModelTree treemodel, DependenciesTreeModel modelToUpdate) {
676 DependentModelTree depTree = new DependentModelTree();
677
678
679 for(ModuleNode mn : treemodel.getModules()) {
680 for(PackageNode pn : mn.getPackages()) {
681 for (ClassNode cn : pn.getClasses()) {
682 for(ClassDependency cd : cn.getClassDependencies()) {
683 if (cd.getDependency().equals(node.getName())) {
684
685
686
687 if (!cn.getName().equals(node.getName())) {
688 ClassNode depClass = treemodel.getClassNode(cn.getName());
689 ClassDependency ncd = new ClassDependency(cn.getName());
690 modelToUpdate.addClassNodeToDependentModelTree(depTree, ncd, depClass);
691 }
692 }
693 }
694 }
695 }
696
697 }
698
699
700 boolean treeUnallowed = false;
701 for(ModuleNode mn : depTree.getModules()) {
702 boolean mnUnallowed = false;
703 for(PackageNode pn : mn.getPackages()) {
704 boolean pnUnallowed = false;
705 for (ClassNode cn : pn.getClasses()) {
706 if (cn instanceof DependentClassNode) {
707 DependentClassNode dcn = (DependentClassNode) cn;
708
709
710 boolean unallowedDependency = false;
711 if (!pn.isUnassignedPackage()) {
712 ModuleNode dModule = mn;
713 ClassNode sourceClass = node;
714
715 if (sourceClass !=null) {
716 PackageNode sourcePackage = (PackageNode) sourceClass.getParent();
717
718 if (!sourcePackage.isUnassignedPackage()) {
719 ModuleNode sourceModule = (ModuleNode) sourcePackage.getParent();
720
721 if (!dModule.isUnassignedModule() && !sourceModule.isUnassignedModule()) {
722 unallowedDependency = isUnallowedDependency(dModule, sourceModule);
723 }
724 }
725 } else {
726 unallowedDependency = true;
727 }
728 }
729 dcn.getClassDependency().setUnallowedDependency(unallowedDependency);
730
731 if (dcn.getClassDependency().isUnallowedDependency()) {
732 pnUnallowed = true;
733 mnUnallowed = true;
734 treeUnallowed = true;
735 }
736 }
737 }
738
739 if (pn instanceof DependentPackageNode) {
740 ((DependentPackageNode) pn).setUnallowedDependency(pnUnallowed);
741 }
742 }
743
744 if (mn instanceof DependentModuleNode) {
745 ((DependentModuleNode) mn).setUnallowedDependency(mnUnallowed);
746 }
747 }
748 depTree.setUnallowedDependency(treeUnallowed);
749
750
751 depTree.sortNodes();
752
753 return depTree;
754 }
755
756
757
758
759
760
761
762
763 public void addEntryClass(String entryClass) {
764 getEntryClasses().add(entryClass);
765 }
766
767
768
769
770
771
772
773 public void addModuleDependency(String moduleName, TreeSet<String> name) {
774 moduleDependencies.put(moduleName, name);
775 }
776
777
778
779
780
781 public void addClassSource(ClassSource source) {
782 classSources.add(source);
783 }
784 }