PageRenderTime 26ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/atlassian-plugins-webresource/src/main/java/com/atlassian/plugin/webresource/graph/DependencyGraph.java

https://bitbucket.org/atlassian/atlassian-plugins-webresource
Java | 261 lines | 159 code | 29 blank | 73 comment | 6 complexity | 1c7e9b913746b5500d652b8194f9d22e MD5 | raw file
  1. package com.atlassian.plugin.webresource.graph;
  2. import com.atlassian.plugin.webresource.ResourceBatchingConfiguration;
  3. import org.jgrapht.Graph;
  4. import org.jgrapht.alg.cycle.CycleDetector;
  5. import org.jgrapht.graph.AsSubgraph;
  6. import org.jgrapht.nio.Attribute;
  7. import org.jgrapht.nio.dot.DOTExporter;
  8. import javax.annotation.Nonnull;
  9. import javax.annotation.Nullable;
  10. import java.io.StringWriter;
  11. import java.io.Writer;
  12. import java.util.Collection;
  13. import java.util.HashSet;
  14. import java.util.LinkedHashMap;
  15. import java.util.Map;
  16. import java.util.Set;
  17. import static java.util.Objects.requireNonNull;
  18. import static org.jgrapht.Graphs.addGraph;
  19. import static org.jgrapht.Graphs.predecessorListOf;
  20. import static org.jgrapht.Graphs.successorListOf;
  21. import static org.jgrapht.graph.builder.GraphTypeBuilder.directed;
  22. import static org.jgrapht.nio.DefaultAttribute.createAttribute;
  23. /**
  24. * Maps the dependencies between items of a given type, where the vertex is the item and the edge is their dependency relation.
  25. *
  26. * @param <V> The vertex type.
  27. */
  28. public class DependencyGraph<V> {
  29. private static final String SOURCE_KEY_MANDATORY_MESSAGE = "The source vertex key is mandatory.";
  30. private final DOTExporter<V, DependencyEdge> exporter;
  31. private final Graph<V, DependencyEdge> resourceGraph;
  32. private final Class<V> verticeClazz;
  33. public DependencyGraph(@Nonnull final Class<V> verticeClazz) {
  34. this(verticeClazz, createGraph(verticeClazz));
  35. }
  36. public DependencyGraph(@Nonnull final Class<V> verticeClazz, @Nonnull final Graph<V, DependencyEdge> resourceGraph) {
  37. this.verticeClazz = requireNonNull(verticeClazz, "The vertice class type is mandatory.");
  38. this.resourceGraph = requireNonNull(resourceGraph, "The resource graph is mandatory.");
  39. exporter = new DOTExporter<>();
  40. exporter.setVertexAttributeProvider(vertex -> {
  41. final Map<String, Attribute> map = new LinkedHashMap<>();
  42. map.put("label", createAttribute(vertex.toString()));
  43. return map;
  44. });
  45. }
  46. private static <V> Graph<V, DependencyEdge> createGraph(final Class<V> verticeClazz) {
  47. return directed()
  48. .allowingMultipleEdges(true)
  49. .vertexClass(verticeClazz)
  50. .edgeClass(DependencyEdge.class)
  51. .allowingSelfLoops(true)
  52. .buildGraph();
  53. }
  54. @Nonnull
  55. public static DependencyGraphBuilder builder() {
  56. return new DependencyGraphBuilder();
  57. }
  58. @Override
  59. public boolean equals(@Nullable final Object other) {
  60. if (other instanceof DependencyGraph) {
  61. final DependencyGraph otherResourceGraph = (DependencyGraph) other;
  62. return resourceGraph.edgeSet().equals(otherResourceGraph.resourceGraph.edgeSet());
  63. }
  64. return false;
  65. }
  66. /**
  67. * Identify all the cycles contained inside the graph.
  68. *
  69. * @param sourceKey The source requestable resource key used as start point.
  70. * @return A map where the key is the requestable and the value is the subgraph for
  71. */
  72. @Nonnull
  73. public DependencyGraph<V> findCyclicSubGraphByVertex(@Nonnull final V sourceKey) {
  74. requireNonNull(sourceKey, SOURCE_KEY_MANDATORY_MESSAGE);
  75. final CycleDetector<V, DependencyEdge> cycleDetector = new CycleDetector<>(resourceGraph);
  76. final Set<V> cycles = cycleDetector.findCyclesContainingVertex(sourceKey);
  77. return new DependencyGraph<>(verticeClazz, new AsSubgraph<>(resourceGraph, cycles));
  78. }
  79. /**
  80. * Retrieve a sub graph of dependants based on a certain source requestable key.
  81. *
  82. * @param sourceKey The source requestable resource key used as start point.
  83. * @return The dependency graph.
  84. */
  85. @Nonnull
  86. public DependencyGraph<V> findDependantsSubGraphByKey(@Nonnull final V sourceKey) {
  87. requireNonNull(sourceKey, SOURCE_KEY_MANDATORY_MESSAGE);
  88. final Set<V> dependants = findAllDependantsByKey(sourceKey);
  89. return new DependencyGraph<>(verticeClazz, new AsSubgraph<>(resourceGraph, dependants));
  90. }
  91. /**
  92. * Retrieve a sub graph of dependency based on a certain source requestable key.
  93. *
  94. * @param sourceKey The source requestable key used as start point.
  95. * @return The dependency graph.
  96. */
  97. @Nonnull
  98. public DependencyGraph<V> findDependencySubGraphByRequestableKey(@Nonnull final V sourceKey) {
  99. requireNonNull(sourceKey, SOURCE_KEY_MANDATORY_MESSAGE);
  100. final Set<V> dependencies = findAllDependenciesByKey(sourceKey);
  101. return new DependencyGraph<>(verticeClazz, new AsSubgraph<>(resourceGraph, dependencies));
  102. }
  103. /**
  104. * Find the intersection between two sub-graphs generated based on a source requestable key and target requestable key.
  105. *
  106. * @param sourceKey The source requestable key used as start point for the first sub-graph to compare.
  107. * @param targetKey The target requestable key used start point for the second sub-graph to compare.
  108. * @return The dependency graph representing the intersection between both graphs.
  109. */
  110. @Nonnull
  111. public DependencyGraph<V> findIntersectionSubGraph(@Nonnull final V sourceKey, @Nonnull final V targetKey) {
  112. requireNonNull(sourceKey, SOURCE_KEY_MANDATORY_MESSAGE);
  113. requireNonNull(targetKey, "The target requestable key is mandatory.");
  114. final Set<V> sourceDependants = findAllDependantsByKey(sourceKey);
  115. final Set<V> targetDependants = findAllDependantsByKey(targetKey);
  116. final Set<V> commonDependants = new HashSet<>(sourceDependants);
  117. commonDependants.retainAll(targetDependants);
  118. return new DependencyGraph<>(verticeClazz, new AsSubgraph<>(resourceGraph, commonDependants));
  119. }
  120. @Override
  121. public int hashCode() {
  122. return resourceGraph.hashCode();
  123. }
  124. @Override
  125. public String toString() {
  126. final Writer writer = new StringWriter();
  127. exporter.exportGraph(resourceGraph, writer);
  128. return writer.toString();
  129. }
  130. public Collection<DependencyEdge> getEdges() {
  131. return resourceGraph.edgeSet();
  132. }
  133. public boolean hasDependency(V key) {
  134. return resourceGraph.containsVertex(key);
  135. }
  136. /**
  137. * @see DependencyGraph#addDependency(Object, Object).
  138. */
  139. void addDependencies(@Nonnull final V sourceKey, @Nonnull final Collection<V> dependencyKeys) {
  140. requireNonNull(sourceKey, SOURCE_KEY_MANDATORY_MESSAGE);
  141. requireNonNull(dependencyKeys, "The dependency keys are mandatory.");
  142. for (final V dependencyKey : dependencyKeys) {
  143. addDependency(sourceKey, dependencyKey);
  144. }
  145. }
  146. /**
  147. * Map a certain dependency between two resources.
  148. *
  149. * @param sourceKey The requestable key.
  150. * @param dependencyKey The dependency key.
  151. */
  152. void addDependency(@Nonnull final V sourceKey, @Nonnull final V dependencyKey) {
  153. requireNonNull(sourceKey, SOURCE_KEY_MANDATORY_MESSAGE);
  154. requireNonNull(dependencyKey, "The dependency key is mandatory.");
  155. resourceGraph.addVertex(sourceKey);
  156. resourceGraph.addVertex(dependencyKey);
  157. final DependencyEdge<V> dependency = new DependencyEdge<>();
  158. dependency.setSource(sourceKey);
  159. dependency.setTarget(dependencyKey);
  160. resourceGraph.addEdge(sourceKey, dependencyKey, dependency);
  161. }
  162. /**
  163. * Merge a source graph into the current graph.
  164. *
  165. * @param sourceGraph The source graph to be merged.
  166. */
  167. void merge(@Nonnull final DependencyGraph<V> sourceGraph) {
  168. requireNonNull(sourceGraph, "The graph to be merged is mandatory.");
  169. addGraph(resourceGraph, sourceGraph.resourceGraph);
  170. }
  171. /**
  172. * Returns a copy of the current vertexes.
  173. *
  174. * @return The set of vertexes part of the graph.
  175. */
  176. @Nonnull
  177. Collection<V> toVertexes() {
  178. return new HashSet<>(resourceGraph.vertexSet());
  179. }
  180. /**
  181. * Find all dependants of a source requestable resource.
  182. *
  183. * @param sourceKey The source requestable key used as start point.
  184. * @return The found collection of dependants of the source requestable resource.
  185. */
  186. private Set<V> findAllDependantsByKey(final V sourceKey) {
  187. final Set<V> dependants = new HashSet<>();
  188. findDependantsSubGraphByKey(dependants, sourceKey);
  189. return dependants;
  190. }
  191. /**
  192. * Find all dependencies by a source requestable resource.
  193. *
  194. * @param sourceKey The source requestable key used as start point.
  195. * @return The found collection of dependencies of the source requestable resource.
  196. */
  197. private Set<V> findAllDependenciesByKey(final V sourceKey) {
  198. final Set<V> dependencies = new HashSet<>();
  199. findDependencySubGraphByRequestableKey(dependencies, sourceKey);
  200. return dependencies;
  201. }
  202. /**
  203. * Retrieve a sub graph of dependants based on a certain source requestable resource.
  204. *
  205. * @param resources The resources part of the final sub-graph.
  206. * @param sourceKey The source requestable key used as start point.
  207. */
  208. private void findDependantsSubGraphByKey(final Collection<V> resources, final V sourceKey) {
  209. if (resources.contains(sourceKey)) {
  210. return;
  211. }
  212. resources.add(sourceKey);
  213. for (final V predecessorRequestable : predecessorListOf(resourceGraph, sourceKey)) {
  214. findDependantsSubGraphByKey(resources, predecessorRequestable);
  215. }
  216. }
  217. /**
  218. * Retrieve a sub graph of dependencies based on a certain source requestable resource.
  219. *
  220. * @param resources The resources part of the final sub-graph.
  221. * @param sourceKey The source requestable used as start point.
  222. */
  223. private void findDependencySubGraphByRequestableKey(final Collection<V> resources, final V sourceKey) {
  224. if (resources.contains(sourceKey)) {
  225. return;
  226. }
  227. resources.add(sourceKey);
  228. for (final V successorRequestable : successorListOf(resourceGraph, sourceKey)) {
  229. findDependencySubGraphByRequestableKey(resources, successorRequestable);
  230. }
  231. }
  232. }