001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License. You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied. See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 *
019 */
020 package org.apache.directory.server.xdbm.search.impl;
021
022
023 import org.apache.directory.server.xdbm.Index;
024 import org.apache.directory.server.xdbm.IndexEntry;
025 import org.apache.directory.server.xdbm.Store;
026 import org.apache.directory.server.xdbm.AbstractIndexCursor;
027 import org.apache.directory.server.xdbm.IndexCursor;
028 import org.apache.directory.server.i18n.I18n;
029 import org.apache.directory.shared.ldap.cursor.InvalidCursorPositionException;
030 import org.apache.directory.shared.ldap.entry.ServerEntry;
031 import org.apache.directory.shared.ldap.entry.Value;
032
033
034 /**
035 * A Cursor over entry candidates matching an equality assertion filter. This
036 * Cursor operates in two modes. The first is when an index exists for the
037 * attribute the equality assertion is built on. The second is when the user
038 * index for the assertion attribute does not exist. Different Cursors are
039 * used in each of these cases where the other remains null.
040 *
041 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
042 * @version $$Rev$$
043 */
044 public class EqualityCursor<V, ID> extends AbstractIndexCursor<V, ServerEntry, ID>
045 {
046 private static final String UNSUPPORTED_MSG = I18n.err( I18n.ERR_714 );
047
048 /** An equality evaluator for candidates */
049 @SuppressWarnings("unchecked")
050 private final EqualityEvaluator equalityEvaluator;
051
052 /** Cursor over attribute entry matching filter: set when index present */
053 private final IndexCursor<V, ServerEntry, ID> userIdxCursor;
054
055 /** NDN Cursor on all entries in (set when no index on user attribute) */
056 private final IndexCursor<String, ServerEntry, ID> ndnIdxCursor;
057
058 /** used only when ndnIdxCursor is used (no index on attribute) */
059 private boolean available = false;
060
061
062 @SuppressWarnings("unchecked")
063 public EqualityCursor( Store<ServerEntry, ID> db, EqualityEvaluator<V, ID> equalityEvaluator ) throws Exception
064 {
065 this.equalityEvaluator = equalityEvaluator;
066
067 String attribute = equalityEvaluator.getExpression().getAttribute();
068 Value<V> value = equalityEvaluator.getExpression().getValue();
069 if ( db.hasIndexOn( attribute ) )
070 {
071 Index<V, ServerEntry, ID> userIndex = ( Index<V, ServerEntry, ID> ) db.getIndex( attribute );
072 userIdxCursor = userIndex.forwardCursor( value.get() );
073 ndnIdxCursor = null;
074 }
075 else
076 {
077 ndnIdxCursor = db.getNdnIndex().forwardCursor();
078 userIdxCursor = null;
079 }
080 }
081
082
083 public boolean available()
084 {
085 if ( userIdxCursor != null )
086 {
087 return userIdxCursor.available();
088 }
089
090 return available;
091 }
092
093
094 public void beforeValue( ID id, V value ) throws Exception
095 {
096 checkNotClosed( "beforeValue()" );
097 if ( userIdxCursor != null )
098 {
099 userIdxCursor.beforeValue( id, value );
100 }
101 else
102 {
103 throw new UnsupportedOperationException( UNSUPPORTED_MSG );
104 }
105 }
106
107
108 public void before( IndexEntry<V, ServerEntry, ID> element ) throws Exception
109 {
110 checkNotClosed( "before()" );
111 if ( userIdxCursor != null )
112 {
113 userIdxCursor.before( element );
114 }
115 else
116 {
117 throw new UnsupportedOperationException( UNSUPPORTED_MSG );
118 }
119 }
120
121
122 public void afterValue( ID id, V key ) throws Exception
123 {
124 checkNotClosed( "afterValue()" );
125 if ( userIdxCursor != null )
126 {
127 userIdxCursor.afterValue( id, key );
128 }
129 else
130 {
131 throw new UnsupportedOperationException( UNSUPPORTED_MSG );
132 }
133 }
134
135
136 public void after( IndexEntry<V, ServerEntry, ID> element ) throws Exception
137 {
138 checkNotClosed( "after()" );
139 if ( userIdxCursor != null )
140 {
141 userIdxCursor.after( element );
142 }
143 else
144 {
145 throw new UnsupportedOperationException( UNSUPPORTED_MSG );
146 }
147 }
148
149
150 public void beforeFirst() throws Exception
151 {
152 checkNotClosed( "beforeFirst()" );
153 if ( userIdxCursor != null )
154 {
155 userIdxCursor.beforeFirst();
156 }
157 else
158 {
159 ndnIdxCursor.beforeFirst();
160 available = false;
161 }
162 }
163
164
165 public void afterLast() throws Exception
166 {
167 checkNotClosed( "afterLast()" );
168 if ( userIdxCursor != null )
169 {
170 userIdxCursor.afterLast();
171 }
172 else
173 {
174 ndnIdxCursor.afterLast();
175 available = false;
176 }
177 }
178
179
180 public boolean first() throws Exception
181 {
182 beforeFirst();
183 return next();
184 }
185
186
187 public boolean last() throws Exception
188 {
189 afterLast();
190 return previous();
191 }
192
193
194 @SuppressWarnings("unchecked")
195 public boolean previous() throws Exception
196 {
197 if ( userIdxCursor != null )
198 {
199 return userIdxCursor.previous();
200 }
201
202 while ( ndnIdxCursor.previous() )
203 {
204 checkNotClosed( "previous()" );
205 IndexEntry<?, ServerEntry, ID> candidate = ndnIdxCursor.get();
206 if ( equalityEvaluator.evaluate( candidate ) )
207 {
208 return available = true;
209 }
210 }
211
212 return available = false;
213 }
214
215
216 @SuppressWarnings("unchecked")
217 public boolean next() throws Exception
218 {
219 if ( userIdxCursor != null )
220 {
221 return userIdxCursor.next();
222 }
223
224 while ( ndnIdxCursor.next() )
225 {
226 checkNotClosed( "next()" );
227 IndexEntry<?, ServerEntry, ID> candidate = ndnIdxCursor.get();
228 if ( equalityEvaluator.evaluate( candidate ) )
229 {
230 return available = true;
231 }
232 }
233
234 return available = false;
235 }
236
237
238 @SuppressWarnings("unchecked")
239 public IndexEntry<V, ServerEntry, ID> get() throws Exception
240 {
241 checkNotClosed( "get()" );
242 if ( userIdxCursor != null )
243 {
244 return userIdxCursor.get();
245 }
246
247 if ( available )
248 {
249 return ( IndexEntry<V, ServerEntry, ID> ) ndnIdxCursor.get();
250 }
251
252 throw new InvalidCursorPositionException( I18n.err( I18n.ERR_708 ) );
253 }
254
255
256 public boolean isElementReused()
257 {
258 if ( userIdxCursor != null )
259 {
260 return userIdxCursor.isElementReused();
261 }
262
263 return ndnIdxCursor.isElementReused();
264 }
265
266
267 public void close() throws Exception
268 {
269 super.close();
270
271 if ( userIdxCursor != null )
272 {
273 userIdxCursor.close();
274 }
275 else
276 {
277 ndnIdxCursor.close();
278 }
279 }
280 }