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
021package org.apache.directory.ldap.client.api;
022
023
024import org.apache.directory.shared.i18n.I18n;
025import org.apache.directory.shared.ldap.model.cursor.AbstractCursor;
026import org.apache.directory.shared.ldap.model.cursor.EntryCursor;
027import org.apache.directory.shared.ldap.model.cursor.InvalidCursorPositionException;
028import org.apache.directory.shared.ldap.model.cursor.SearchCursor;
029import org.apache.directory.shared.ldap.model.entry.Entry;
030import org.apache.directory.shared.ldap.model.exception.LdapException;
031import org.apache.directory.shared.ldap.model.message.Response;
032import org.apache.directory.shared.ldap.model.message.SearchResultDone;
033import org.apache.directory.shared.ldap.model.message.SearchResultEntry;
034
035
036/**
037 * An implementation of Cursor based on the underlying SearchFuture instance.
038 * 
039 * Note: This is a forward only cursor hence the only valid operations are next(), get() and close() 
040 * 
041 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
042 */
043public class EntryCursorImpl extends AbstractCursor<Entry> implements EntryCursor
044{
045    /** a reference to hold the retrieved SearchResponse object from SearchFuture */
046    private Response response;
047
048    /** The encapsulated search cursor */
049    private SearchCursor searchCursor;
050
051    /** The underlying messageId */
052    private int messageId;
053
054    /**
055     * Instantiates a new search cursor, embedding a SearchCursor.
056     *
057     * @param searchCursor the embedded SearchResponse cursor
058     */
059    public EntryCursorImpl( SearchCursor searchCursor )
060    {
061        this.searchCursor = searchCursor;
062        messageId = -1;
063    }
064    
065
066    /**
067     * {@inheritDoc}
068     */
069    public boolean next() throws Exception
070    {
071        if ( !searchCursor.next() )
072        {
073            return false;
074        }
075
076        try
077        {
078            
079            do
080            {
081                response = searchCursor.get();
082
083                if ( response == null )
084                {
085                    throw new LdapException( LdapNetworkConnection.TIME_OUT_ERROR );
086                }
087
088                messageId = response.getMessageId();
089
090                if ( response instanceof SearchResultEntry )
091                {
092                    return true;
093                }
094            }
095            while ( !( response instanceof SearchResultDone ) );
096            
097            return false;
098        }
099        catch ( Exception e )
100        {
101            LdapException ldapException = new LdapException( LdapNetworkConnection.NO_RESPONSE_ERROR );
102            ldapException.initCause( e );
103
104            // close the cursor
105            close( ldapException );
106
107            throw ldapException;
108        }
109    }
110
111
112    /**
113     * {@inheritDoc}
114     */
115    public Entry get() throws Exception
116    {
117        if ( !searchCursor.available() )
118        {
119            throw new InvalidCursorPositionException();
120        }
121
122        do
123        {
124            if ( response instanceof SearchResultEntry )
125            {
126                return ((SearchResultEntry)response).getEntry();
127            }
128        }
129        while ( next() && !( response instanceof SearchResultDone ) );
130        
131        return null;
132    }
133
134
135    /**
136     * {@inheritDoc}
137     */
138    public SearchResultDone getSearchResultDone()
139    {
140        return searchCursor.getSearchResultDone();
141    }
142
143
144    /**
145     * {@inheritDoc}
146     */
147    public boolean available()
148    {
149        return searchCursor.available();
150    }
151
152
153    /**
154     * {@inheritDoc}
155     */
156    @Override
157    public void close() throws Exception
158    {
159        searchCursor.close();
160    }
161
162
163    /**
164     * {@inheritDoc}
165     */
166    @Override
167    public void close( Exception cause ) throws Exception
168    {
169        searchCursor.close( cause );
170    }
171
172
173    // rest of all operations will throw UnsupportedOperationException
174
175    /**
176     * This operation is not supported in SearchCursor.
177     * {@inheritDoc}
178     */
179    public void after( Entry element ) throws Exception
180    {
181        throw new UnsupportedOperationException( I18n.err( I18n.ERR_02014_UNSUPPORTED_OPERATION, getClass().getName()
182            .concat( "." ).concat( "after( Response element )" ) ) );
183    }
184
185
186    /**
187     * This operation is not supported in SearchCursor.
188     * {@inheritDoc}
189     */
190    public void afterLast() throws Exception
191    {
192        throw new UnsupportedOperationException( I18n.err( I18n.ERR_02014_UNSUPPORTED_OPERATION, getClass().getName()
193            .concat( "." ).concat( "afterLast()" ) ) );
194    }
195
196
197    /**
198     * This operation is not supported in SearchCursor.
199     * {@inheritDoc}
200     */
201    public void before( Entry element ) throws Exception
202    {
203        throw new UnsupportedOperationException( I18n.err( I18n.ERR_02014_UNSUPPORTED_OPERATION, getClass().getName()
204            .concat( "." ).concat( "before( Response element )" ) ) );
205    }
206
207
208    /**
209     * This operation is not supported in SearchCursor.
210     * {@inheritDoc}
211     */
212    public void beforeFirst() throws Exception
213    {
214        throw new UnsupportedOperationException( I18n.err( I18n.ERR_02014_UNSUPPORTED_OPERATION, getClass().getName()
215            .concat( "." ).concat( "beforeFirst()" ) ) );
216    }
217
218
219    /**
220     * This operation is not supported in SearchCursor.
221     * {@inheritDoc}
222     */
223    public boolean first() throws Exception
224    {
225        throw new UnsupportedOperationException( I18n.err( I18n.ERR_02014_UNSUPPORTED_OPERATION, getClass().getName()
226            .concat( "." ).concat( "first()" ) ) );
227    }
228
229
230    /**
231     * This operation is not supported in SearchCursor.
232     * {@inheritDoc}
233     */
234    public boolean last() throws Exception
235    {
236        throw new UnsupportedOperationException( I18n.err( I18n.ERR_02014_UNSUPPORTED_OPERATION, getClass().getName()
237            .concat( "." ).concat( "last()" ) ) );
238    }
239
240
241    /**
242     * This operation is not supported in SearchCursor.
243     * {@inheritDoc}
244     */
245    public boolean previous() throws Exception
246    {
247        throw new UnsupportedOperationException( I18n.err( I18n.ERR_02014_UNSUPPORTED_OPERATION, getClass().getName()
248            .concat( "." ).concat( "previous()" ) ) );
249    }
250
251
252    /**
253     * {@inheritDoc}
254     */
255    public int getMessageId()
256    {
257        return messageId;
258    }
259}