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.shared.ldap.codec.decorators; 021 022 023import java.nio.BufferOverflowException; 024import java.nio.ByteBuffer; 025import java.util.LinkedList; 026import java.util.List; 027 028import org.apache.directory.shared.asn1.EncoderException; 029import org.apache.directory.shared.asn1.ber.tlv.TLV; 030import org.apache.directory.shared.asn1.ber.tlv.UniversalTag; 031import org.apache.directory.shared.asn1.ber.tlv.Value; 032import org.apache.directory.shared.asn1.util.Asn1StringUtils; 033import org.apache.directory.shared.i18n.I18n; 034import org.apache.directory.shared.ldap.codec.api.LdapApiService; 035import org.apache.directory.shared.ldap.codec.api.LdapConstants; 036import org.apache.directory.shared.ldap.codec.api.MessageDecorator; 037import org.apache.directory.shared.ldap.model.entry.DefaultAttribute; 038import org.apache.directory.shared.ldap.model.entry.Entry; 039import org.apache.directory.shared.ldap.model.entry.Attribute; 040import org.apache.directory.shared.ldap.model.exception.LdapException; 041import org.apache.directory.shared.ldap.model.message.SearchResultEntry; 042import org.apache.directory.shared.ldap.model.name.Dn; 043import org.apache.directory.shared.util.Strings; 044 045 046/** 047 * A decorator for the SearchResultEntry message 048 * 049 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 050 */ 051public class SearchResultEntryDecorator extends MessageDecorator<SearchResultEntry> implements SearchResultEntry 052{ 053 /** A temporary storage for the byte[] representing the objectName */ 054 private byte[] objectNameBytes; 055 056 /** The search result entry length */ 057 private int searchResultEntryLength; 058 059 /** The partial attributes length */ 060 private int attributesLength; 061 062 /** The list of all attributes length */ 063 private List<Integer> attributeLength; 064 065 /** The list of all vals length */ 066 private List<Integer> valsLength; 067 068 /** The current attribute being processed */ 069 private Attribute currentAttribute; 070 071 072 /** 073 * Makes a SearchResultEntry encodable. 074 * 075 * @param decoratedMessage the decorated SearchResultEntry 076 */ 077 public SearchResultEntryDecorator( LdapApiService codec, SearchResultEntry decoratedMessage ) 078 { 079 super( codec, decoratedMessage ); 080 } 081 082 083 /** 084 * Gets the distinguished name bytes of the entry object returned. 085 * 086 * @return the Dn bytes of the entry returned. 087 */ 088 public byte[] getObjectNameBytes() 089 { 090 return objectNameBytes; 091 } 092 093 094 /** 095 * Sets the distinguished name bytes of the entry object returned. 096 * 097 * @param objectNameBytes the Dn bytes of the entry returned. 098 */ 099 public void setObjectNameBytes( byte[] objectNameBytes ) 100 { 101 this.objectNameBytes = objectNameBytes; 102 } 103 104 105 /** 106 * @return The encoded SearchResultEntry's length 107 */ 108 public int getSearchResultEntryLength() 109 { 110 return searchResultEntryLength; 111 } 112 113 114 /** 115 * Stores the encoded length for the SearchResultEntry 116 * @param searchResultEntryLength The encoded length 117 */ 118 public void setSearchResultEntryLength( int searchResultEntryLength ) 119 { 120 this.searchResultEntryLength = searchResultEntryLength; 121 } 122 123 124 /** 125 * @return The encoded PartialAttributeList's length 126 */ 127 public int getAttributesLength() 128 { 129 return attributesLength; 130 } 131 132 133 /** 134 * Stores the encoded length for the Attributes 135 * @param attributesLength The list of encoded lengths 136 */ 137 public void setAttributesLength( int attributesLength ) 138 { 139 this.attributesLength = attributesLength; 140 } 141 142 143 /** 144 * @return The encoded PartialAttributeList's length 145 */ 146 public List<Integer> getAttributeLength() 147 { 148 return attributeLength; 149 } 150 151 152 /** 153 * @return The list of encoded Attributes' length 154 */ 155 public void setAttributeLength( List<Integer> attributeLength ) 156 { 157 this.attributeLength = attributeLength; 158 } 159 160 161 /** 162 * @return The list of encoded values' length 163 */ 164 public List<Integer> getValsLength() 165 { 166 return valsLength; 167 } 168 169 170 /** 171 * Stores the list of encoded length for the values 172 * @param valsLength The list of encoded lengths 173 */ 174 public void setValsLength( List<Integer> valsLength ) 175 { 176 this.valsLength = valsLength; 177 } 178 179 180 public Attribute getCurrentAttribute() 181 { 182 return currentAttribute; 183 } 184 185 186 /** 187 * Create a new attribute 188 * 189 * @param type The attribute's type 190 */ 191 public void addAttribute( String type ) throws LdapException 192 { 193 currentAttribute = new DefaultAttribute( type ); 194 195 getDecorated().getEntry().put( currentAttribute ); 196 } 197 198 199 /** 200 * Add a new value to the current attribute 201 * 202 * @param value The added value 203 */ 204 public void addAttributeValue( Object value ) throws LdapException 205 { 206 if ( value instanceof String ) 207 { 208 currentAttribute.add( ( String ) value ); 209 } 210 else 211 { 212 currentAttribute.add( ( byte[] ) value ); 213 } 214 } 215 216 217 //------------------------------------------------------------------------- 218 // The IntermediateResponse methods 219 //------------------------------------------------------------------------- 220 221 222 /** 223 * {@inheritDoc} 224 */ 225 public Dn getObjectName() 226 { 227 return getDecorated().getObjectName(); 228 } 229 230 231 /** 232 * {@inheritDoc} 233 */ 234 public void setObjectName( Dn objectName ) 235 { 236 getDecorated().setObjectName( objectName ); 237 } 238 239 240 /** 241 * {@inheritDoc} 242 */ 243 public Entry getEntry() 244 { 245 return getDecorated().getEntry(); 246 } 247 248 249 /** 250 * {@inheritDoc} 251 */ 252 public void setEntry( Entry entry ) 253 { 254 getDecorated().setEntry( entry ); 255 } 256 257 258 //------------------------------------------------------------------------- 259 // The Decorator methods 260 //------------------------------------------------------------------------- 261 262 263 /** 264 * Compute the SearchResultEntry length 265 * 266 * SearchResultEntry : 267 * <pre> 268 * 0x64 L1 269 * | 270 * +--> 0x04 L2 objectName 271 * +--> 0x30 L3 (attributes) 272 * | 273 * +--> 0x30 L4-1 (partial attributes list) 274 * | | 275 * | +--> 0x04 L5-1 type 276 * | +--> 0x31 L6-1 (values) 277 * | | 278 * | +--> 0x04 L7-1-1 value 279 * | +--> ... 280 * | +--> 0x04 L7-1-n value 281 * | 282 * +--> 0x30 L4-2 (partial attributes list) 283 * | | 284 * | +--> 0x04 L5-2 type 285 * | +--> 0x31 L6-2 (values) 286 * | | 287 * | +--> 0x04 L7-2-1 value 288 * | +--> ... 289 * | +--> 0x04 L7-2-n value 290 * | 291 * +--> ... 292 * | 293 * +--> 0x30 L4-m (partial attributes list) 294 * | 295 * +--> 0x04 L5-m type 296 * +--> 0x31 L6-m (values) 297 * | 298 * +--> 0x04 L7-m-1 value 299 * +--> ... 300 * +--> 0x04 L7-m-n value 301 * </pre> 302 */ 303 public int computeLength() 304 { 305 Dn dn = getObjectName(); 306 307 byte[] dnBytes = Strings.getBytesUtf8(dn.getName()); 308 309 // The entry 310 int searchResultEntryLength = 1 + TLV.getNbBytes( dnBytes.length ) + dnBytes.length; 311 setObjectNameBytes( dnBytes ); 312 313 // The attributes sequence 314 int attributesLength = 0; 315 316 Entry entry = getEntry(); 317 318 if ( ( entry != null ) && ( entry.size() != 0 ) ) 319 { 320 List<Integer> attributeLength = new LinkedList<Integer>(); 321 List<Integer> valsLength = new LinkedList<Integer>(); 322 323 // Store those lists in the object 324 setAttributeLength( attributeLength ); 325 setValsLength( valsLength ); 326 327 // Compute the attributes length 328 for ( Attribute attribute : entry ) 329 { 330 int localAttributeLength = 0; 331 int localValuesLength = 0; 332 333 // Get the type length 334 int idLength = attribute.getUpId().getBytes().length; 335 localAttributeLength = 1 + TLV.getNbBytes( idLength ) + idLength; 336 337 if ( attribute.size() != 0 ) 338 { 339 // The values 340 if ( attribute.size() > 0 ) 341 { 342 localValuesLength = 0; 343 344 for ( org.apache.directory.shared.ldap.model.entry.Value<?> value : attribute ) 345 { 346 byte[] binaryValue = value.getBytes(); 347 localValuesLength += 1 + TLV.getNbBytes( binaryValue.length ) + binaryValue.length; 348 } 349 350 localAttributeLength += 1 + TLV.getNbBytes( localValuesLength ) + localValuesLength; 351 } 352 else 353 { 354 // We have to deal with the special wase where 355 // we don't have a value. 356 // It will be encoded as an empty OCTETSTRING, 357 // so it will be two byte slong (0x04 0x00) 358 localAttributeLength += 1 + 1; 359 } 360 } 361 else 362 { 363 // We have no values. We will just have an empty SET OF : 364 // 0x31 0x00 365 localAttributeLength += 1 + 1; 366 } 367 368 // add the attribute length to the attributes length 369 attributesLength += 1 + TLV.getNbBytes( localAttributeLength ) + localAttributeLength; 370 371 // Store the lengths of the encoded attributes and values 372 attributeLength.add( localAttributeLength ); 373 valsLength.add( localValuesLength ); 374 } 375 376 // Store the lengths of the entry 377 setAttributesLength( attributesLength ); 378 } 379 380 searchResultEntryLength += 1 + TLV.getNbBytes( attributesLength ) + attributesLength; 381 382 // Store the length of the response 383 setSearchResultEntryLength( searchResultEntryLength ); 384 385 // Return the result. 386 return 1 + TLV.getNbBytes( searchResultEntryLength ) + searchResultEntryLength; 387 } 388 389 390 /** 391 * Encode the SearchResultEntry message to a PDU. 392 * 393 * SearchResultEntry : 394 * <pre> 395 * 0x64 LL 396 * 0x04 LL objectName 397 * 0x30 LL attributes 398 * 0x30 LL partialAttributeList 399 * 0x04 LL type 400 * 0x31 LL vals 401 * 0x04 LL attributeValue 402 * ... 403 * 0x04 LL attributeValue 404 * ... 405 * 0x30 LL partialAttributeList 406 * 0x04 LL type 407 * 0x31 LL vals 408 * 0x04 LL attributeValue 409 * ... 410 * 0x04 LL attributeValue 411 * </pre> 412 * @param buffer The buffer where to put the PDU 413 * @param searchResultEntryDecorator the SearchResultEntry decorator 414 * @return The PDU. 415 */ 416 public ByteBuffer encode( ByteBuffer buffer ) throws EncoderException 417 { 418 try 419 { 420 // The SearchResultEntry Tag 421 buffer.put( LdapConstants.SEARCH_RESULT_ENTRY_TAG ); 422 buffer.put( TLV.getBytes( getSearchResultEntryLength() ) ); 423 424 // The objectName 425 Value.encode( buffer, getObjectNameBytes() ); 426 427 // The attributes sequence 428 buffer.put( UniversalTag.SEQUENCE.getValue() ); 429 buffer.put( TLV.getBytes( getAttributesLength() ) ); 430 431 // The partial attribute list 432 Entry entry = getEntry(); 433 434 if ( ( entry != null ) && ( entry.size() != 0 ) ) 435 { 436 int attributeNumber = 0; 437 438 // Compute the attributes length 439 for ( Attribute attribute : entry ) 440 { 441 // The partial attribute list sequence 442 buffer.put( UniversalTag.SEQUENCE.getValue() ); 443 int localAttributeLength = getAttributeLength().get( attributeNumber ); 444 buffer.put( TLV.getBytes( localAttributeLength ) ); 445 446 // The attribute type 447 Value.encode( buffer, Asn1StringUtils.asciiStringToByte( attribute.getUpId() ) ); 448 449 // The values 450 buffer.put( UniversalTag.SET.getValue() ); 451 int localValuesLength = getValsLength().get( attributeNumber ); 452 buffer.put( TLV.getBytes( localValuesLength ) ); 453 454 if ( attribute.size() > 0 ) 455 { 456 for ( org.apache.directory.shared.ldap.model.entry.Value<?> value : attribute ) 457 { 458 if ( value.isHumanReadable() ) 459 { 460 Value.encode( buffer, value.getString() ); 461 } 462 else 463 { 464 Value.encode( buffer, value.getBytes() ); 465 } 466 } 467 } 468 469 // Go to the next attribute number; 470 attributeNumber++; 471 } 472 } 473 } 474 catch ( BufferOverflowException boe ) 475 { 476 throw new EncoderException( I18n.err( I18n.ERR_04005 ) ); 477 } 478 479 return buffer; 480 } 481}