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}