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.IndexEntry;
024 import org.apache.directory.server.xdbm.Store;
025 import org.apache.directory.server.xdbm.AbstractIndexCursor;
026 import org.apache.directory.server.xdbm.IndexCursor;
027 import org.apache.directory.server.i18n.I18n;
028 import org.apache.directory.shared.ldap.cursor.InvalidCursorPositionException;
029 import org.apache.directory.shared.ldap.entry.ServerEntry;
030 import org.apache.directory.shared.ldap.schema.AttributeType;
031
032
033 /**
034 * A returning candidates satisfying an attribute presence expression.
035 *
036 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
037 * @version $$Rev$$
038 */
039 public class PresenceCursor<ID> extends AbstractIndexCursor<String, ServerEntry, ID>
040 {
041 private static final String UNSUPPORTED_MSG = I18n.err( I18n.ERR_724 );
042 private final IndexCursor<String, ServerEntry, ID> ndnCursor;
043 private final IndexCursor<String, ServerEntry, ID> presenceCursor;
044 private final PresenceEvaluator<ID> presenceEvaluator;
045 private boolean available = false;
046
047
048 public PresenceCursor( Store<ServerEntry, ID> db, PresenceEvaluator<ID> presenceEvaluator ) throws Exception
049 {
050 this.presenceEvaluator = presenceEvaluator;
051 AttributeType type = presenceEvaluator.getAttributeType();
052
053 // we don't maintain a presence index for objectClass, entryUUID, and entryCSN
054 // as it doesn't make sense because every entry has such an attribute
055 // instead for those attributes and all un-indexed attributes we use the ndn index
056 if ( db.hasUserIndexOn( type.getOid() ) )
057 {
058 presenceCursor = db.getPresenceIndex().forwardCursor( type.getOid() );
059 ndnCursor = null;
060 }
061 else
062 {
063 presenceCursor = null;
064 ndnCursor = db.getNdnIndex().forwardCursor();
065 }
066 }
067
068
069 public boolean available()
070 {
071 if ( presenceCursor != null )
072 {
073 return presenceCursor.available();
074 }
075
076 return available;
077 }
078
079
080 public void beforeValue( ID id, String value ) throws Exception
081 {
082 checkNotClosed( "beforeValue()" );
083 if ( presenceCursor != null )
084 {
085 presenceCursor.beforeValue( id, value );
086 return;
087 }
088
089 throw new UnsupportedOperationException( UNSUPPORTED_MSG );
090 }
091
092
093 public void before( IndexEntry<String, ServerEntry, ID> element ) throws Exception
094 {
095 checkNotClosed( "before()" );
096 if ( presenceCursor != null )
097 {
098 presenceCursor.before( element );
099 return;
100 }
101
102 throw new UnsupportedOperationException( UNSUPPORTED_MSG );
103 }
104
105
106 public void afterValue( ID id, String value ) throws Exception
107 {
108 checkNotClosed( "afterValue()" );
109 if ( presenceCursor != null )
110 {
111 presenceCursor.afterValue( id, value );
112 return;
113 }
114
115 throw new UnsupportedOperationException( UNSUPPORTED_MSG );
116 }
117
118
119 public void after( IndexEntry<String, ServerEntry, ID> element ) throws Exception
120 {
121 checkNotClosed( "after()" );
122 if ( presenceCursor != null )
123 {
124 presenceCursor.after( element );
125 return;
126 }
127
128 throw new UnsupportedOperationException( UNSUPPORTED_MSG );
129 }
130
131
132 public void beforeFirst() throws Exception
133 {
134 checkNotClosed( "beforeFirst()" );
135 if ( presenceCursor != null )
136 {
137 presenceCursor.beforeFirst();
138 return;
139 }
140
141 ndnCursor.beforeFirst();
142 available = false;
143 }
144
145
146 public void afterLast() throws Exception
147 {
148 checkNotClosed( "afterLast()" );
149 if ( presenceCursor != null )
150 {
151 presenceCursor.afterLast();
152 return;
153 }
154
155 ndnCursor.afterLast();
156 available = false;
157 }
158
159
160 public boolean first() throws Exception
161 {
162 checkNotClosed( "first()" );
163 if ( presenceCursor != null )
164 {
165 return presenceCursor.first();
166 }
167
168 beforeFirst();
169 return next();
170 }
171
172
173 public boolean last() throws Exception
174 {
175 checkNotClosed( "last()" );
176 if ( presenceCursor != null )
177 {
178 return presenceCursor.last();
179 }
180
181 afterLast();
182 return previous();
183 }
184
185
186 public boolean previous() throws Exception
187 {
188 checkNotClosed( "previous()" );
189 if ( presenceCursor != null )
190 {
191 return presenceCursor.previous();
192 }
193
194 while ( ndnCursor.previous() )
195 {
196 checkNotClosed( "previous()" );
197 IndexEntry<?, ServerEntry, ID> candidate = ndnCursor.get();
198 if ( presenceEvaluator.evaluate( candidate ) )
199 {
200 return available = true;
201 }
202 }
203
204 return available = false;
205 }
206
207
208 public boolean next() throws Exception
209 {
210 checkNotClosed( "next()" );
211 if ( presenceCursor != null )
212 {
213 return presenceCursor.next();
214 }
215
216 while ( ndnCursor.next() )
217 {
218 checkNotClosed( "next()" );
219 IndexEntry<?, ServerEntry, ID> candidate = ndnCursor.get();
220 if ( presenceEvaluator.evaluate( candidate ) )
221 {
222 return available = true;
223 }
224 }
225
226 return available = false;
227 }
228
229
230 public IndexEntry<String, ServerEntry, ID> get() throws Exception
231 {
232 checkNotClosed( "get()" );
233 if ( presenceCursor != null )
234 {
235 if ( presenceCursor.available() )
236 {
237 return presenceCursor.get();
238 }
239
240 throw new InvalidCursorPositionException( I18n.err( I18n.ERR_708 ) );
241 }
242
243 if ( available )
244 {
245 /*
246 * The value of NDN indices is the normalized dn and we want the
247 * value to be the value of the attribute in question. So we will
248 * set that accordingly here.
249 */
250 IndexEntry<String, ServerEntry, ID> indexEntry = ndnCursor.get();
251 indexEntry.setValue( presenceEvaluator.getAttributeType().getOid() );
252 return indexEntry;
253 }
254
255 throw new InvalidCursorPositionException( I18n.err( I18n.ERR_708 ) );
256 }
257
258
259 public boolean isElementReused()
260 {
261 if ( presenceCursor != null )
262 {
263 return presenceCursor.isElementReused();
264 }
265
266 return ndnCursor.isElementReused();
267 }
268
269
270 public void close() throws Exception
271 {
272 super.close();
273
274 if ( presenceCursor != null )
275 {
276 presenceCursor.close();
277 }
278 else
279 {
280 ndnCursor.close();
281 }
282 }
283 }