/atlassian-plugins-webresource/src/main/java/com/atlassian/plugin/webresource/graph/DependencyGraph.java
Java | 261 lines | 159 code | 29 blank | 73 comment | 6 complexity | 1c7e9b913746b5500d652b8194f9d22e MD5 | raw file
- package com.atlassian.plugin.webresource.graph;
- import com.atlassian.plugin.webresource.ResourceBatchingConfiguration;
- import org.jgrapht.Graph;
- import org.jgrapht.alg.cycle.CycleDetector;
- import org.jgrapht.graph.AsSubgraph;
- import org.jgrapht.nio.Attribute;
- import org.jgrapht.nio.dot.DOTExporter;
- import javax.annotation.Nonnull;
- import javax.annotation.Nullable;
- import java.io.StringWriter;
- import java.io.Writer;
- import java.util.Collection;
- import java.util.HashSet;
- import java.util.LinkedHashMap;
- import java.util.Map;
- import java.util.Set;
- import static java.util.Objects.requireNonNull;
- import static org.jgrapht.Graphs.addGraph;
- import static org.jgrapht.Graphs.predecessorListOf;
- import static org.jgrapht.Graphs.successorListOf;
- import static org.jgrapht.graph.builder.GraphTypeBuilder.directed;
- import static org.jgrapht.nio.DefaultAttribute.createAttribute;
- /**
- * Maps the dependencies between items of a given type, where the vertex is the item and the edge is their dependency relation.
- *
- * @param <V> The vertex type.
- */
- public class DependencyGraph<V> {
- private static final String SOURCE_KEY_MANDATORY_MESSAGE = "The source vertex key is mandatory.";
- private final DOTExporter<V, DependencyEdge> exporter;
- private final Graph<V, DependencyEdge> resourceGraph;
- private final Class<V> verticeClazz;
- public DependencyGraph(@Nonnull final Class<V> verticeClazz) {
- this(verticeClazz, createGraph(verticeClazz));
- }
- public DependencyGraph(@Nonnull final Class<V> verticeClazz, @Nonnull final Graph<V, DependencyEdge> resourceGraph) {
- this.verticeClazz = requireNonNull(verticeClazz, "The vertice class type is mandatory.");
- this.resourceGraph = requireNonNull(resourceGraph, "The resource graph is mandatory.");
- exporter = new DOTExporter<>();
- exporter.setVertexAttributeProvider(vertex -> {
- final Map<String, Attribute> map = new LinkedHashMap<>();
- map.put("label", createAttribute(vertex.toString()));
- return map;
- });
- }
- private static <V> Graph<V, DependencyEdge> createGraph(final Class<V> verticeClazz) {
- return directed()
- .allowingMultipleEdges(true)
- .vertexClass(verticeClazz)
- .edgeClass(DependencyEdge.class)
- .allowingSelfLoops(true)
- .buildGraph();
- }
- @Nonnull
- public static DependencyGraphBuilder builder() {
- return new DependencyGraphBuilder();
- }
- @Override
- public boolean equals(@Nullable final Object other) {
- if (other instanceof DependencyGraph) {
- final DependencyGraph otherResourceGraph = (DependencyGraph) other;
- return resourceGraph.edgeSet().equals(otherResourceGraph.resourceGraph.edgeSet());
- }
- return false;
- }
- /**
- * Identify all the cycles contained inside the graph.
- *
- * @param sourceKey The source requestable resource key used as start point.
- * @return A map where the key is the requestable and the value is the subgraph for
- */
- @Nonnull
- public DependencyGraph<V> findCyclicSubGraphByVertex(@Nonnull final V sourceKey) {
- requireNonNull(sourceKey, SOURCE_KEY_MANDATORY_MESSAGE);
- final CycleDetector<V, DependencyEdge> cycleDetector = new CycleDetector<>(resourceGraph);
- final Set<V> cycles = cycleDetector.findCyclesContainingVertex(sourceKey);
- return new DependencyGraph<>(verticeClazz, new AsSubgraph<>(resourceGraph, cycles));
- }
- /**
- * Retrieve a sub graph of dependants based on a certain source requestable key.
- *
- * @param sourceKey The source requestable resource key used as start point.
- * @return The dependency graph.
- */
- @Nonnull
- public DependencyGraph<V> findDependantsSubGraphByKey(@Nonnull final V sourceKey) {
- requireNonNull(sourceKey, SOURCE_KEY_MANDATORY_MESSAGE);
- final Set<V> dependants = findAllDependantsByKey(sourceKey);
- return new DependencyGraph<>(verticeClazz, new AsSubgraph<>(resourceGraph, dependants));
- }
- /**
- * Retrieve a sub graph of dependency based on a certain source requestable key.
- *
- * @param sourceKey The source requestable key used as start point.
- * @return The dependency graph.
- */
- @Nonnull
- public DependencyGraph<V> findDependencySubGraphByRequestableKey(@Nonnull final V sourceKey) {
- requireNonNull(sourceKey, SOURCE_KEY_MANDATORY_MESSAGE);
- final Set<V> dependencies = findAllDependenciesByKey(sourceKey);
- return new DependencyGraph<>(verticeClazz, new AsSubgraph<>(resourceGraph, dependencies));
- }
- /**
- * Find the intersection between two sub-graphs generated based on a source requestable key and target requestable key.
- *
- * @param sourceKey The source requestable key used as start point for the first sub-graph to compare.
- * @param targetKey The target requestable key used start point for the second sub-graph to compare.
- * @return The dependency graph representing the intersection between both graphs.
- */
- @Nonnull
- public DependencyGraph<V> findIntersectionSubGraph(@Nonnull final V sourceKey, @Nonnull final V targetKey) {
- requireNonNull(sourceKey, SOURCE_KEY_MANDATORY_MESSAGE);
- requireNonNull(targetKey, "The target requestable key is mandatory.");
- final Set<V> sourceDependants = findAllDependantsByKey(sourceKey);
- final Set<V> targetDependants = findAllDependantsByKey(targetKey);
- final Set<V> commonDependants = new HashSet<>(sourceDependants);
- commonDependants.retainAll(targetDependants);
- return new DependencyGraph<>(verticeClazz, new AsSubgraph<>(resourceGraph, commonDependants));
- }
- @Override
- public int hashCode() {
- return resourceGraph.hashCode();
- }
- @Override
- public String toString() {
- final Writer writer = new StringWriter();
- exporter.exportGraph(resourceGraph, writer);
- return writer.toString();
- }
- public Collection<DependencyEdge> getEdges() {
- return resourceGraph.edgeSet();
- }
- public boolean hasDependency(V key) {
- return resourceGraph.containsVertex(key);
- }
- /**
- * @see DependencyGraph#addDependency(Object, Object).
- */
- void addDependencies(@Nonnull final V sourceKey, @Nonnull final Collection<V> dependencyKeys) {
- requireNonNull(sourceKey, SOURCE_KEY_MANDATORY_MESSAGE);
- requireNonNull(dependencyKeys, "The dependency keys are mandatory.");
- for (final V dependencyKey : dependencyKeys) {
- addDependency(sourceKey, dependencyKey);
- }
- }
- /**
- * Map a certain dependency between two resources.
- *
- * @param sourceKey The requestable key.
- * @param dependencyKey The dependency key.
- */
- void addDependency(@Nonnull final V sourceKey, @Nonnull final V dependencyKey) {
- requireNonNull(sourceKey, SOURCE_KEY_MANDATORY_MESSAGE);
- requireNonNull(dependencyKey, "The dependency key is mandatory.");
- resourceGraph.addVertex(sourceKey);
- resourceGraph.addVertex(dependencyKey);
- final DependencyEdge<V> dependency = new DependencyEdge<>();
- dependency.setSource(sourceKey);
- dependency.setTarget(dependencyKey);
- resourceGraph.addEdge(sourceKey, dependencyKey, dependency);
- }
- /**
- * Merge a source graph into the current graph.
- *
- * @param sourceGraph The source graph to be merged.
- */
- void merge(@Nonnull final DependencyGraph<V> sourceGraph) {
- requireNonNull(sourceGraph, "The graph to be merged is mandatory.");
- addGraph(resourceGraph, sourceGraph.resourceGraph);
- }
- /**
- * Returns a copy of the current vertexes.
- *
- * @return The set of vertexes part of the graph.
- */
- @Nonnull
- Collection<V> toVertexes() {
- return new HashSet<>(resourceGraph.vertexSet());
- }
- /**
- * Find all dependants of a source requestable resource.
- *
- * @param sourceKey The source requestable key used as start point.
- * @return The found collection of dependants of the source requestable resource.
- */
- private Set<V> findAllDependantsByKey(final V sourceKey) {
- final Set<V> dependants = new HashSet<>();
- findDependantsSubGraphByKey(dependants, sourceKey);
- return dependants;
- }
- /**
- * Find all dependencies by a source requestable resource.
- *
- * @param sourceKey The source requestable key used as start point.
- * @return The found collection of dependencies of the source requestable resource.
- */
- private Set<V> findAllDependenciesByKey(final V sourceKey) {
- final Set<V> dependencies = new HashSet<>();
- findDependencySubGraphByRequestableKey(dependencies, sourceKey);
- return dependencies;
- }
- /**
- * Retrieve a sub graph of dependants based on a certain source requestable resource.
- *
- * @param resources The resources part of the final sub-graph.
- * @param sourceKey The source requestable key used as start point.
- */
- private void findDependantsSubGraphByKey(final Collection<V> resources, final V sourceKey) {
- if (resources.contains(sourceKey)) {
- return;
- }
- resources.add(sourceKey);
- for (final V predecessorRequestable : predecessorListOf(resourceGraph, sourceKey)) {
- findDependantsSubGraphByKey(resources, predecessorRequestable);
- }
- }
- /**
- * Retrieve a sub graph of dependencies based on a certain source requestable resource.
- *
- * @param resources The resources part of the final sub-graph.
- * @param sourceKey The source requestable used as start point.
- */
- private void findDependencySubGraphByRequestableKey(final Collection<V> resources, final V sourceKey) {
- if (resources.contains(sourceKey)) {
- return;
- }
- resources.add(sourceKey);
- for (final V successorRequestable : successorListOf(resourceGraph, sourceKey)) {
- findDependencySubGraphByRequestableKey(resources, successorRequestable);
- }
- }
- }