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