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