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.Collections;
26 import java.util.List;
27
28 import org.apache.directory.api.ldap.model.constants.Loggers;
29 import org.apache.directory.api.ldap.model.cursor.Cursor;
30 import org.apache.directory.api.ldap.model.cursor.CursorException;
31 import org.apache.directory.api.ldap.model.cursor.InvalidCursorPositionException;
32 import org.apache.directory.api.ldap.model.exception.LdapException;
33 import org.apache.directory.api.ldap.model.filter.ExprNode;
34 import org.apache.directory.server.core.api.partition.PartitionTxn;
35 import org.apache.directory.server.i18n.I18n;
36 import org.apache.directory.server.xdbm.AbstractIndexCursor;
37 import org.apache.directory.server.xdbm.IndexEntry;
38 import org.apache.directory.server.xdbm.search.Evaluator;
39 import org.apache.directory.server.xdbm.search.impl.ScanCountComparator;
40 import org.slf4j.Logger;
41 import org.slf4j.LoggerFactory;
42
43
44
45
46
47
48
49 public class AndCursor<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
58 private static final String UNSUPPORTED_MSG = I18n.err( I18n.ERR_707 );
59
60
61 private final Cursor<IndexEntry<V, String>> wrapped;
62
63
64 private final List<Evaluator<? extends ExprNode>> evaluators;
65
66
67
68
69
70
71
72
73
74
75 public AndCursor( PartitionTxn partitionTxn, Cursor<IndexEntry<V, String>> wrapped,
76 List<Evaluator<? extends ExprNode>> evaluators )
77 {
78 if ( IS_DEBUG )
79 {
80 LOG_CURSOR.debug( "Creating AndCursor {}", this );
81 }
82
83 this.wrapped = wrapped;
84 this.evaluators = optimize( evaluators );
85 this.partitionTxn = partitionTxn;
86 }
87
88
89
90
91
92 protected String getUnsupportedMessage()
93 {
94 return UNSUPPORTED_MSG;
95 }
96
97
98
99
100
101 public void beforeFirst() throws LdapException, CursorException
102 {
103 checkNotClosed();
104 wrapped.beforeFirst();
105 setAvailable( false );
106 }
107
108
109
110
111
112 public void afterLast() throws LdapException, CursorException
113 {
114 checkNotClosed();
115 wrapped.afterLast();
116 setAvailable( false );
117 }
118
119
120
121
122
123 public boolean first() throws LdapException, CursorException
124 {
125 beforeFirst();
126
127 return next();
128 }
129
130
131
132
133
134 public boolean last() throws LdapException, CursorException
135 {
136 afterLast();
137
138 return previous();
139 }
140
141
142
143
144
145 public boolean previous( PartitionTxn partitionTxn ) throws LdapException, CursorException
146 {
147 while ( wrapped.previous() )
148 {
149 checkNotClosed();
150
151 IndexEntry<V, String> candidate = wrapped.get();
152
153 if ( matches( partitionTxn, candidate ) )
154 {
155 return setAvailable( true );
156 }
157 }
158
159 return setAvailable( false );
160 }
161
162
163
164
165
166 public boolean next( PartitionTxn partitionTxn ) throws LdapException, CursorException
167 {
168 while ( wrapped.next() )
169 {
170 checkNotClosed();
171 IndexEntry<V, String> candidate = wrapped.get();
172
173 if ( matches( partitionTxn, candidate ) )
174 {
175 return setAvailable( true );
176 }
177 }
178
179 return setAvailable( false );
180 }
181
182
183
184
185
186 public IndexEntry<V, String> get() throws CursorException
187 {
188 checkNotClosed();
189
190 if ( available() )
191 {
192 return wrapped.get();
193 }
194
195 throw new InvalidCursorPositionException( I18n.err( I18n.ERR_708 ) );
196 }
197
198
199
200
201
202 @Override
203 public void close() throws IOException
204 {
205 if ( IS_DEBUG )
206 {
207 LOG_CURSOR.debug( "Closing AndCursor {}", this );
208 }
209
210 super.close();
211 wrapped.close();
212 }
213
214
215
216
217
218 @Override
219 public void close( Exception cause ) throws IOException
220 {
221 if ( IS_DEBUG )
222 {
223 LOG_CURSOR.debug( "Closing AndCursor {}", this );
224 }
225
226 super.close( cause );
227 wrapped.close( cause );
228 }
229
230
231
232
233
234
235
236
237
238
239
240
241
242 private List<Evaluator<? extends ExprNode>> optimize(
243 List<Evaluator<? extends ExprNode>> unoptimized )
244 {
245 List<Evaluator<? extends ExprNode>> optimized = new ArrayList<>(
246 unoptimized.size() );
247 optimized.addAll( unoptimized );
248
249 Collections.sort( optimized, new ScanCountComparator() );
250
251 return optimized;
252 }
253
254
255
256
257
258 private boolean matches( PartitionTxn partitionTxn, IndexEntry<V, String> indexEntry ) throws LdapException
259 {
260 for ( Evaluator<?> evaluator : evaluators )
261 {
262 if ( !evaluator.evaluate( partitionTxn, indexEntry ) )
263 {
264 return false;
265 }
266 }
267
268 return true;
269 }
270
271
272
273
274
275 private String dumpEvaluators( String tabs )
276 {
277 StringBuilder sb = new StringBuilder();
278
279 for ( Evaluator<? extends ExprNode> evaluator : evaluators )
280 {
281 sb.append( evaluator.toString( tabs + " >>" ) );
282 }
283
284 return sb.toString();
285 }
286
287
288
289
290
291 @Override
292 public String toString( String tabs )
293 {
294 StringBuilder sb = new StringBuilder();
295
296 sb.append( tabs ).append( "AndCursor (" );
297
298 if ( available() )
299 {
300 sb.append( "available) :\n" );
301 }
302 else
303 {
304 sb.append( "absent) :\n" );
305 }
306
307 if ( ( evaluators != null ) && !evaluators.isEmpty() )
308 {
309 sb.append( dumpEvaluators( tabs ) );
310 }
311
312 sb.append( wrapped.toString( tabs + " " ) );
313
314 return sb.toString();
315 }
316
317
318
319
320
321 public String toString()
322 {
323 return toString( "" );
324 }
325 }