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