1 package net.sourceforge.pmd.lang.java.rule.coupling;
2
3 import java.util.ArrayList;
4 import java.util.Arrays;
5 import java.util.Collections;
6 import java.util.List;
7
8 import net.sourceforge.pmd.PropertySource;
9 import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit;
10 import net.sourceforge.pmd.lang.java.ast.ASTImportDeclaration;
11 import net.sourceforge.pmd.lang.java.ast.ASTPackageDeclaration;
12 import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
13 import net.sourceforge.pmd.lang.rule.properties.StringMultiProperty;
14 import net.sourceforge.pmd.util.CollectionUtil;
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35 public class LoosePackageCouplingRule extends AbstractJavaRule {
36
37 public static final StringMultiProperty PACKAGES_DESCRIPTOR = new StringMultiProperty("packages", "Restricted packages",
38 new String[] {}, 1.0f, ',');
39
40 public static final StringMultiProperty CLASSES_DESCRIPTOR = new StringMultiProperty("classes", "Allowed classes",
41 new String[] {}, 2.0f, ',');
42
43
44 private String thisPackage;
45
46
47 private List<String> restrictedPackages;
48
49 public LoosePackageCouplingRule() {
50 definePropertyDescriptor(PACKAGES_DESCRIPTOR);
51 definePropertyDescriptor(CLASSES_DESCRIPTOR);
52
53 addRuleChainVisit(ASTCompilationUnit.class);
54 addRuleChainVisit(ASTPackageDeclaration.class);
55 addRuleChainVisit(ASTImportDeclaration.class);
56 }
57
58 @Override
59 public Object visit(ASTCompilationUnit node, Object data) {
60 this.thisPackage = "";
61
62
63
64 this.restrictedPackages = new ArrayList<String>(Arrays.asList(super.getProperty(PACKAGES_DESCRIPTOR)));
65 Collections.sort(restrictedPackages, Collections.reverseOrder());
66
67 return data;
68 }
69
70 @Override
71 public Object visit(ASTPackageDeclaration node, Object data) {
72 this.thisPackage = node.getPackageNameImage();
73 return data;
74 }
75
76 @Override
77 public Object visit(ASTImportDeclaration node, Object data) {
78
79 String importPackage = node.getPackageName();
80
81
82 for (String pkg : getRestrictedPackages()) {
83
84 if (isContainingPackage(pkg, importPackage)) {
85
86 if (pkg.equals(thisPackage) || isContainingPackage(pkg, thisPackage)) {
87
88 break;
89 } else {
90
91 if (node.isImportOnDemand()) {
92 addViolation(data, node, new Object[] { node.getImportedName(), pkg });
93 break;
94 } else {
95 if (!isAllowedClass(node)) {
96 addViolation(data, node, new Object[] { node.getImportedName(), pkg });
97 break;
98 }
99 }
100 }
101 }
102 }
103 return data;
104 }
105
106 protected List<String> getRestrictedPackages() {
107 return restrictedPackages;
108 }
109
110
111 protected boolean isContainingPackage(String pkg1, String pkg2) {
112 return pkg1.equals(pkg2)
113 || (pkg1.length() < pkg2.length() && pkg2.startsWith(pkg1) && pkg2.charAt(pkg1.length()) == '.');
114 }
115
116 protected boolean isAllowedClass(ASTImportDeclaration node) {
117 String importedName = node.getImportedName();
118 for (String clazz : getProperty(CLASSES_DESCRIPTOR)) {
119 if (importedName.equals(clazz)) {
120 return true;
121 }
122
123 }
124 return false;
125 }
126
127 public boolean checksNothing() {
128
129 return
130 CollectionUtil.isEmpty(getProperty(PACKAGES_DESCRIPTOR)) &&
131 CollectionUtil.isEmpty(getProperty(CLASSES_DESCRIPTOR)) ;
132 }
133
134
135
136
137 @Override
138 public String dysfunctionReason() {
139 return checksNothing() ?
140 "No packages or classes specified" :
141 null;
142 }
143 }