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 */ 019package org.apache.directory.server.xdbm; 020 021 022import java.io.IOException; 023 024import org.apache.directory.api.ldap.model.constants.Loggers; 025import org.apache.directory.api.ldap.model.cursor.AbstractCursor; 026import org.apache.directory.api.ldap.model.cursor.CursorException; 027import org.apache.directory.api.ldap.model.cursor.InvalidCursorPositionException; 028import org.apache.directory.api.ldap.model.cursor.Tuple; 029import org.apache.directory.api.ldap.model.exception.LdapException; 030import org.apache.directory.server.core.avltree.ArrayTree; 031import org.apache.directory.server.core.avltree.ArrayTreeCursor; 032import org.apache.directory.server.i18n.I18n; 033import org.slf4j.Logger; 034import org.slf4j.LoggerFactory; 035 036 037/** 038 * Cursor over a set of values for the same key which are store in an in 039 * memory ArrayTree. This Cursor is limited to the same key and it's tuples 040 * will always return the same key. 041 * 042 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 043 */ 044public class KeyTupleArrayCursor<K, V> extends AbstractCursor<Tuple<K, V>> 045{ 046 /** A dedicated log for cursors */ 047 private static final Logger LOG_CURSOR = LoggerFactory.getLogger( Loggers.CURSOR_LOG.getName() ); 048 049 /** Speedup for logs */ 050 private static final boolean IS_DEBUG = LOG_CURSOR.isDebugEnabled(); 051 052 private final ArrayTreeCursor<V> wrapped; 053 private final K key; 054 055 private Tuple<K, V> returnedTuple = new Tuple<>(); 056 private boolean valueAvailable; 057 058 059 /** 060 * Creates a Cursor over the tuples of an ArrayTree. 061 * 062 * @param arrayTree the ArrayTree to build a Tuple returning Cursor over 063 * @param key the constant key for which values are returned 064 */ 065 public KeyTupleArrayCursor( ArrayTree<V> arrayTree, K key ) 066 { 067 this.key = key; 068 this.wrapped = new ArrayTreeCursor<>( arrayTree ); 069 070 if ( IS_DEBUG ) 071 { 072 LOG_CURSOR.debug( "Creating KeyTupleArrayCursor {}", this ); 073 } 074 } 075 076 077 private void clearValue() 078 { 079 returnedTuple.setKey( key ); 080 returnedTuple.setValue( null ); 081 valueAvailable = false; 082 } 083 084 085 public boolean available() 086 { 087 return valueAvailable; 088 } 089 090 091 public void beforeKey( K key ) throws Exception 092 { 093 throw new UnsupportedOperationException( I18n.err( I18n.ERR_446 ) ); 094 } 095 096 097 public void afterKey( K key ) throws Exception 098 { 099 throw new UnsupportedOperationException( I18n.err( I18n.ERR_446 ) ); 100 } 101 102 103 public void beforeValue( K key, V value ) throws Exception 104 { 105 checkNotClosed(); 106 if ( key != null && !key.equals( this.key ) ) 107 { 108 throw new UnsupportedOperationException( I18n.err( I18n.ERR_446 ) ); 109 } 110 111 wrapped.before( value ); 112 clearValue(); 113 } 114 115 116 public void afterValue( K key, V value ) throws Exception 117 { 118 checkNotClosed(); 119 if ( key != null && !key.equals( this.key ) ) 120 { 121 throw new UnsupportedOperationException( I18n.err( I18n.ERR_446 ) ); 122 } 123 124 wrapped.after( value ); 125 clearValue(); 126 } 127 128 129 /** 130 * Positions this Cursor over the same keys before the value of the 131 * supplied element Tuple. The supplied element Tuple's key is not 132 * considered at all. 133 * 134 * @param element the valueTuple who's value is used to position this Cursor 135 * @throws LdapException if there are failures to position the Cursor 136 * @throws CursorException if there are failures to position the Cursor 137 */ 138 public void before( Tuple<K, V> element ) throws LdapException, CursorException 139 { 140 checkNotClosed(); 141 wrapped.before( element.getValue() ); 142 clearValue(); 143 } 144 145 146 /** 147 * {@inheritDoc} 148 */ 149 public void after( Tuple<K, V> element ) throws LdapException, CursorException 150 { 151 checkNotClosed(); 152 wrapped.after( element.getValue() ); 153 clearValue(); 154 } 155 156 157 /** 158 * {@inheritDoc} 159 */ 160 public void beforeFirst() throws LdapException, CursorException 161 { 162 checkNotClosed(); 163 wrapped.beforeFirst(); 164 clearValue(); 165 } 166 167 168 /** 169 * {@inheritDoc} 170 */ 171 public void afterLast() throws LdapException, CursorException 172 { 173 checkNotClosed(); 174 wrapped.afterLast(); 175 clearValue(); 176 } 177 178 179 /** 180 * {@inheritDoc} 181 */ 182 public boolean first() throws LdapException, CursorException 183 { 184 beforeFirst(); 185 186 return next(); 187 } 188 189 190 /** 191 * {@inheritDoc} 192 */ 193 public boolean last() throws LdapException, CursorException 194 { 195 afterLast(); 196 197 return previous(); 198 } 199 200 201 /** 202 * {@inheritDoc} 203 */ 204 public boolean previous() throws LdapException, CursorException 205 { 206 checkNotClosed(); 207 if ( wrapped.previous() ) 208 { 209 returnedTuple.setKey( key ); 210 returnedTuple.setValue( wrapped.get() ); 211 valueAvailable = true; 212 return true; 213 } 214 else 215 { 216 clearValue(); 217 return false; 218 } 219 } 220 221 222 /** 223 * {@inheritDoc} 224 */ 225 public boolean next() throws LdapException, CursorException 226 { 227 checkNotClosed(); 228 if ( wrapped.next() ) 229 { 230 returnedTuple.setKey( key ); 231 returnedTuple.setValue( wrapped.get() ); 232 valueAvailable = true; 233 return true; 234 } 235 else 236 { 237 clearValue(); 238 return false; 239 } 240 } 241 242 243 /** 244 * {@inheritDoc} 245 */ 246 public Tuple<K, V> get() throws CursorException 247 { 248 checkNotClosed(); 249 250 if ( valueAvailable ) 251 { 252 return returnedTuple; 253 } 254 255 throw new InvalidCursorPositionException(); 256 } 257 258 259 /** 260 * {@inheritDoc} 261 */ 262 @Override 263 public void close() throws IOException 264 { 265 if ( IS_DEBUG ) 266 { 267 LOG_CURSOR.debug( "Closing KeyTupleArrayCursor {}", this ); 268 } 269 270 if ( wrapped != null ) 271 { 272 wrapped.close(); 273 } 274 275 super.close(); 276 } 277 278 279 /** 280 * {@inheritDoc} 281 */ 282 @Override 283 public void close( Exception reason ) throws IOException 284 { 285 if ( IS_DEBUG ) 286 { 287 LOG_CURSOR.debug( "Closing KeyTupleArrayCursor {}", this ); 288 } 289 290 if ( wrapped != null ) 291 { 292 wrapped.close( reason ); 293 } 294 295 super.close( reason ); 296 } 297 298 299 /** 300 * @see Object#toString() 301 */ 302 @Override 303 public String toString( String tabs ) 304 { 305 StringBuilder sb = new StringBuilder(); 306 307 sb.append( tabs ).append( "KeyTupleArrayCursor (" ); 308 309 if ( available() ) 310 { 311 sb.append( "available)" ); 312 } 313 else 314 { 315 sb.append( "absent)" ); 316 } 317 318 sb.append( "#" ).append( key ); 319 320 sb.append( " :\n" ); 321 322 sb.append( wrapped.toString( tabs + " " ) ); 323 324 return sb.toString(); 325 } 326 327 328 /** 329 * @see Object#toString() 330 */ 331 public String toString() 332 { 333 return toString( "" ); 334 } 335}