1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 package org.dbunit.util.search;
23
24 import java.util.HashSet;
25 import java.util.Iterator;
26 import java.util.Set;
27 import java.util.SortedSet;
28
29 import org.apache.commons.collections.set.ListOrderedSet;
30 import org.slf4j.Logger;
31 import org.slf4j.LoggerFactory;
32 import org.dbunit.util.CollectionsHelper;
33
34
35
36
37
38
39
40
41
42
43
44
45
46 public class DepthFirstSearch implements ISearchAlgorithm {
47
48
49 private Set scannedNodes;
50 private Set reverseScannedNodes;
51
52 protected final Logger logger = LoggerFactory.getLogger(getClass());
53
54
55 private ListOrderedSet result;
56
57
58 private Set nodesFrom;
59
60
61 private ISearchCallback callback;
62
63
64 private boolean searching = false;
65
66
67
68
69 private int searchDepth = Integer.MAX_VALUE;
70
71
72
73
74
75 public DepthFirstSearch()
76 {
77 super();
78 }
79
80
81
82
83
84
85 public DepthFirstSearch(int searchDepth)
86 {
87 super();
88 if(searchDepth<=0){
89 throw new IllegalArgumentException("The searchDepth must be > 0. Given: " + searchDepth);
90 }
91 this.searchDepth = searchDepth;
92 }
93
94
95
96
97
98 public ListOrderedSet search(Object[] nodesFrom, ISearchCallback callback)
99 throws SearchException
100 {
101 if(logger.isDebugEnabled())
102 logger.debug("search(nodesFrom={}, callback={}) - start", nodesFrom, callback);
103
104 return search(CollectionsHelper.objectsToSet(nodesFrom), callback);
105 }
106
107
108
109
110 public ListOrderedSet search(Set nodesFrom, ISearchCallback callback)
111 throws SearchException {
112 if(logger.isDebugEnabled())
113 logger.debug("search(nodesFrom={}, callback={}) - start", nodesFrom, callback);
114
115 synchronized (this) {
116 if (searching) {
117 throw new IllegalStateException("already searching/searched");
118 }
119 this.searching = true;
120 }
121
122
123
124 this.result = new ListOrderedSet();
125
126
127 this.callback = callback;
128
129 this.nodesFrom = new ListOrderedSet();
130
131 int sizeNodesFromBefore = 0;
132 int sizeResultBefore = 0;
133 boolean keepSearching = true;
134 this.scannedNodes = new HashSet();
135 this.reverseScannedNodes = new HashSet();
136 this.scannedNodes = new HashSet();
137 do {
138
139
140
141
142
143
144
145 Iterator iterator = nodesFrom.iterator();
146 while (iterator.hasNext()) {
147 Object node = iterator.next();
148 reverseSearch(node, 0);
149 }
150
151
152
153 iterator = this.nodesFrom.iterator();
154
155 while (iterator.hasNext()) {
156 Object node = iterator.next();
157 search(node, 0);
158 }
159
160 nodesFrom = new HashSet(this.result);
161
162
163 boolean sizesDontMatch = this.result.size() != this.nodesFrom.size();
164 boolean resultChanged = this.result.size() != sizeResultBefore;
165 boolean nodesFromChanged = this.nodesFrom.size() != sizeNodesFromBefore;
166 sizeNodesFromBefore = this.nodesFrom.size();
167 sizeResultBefore = this.result.size();
168 keepSearching = sizesDontMatch && ( resultChanged || nodesFromChanged );
169
170 } while ( keepSearching );
171
172 return this.result;
173
174 }
175
176
177
178
179
180
181
182
183
184 private boolean search(Object node, int currentSearchDepth) throws SearchException {
185 if ( this.logger.isDebugEnabled() ) {
186 this.logger.debug( "search:" + node );
187 }
188 if (this.scannedNodes.contains(node)) {
189 if ( this.logger.isDebugEnabled() ) {
190 this.logger.debug( "already searched; returning true" );
191 }
192 return true;
193 }
194 if (!this.callback.searchNode(node)) {
195 if ( this.logger.isDebugEnabled() ) {
196 this.logger.debug( "Callback handler blocked search for node " + node );
197 }
198 return true;
199 }
200
201 if ( this.logger.isDebugEnabled() ) {
202 this.logger.debug("Pushing " + node);
203 }
204 this.scannedNodes.add(node);
205
206 if(currentSearchDepth < this.searchDepth) {
207
208 SortedSet edges = this.callback.getEdges(node);
209 if (edges != null) {
210 Iterator iterator = edges.iterator();
211 while (iterator.hasNext()) {
212
213 IEdge edge = (IEdge) iterator.next();
214 Object toNode = edge.getTo();
215 search(toNode, currentSearchDepth++);
216 }
217 }
218 }
219
220
221 if ( this.logger.isDebugEnabled() ) {
222 this.logger.debug( "Adding node " + node + " to the final result" );
223 }
224
225 this.callback.nodeAdded(node);
226 result.add(node);
227
228 return false;
229 }
230
231
232
233
234
235
236
237
238
239 private boolean reverseSearch(Object node, int currentSearchDepth) throws SearchException {
240 if ( this.logger.isDebugEnabled() ) {
241 this.logger.debug( "reverseSearch:" + node );
242 }
243 if (this.reverseScannedNodes.contains(node)) {
244 if ( this.logger.isDebugEnabled() ) {
245 this.logger.debug( "already searched; returning true" );
246 }
247 return true;
248 }
249
250 if (!this.callback.searchNode(node)) {
251 if ( this.logger.isDebugEnabled() ) {
252 this.logger.debug( "callback handler blocked reverse search for node " + node );
253 }
254 return true;
255 }
256
257 if ( this.logger.isDebugEnabled() ) {
258 this.logger.debug("Pushing (reverse) " + node);
259 }
260 this.reverseScannedNodes.add(node);
261
262 if(currentSearchDepth < this.searchDepth) {
263
264 SortedSet edges = this.callback.getEdges(node);
265 if (edges != null) {
266 Iterator iterator = edges.iterator();
267 while (iterator.hasNext()) {
268
269 IEdge edge = (IEdge) iterator.next();
270 Object toNode = edge.getTo();
271 if ( toNode.equals(node) ) {
272 Object fromNode = edge.getFrom();
273 reverseSearch(fromNode, currentSearchDepth++);
274 }
275 }
276 }
277 }
278
279
280 this.nodesFrom.add(node);
281
282 return false;
283
284 }
285
286
287 }