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 */
020package org.apache.directory.api.ldap.codec.controls.search.pagedSearch;
021
022
023import java.nio.ByteBuffer;
024import java.util.Arrays;
025
026import org.apache.directory.api.asn1.Asn1Object;
027import org.apache.directory.api.asn1.DecoderException;
028import org.apache.directory.api.asn1.EncoderException;
029import org.apache.directory.api.asn1.ber.Asn1Decoder;
030import org.apache.directory.api.asn1.ber.tlv.BerValue;
031import org.apache.directory.api.asn1.ber.tlv.TLV;
032import org.apache.directory.api.asn1.ber.tlv.UniversalTag;
033import org.apache.directory.api.i18n.I18n;
034import org.apache.directory.api.ldap.codec.api.ControlDecorator;
035import org.apache.directory.api.ldap.codec.api.LdapApiService;
036import org.apache.directory.api.ldap.model.message.controls.PagedResults;
037import org.apache.directory.api.ldap.model.message.controls.PagedResultsImpl;
038import org.apache.directory.api.util.Strings;
039
040
041/**
042 * A codec decorator for the {@link PagedResultsImpl}.
043 * 
044 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
045 */
046public class PagedResultsDecorator extends ControlDecorator<PagedResults> implements PagedResults
047{
048    /** The entry change global length */
049    private int pscSeqLength;
050
051    /** An instance of this decoder */
052    private static final Asn1Decoder DECODER = new Asn1Decoder();
053
054
055    /**
056     * Creates a new instance of PagedResultsDecorator with a newly created decorated
057     * PagedResults Control.
058     * 
059     * @param codec The LDAP service instance
060     */
061    public PagedResultsDecorator( LdapApiService codec )
062    {
063        this( codec, new PagedResultsImpl() );
064    }
065
066
067    /**
068     * Creates a new instance of PagedResultsDecorator using the supplied PagedResults
069     * Control to be decorated.
070     *
071     * @param codec The LDAP service instance
072     * @param  pagedResults The PagedResults Control to be decorated.
073     */
074    public PagedResultsDecorator( LdapApiService codec, PagedResults pagedResults )
075    {
076        super( codec, pagedResults );
077    }
078
079
080    /**
081     * Compute the PagedSearchControl length, which is the sum
082     * of the control length and the value length.
083     * 
084     * <pre>
085     * PagedSearchControl value length :
086     * 
087     * 0x30 L1 
088     *   | 
089     *   +--&gt; 0x02 0x0(1-4) [0..2^63-1] (size) 
090     *   +--&gt; 0x04 L2 (cookie)
091     * </pre>
092     *  
093     * @return the control length.
094     */
095    @Override
096    public int computeLength()
097    {
098        int sizeLength = 1 + 1 + BerValue.getNbBytes( getSize() );
099
100        int cookieLength;
101
102        if ( getCookie() != null )
103        {
104            cookieLength = 1 + TLV.getNbBytes( getCookie().length ) + getCookie().length;
105        }
106        else
107        {
108            cookieLength = 1 + 1;
109        }
110
111        pscSeqLength = sizeLength + cookieLength;
112        valueLength = 1 + TLV.getNbBytes( pscSeqLength ) + pscSeqLength;
113
114        return valueLength;
115    }
116
117
118    /**
119     * Encodes the paged search control.
120     * 
121     * @param buffer The encoded sink
122     * @return A ByteBuffer that contains the encoded PDU
123     * @throws EncoderException If anything goes wrong.
124     */
125    @Override
126    public ByteBuffer encode( ByteBuffer buffer ) throws EncoderException
127    {
128        if ( buffer == null )
129        {
130            throw new EncoderException( I18n.err( I18n.ERR_04023 ) );
131        }
132
133        // Now encode the PagedSearch specific part
134        buffer.put( UniversalTag.SEQUENCE.getValue() );
135        buffer.put( TLV.getBytes( pscSeqLength ) );
136
137        BerValue.encode( buffer, getSize() );
138        BerValue.encode( buffer, getCookie() );
139
140        return buffer;
141    }
142
143
144    /**
145     * {@inheritDoc}
146     */
147    @Override
148    public byte[] getValue()
149    {
150        if ( value == null )
151        {
152            try
153            {
154                computeLength();
155                ByteBuffer buffer = ByteBuffer.allocate( valueLength );
156
157                // Now encode the PagedSearch specific part
158                buffer.put( UniversalTag.SEQUENCE.getValue() );
159                buffer.put( TLV.getBytes( pscSeqLength ) );
160
161                BerValue.encode( buffer, getSize() );
162                BerValue.encode( buffer, getCookie() );
163
164                value = buffer.array();
165            }
166            catch ( Exception e )
167            {
168                return null;
169            }
170        }
171
172        return value;
173    }
174
175
176    /**
177     * @return The requested or returned number of entries
178     */
179    @Override
180    public int getSize()
181    {
182        return getDecorated().getSize();
183    }
184
185
186    /**
187     * Set the number of entry requested or returned
188     *
189     * @param size The number of entries 
190     */
191    @Override
192    public void setSize( int size )
193    {
194        getDecorated().setSize( size );
195    }
196
197
198    /**
199     * @return The stored cookie
200     */
201    @Override
202    public byte[] getCookie()
203    {
204        return getDecorated().getCookie();
205    }
206
207
208    /**
209     * Set the cookie
210     *
211     * @param cookie The cookie to store in this control
212     */
213    @Override
214    public void setCookie( byte[] cookie )
215    {
216        // Copy the bytes
217        if ( !Strings.isEmpty( cookie ) )
218        {
219            byte[] copy = new byte[cookie.length];
220            System.arraycopy( cookie, 0, copy, 0, cookie.length );
221            getDecorated().setCookie( copy );
222        }
223        else
224        {
225            getDecorated().setCookie( null );
226        }
227    }
228
229
230    /**
231     * @return The integer value for the current cookie
232     */
233    @Override
234    public int getCookieValue()
235    {
236        int value = 0;
237
238        switch ( getCookie().length )
239        {
240            case 1:
241                value = getCookie()[0] & 0x00FF;
242                break;
243
244            case 2:
245                value = ( ( getCookie()[0] & 0x00FF ) << 8 ) + ( getCookie()[1] & 0x00FF );
246                break;
247
248            case 3:
249                value = ( ( getCookie()[0] & 0x00FF ) << 16 ) + ( ( getCookie()[1] & 0x00FF ) << 8 )
250                    + ( getCookie()[2] & 0x00FF );
251                break;
252
253            case 4:
254                value = ( ( getCookie()[0] & 0x00FF ) << 24 ) + ( ( getCookie()[1] & 0x00FF ) << 16 )
255                    + ( ( getCookie()[2] & 0x00FF ) << 8 ) + ( getCookie()[3] & 0x00FF );
256                break;
257
258            default:
259                break;
260
261        }
262
263        return value;
264    }
265
266
267    /**
268     * @see Object#hashCode()
269     */
270    @Override
271    public int hashCode()
272    {
273        int hash = super.hashCode();
274
275        hash = hash * 17 + pscSeqLength;
276
277        return hash;
278    }
279
280
281    /**
282     * @see Object#equals(Object)
283     */
284    @Override
285    public boolean equals( Object o )
286    {
287        if ( !super.equals( o ) )
288        {
289            return false;
290        }
291
292        PagedResults otherControl = ( PagedResults ) o;
293
294        return ( getSize() == otherControl.getSize() ) && Arrays.equals( getCookie(), otherControl.getCookie() );
295    }
296
297
298    /**
299     * Return a String representing this PagedSearchControl.
300     */
301    @Override
302    public String toString()
303    {
304        StringBuilder sb = new StringBuilder();
305
306        sb.append( "    Paged Search Control\n" );
307        sb.append( "        oid : " ).append( getOid() ).append( '\n' );
308        sb.append( "        critical : " ).append( isCritical() ).append( '\n' );
309        sb.append( "        size   : '" ).append( getSize() ).append( "'\n" );
310        sb.append( "        cookie   : '" ).append( Strings.dumpBytes( getCookie() ) ).append( "'\n" );
311
312        return sb.toString();
313    }
314
315
316    /**
317     * {@inheritDoc}
318     */
319    @Override
320    public Asn1Object decode( byte[] controlBytes ) throws DecoderException
321    {
322        ByteBuffer bb = ByteBuffer.wrap( controlBytes );
323        PagedResultsContainer container = new PagedResultsContainer( getCodecService(), this );
324        DECODER.decode( bb, container );
325        return this;
326    }
327}