1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.project;
20
21 import java.util.*;
22
23 class Graph {
24 private enum DfsState {
25 VISITING,
26 VISITED
27 }
28
29 final Map<String, Vertex> vertices = new LinkedHashMap<>();
30
31 public Vertex getVertex(String id) {
32 return vertices.get(id);
33 }
34
35 public Collection<Vertex> getVertices() {
36 return vertices.values();
37 }
38
39 Vertex addVertex(String label) {
40 return vertices.computeIfAbsent(label, Vertex::new);
41 }
42
43 void addEdge(Vertex from, Vertex to) throws CycleDetectedException {
44 from.children.add(to);
45 to.parents.add(from);
46 List<String> cycle = findCycle(to);
47 if (cycle != null) {
48
49 removeEdge(from, to);
50 throw new CycleDetectedException(
51 "Edge between '" + from.label + "' and '" + to.label + "' introduces to cycle in the graph", cycle);
52 }
53 }
54
55 void removeEdge(Vertex from, Vertex to) {
56 from.children.remove(to);
57 to.parents.remove(from);
58 }
59
60 List<String> visitAll() {
61 return visitAll(vertices.values(), new HashMap<>(), new ArrayList<>());
62 }
63
64 List<String> findCycle(Vertex vertex) {
65 return visitCycle(Collections.singleton(vertex), new HashMap<>(), new LinkedList<>());
66 }
67
68 private static List<String> visitAll(
69 Collection<Vertex> children, Map<Vertex, DfsState> stateMap, List<String> list) {
70 for (Vertex v : children) {
71 DfsState state = stateMap.putIfAbsent(v, DfsState.VISITING);
72 if (state == null) {
73 visitAll(v.children, stateMap, list);
74 stateMap.put(v, DfsState.VISITED);
75 list.add(v.label);
76 }
77 }
78 return list;
79 }
80
81 private static List<String> visitCycle(
82 Collection<Vertex> children, Map<Vertex, DfsState> stateMap, LinkedList<String> cycle) {
83 for (Vertex v : children) {
84 DfsState state = stateMap.putIfAbsent(v, DfsState.VISITING);
85 if (state == null) {
86 cycle.addLast(v.label);
87 List<String> ret = visitCycle(v.children, stateMap, cycle);
88 if (ret != null) {
89 return ret;
90 }
91 cycle.removeLast();
92 stateMap.put(v, DfsState.VISITED);
93 } else if (state == DfsState.VISITING) {
94
95 int pos = cycle.lastIndexOf(v.label);
96 List<String> ret = cycle.subList(pos, cycle.size());
97 ret.add(v.label);
98 return ret;
99 }
100 }
101 return null;
102 }
103
104 static class Vertex {
105 final String label;
106 final List<Vertex> children = new ArrayList<>();
107 final List<Vertex> parents = new ArrayList<>();
108
109 Vertex(String label) {
110 this.label = label;
111 }
112
113 String getLabel() {
114 return label;
115 }
116
117 List<Vertex> getChildren() {
118 return children;
119 }
120
121 List<Vertex> getParents() {
122 return parents;
123 }
124 }
125 }