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 * +--> 0x02 0x0(1-4) [0..2^63-1] (size) 090 * +--> 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}