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.i18n.I18n; 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.shared.ldap.cursor.Cursor; 029 import org.apache.directory.shared.ldap.cursor.InvalidCursorPositionException; 030 import org.apache.directory.shared.ldap.entry.ServerEntry; 031 032 033 /** 034 * A Cursor over entries satisfying one level scope constraints with alias 035 * dereferencing considerations when enabled during search. 036 * 037 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 038 * @version $Rev$ 039 */ 040 public class OneLevelScopeCursor<ID> extends AbstractIndexCursor<ID, ServerEntry, ID> 041 { 042 /** Error message for unsupported operations */ 043 private static final String UNSUPPORTED_MSG = I18n.err( I18n.ERR_719 ); 044 045 /** The entry database/store */ 046 private final Store<ServerEntry, ID> db; 047 048 /** A onelevel ScopeNode Evaluator */ 049 @SuppressWarnings("unchecked") 050 private final OneLevelScopeEvaluator evaluator; 051 052 /** A Cursor over the entries in the scope of the search base */ 053 private final IndexCursor<ID, ServerEntry, ID> scopeCursor; 054 055 /** A Cursor over entries brought into scope by alias dereferencing */ 056 private final Cursor<IndexEntry<ID, ServerEntry, ID>> dereferencedCursor; 057 058 /** Currently active Cursor: we switch between two cursors */ 059 private Cursor<IndexEntry<ID, ServerEntry, ID>> cursor; 060 061 /** Whether or not this Cursor is positioned so an entry is available */ 062 private boolean available = false; 063 064 065 /** 066 * Creates a Cursor over entries satisfying one level scope criteria. 067 * 068 * @param db the entry store 069 * @param evaluator an IndexEntry (candidate) evaluator 070 * @throws Exception on db access failures 071 */ 072 //@SuppressWarnings("unchecked") 073 public OneLevelScopeCursor( Store<ServerEntry, ID> db, OneLevelScopeEvaluator<ServerEntry, ID> evaluator ) 074 throws Exception 075 { 076 this.db = db; 077 this.evaluator = evaluator; 078 scopeCursor = db.getOneLevelIndex().forwardCursor( evaluator.getBaseId() ); 079 080 if ( evaluator.isDereferencing() ) 081 { 082 dereferencedCursor = db.getOneAliasIndex().forwardCursor( evaluator.getBaseId() ); 083 } 084 else 085 { 086 dereferencedCursor = null; 087 } 088 } 089 090 091 public boolean available() 092 { 093 return available; 094 } 095 096 097 public void beforeValue( ID id, ID value ) throws Exception 098 { 099 throw new UnsupportedOperationException( UNSUPPORTED_MSG ); 100 } 101 102 103 public void afterValue( ID id, ID value ) throws Exception 104 { 105 throw new UnsupportedOperationException( UNSUPPORTED_MSG ); 106 } 107 108 109 public void before( IndexEntry<ID, ServerEntry, ID> element ) throws Exception 110 { 111 throw new UnsupportedOperationException( UNSUPPORTED_MSG ); 112 } 113 114 115 public void after( IndexEntry<ID, ServerEntry, ID> element ) throws Exception 116 { 117 throw new UnsupportedOperationException( UNSUPPORTED_MSG ); 118 } 119 120 121 public void beforeFirst() throws Exception 122 { 123 checkNotClosed( "beforeFirst()" ); 124 cursor = scopeCursor; 125 cursor.beforeFirst(); 126 available = false; 127 } 128 129 130 public void afterLast() throws Exception 131 { 132 checkNotClosed( "afterLast()" ); 133 if ( evaluator.isDereferencing() ) 134 { 135 cursor = dereferencedCursor; 136 } 137 else 138 { 139 cursor = scopeCursor; 140 } 141 142 cursor.afterLast(); 143 available = false; 144 } 145 146 147 public boolean first() throws Exception 148 { 149 beforeFirst(); 150 return next(); 151 } 152 153 154 public boolean last() throws Exception 155 { 156 afterLast(); 157 return previous(); 158 } 159 160 161 public boolean previous() throws Exception 162 { 163 checkNotClosed( "previous()" ); 164 // if the cursor has not been set - position it after last element 165 if ( cursor == null ) 166 { 167 afterLast(); 168 } 169 170 // if we're using the scopeCursor (1st Cursor) then return result as is 171 if ( cursor == scopeCursor ) 172 { 173 /* 174 * If dereferencing is enabled then we must ignore alias entries, not 175 * returning them as part of the results. 176 */ 177 if ( evaluator.isDereferencing() ) 178 { 179 // advance until nothing is available or until we find a non-alias 180 do 181 { 182 checkNotClosed( "previous()" ); 183 available = cursor.previous(); 184 185 if ( available && db.getAliasIndex().reverseLookup( cursor.get().getId() ) == null ) 186 { 187 break; 188 } 189 } 190 while ( available ); 191 } 192 else 193 { 194 available = cursor.previous(); 195 } 196 197 return available; 198 } 199 200 /* 201 * Below here we are using the dereferencedCursor so if nothing is 202 * available after an advance backwards we need to switch to the 203 * scopeCursor and try a previous call after positioning past it's 204 * last element. 205 */ 206 available = cursor.previous(); 207 if ( !available ) 208 { 209 cursor = scopeCursor; 210 cursor.afterLast(); 211 212 // advance until nothing is available or until we find a non-alias 213 do 214 { 215 checkNotClosed( "previous()" ); 216 available = cursor.previous(); 217 218 if ( available && db.getAliasIndex().reverseLookup( cursor.get().getId() ) == null ) 219 { 220 break; 221 } 222 } 223 while ( available ); 224 225 return available; 226 } 227 228 return true; 229 } 230 231 232 public boolean next() throws Exception 233 { 234 checkNotClosed( "next()" ); 235 // if the cursor hasn't been set position it before the first element 236 if ( cursor == null ) 237 { 238 beforeFirst(); 239 } 240 241 /* 242 * If dereferencing is enabled then we must ignore alias entries, not 243 * returning them as part of the results. 244 */ 245 if ( evaluator.isDereferencing() ) 246 { 247 // advance until nothing is available or until we find a non-alias 248 do 249 { 250 checkNotClosed( "next()" ); 251 available = cursor.next(); 252 253 if ( available && db.getAliasIndex().reverseLookup( cursor.get().getId() ) == null ) 254 { 255 break; 256 } 257 } 258 while ( available ); 259 } 260 else 261 { 262 available = cursor.next(); 263 } 264 265 // if we're using dereferencedCursor (2nd) then we return the result 266 if ( cursor == dereferencedCursor ) 267 { 268 return available; 269 } 270 271 /* 272 * Below here we are using the scopeCursor so if nothing is 273 * available after an advance forward we need to switch to the 274 * dereferencedCursor and try a previous call after positioning past 275 * it's last element. 276 */ 277 if ( !available ) 278 { 279 if ( dereferencedCursor != null ) 280 { 281 cursor = dereferencedCursor; 282 cursor.beforeFirst(); 283 return available = cursor.next(); 284 } 285 286 return false; 287 } 288 289 return true; 290 } 291 292 293 public IndexEntry<ID, ServerEntry, ID> get() throws Exception 294 { 295 checkNotClosed( "get()" ); 296 if ( available ) 297 { 298 return cursor.get(); 299 } 300 301 throw new InvalidCursorPositionException( I18n.err( I18n.ERR_708 ) ); 302 } 303 304 305 public boolean isElementReused() 306 { 307 return scopeCursor.isElementReused() || ( dereferencedCursor != null && dereferencedCursor.isElementReused() ); 308 } 309 }