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

guru.nidi.codeassert.dependency.Tarjan Maven / Gradle / Ivy

There is a newer version: 0.9.15
Show newest version
/*
 * Copyright © 2015 Stefan Niederhauser ([email protected])
 *
 * 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 guru.nidi.codeassert.dependency;

import guru.nidi.codeassert.model.UsingElement;

import java.util.*;

class Tarjan> {
    private int index;
    private final Stack stack = new Stack<>();
    private final Map nodes = new HashMap<>();
    private final Set result = new HashSet<>();

    private static class Node {
        int index = -1;
        int lowlink;
        boolean onStack;
    }

    public Set analyzeCycles(Iterable elems, boolean allowIntraPackageCycles) {
        index = 0;
        final Map map = new HashMap<>();
        for (final T elem : elems) {
            map.put(elem.getName(), elem);
            if (node(elem).index < 0) {
                strongConnect(elem);
            }
        }
        return removeInnerCycles(map, true, allowIntraPackageCycles);
    }

    private Set removeInnerCycles(Map elems, boolean innerClasses, boolean intraPackages) {
        final Set res = new HashSet<>();
        for (final DependencyMap map : result) {
            final DependencyMap filtered = new DependencyMap();
            for (final String from : map.getElements()) {
                for (final Map.Entry entry : map.getDependencies(from).entrySet()) {
                    final String to = entry.getKey();
                    final boolean innerClassOk = innerClasses && areInnerClasses(from, to);
                    final boolean intraPackageOk = intraPackages && areSamePackage(elems, from, to);
                    if (!innerClassOk && !intraPackageOk) {
                        filtered.with(entry.getValue().getSpecificity(), from, entry.getValue().getVias(), to);
                    }
                }
            }
            if (!filtered.isEmpty()) {
                res.add(filtered);
            }
        }
        return res;
    }

    private boolean areSamePackage(Map elems, String c1, String c2) {
        return elems.get(c1).getPackageName().equals(elems.get(c2).getPackageName());
    }

    private boolean areInnerClasses(String c1, String c2) {
        return c1.startsWith(c2 + "$") || c2.startsWith(c1 + "$");
    }

    private Node node(T elem) {
        Node node = nodes.get(elem.getName());
        if (node == null) {
            node = new Node();
            nodes.put(elem.getName(), node);
        }
        return node;
    }

    private void strongConnect(T elem) {
        final Node v = init(elem);
        processUses(elem, v);

        if (v.lowlink == v.index) {
            final Set group = createGroup(elem);
            if (group.size() > 1) {
                addCycle(group);
            }
        }
    }

    private Node init(T elem) {
        final Node v = node(elem);
        v.index = index;
        v.lowlink = index;
        index++;
        stack.push(elem);
        v.onStack = true;
        return v;
    }

    private void processUses(T elem, Node v) {
        for (final T dep : elem.uses()) {
            final Node w = node(dep);
            if (w.index < 0) {
                strongConnect(dep);
                v.lowlink = Math.min(v.lowlink, w.lowlink);
            } else if (w.onStack) {
                v.lowlink = Math.min(v.lowlink, w.index);
            }
        }
    }

    private Set createGroup(T elem) {
        final Set group = new HashSet<>();
        T w;
        do {
            w = stack.pop();
            node(w).onStack = false;
            group.add(w);
        } while (!elem.equals(w));
        return group;
    }

    private void addCycle(Set group) {
        final DependencyMap g = new DependencyMap();
        for (final T elem : group) {
            for (final T dep : elem.uses()) {
                if (group.contains(dep)) {
                    g.with(0, elem, dep);
                }
            }
        }
        result.add(g);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy