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", "Restricted packages",
41 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 if (isContainingPackage(pkg, importPackage)) {
88
89 if (pkg.equals(thisPackage) || isContainingPackage(pkg, thisPackage)) {
90
91 break;
92 } else {
93
94 if (node.isImportOnDemand()) {
95 addViolation(data, node, new Object[] { node.getImportedName(), pkg });
96 break;
97 } else {
98 if (!isAllowedClass(node)) {
99 addViolation(data, node, new Object[] { node.getImportedName(), pkg });
100 break;
101 }
102 }
103 }
104 }
105 }
106 return data;
107 }
108
109 protected List<String> getRestrictedPackages() {
110 return restrictedPackages;
111 }
112
113
114 protected boolean isContainingPackage(String pkg1, String pkg2) {
115 return pkg1.equals(pkg2)
116 || (pkg1.length() < pkg2.length() && pkg2.startsWith(pkg1) && pkg2.charAt(pkg1.length()) == '.');
117 }
118
119 protected boolean isAllowedClass(ASTImportDeclaration node) {
120 String importedName = node.getImportedName();
121 for (String clazz : getProperty(CLASSES_DESCRIPTOR)) {
122 if (importedName.equals(clazz)) {
123 return true;
124 }
125
126 }
127 return false;
128 }
129
130 public boolean checksNothing() {
131
132 return
133 CollectionUtil.isEmpty(getProperty(PACKAGES_DESCRIPTOR)) &&
134 CollectionUtil.isEmpty(getProperty(CLASSES_DESCRIPTOR)) ;
135 }
136
137
138
139
140 @Override
141 public String dysfunctionReason() {
142 return checksNothing() ?
143 "No packages or classes specified" :
144 null;
145 }
146 }