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