1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.myfaces.tobago.internal.config;
21
22 import org.slf4j.Logger;
23 import org.slf4j.LoggerFactory;
24
25 import java.lang.invoke.MethodHandles;
26 import java.util.ArrayList;
27 import java.util.List;
28
29 public class TobagoConfigSorter {
30
31 private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
32
33 private final List<Vertex> vertices = new ArrayList<>();
34
35 public TobagoConfigSorter(final List<TobagoConfigFragment> fragmentList) {
36 for (TobagoConfigFragment tobagoConfigFragment : fragmentList) {
37 vertices.add(new Vertex(tobagoConfigFragment));
38 }
39 }
40
41
42
43
44
45
46 public List<TobagoConfigFragment> topologicalSort() {
47
48 createEdges();
49 checkCycles();
50
51 List<TobagoConfigFragment> result = new ArrayList<>();
52
53 for (Vertex vertex : vertices) {
54 topologicalSort0(vertex, result);
55 }
56
57 logResult(result);
58
59 return result;
60 }
61
62
63
64
65 private void topologicalSort0(Vertex vertex, List<TobagoConfigFragment> result) {
66 if (vertex.isVisited()) {
67 return;
68 }
69
70 vertex.setVisited(true);
71
72
73 for (Vertex adjacent : vertex.getAdjacencyList()) {
74 topologicalSort0(adjacent, result);
75 }
76
77 result.add(vertex.getFragment());
78 }
79
80 private void logResult(List<TobagoConfigFragment> result) {
81 if (LOG.isInfoEnabled()) {
82 StringBuilder builder = new StringBuilder("Order of the Tobago config files: ");
83 for (TobagoConfigFragment fragment : result) {
84 final String name = fragment.getName();
85 if (LOG.isDebugEnabled()) {
86 builder.append("name=");
87 if (name == null) {
88 builder.append("<unnamed>");
89 } else {
90 builder.append("'");
91 builder.append(name);
92 builder.append("'");
93 }
94 builder.append(" url='");
95 builder.append(fragment.getUrl());
96 builder.append("'");
97 } else {
98 builder.append(name);
99 builder.append(", ");
100 }
101 }
102 LOG.info(builder.toString());
103 }
104 }
105
106
107 private void createEdges() {
108
109
110 for (final Vertex vertex : vertices) {
111 final TobagoConfigFragment current = vertex.getFragment();
112
113 for (final String befores : current.getBefore()) {
114 final TobagoConfigFragment before = findByName(befores);
115 if (before != null) {
116 findVertex(before).addAdjacent(findVertex(current));
117 }
118 }
119 for (final String afters : current.getAfter()) {
120 final TobagoConfigFragment after = findByName(afters);
121 if (after != null) {
122 findVertex(current).addAdjacent(findVertex(after));
123 }
124 }
125 }
126 }
127
128
129
130
131
132
133 private void checkCycles() {
134 LOG.debug("Cycle detection:");
135 for (Vertex vertex : vertices) {
136 if (LOG.isDebugEnabled()) {
137 LOG.debug("Checking reachable vertices from base {}", vertex.getFragment());
138 }
139 checkCycles0(vertex, vertex);
140 }
141 }
142
143 private void checkCycles0(final Vertex vertex, final Vertex base) {
144 if (LOG.isDebugEnabled()) {
145 LOG.debug("vertex: {}", vertex.toString());
146 LOG.debug("base: {}", base.getFragment().toString());
147 }
148 for (Vertex adjacent : vertex.getAdjacencyList()) {
149 if (base == adjacent) {
150 throw new IllegalStateException("Cycle detected name='" + vertex + "' base=" + base + "! ");
151 }
152 checkCycles0(adjacent, base);
153 }
154 }
155
156 private Vertex findVertex(final TobagoConfigFragment fragment) {
157 for (Vertex vertex : vertices) {
158 if (vertex.getFragment() == fragment) {
159 return vertex;
160 }
161 }
162 throw new RuntimeException("Problem with sorting! This might be a bug.");
163 }
164
165 private TobagoConfigFragment findByName(final String name) {
166 for (final Vertex vertex : vertices) {
167 TobagoConfigFragment fragment = vertex.getFragment();
168 if (name.equals(fragment.getName())) {
169 return fragment;
170 }
171 }
172 return null;
173 }
174
175 private static class Vertex {
176
177 private final TobagoConfigFragment fragment;
178 private final List<Vertex> adjacencyList;
179 private boolean visited;
180
181 private Vertex(final TobagoConfigFragment fragment) {
182 this.fragment = fragment;
183 this.adjacencyList = new ArrayList<>();
184 }
185
186 public boolean isVisited() {
187 return visited;
188 }
189
190 public void setVisited(boolean visited) {
191 this.visited = visited;
192 }
193
194 public TobagoConfigFragment getFragment() {
195 return fragment;
196 }
197
198 public void addAdjacent(Vertex higher) {
199 adjacencyList.add(higher);
200 }
201
202 public List<Vertex> getAdjacencyList() {
203 return adjacencyList;
204 }
205
206 @Override
207 public String toString() {
208 StringBuilder builder = new StringBuilder();
209 builder.append(fragment);
210 builder.append(" -> [");
211 for (Vertex vertex : adjacencyList) {
212 builder.append(vertex.getFragment());
213 builder.append(", ");
214 }
215 if (builder.charAt(builder.length() - 1) == ' ') {
216 builder.delete(builder.length() - 2, builder.length());
217 }
218 builder.append("]");
219 return builder.toString();
220 }
221 }
222 }