Skip to content

Commit c00a982

Browse files
gselzerctrueden
authored andcommitted
Add Subpackage Dependence Rule
1 parent 1a44d6e commit c00a982

File tree

5 files changed

+236
-6
lines changed

5 files changed

+236
-6
lines changed

src/main/java/org/scijava/packages/plugin/DirectoriesWithClasses.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,18 @@ public class DirectoriesWithClasses implements Iterable<File>{
1717

1818
private final List<File> directories = new LinkedList<File>();
1919

20-
public DirectoriesWithClasses(EnforcerRuleHelper helper, boolean includeTests) throws ExpressionEvaluationException {
21-
addDirectoryIfExists(helper, MAVEN_PROJECT_BUILD_OUTPUT_DIRECTORY_VAR);
20+
public DirectoriesWithClasses(EnforcerRuleHelper helper, String rule, boolean includeTests) throws ExpressionEvaluationException {
21+
addDirectoryIfExists(helper, MAVEN_PROJECT_BUILD_OUTPUT_DIRECTORY_VAR, rule);
2222
if (includeTests) {
23-
addDirectoryIfExists(helper, MAVEN_PROJECT_BUILD_TEST_OUTPUT_DIRECTORY_VAR);
23+
addDirectoryIfExists(helper, MAVEN_PROJECT_BUILD_TEST_OUTPUT_DIRECTORY_VAR, rule);
2424
}
2525
}
2626

27-
private void addDirectoryIfExists(EnforcerRuleHelper helper, String variable)
27+
private void addDirectoryIfExists(EnforcerRuleHelper helper, String variable, String rule)
2828
throws ExpressionEvaluationException {
2929
File directory = new File((String) helper.evaluate(variable));
3030
if(directory.exists()) {
31-
helper.getLog().info("Adding directory " + directory.getAbsolutePath() + " for package cycles search.");
31+
helper.getLog().info("Adding directory " + directory.getAbsolutePath() + " for " + rule + " search.");
3232
directories.add(directory);
3333
} else {
3434
helper.getLog().info("Directory " + directory.getAbsolutePath() + " could not be found.");

src/main/java/org/scijava/packages/plugin/NoPackageCyclesRule.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public void execute(EnforcerRuleHelper helper) throws EnforcerRuleException {
3333

3434
private void executePackageCycleCheckIfNecessary(EnforcerRuleHelper helper)
3535
throws ExpressionEvaluationException, IOException, EnforcerRuleException {
36-
DirectoriesWithClasses directories = new DirectoriesWithClasses(helper, includeTests);
36+
DirectoriesWithClasses directories = new DirectoriesWithClasses(helper, "package cycles", includeTests);
3737
if (directories.directoriesWithClassesFound()) {
3838
executePackageCycleCheck(helper, directories);
3939
} else {
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
package org.scijava.packages.plugin;
2+
3+
import java.io.File;
4+
import java.io.IOException;
5+
import java.util.ArrayList;
6+
import java.util.Collection;
7+
import java.util.HashMap;
8+
import java.util.List;
9+
import java.util.Map;
10+
import java.util.Map.Entry;
11+
import java.util.stream.Collectors;
12+
13+
import org.apache.maven.enforcer.rule.api.EnforcerRule;
14+
import org.apache.maven.enforcer.rule.api.EnforcerRuleException;
15+
import org.apache.maven.enforcer.rule.api.EnforcerRuleHelper;
16+
import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException;
17+
18+
import jdepend.framework.JDepend;
19+
import jdepend.framework.JavaPackage;
20+
import jdepend.framework.PackageFilter;
21+
22+
public class NoSubpackageDependenceRule implements EnforcerRule {
23+
24+
private boolean includeTests = true;
25+
private List<String> includedPackages = new ArrayList<>();
26+
private List<String> excludedPackages = new ArrayList<>();
27+
28+
@Override
29+
public void execute(EnforcerRuleHelper helper) throws EnforcerRuleException {
30+
try {
31+
executeSubpackageDependenceCheckIfNecessary(helper);
32+
} catch (ExpressionEvaluationException e) {
33+
throw new EnforcerRuleException("Unable to lookup an expression " + e.getLocalizedMessage(), e);
34+
} catch (IOException e) {
35+
throw new EnforcerRuleException("Unable to access target directory " + e.getLocalizedMessage(), e);
36+
}
37+
}
38+
39+
private void executeSubpackageDependenceCheckIfNecessary(EnforcerRuleHelper helper)
40+
throws ExpressionEvaluationException, IOException, EnforcerRuleException {
41+
DirectoriesWithClasses directories = new DirectoriesWithClasses(helper, "subpackage dependence", includeTests);
42+
if (directories.directoriesWithClassesFound()) {
43+
executeSubpackageDependenceCheck(helper, directories);
44+
} else {
45+
helper.getLog().info("No directories with classes to check for subpackage dependence found.");
46+
}
47+
}
48+
49+
private void executeSubpackageDependenceCheck(EnforcerRuleHelper helper, Iterable<File> directories) throws IOException, EnforcerRuleException {
50+
JDepend jdepend = createJDepend(helper);
51+
for (File directory : directories) {
52+
jdepend.addDirectory(directory.getAbsolutePath());
53+
}
54+
jdepend.analyze();
55+
56+
Map<JavaPackage, List<JavaPackage>> subpackageLists = new HashMap<>();
57+
for (JavaPackage p : jdepend.getPackages()) {
58+
// create running List of subpackages, evaluate all previously added to the Map
59+
List<JavaPackage> subpackages = new ArrayList<>();
60+
for (JavaPackage other : subpackageLists.keySet()) {
61+
String dString = p.getName();
62+
String oString = other.getName();
63+
if (dString == oString) continue;
64+
if (dString.contains(oString)) subpackageLists.get(other).add(p);
65+
if (oString.contains(dString)) subpackages.add(other);
66+
}
67+
subpackageLists.put(p, subpackages);
68+
}
69+
Map<JavaPackage, List<JavaPackage>> subpackageDependence = new HashMap<>();
70+
for (Entry<JavaPackage, List<JavaPackage>> e : subpackageLists.entrySet()) {
71+
Collection<JavaPackage> efferents = e.getKey().getEfferents();
72+
List<JavaPackage> subpackagesDependedUpon = efferents.stream().filter(e.getValue()::contains).collect(Collectors.toList());
73+
subpackageDependence.put(e.getKey(), subpackagesDependedUpon);
74+
}
75+
for (List<JavaPackage> l : subpackageDependence.values()) {
76+
if (!l.isEmpty()) {
77+
throw new EnforcerRuleException("Some packages depend on subpackages:" +
78+
new SubpackageDependenceOutput(subpackageDependence).getOutput());
79+
}
80+
}
81+
}
82+
83+
protected JDepend createJDepend(EnforcerRuleHelper helper) {
84+
if (!includedPackages.isEmpty()) {
85+
helper.getLog().warn("Subpackage dependence rule check is restricted to check only these packages: " + includedPackages);
86+
}
87+
if (!excludedPackages.isEmpty()) {
88+
helper.getLog().warn("These packages were excluded from subpackage dependence rule check: " + excludedPackages);
89+
}
90+
return new JDepend(PackageFilter.all()
91+
.including(includedPackages)
92+
.excluding(excludedPackages));
93+
}
94+
95+
public void setIncludeTests(boolean includeTests) {
96+
this.includeTests = includeTests;
97+
}
98+
99+
public void setIncludedPackages(List<String> includedPackages) {
100+
this.includedPackages = includedPackages;
101+
}
102+
103+
public void setExcludedPackages(List<String> excludedPackages) {
104+
this.excludedPackages = excludedPackages;
105+
}
106+
107+
@Override
108+
public boolean isCacheable() {
109+
// TODO Auto-generated method stub
110+
return false;
111+
}
112+
113+
@Override
114+
public boolean isResultValid(EnforcerRule cachedRule) {
115+
// TODO Auto-generated method stub
116+
return false;
117+
}
118+
119+
@Override
120+
public String getCacheId() {
121+
// TODO Auto-generated method stub
122+
return null;
123+
}
124+
125+
}
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
package org.scijava.packages.plugin;
2+
3+
import static org.scijava.packages.plugin.CollectionOutput.joinCollection;
4+
5+
import java.util.ArrayList;
6+
import java.util.Collection;
7+
import java.util.Collections;
8+
import java.util.List;
9+
import java.util.Map;
10+
11+
import org.scijava.packages.plugin.CollectionOutput.Appender;
12+
import org.scijava.packages.plugin.comparator.JavaClassNameComparator;
13+
import org.scijava.packages.plugin.comparator.JavaPackageNameComparator;
14+
15+
import jdepend.framework.JavaClass;
16+
import jdepend.framework.JavaPackage;
17+
18+
public class SubpackageDependenceOutput {
19+
20+
private Map<JavaPackage, List<JavaPackage>> packages;
21+
private StringBuilder output;
22+
23+
public SubpackageDependenceOutput(Map<JavaPackage, List<JavaPackage>> packages) {
24+
this.packages = packages;
25+
}
26+
27+
public String getOutput() {
28+
output = new StringBuilder();
29+
for (Map.Entry<JavaPackage, List<JavaPackage>> e : collectAndSortCycles()) {
30+
if (!e.getValue().isEmpty()) appendOutputForSubpackageDependence(e);
31+
}
32+
return output.toString();
33+
}
34+
35+
private List<Map.Entry<JavaPackage, List<JavaPackage>>> collectAndSortCycles() {
36+
List<Map.Entry<JavaPackage, List<JavaPackage>>> orderedList = new ArrayList<>();
37+
for (Map.Entry<JavaPackage, List<JavaPackage>> e : packages.entrySet()) {
38+
Collections.sort(e.getValue(), JavaPackageNameComparator.INSTANCE);
39+
orderedList.add(e);
40+
}
41+
Collections.sort(orderedList, (e1, e2) -> JavaPackageNameComparator.INSTANCE.compare(e1.getKey(), e2.getKey()));
42+
return orderedList;
43+
}
44+
45+
private void appendOutputForSubpackageDependence(Map.Entry<JavaPackage, List<JavaPackage>> subpackageDependence) {
46+
JavaPackage p = subpackageDependence.getKey();
47+
packages.remove(p);
48+
appendHeaderForSubpackageDependence(subpackageDependence);
49+
for (JavaPackage cyclicPackage : subpackageDependence.getValue()) {
50+
appendOutputForSubPackage(p, cyclicPackage);
51+
}
52+
}
53+
54+
private void appendHeaderForSubpackageDependence(Map.Entry<JavaPackage, List<JavaPackage>> entry) {
55+
output.append("\n\n").append("Package " + entry.getKey().getName() + " depends on subpackages: ");
56+
}
57+
58+
private void appendOutputForSubPackage(final JavaPackage javaPackage, JavaPackage subpackage) {
59+
if (javaPackage.equals(subpackage)) {
60+
return;
61+
}
62+
List<JavaClass> dependentClasses = getOrderedDependentClasses(javaPackage, subpackage);
63+
if (!dependentClasses.isEmpty()) {
64+
appendOutputForDependentCyclicPackage(subpackage, dependentClasses);
65+
}
66+
}
67+
68+
private List<JavaClass> getOrderedDependentClasses(JavaPackage javaPackage, JavaPackage cyclicPackage) {
69+
List<JavaClass> dependentClasses = new ArrayList<>();
70+
Collection<JavaClass> allClasses = javaPackage.getClasses();
71+
for (JavaClass javaClass : allClasses) {
72+
if (javaClass.getImportedPackages().contains(cyclicPackage)) {
73+
dependentClasses.add(javaClass);
74+
}
75+
}
76+
Collections.sort(dependentClasses, JavaClassNameComparator.INSTANCE);
77+
return dependentClasses;
78+
}
79+
80+
private void appendOutputForDependentCyclicPackage(JavaPackage cyclicPackage,
81+
List<JavaClass> dependentClasses) {
82+
output.append("\n ").append(cyclicPackage.getName()).append(" (");
83+
appendOutputForCyclicPackageClasses(dependentClasses);
84+
output.append(")");
85+
}
86+
87+
private void appendOutputForCyclicPackageClasses(List<JavaClass> dependentClasses) {
88+
joinCollection(dependentClasses, output, new Appender<JavaClass>() {
89+
@Override
90+
public void append(JavaClass packageClass) {
91+
output.append(packageClass.getName().substring(packageClass.getPackageName().length() + 1));
92+
}
93+
}, ", ");
94+
}
95+
96+
}

src/test/java/org/scijava/packages/plugin/mock/JDependMock.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
public class JDependMock extends JDepend {
88
private boolean addDirectoryThrowsException;
99
private boolean containsCycles;
10+
private boolean subpackageDependence;
1011

1112
public void setAddDirectoryThrowsException(boolean addDirectoryThrowsException) {
1213
this.addDirectoryThrowsException = addDirectoryThrowsException;
@@ -25,7 +26,15 @@ public boolean containsCycles() {
2526
return containsCycles;
2627
}
2728

29+
public boolean containsSubpackageDependence() {
30+
return subpackageDependence;
31+
}
32+
2833
public void setContainsCycles(boolean containsCycles) {
2934
this.containsCycles = containsCycles;
3035
}
36+
37+
public void setSubpackageDependence(boolean dependsOnSubpackage) {
38+
this.subpackageDependence = dependsOnSubpackage;
39+
}
3140
}

0 commit comments

Comments
 (0)