View Javadoc
1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    *  
10   *    http://www.apache.org/licenses/LICENSE-2.0
11   *  
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License. 
18   *  
19   */
20  package org.apache.directory.api.ldap.codec.controls.search.pagedSearch;
21  
22  
23  import java.nio.ByteBuffer;
24  import java.util.Arrays;
25  
26  import org.apache.directory.api.asn1.Asn1Object;
27  import org.apache.directory.api.asn1.DecoderException;
28  import org.apache.directory.api.asn1.EncoderException;
29  import org.apache.directory.api.asn1.ber.Asn1Decoder;
30  import org.apache.directory.api.asn1.ber.tlv.BerValue;
31  import org.apache.directory.api.asn1.ber.tlv.TLV;
32  import org.apache.directory.api.asn1.ber.tlv.UniversalTag;
33  import org.apache.directory.api.i18n.I18n;
34  import org.apache.directory.api.ldap.codec.api.ControlDecorator;
35  import org.apache.directory.api.ldap.codec.api.LdapApiService;
36  import org.apache.directory.api.ldap.model.message.controls.PagedResults;
37  import org.apache.directory.api.ldap.model.message.controls.PagedResultsImpl;
38  import org.apache.directory.api.util.Strings;
39  
40  
41  /**
42   * A codec decorator for the {@link PagedResultsImpl}.
43   * 
44   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
45   */
46  public class PagedResultsDecorator extends ControlDecorator<PagedResults> implements PagedResults
47  {
48      /** The entry change global length */
49      private int pscSeqLength;
50  
51      /** An instance of this decoder */
52      private static final Asn1Decoder DECODER = new Asn1Decoder();
53  
54  
55      /**
56       * Creates a new instance of PagedResultsDecorator with a newly created decorated
57       * PagedResults Control.
58       * 
59       * @param codec The LDAP service instance
60       */
61      public PagedResultsDecorator( LdapApiService codec )
62      {
63          this( codec, new PagedResultsImpl() );
64      }
65  
66  
67      /**
68       * Creates a new instance of PagedResultsDecorator using the supplied PagedResults
69       * Control to be decorated.
70       *
71       * @param codec The LDAP service instance
72       * @param  pagedResults The PagedResults Control to be decorated.
73       */
74      public PagedResultsDecorator( LdapApiService codec, PagedResults pagedResults )
75      {
76          super( codec, pagedResults );
77      }
78  
79  
80      /**
81       * Compute the PagedSearchControl length, which is the sum
82       * of the control length and the value length.
83       * 
84       * <pre>
85       * PagedSearchControl value length :
86       * 
87       * 0x30 L1 
88       *   | 
89       *   +--&gt; 0x02 0x0(1-4) [0..2^63-1] (size) 
90       *   +--&gt; 0x04 L2 (cookie)
91       * </pre>
92       *  
93       * @return the control length.
94       */
95      @Override
96      public int computeLength()
97      {
98          int sizeLength = 1 + 1 + BerValue.getNbBytes( getSize() );
99  
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 }