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

cdc.graphs.core.GraphTransitiveClosure Maven / Gradle / Ivy

There is a newer version: 0.71.2
Show newest version
package cdc.graphs.core;

import java.util.Collection;
import java.util.HashSet;
import java.util.Set;

import cdc.graphs.EdgeDirection;
import cdc.graphs.EdgeTip;
import cdc.graphs.GraphAdapter;
import cdc.graphs.impl.ExplicitSubGraph;
import cdc.graphs.impl.ExtensionSubGraph;
import cdc.util.lang.Checks;

/**
 * Utility used to compute transitive closure of a node or a set of nodes.
 *
 * @author Damien Carbonne
 *
 * @param  Node type
 * @param  Edge type
 */
public class GraphTransitiveClosure extends GraphBase {
    private static final String NODE = "node";
    private static final String NODES = "nodes";

    public GraphTransitiveClosure(GraphAdapter adapter) {
        super(adapter);
    }

    public Set computeTransitiveClosureNodes(N node) {
        Checks.isNotNull(node, NODE);

        final Set result = new HashSet<>();
        addClosure(node, result);
        return result;
    }

    public Set computeTransitiveClosureNodes(Collection nodes) {
        Checks.isNotNull(nodes, NODES);

        final Set result = new HashSet<>();
        for (final N node : nodes) {
            addClosure(node, result);
        }
        return result;
    }

    public Set computeTransitiveClosureNodes(N node,
                                                EdgeDirection direction) {
        Checks.isNotNull(node, NODE);

        final Set resultIn =
                direction != EdgeDirection.OUTGOING ? new HashSet<>() : null;
        final Set resultOut =
                direction != EdgeDirection.INGOING ? new HashSet<>() : null;

        if (resultOut != null) {
            addOutgoingClosure(node, resultOut);
        }
        if (resultIn != null) {
            addIngoingClosure(node, resultIn);
        }
        return merge(resultIn, resultOut);
    }

    public Set computeTransitiveClosureNodes(Collection nodes,
                                                EdgeDirection direction) {
        Checks.isNotNull(nodes, NODES);

        final Set resultIn =
                direction != EdgeDirection.OUTGOING ? new HashSet<>() : null;
        final Set resultOut =
                direction != EdgeDirection.INGOING ? new HashSet<>() : null;

        for (final N node : nodes) {
            if (resultOut != null) {
                addOutgoingClosure(node, resultOut);
            }
            if (resultIn != null) {
                addIngoingClosure(node, resultIn);
            }
        }
        return merge(resultIn, resultOut);
    }

    public ExplicitSubGraph computeTransitiveClosure(N node) {
        Checks.isNotNull(node, NODE);

        final ExplicitSubGraph result = new ExtensionSubGraph<>(adapter);
        addClosure(node, result);
        return result;
    }

    public ExplicitSubGraph computeTransitiveClosure(Collection nodes) {
        Checks.isNotNull(nodes, NODES);

        final ExplicitSubGraph result = new ExtensionSubGraph<>(adapter);
        for (final N node : nodes) {
            addClosure(node, result);
        }
        return result;
    }

    /**
     * Computes the transitive closure of a node.
     *
     * @param node The node.
     * @param direction The transitive closure direction. {@code null} means both directions.
     * @return The transitive closure of {@code node}.
     */
    public ExplicitSubGraph computeTransitiveClosure(N node,
                                                           EdgeDirection direction) {
        Checks.isNotNull(node, NODE);

        final ExplicitSubGraph resultIn =
                direction != EdgeDirection.OUTGOING ? new ExtensionSubGraph<>(adapter) : null;
        final ExplicitSubGraph resultOut =
                direction != EdgeDirection.INGOING ? new ExtensionSubGraph<>(adapter) : null;

        if (resultOut != null) {
            addOutgoingClosure(node, resultOut);
        }
        if (resultIn != null) {
            addIngoingClosure(node, resultIn);
        }
        return merge(resultIn, resultOut);
    }

    /**
     * Computes the transitive closure of a collection of nodes.
     *
     * @param nodes The collection of nodes.
     * @param direction The transitive closure direction. {@code null} means both directions.
     * @return The transitive closure of {@code nodes}.
     */
    public ExplicitSubGraph computeTransitiveClosure(Collection nodes,
                                                           EdgeDirection direction) {
        Checks.isNotNull(nodes, NODES);

        final ExplicitSubGraph resultIn =
                direction != EdgeDirection.OUTGOING ? new ExtensionSubGraph<>(adapter) : null;
        final ExplicitSubGraph resultOut =
                direction != EdgeDirection.INGOING ? new ExtensionSubGraph<>(adapter) : null;

        for (final N node : nodes) {
            if (resultOut != null) {
                addOutgoingClosure(node, resultOut);
            }
            if (resultIn != null) {
                addIngoingClosure(node, resultIn);
            }
        }
        return merge(resultIn, resultOut);
    }

    private void addClosure(N node,
                            ExplicitSubGraph result) {
        if (!result.containsNode(node)) {
            result.addNode(node);
            for (final E edge : adapter.getEdges(node, EdgeDirection.OUTGOING)) {
                if (!result.containsEdge(edge)) {
                    final N target = adapter.getTip(edge, EdgeTip.TARGET);
                    addOutgoingClosure(target, result);
                    result.addEdge(edge);
                }
            }
            for (final E edge : adapter.getEdges(node, EdgeDirection.INGOING)) {
                if (!result.containsEdge(edge)) {
                    final N source = adapter.getTip(edge, EdgeTip.SOURCE);
                    addOutgoingClosure(source, result);
                    result.addEdge(edge);
                }
            }
        }
    }

    private void addOutgoingClosure(N node,
                                    ExplicitSubGraph result) {
        if (!result.containsNode(node)) {
            result.addNode(node);
            for (final E edge : adapter.getEdges(node, EdgeDirection.OUTGOING)) {
                if (!result.containsEdge(edge)) {
                    final N target = adapter.getTip(edge, EdgeTip.TARGET);
                    addOutgoingClosure(target, result);
                    result.addEdge(edge);
                }
            }
        }
    }

    private void addIngoingClosure(N node,
                                   ExplicitSubGraph result) {
        if (!result.containsNode(node)) {
            result.addNode(node);
            for (final E edge : adapter.getEdges(node, EdgeDirection.INGOING)) {
                if (!result.containsEdge(edge)) {
                    final N source = adapter.getTip(edge, EdgeTip.SOURCE);
                    addIngoingClosure(source, result);
                    result.addEdge(edge);
                }
            }
        }
    }

    private ExplicitSubGraph merge(ExplicitSubGraph in,
                                         ExplicitSubGraph out) {
        if (in == null) {
            return out;
        } else if (out == null) {
            return in;
        } else {
            for (final N n : in.getNodes()) {
                out.addNode(n);
            }
            for (final E e : in.getEdges()) {
                out.addEdge(e);
            }
            return out;
        }
    }

    private void addClosure(N node,
                            Set result) {
        if (!result.contains(node)) {
            result.add(node);
            for (final E edge : adapter.getEdges(node, EdgeDirection.OUTGOING)) {
                final N target = adapter.getTip(edge, EdgeTip.TARGET);
                addOutgoingClosure(target, result);
            }
            for (final E edge : adapter.getEdges(node, EdgeDirection.INGOING)) {
                final N source = adapter.getTip(edge, EdgeTip.SOURCE);
                addOutgoingClosure(source, result);
            }
        }
    }

    private void addOutgoingClosure(N node,
                                    Set result) {
        if (!result.contains(node)) {
            result.add(node);
            for (final E edge : adapter.getEdges(node, EdgeDirection.OUTGOING)) {
                final N target = adapter.getTip(edge, EdgeTip.TARGET);
                addOutgoingClosure(target, result);
            }
        }
    }

    private void addIngoingClosure(N node,
                                   Set result) {
        if (!result.contains(node)) {
            result.add(node);
            for (final E edge : adapter.getEdges(node, EdgeDirection.INGOING)) {
                final N source = adapter.getTip(edge, EdgeTip.SOURCE);
                addIngoingClosure(source, result);
            }
        }
    }

    private Set merge(Set in,
                         Set out) {
        if (in == null) {
            return out;
        } else if (out == null) {
            return in;
        } else {
            out.addAll(in);
            return out;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy