1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.grouplens.grapht.graph;
21
22 import com.google.common.base.*;
23 import com.google.common.collect.*;
24 import org.apache.commons.lang3.tuple.Pair;
25
26 import javax.annotation.Nonnull;
27 import javax.annotation.Nullable;
28 import javax.annotation.concurrent.Immutable;
29 import java.io.IOException;
30 import java.io.ObjectInputStream;
31 import java.io.Serializable;
32 import java.util.*;
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52 @Immutable
53 public class DAGNode<V,E> implements Serializable {
54 private static final long serialVersionUID = 1L;
55
56 @Nonnull
57 @SuppressWarnings("squid:S1948")
58 private final V label;
59 @Nonnull
60 private final ImmutableSet<DAGEdge<V,E>> outgoingEdges;
61
62 private transient Supplier<SetMultimap<DAGNode<V,E>,DAGEdge<V,E>>> reverseEdgeCache;
63 private transient Supplier<Set<DAGNode<V,E>>> reachableNodeCache;
64 private transient Supplier<List<DAGNode<V,E>>> topologicalSortCache;
65
66
67
68
69
70
71
72 public static <V,E> DAGNode<V,E> singleton(V label) {
73 Preconditions.checkNotNull(label, "node label");
74 return new DAGNode<V,E>(label, ImmutableSet.<Pair<DAGNode<V, E>, E>>of());
75 }
76
77
78
79
80
81
82
83 public static <V,E> DAGNodeBuilder<V,E> newBuilder() {
84 return new DAGNodeBuilder<V, E>();
85 }
86
87
88
89
90
91
92
93
94 public static <V,E> DAGNodeBuilder<V,E> newBuilder(V label) {
95 return new DAGNodeBuilder<V, E>(label);
96 }
97
98
99
100
101
102
103
104
105 public static <V,E> DAGNodeBuilder<V,E> copyBuilder(DAGNode<V,E> node) {
106 DAGNodeBuilder<V,E> bld = newBuilder(node.getLabel());
107 for (DAGEdge<V,E> edge: node.getOutgoingEdges()) {
108 bld.addEdge(edge.getTail(), edge.getLabel());
109 }
110 return bld;
111 }
112
113
114
115
116
117
118
119
120 DAGNode(@Nonnull V lbl, Iterable<Pair<DAGNode<V,E>,E>> edges) {
121 label = lbl;
122 ImmutableSet.Builder<DAGEdge<V,E>> bld = ImmutableSet.builder();
123 for (Pair<DAGNode<V,E>,E> pair: edges) {
124 DAGEdge<V,E> edge = new DAGEdge<V, E>(this, pair.getLeft(), pair.getRight());
125 bld.add(edge);
126 }
127 outgoingEdges = bld.build();
128 initializeCaches();
129 }
130
131
132
133
134 private void initializeCaches() {
135 reverseEdgeCache = Suppliers.memoize(new EdgeMapSupplier());
136 reachableNodeCache = Suppliers.memoize(new NodeSetSupplier());
137 topologicalSortCache = Suppliers.memoize(new TopologicalSortSupplier());
138 }
139
140
141
142
143
144
145
146
147
148 private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
149 stream.defaultReadObject();
150 initializeCaches();
151 }
152
153
154
155
156
157 @Nonnull
158 public V getLabel() {
159 return label;
160 }
161
162
163
164
165
166 @Nonnull
167 public Set<DAGEdge<V,E>> getOutgoingEdges() {
168 return outgoingEdges;
169 }
170
171
172
173
174
175
176
177
178 public DAGEdge<V,E> getOutgoingEdge(DAGNode<V,E> target, E label) {
179 for (DAGEdge<V,E> edge: outgoingEdges) {
180 if (edge.getTail().equals(target) && edge.getLabel().equals(label)) {
181 return edge;
182 }
183 }
184 return null;
185 }
186
187
188
189
190
191
192
193
194 public DAGEdge<V,E> getOutgoingEdgeWithLabel(E label) {
195 return getOutgoingEdgeWithLabel(Predicates.equalTo(label));
196 }
197
198
199
200
201
202
203
204
205
206 public DAGEdge<V, E> getOutgoingEdgeWithLabel(Predicate<? super E> predicate) {
207 Predicate<DAGEdge<?, E>> edgePred = DAGEdge.labelMatches(predicate);
208 return Iterables.find(outgoingEdges, edgePred, null);
209 }
210
211
212
213
214
215 public Set<DAGNode<V,E>> getAdjacentNodes() {
216 return FluentIterable.from(outgoingEdges)
217 .transform(DAGEdge.<V, E>extractTail())
218 .toSet();
219 }
220
221
222
223
224
225
226
227 @Nonnull
228 private SetMultimap<DAGNode<V,E>,DAGEdge<V,E>> getIncomingEdgeMap() {
229 return reverseEdgeCache.get();
230 }
231
232 @Nonnull
233 public Set<DAGNode<V,E>> getReachableNodes() {
234 return reachableNodeCache.get();
235 }
236
237
238
239
240
241
242
243
244
245
246 @Nonnull
247 public List<DAGNode<V,E>> getSortedNodes() {
248 return topologicalSortCache.get();
249 }
250
251
252
253
254
255
256
257
258 private void sortVisit(LinkedHashSet<DAGNode<V,E>> visited) {
259 if (!visited.contains(this)) {
260 for (DAGEdge<V,E> nbr: outgoingEdges) {
261 nbr.getTail().sortVisit(visited);
262 }
263
264 assert !visited.contains(this);
265 visited.add(this);
266 }
267 }
268
269
270
271
272
273 @Nonnull
274 public Set<DAGEdge<V,E>> getIncomingEdges(DAGNode<V,E> node) {
275 return getIncomingEdgeMap().get(node);
276 }
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291 public DAGNode<V,E> replaceNode(DAGNode<V,E> node, DAGNode<V,E> replacement,
292 Map<DAGNode<V,E>,DAGNode<V,E>> memory) {
293 if (this.equals(node)) {
294 memory.put(node, replacement);
295 return replacement;
296 } else if (memory.containsKey(this)) {
297
298 return memory.get(this);
299 } else if (getReachableNodes().contains(node)) {
300 DAGNodeBuilder<V,E> bld = newBuilder(label);
301 for (DAGEdge<V,E> edge: outgoingEdges) {
302 DAGNode<V,E> newTail = edge.getTail().replaceNode(node, replacement, memory);
303 bld.addEdge(newTail, edge.getLabel());
304 }
305 DAGNode<V,E> repl = bld.build();
306 memory.put(this, repl);
307 return repl;
308 } else {
309 return this;
310 }
311 }
312
313
314
315
316
317
318
319
320 public DAGNode<V, E> findNodeBFS(@Nonnull Predicate<? super DAGNode<V, E>> pred) {
321 if (pred.apply(this)) {
322 return this;
323 }
324
325 Queue<DAGNode<V, E>> work = Lists.newLinkedList();
326 Set<DAGNode<V, E>> seen = Sets.newHashSet();
327 work.add(this);
328 seen.add(this);
329 while (!work.isEmpty()) {
330 DAGNode<V, E> node = work.remove();
331 for (DAGEdge<V, E> e : node.getOutgoingEdges()) {
332
333 DAGNode<V, E> nbr = e.getTail();
334 if (!seen.contains(nbr)) {
335 if (pred.apply(nbr)) {
336 return nbr;
337 } else {
338 seen.add(nbr);
339 work.add(nbr);
340 }
341 }
342 }
343 }
344
345
346 return null;
347 }
348
349
350
351
352
353
354
355
356 public DAGEdge<V, E> findEdgeBFS(@Nonnull Predicate<? super DAGEdge<V, E>> pred) {
357 Queue<DAGNode<V, E>> work = Lists.newLinkedList();
358 Set<DAGNode<V, E>> seen = Sets.newHashSet();
359 work.add(this);
360 seen.add(this);
361 while (!work.isEmpty()) {
362 DAGNode<V, E> node = work.remove();
363 for (DAGEdge<V, E> e : node.getOutgoingEdges()) {
364
365 if (pred.apply(e)) {
366 return e;
367 } else if (!seen.contains(e.getTail())) {
368 seen.add(e.getTail());
369 work.add(e.getTail());
370 }
371 }
372 }
373
374
375 return null;
376 }
377
378
379
380
381
382
383
384
385
386
387
388
389 public DAGNode<V,E> transformEdges(Function<? super DAGEdge<V,E>, ? extends DAGEdge<V,E>> function) {
390
391 DAGNodeBuilder<V,E> builder = null;
392
393 List<DAGEdge<V,E>> intact = Lists.newArrayListWithCapacity(outgoingEdges.size());
394 for (DAGEdge<V,E> edge: outgoingEdges) {
395 DAGNode<V,E> tail = edge.getTail();
396 DAGNode<V,E> transformedTail = tail.transformEdges(function);
397 DAGEdge<V,E> toQuery = edge;
398 if (transformedTail != tail) {
399
400 toQuery = DAGEdge.create(this, transformedTail, edge.getLabel());
401 }
402 DAGEdge<V,E> transformedEdge = function.apply(toQuery);
403 if (transformedEdge == null) {
404 transformedEdge = toQuery;
405 }
406 if (edge.equals(transformedEdge)) {
407
408 if (builder == null) {
409 intact.add(transformedEdge);
410 } else {
411 builder.addEdge(transformedEdge.getTail(), transformedEdge.getLabel());
412 }
413 } else {
414
415 if (builder == null) {
416 builder = newBuilder(label);
417 for (DAGEdge<V,E> done: intact) {
418 builder.addEdge(done.getTail(), done.getLabel());
419 }
420 }
421 builder.addEdge(transformedEdge.getTail(), transformedEdge.getLabel());
422 }
423 }
424
425 if (builder != null) {
426 return builder.build();
427 } else {
428 return this;
429 }
430 }
431
432 @Override
433 public String toString() {
434 StringBuilder sb = new StringBuilder();
435 sb.append("node ")
436 .append(label)
437 .append(" with ")
438 .append(getReachableNodes().size())
439 .append(" nodes and ")
440 .append(outgoingEdges.size())
441 .append(" edges");
442 return sb.toString();
443 }
444
445 public static <V> Predicate<DAGNode<V,?>> labelMatches(final Predicate<? super V> pred) {
446 return new Predicate<DAGNode<V, ?>>() {
447 @Override
448 public boolean apply(@Nullable DAGNode<V, ?> input) {
449 V lbl = input == null ? null : input.getLabel();
450 return pred.apply(lbl);
451 }
452 };
453 }
454
455
456
457
458 private class EdgeMapSupplier implements Supplier<SetMultimap<DAGNode<V, E>, DAGEdge<V, E>>> {
459 @Override
460 public SetMultimap<DAGNode<V, E>, DAGEdge<V, E>> get() {
461 ImmutableSetMultimap.Builder<DAGNode<V,E>,DAGEdge<V,E>> bld = ImmutableSetMultimap.builder();
462 for (DAGEdge<V,E> nbr: outgoingEdges) {
463 bld.put(nbr.getTail(), nbr);
464 bld.putAll(nbr.getTail().getIncomingEdgeMap());
465 }
466 return bld.build();
467 }
468 }
469
470
471
472
473 private class NodeSetSupplier implements Supplier<Set<DAGNode<V, E>>> {
474 @Override
475 public ImmutableSet<DAGNode<V, E>> get() {
476 return ImmutableSet.copyOf(getSortedNodes());
477 }
478 }
479
480 private class TopologicalSortSupplier implements Supplier<List<DAGNode<V,E>>> {
481 @Override
482 public List<DAGNode<V, E>> get() {
483 LinkedHashSet<DAGNode<V,E>> visited = Sets.newLinkedHashSet();
484 sortVisit(visited);
485 return ImmutableList.copyOf(visited);
486 }
487 }
488 }