All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.tngtech.archunit.library.dependencies.Graph Maven / Gradle / Ivy

Go to download

A Java architecture test library, to specify and assert architecture rules in plain Java - Module 'archunit'

There is a newer version: 1.3.0
Show newest version
/*
 * 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