![JAR search and dependency download from the Maven repository](/logo.png)
com.tngtech.archunit.library.dependencies.Graph Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of archunit Show documentation
Show all versions of archunit Show documentation
A Java architecture test library, to specify and assert architecture rules in plain Java - Module 'archunit'
/*
* Copyright 2014-2022 TNG Technology Consulting GmbH
*
* Licensed 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.tngtech.archunit.library.dependencies;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ForwardingCollection;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Multimap;
import static com.google.common.base.Preconditions.checkArgument;
class Graph {
private final Map nodes = new HashMap<>();
private final ListMultimap> outgoingEdges = ArrayListMultimap.create();
void addNodes(Iterable nodes) {
for (T node : nodes) {
if (!this.nodes.containsKey(node)) {
this.nodes.put(node, this.nodes.size());
}
}
}
void addEdges(Iterable> outgoingEdges) {
for (Edge edge : outgoingEdges) {
checkArgument(nodes.containsKey(edge.getFrom()), "Node %s of edge %s is not part of the graph", edge.getFrom(), edge);
checkArgument(nodes.containsKey(edge.getTo()), "Node %s of edge %s is not part of the graph", edge.getTo(), edge);
this.outgoingEdges.put(nodes.get(edge.getFrom()), edge);
}
}
Cycles findCycles() {
Map>> edgesByTargetIndexByOriginIndex = indexEdgesByTargetIndexByOriginIndex(nodes, outgoingEdges);
JohnsonCycleFinder johnsonCycleFinder = new JohnsonCycleFinder(createPrimitiveGraph());
ImmutableList.Builder> result = ImmutableList.builder();
JohnsonCycleFinder.Result cycles = johnsonCycleFinder.findCycles();
for (int[] rawCycle : cycles) {
result.add(mapToCycle(edgesByTargetIndexByOriginIndex, rawCycle));
}
return new Cycles<>(result.build(), cycles.maxNumberOfCyclesReached());
}
private PrimitiveGraph createPrimitiveGraph() {
int[][] edges = new int[nodes.size()][];
for (Map.Entry nodeToIndex : nodes.entrySet()) {
List> outgoing = outgoingEdges.get(nodeToIndex.getValue());
edges[nodeToIndex.getValue()] = new int[outgoing.size()];
for (int j = 0; j < outgoing.size(); j++) {
edges[nodeToIndex.getValue()][j] = nodes.get(outgoing.get(j).getTo());
}
}
return new PrimitiveGraph(edges);
}
private ImmutableMap>> indexEdgesByTargetIndexByOriginIndex(
Map nodes,
Multimap> outgoingEdges) {
ImmutableMap.Builder>> edgeMapBuilder = ImmutableMap.builder();
for (Map.Entry>> originIndexToEdges : outgoingEdges.asMap().entrySet()) {
ImmutableMap.Builder> targetIndexToEdges = ImmutableMap.builder();
for (Edge edge : originIndexToEdges.getValue()) {
targetIndexToEdges.put(nodes.get(edge.getTo()), edge);
}
edgeMapBuilder.put(originIndexToEdges.getKey(), targetIndexToEdges.build());
}
return edgeMapBuilder.build();
}
private Cycle mapToCycle(Map>> edgesByTargetIndexByOriginIndex, int[] rawCycle) {
ImmutableList.Builder> edges = ImmutableList.builder();
int originIndex = -1;
for (int targetIndex : rawCycle) {
if (originIndex >= 0) {
edges.add(edgesByTargetIndexByOriginIndex.get(originIndex).get(targetIndex));
}
originIndex = targetIndex;
}
edges.add(edgesByTargetIndexByOriginIndex.get(originIndex).get(rawCycle[0]));
return new Cycle<>(edges.build());
}
@Override
public String toString() {
return "Graph{" +
"nodes=" + nodes +
", edges=" + outgoingEdges +
'}';
}
static class Cycles extends ForwardingCollection> {
private final Collection> cycles;
private final boolean maxNumberOfCyclesReached;
private Cycles(Collection> cycles, boolean maxNumberOfCyclesReached) {
this.cycles = cycles;
this.maxNumberOfCyclesReached = maxNumberOfCyclesReached;
}
boolean maxNumberOfCyclesReached() {
return maxNumberOfCyclesReached;
}
@Override
protected Collection> delegate() {
return cycles;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy