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    }