1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.directory.server.xdbm.search.cursor;
21
22
23 import java.io.IOException;
24 import java.util.ArrayList;
25 import java.util.HashSet;
26 import java.util.List;
27 import java.util.Set;
28
29 import org.apache.directory.api.ldap.model.constants.Loggers;
30 import org.apache.directory.api.ldap.model.cursor.Cursor;
31 import org.apache.directory.api.ldap.model.cursor.CursorException;
32 import org.apache.directory.api.ldap.model.cursor.InvalidCursorPositionException;
33 import org.apache.directory.api.ldap.model.exception.LdapException;
34 import org.apache.directory.api.ldap.model.filter.ExprNode;
35 import org.apache.directory.server.core.api.partition.PartitionTxn;
36 import org.apache.directory.server.i18n.I18n;
37 import org.apache.directory.server.xdbm.AbstractIndexCursor;
38 import org.apache.directory.server.xdbm.IndexEntry;
39 import org.apache.directory.server.xdbm.search.Evaluator;
40 import org.slf4j.Logger;
41 import org.slf4j.LoggerFactory;
42
43
44
45
46
47
48
49 public class OrCursor<V> extends AbstractIndexCursor<V>
50 {
51
52 private static final Logger LOG_CURSOR = LoggerFactory.getLogger( Loggers.CURSOR_LOG.getName() );
53
54
55 private static final boolean IS_DEBUG = LOG_CURSOR.isDebugEnabled();
56
57 private static final String UNSUPPORTED_MSG = I18n.err( I18n.ERR_722 );
58 private final List<Cursor<IndexEntry<V, String>>> cursors;
59 private final List<Evaluator<? extends ExprNode>> evaluators;
60 private final List<Set<String>> blacklists;
61 private int cursorIndex = -1;
62
63
64 private IndexEntry<V, String> prefetched;
65
66
67
68
69
70
71
72
73
74
75 public OrCursor( PartitionTxn partitionTxn, List<Cursor<IndexEntry<V, String>>> cursors,
76 List<Evaluator<? extends ExprNode>> evaluators )
77 {
78 if ( IS_DEBUG )
79 {
80 LOG_CURSOR.debug( "Creating OrCursor {}", this );
81 }
82
83 if ( cursors.size() <= 1 )
84 {
85 throw new IllegalArgumentException( I18n.err( I18n.ERR_723 ) );
86 }
87
88 this.cursors = cursors;
89 this.evaluators = evaluators;
90 this.blacklists = new ArrayList<>();
91 this.partitionTxn = partitionTxn;
92
93 for ( int i = 0; i < cursors.size(); i++ )
94 {
95 this.blacklists.add( new HashSet<String>() );
96 }
97
98 this.cursorIndex = 0;
99 }
100
101
102
103
104
105 protected String getUnsupportedMessage()
106 {
107 return UNSUPPORTED_MSG;
108 }
109
110
111
112
113
114 public void beforeFirst() throws LdapException, CursorException
115 {
116 checkNotClosed();
117 cursorIndex = 0;
118 cursors.get( cursorIndex ).beforeFirst();
119 setAvailable( false );
120 prefetched = null;
121 }
122
123
124
125
126
127 public void afterLast() throws LdapException, CursorException
128 {
129 checkNotClosed();
130 cursorIndex = cursors.size() - 1;
131 cursors.get( cursorIndex ).afterLast();
132 setAvailable( false );
133 prefetched = null;
134 }
135
136
137
138
139
140 public boolean first() throws LdapException, CursorException
141 {
142 beforeFirst();
143
144 return setAvailable( next() );
145 }
146
147
148
149
150
151 public boolean last() throws LdapException, CursorException
152 {
153 afterLast();
154
155 return setAvailable( previous() );
156 }
157
158
159 private boolean isBlackListed( String id )
160 {
161 return blacklists.get( cursorIndex ).contains( id );
162 }
163
164
165
166
167
168
169
170
171
172 private void blackListIfDuplicate( PartitionTxn partitionTxn, IndexEntry<?, String> indexEntry ) throws LdapException
173 {
174 for ( int ii = 0; ii < evaluators.size(); ii++ )
175 {
176 if ( ii == cursorIndex )
177 {
178 continue;
179 }
180
181 if ( evaluators.get( ii ).evaluate( partitionTxn, indexEntry ) )
182 {
183 blacklists.get( ii ).add( indexEntry.getId() );
184 }
185 }
186 }
187
188
189
190
191
192 @Override
193 public boolean previous() throws LdapException, CursorException
194 {
195 while ( cursors.get( cursorIndex ).previous() )
196 {
197 checkNotClosed();
198 IndexEntry<V, String> candidate = cursors.get( cursorIndex ).get();
199
200 if ( !isBlackListed( candidate.getId() ) )
201 {
202 blackListIfDuplicate( partitionTxn, candidate );
203
204 prefetched = candidate;
205 return setAvailable( true );
206 }
207 }
208
209 while ( cursorIndex > 0 )
210 {
211 checkNotClosed();
212 cursorIndex--;
213 cursors.get( cursorIndex ).afterLast();
214
215 while ( cursors.get( cursorIndex ).previous() )
216 {
217 checkNotClosed();
218 IndexEntry<V, String> candidate = cursors.get( cursorIndex ).get();
219
220 if ( !isBlackListed( candidate.getId() ) )
221 {
222 blackListIfDuplicate( partitionTxn, candidate );
223
224 prefetched = candidate;
225 return setAvailable( true );
226 }
227 }
228 }
229
230 prefetched = null;
231
232 return setAvailable( false );
233 }
234
235
236
237
238
239 @Override
240 public boolean next() throws LdapException, CursorException
241 {
242 while ( cursors.get( cursorIndex ).next() )
243 {
244 checkNotClosed();
245 IndexEntry<V, String> candidate = cursors.get( cursorIndex ).get();
246
247 if ( !isBlackListed( candidate.getId() ) )
248 {
249 blackListIfDuplicate( partitionTxn, candidate );
250
251 prefetched = candidate;
252
253 return setAvailable( true );
254 }
255 }
256
257 while ( cursorIndex < cursors.size() - 1 )
258 {
259 checkNotClosed();
260 cursorIndex++;
261 cursors.get( cursorIndex ).beforeFirst();
262
263 while ( cursors.get( cursorIndex ).next() )
264 {
265 checkNotClosed();
266 IndexEntry<V, String> candidate = cursors.get( cursorIndex ).get();
267
268 if ( !isBlackListed( candidate.getId() ) )
269 {
270 blackListIfDuplicate( partitionTxn, candidate );
271
272 prefetched = candidate;
273
274 return setAvailable( true );
275 }
276 }
277 }
278
279 prefetched = null;
280
281 return setAvailable( false );
282 }
283
284
285
286
287
288 public IndexEntry<V, String> get() throws CursorException
289 {
290 checkNotClosed();
291
292 if ( available() )
293 {
294 return prefetched;
295 }
296
297 throw new InvalidCursorPositionException( I18n.err( I18n.ERR_708 ) );
298 }
299
300
301
302
303
304 @Override
305 public void close() throws IOException
306 {
307 if ( IS_DEBUG )
308 {
309 LOG_CURSOR.debug( "Closing OrCursor {}", this );
310 }
311
312 super.close();
313
314 for ( Cursor<?> cursor : cursors )
315 {
316 cursor.close();
317 }
318 }
319
320
321
322
323
324 @Override
325 public void close( Exception cause ) throws IOException
326 {
327 if ( IS_DEBUG )
328 {
329 LOG_CURSOR.debug( "Closing OrCursor {}", this );
330 }
331
332 super.close( cause );
333
334 for ( Cursor<?> cursor : cursors )
335 {
336 cursor.close( cause );
337 }
338 }
339
340
341
342
343
344 private String dumpEvaluators( String tabs )
345 {
346 StringBuilder sb = new StringBuilder();
347
348 for ( Evaluator<? extends ExprNode> evaluator : evaluators )
349 {
350 sb.append( evaluator.toString( tabs + " >>" ) );
351 }
352
353 return sb.toString();
354 }
355
356
357
358
359
360 private String dumpCursors( String tabs )
361 {
362 StringBuilder sb = new StringBuilder();
363
364 for ( Cursor<IndexEntry<V, String>> cursor : cursors )
365 {
366 sb.append( cursor.toString( tabs + " " ) );
367 sb.append( "\n" );
368 }
369
370 return sb.toString();
371 }
372
373
374
375
376
377 @Override
378 public String toString( String tabs )
379 {
380 StringBuilder sb = new StringBuilder();
381
382 sb.append( tabs ).append( "OrCursor (" );
383
384 if ( available() )
385 {
386 sb.append( "available)" );
387 }
388 else
389 {
390 sb.append( "absent)" );
391 }
392
393 sb.append( "#" ).append( cursorIndex ).append( " : \n" );
394
395 if ( ( evaluators != null ) && !evaluators.isEmpty() )
396 {
397 sb.append( dumpEvaluators( tabs ) );
398 }
399
400 if ( ( cursors != null ) && !cursors.isEmpty() )
401 {
402 sb.append( dumpCursors( tabs ) ).append( '\n' );
403 }
404
405 return sb.toString();
406 }
407
408
409
410
411
412 public String toString()
413 {
414 return toString( "" );
415 }
416 }