1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.model.building;
20
21 import java.util.Collection;
22 import java.util.Collections;
23 import java.util.HashMap;
24 import java.util.HashSet;
25 import java.util.LinkedHashMap;
26 import java.util.LinkedList;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.Set;
30
31
32
33
34 @Deprecated(since = "4.0.0")
35 class Graph {
36
37 final Map<String, Set<String>> graph = new LinkedHashMap<>();
38
39 synchronized void addEdge(String from, String to) throws CycleDetectedException {
40 if (graph.computeIfAbsent(from, l -> new HashSet<>()).add(to)) {
41 List<String> cycle = visitCycle(graph, Collections.singleton(to), new HashMap<>(), new LinkedList<>());
42 if (cycle != null) {
43
44 throw new CycleDetectedException(
45 "Edge between '" + from + "' and '" + to + "' introduces to cycle in the graph", cycle);
46 }
47 }
48 }
49
50 private enum DfsState {
51 VISITING,
52 VISITED
53 }
54
55 private static List<String> visitCycle(
56 Map<String, Set<String>> graph,
57 Collection<String> children,
58 Map<String, DfsState> stateMap,
59 LinkedList<String> cycle) {
60 if (children != null) {
61 for (String v : children) {
62 DfsState state = stateMap.putIfAbsent(v, DfsState.VISITING);
63 if (state == null) {
64 cycle.addLast(v);
65 List<String> ret = visitCycle(graph, graph.get(v), stateMap, cycle);
66 if (ret != null) {
67 return ret;
68 }
69 cycle.removeLast();
70 stateMap.put(v, DfsState.VISITED);
71 } else if (state == DfsState.VISITING) {
72
73 int pos = cycle.lastIndexOf(v);
74 List<String> ret = cycle.subList(pos, cycle.size());
75 ret.add(v);
76 return ret;
77 }
78 }
79 }
80 return null;
81 }
82
83 public static class CycleDetectedException extends Exception {
84 private final List<String> cycle;
85
86 CycleDetectedException(String message, List<String> cycle) {
87 super(message);
88 this.cycle = cycle;
89 }
90
91 public List<String> getCycle() {
92 return cycle;
93 }
94
95 @Override
96 public String getMessage() {
97 return super.getMessage() + " " + String.join(" --> ", cycle);
98 }
99 }
100 }