1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32 package org.spf4j.reflect;
33
34 import com.google.common.graph.GraphBuilder;
35 import com.google.common.graph.MutableGraph;
36 import com.google.common.reflect.TypeToken;
37 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
38 import java.lang.reflect.Type;
39 import java.util.ArrayList;
40 import java.util.HashMap;
41 import java.util.HashSet;
42 import java.util.List;
43 import java.util.Map;
44 import java.util.Set;
45 import org.spf4j.ds.Graphs;
46
47
48
49
50 public final class GraphTypeMap<H> implements TypeMap<H> {
51
52 private final MutableGraph<TypeToken> typeGraph;
53
54 private final Map<TypeToken, H> handlers;
55
56 public GraphTypeMap() {
57 this(16);
58 }
59
60 public GraphTypeMap(final int expectedSize) {
61 typeGraph = GraphBuilder.directed().allowsSelfLoops(false)
62 .expectedNodeCount(expectedSize).build();
63 handlers = new HashMap<>(expectedSize);
64 }
65
66 @Override
67 public Set<H> getAll(final Type t) {
68 Set<H> result = new HashSet<>(1);
69 TypeToken tt = TypeToken.of(t);
70 MutableGraph<TypeToken> traverseGraph = Graphs.clone(typeGraph);
71 Set<TypeToken> nodes = traverseGraph.nodes();
72 List<TypeToken> nodesToRemove = new ArrayList<>();
73 do {
74 for (TypeToken token : nodes) {
75 if (traverseGraph.inDegree(token) == 0) {
76 nodesToRemove.add(token);
77 if (tt.isSubtypeOf(token)) {
78 result.add(handlers.get(token));
79 }
80 }
81 }
82 for (TypeToken token : nodesToRemove) {
83 if (!traverseGraph.removeNode(token)) {
84 throw new IllegalStateException("Cannot remove " + token + " from " + traverseGraph);
85 }
86 }
87 nodesToRemove.clear();
88 nodes = traverseGraph.nodes();
89 } while (result.isEmpty() && !nodes.isEmpty());
90 return result;
91 }
92
93
94 @Override
95 @SuppressFBWarnings("RV_RETURN_VALUE_IGNORED")
96 public boolean putIfNotPresent(final Type type, final H value) {
97 TypeToken<?> nType = TypeToken.of(type);
98 if (typeGraph.addNode(nType)) {
99 handlers.put(nType, value);
100 for (TypeToken t : typeGraph.nodes()) {
101 if (!nType.equals(t)) {
102 if (nType.isSubtypeOf(t)) {
103 typeGraph.putEdge(nType, t);
104 } else if (t.isSubtypeOf(nType)) {
105 typeGraph.putEdge(t, nType);
106 }
107 }
108 }
109 return true;
110 } else {
111 return false;
112 }
113 }
114
115 @Override
116 public boolean remove(final Type type) {
117 TypeToken<?> tt = TypeToken.of(type);
118 if (typeGraph.removeNode(tt)) {
119 handlers.remove(tt);
120 return true;
121 } else {
122 return false;
123 }
124 }
125
126 @Override
127 public String toString() {
128 return "GraphTypeMap{" + "typeGraph=" + typeGraph + ", handlers=" + handlers + '}';
129 }
130
131 @Override
132 public H getExact(final Type t) {
133 return handlers.get(TypeToken.of(t));
134 }
135
136 }