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      public PagedResultsDecorator( LdapApiService codec )
60      {
61          this( codec, new PagedResultsImpl() );
62      }
63  
64  
65      /**
66       * Creates a new instance of PagedResultsDecorator using the supplied PagedResults
67       * Control to be decorated.
68       *
69       * @param  pagedResults The PagedResults Control to be decorated.
70       */
71      public PagedResultsDecorator( LdapApiService codec, PagedResults pagedResults )
72      {
73          super( codec, pagedResults );
74      }
75  
76  
77      /**
78       * Compute the PagedSearchControl length, which is the sum
79       * of the control length and the value length.
80       * 
81       * <pre>
82       * PagedSearchControl value length :
83       * 
84       * 0x30 L1 
85       *   | 
86       *   +--> 0x02 0x0(1-4) [0..2^63-1] (size) 
87       *   +--> 0x04 L2 (cookie)
88       * </pre> 
89       */
90      public int computeLength()
91      {
92          int sizeLength = 1 + 1 + BerValue.getNbBytes( getSize() );
93  
94          int cookieLength;
95  
96          if ( getCookie() != null )
97          {
98              cookieLength = 1 + TLV.getNbBytes( getCookie().length ) + getCookie().length;
99          }
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 }