com.indoqa.cycle.plugin.CycleDetector Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of cycle-maven-plugin Show documentation
Show all versions of cycle-maven-plugin Show documentation
Detect Package Cycles during the Maven build
The newest version!
/*
* Licensed to the Indoqa Software Design und Beratung GmbH (Indoqa) under
* one or more contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright ownership.
* Indoqa licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.indoqa.cycle.plugin;
import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.util.*;
import java.util.stream.Stream;
import com.tngtech.archunit.core.domain.Dependency;
import com.tngtech.archunit.core.domain.JavaClass;
import com.tngtech.archunit.core.domain.JavaClasses;
import com.tngtech.archunit.core.domain.JavaPackage;
import com.tngtech.archunit.core.importer.ClassFileImporter;
import com.tngtech.archunit.core.importer.ImportOption.Predefined;
public class CycleDetector {
private static final String NEW_LINE = "\r\n";
private JavaClasses javaClasses;
private String[] excludedPackages;
public CycleDetector(File directory, String[] excludedPackages) {
super();
this.excludedPackages = excludedPackages;
this.javaClasses = new ClassFileImporter(Arrays.asList(Predefined.DO_NOT_INCLUDE_ARCHIVES)).importPath(directory.toPath());
}
private static void indent(BufferedWriter writer, int depth) throws IOException {
for (int i = 0; i < depth; i++) {
writer.write(" ");
}
}
private static Cycle toCycle(Path path) {
Cycle result = new Cycle();
for (JavaPackage eachPackageNode : path) {
result.addInvolvedPackage(eachPackageNode.getName());
}
return result;
}
public List getPackageCycles() {
List result = new ArrayList<>();
Set packages = this.javaClasses.getDefaultPackage().getSubpackagesInTree();
for (JavaPackage eachPackage : packages) {
if (this.isExcluded(eachPackage)) {
continue;
}
List> dependencyChains = this.getDependencyChains(eachPackage);
for (Path eachDependencyChain : dependencyChains) {
if (eachDependencyChain.containsCycle()) {
result.add(toCycle(eachDependencyChain));
}
}
}
return result;
}
public void writeCycleFile(List cycles, java.nio.file.Path targetPath) throws IOException {
try (BufferedWriter writer = Files.newBufferedWriter(targetPath, Charset.forName("UTF-8"))) {
for (Cycle eachCycle : cycles) {
writer.write("Cycle");
writer.write(NEW_LINE);
for (String eachPackage : eachCycle.getInvolvedPackages()) {
indent(writer, 1);
writer.write(eachPackage);
writer.write(NEW_LINE);
}
writer.write(NEW_LINE);
indent(writer, 1);
writer.write("Involved Classes From Each Package");
writer.write(NEW_LINE);
List involvedPackages = eachCycle.getInvolvedPackages();
for (int i = 0; i < involvedPackages.size() - 1; i++) {
JavaPackage startPackage = this.getPackage(involvedPackages.get(i));
JavaPackage endPackage = this.getPackage(involvedPackages.get(i + 1));
indent(writer, 2);
writer.write(startPackage.getName());
writer.write(NEW_LINE);
Set> classConnections = this.getClassConnections(startPackage, endPackage);
for (Connection eachClassConnection : classConnections) {
indent(writer, 3);
writer.write(eachClassConnection.getStart().getName());
writer.write(" -> ");
writer.write(eachClassConnection.getEnd().getName());
writer.write(NEW_LINE);
}
writer.write(NEW_LINE);
}
writer.write(NEW_LINE);
}
}
}
private Set> getClassConnections(JavaPackage startPackage, JavaPackage endPackage) {
Set> result = new HashSet<>();
Set startClasses = startPackage.getClasses();
Set endClasses = endPackage.getClasses();
for (JavaClass eachStartClass : startClasses) {
Set connections = eachStartClass.getDirectDependenciesFromSelf();
for (Dependency eachConnection : connections) {
if (!endClasses.contains(eachConnection.getTargetClass())) {
continue;
}
result.add(new Connection<>(eachStartClass, eachConnection.getTargetClass()));
}
}
return result;
}
private List> getDependencyChains(JavaPackage packageNode) {
return this.getDependencyChains(new Path<>(packageNode));
}
private List> getDependencyChains(Path dependencyChain) {
if (dependencyChain.containsCycle()) {
return Collections.singletonList(dependencyChain);
}
Set dependencies = dependencyChain.getLastElement().getPackageDependenciesFromThisPackage();
if (dependencies == null || dependencies.isEmpty()) {
return Collections.singletonList(dependencyChain);
}
List> result = new ArrayList<>();
for (JavaPackage eachDependency : dependencies) {
result.addAll(this.getDependencyChains(dependencyChain.createChild(eachDependency)));
}
return result;
}
private JavaPackage getPackage(String packageName) {
return this.javaClasses.getPackage(packageName);
}
private boolean isExcluded(JavaPackage javaPackage) {
if (this.excludedPackages == null || this.excludedPackages.length == 0) {
return false;
}
String name = javaPackage.getName();
return Stream.of(this.excludedPackages).anyMatch(excludedPackage -> name.equals(excludedPackage));
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy