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 */ 020 package org.apache.directory.shared.ldap.codec.search; 021 022 023 import java.nio.BufferOverflowException; 024 import java.nio.ByteBuffer; 025 import java.util.LinkedList; 026 import java.util.List; 027 028 import org.apache.directory.shared.ldap.exception.LdapException; 029 030 import org.apache.directory.shared.asn1.ber.tlv.TLV; 031 import org.apache.directory.shared.asn1.ber.tlv.UniversalTag; 032 import org.apache.directory.shared.asn1.ber.tlv.Value; 033 import org.apache.directory.shared.asn1.codec.EncoderException; 034 import org.apache.directory.shared.asn1.util.Asn1StringUtils; 035 import org.apache.directory.shared.i18n.I18n; 036 import org.apache.directory.shared.ldap.codec.LdapConstants; 037 import org.apache.directory.shared.ldap.codec.LdapMessageCodec; 038 import org.apache.directory.shared.ldap.codec.MessageTypeEnum; 039 import org.apache.directory.shared.ldap.entry.Entry; 040 import org.apache.directory.shared.ldap.entry.EntryAttribute; 041 import org.apache.directory.shared.ldap.entry.client.DefaultClientAttribute; 042 import org.apache.directory.shared.ldap.entry.client.DefaultClientEntry; 043 import org.apache.directory.shared.ldap.name.DN; 044 import org.apache.directory.shared.ldap.util.StringTools; 045 046 047 /** 048 * A SearchResultEntry Message. Its syntax is : 049 * SearchResultEntry ::= [APPLICATION 4] SEQUENCE { 050 * objectName LDAPDN, 051 * attributes PartialAttributeList } 052 * 053 * PartialAttributeList ::= SEQUENCE OF SEQUENCE { 054 * type AttributeDescription, 055 * vals SET OF AttributeValue } 056 * 057 * AttributeDescription ::= LDAPString 058 * 059 * AttributeValue ::= OCTET STRING 060 * 061 * It contains an entry, with all its attributes, and all the attributes 062 * values. If a search request is submited, all the results are sent one 063 * by one, followed by a searchResultDone message. 064 * 065 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 066 * @version $Rev: 923524 $, $Date: 2010-03-16 01:31:36 +0100 (Tue, 16 Mar 2010) $, 067 */ 068 public class SearchResultEntryCodec extends LdapMessageCodec 069 { 070 // ~ Instance fields 071 // ---------------------------------------------------------------------------- 072 073 /** A temporary storage for the byte[] representing the objectName */ 074 private byte[] objectNameBytes; 075 076 /** The entry */ 077 private Entry entry = new DefaultClientEntry(); 078 079 /** The current attribute being decoded */ 080 private EntryAttribute currentAttributeValue; 081 082 /** The search result entry length */ 083 private int searchResultEntryLength; 084 085 /** The partial attributes length */ 086 private int attributesLength; 087 088 /** The list of all attributes length */ 089 private List<Integer> attributeLength; 090 091 /** The list of all vals length */ 092 private List<Integer> valsLength; 093 094 095 // ~ Constructors 096 // ------------------------------------------------------------------------------- 097 098 /** 099 * Creates a new SearchResultEntry object. 100 */ 101 public SearchResultEntryCodec() 102 { 103 super(); 104 } 105 106 107 // ~ Methods 108 // ------------------------------------------------------------------------------------ 109 110 /** 111 * Get the message type 112 * 113 * @return Returns the type. 114 */ 115 public MessageTypeEnum getMessageType() 116 { 117 return MessageTypeEnum.SEARCH_RESULT_ENTRY; 118 } 119 120 121 /** 122 * {@inheritDoc} 123 */ 124 public String getMessageTypeName() 125 { 126 return "SEARCH_RESULT_ENTRY"; 127 } 128 129 130 /** 131 * Get the entry DN 132 * 133 * @return Returns the objectName. 134 */ 135 public DN getObjectName() 136 { 137 return entry.getDn(); 138 } 139 140 141 /** 142 * Set the entry DN. 143 * 144 * @param objectName The objectName to set. 145 */ 146 public void setObjectName( DN objectName ) 147 { 148 entry.setDn( objectName ); 149 } 150 151 152 /** 153 * Get the entry. 154 * 155 * @return Returns the entry 156 */ 157 public Entry getEntry() 158 { 159 return entry; 160 } 161 162 163 /** 164 * Sets the entry. 165 * 166 * @param entry 167 * the entry 168 */ 169 public void setEntry( Entry entry ) 170 { 171 this.entry = entry; 172 } 173 174 175 /** 176 * Create a new attributeValue 177 * 178 * @param type The attribute's name 179 */ 180 public void addAttributeValues( String type ) 181 { 182 currentAttributeValue = new DefaultClientAttribute( type ); 183 184 try 185 { 186 entry.put( currentAttributeValue ); 187 } 188 catch ( LdapException ne ) 189 { 190 // Too bad... But there is nothing we can do. 191 } 192 } 193 194 195 /** 196 * Add a new value to the current attribute 197 * 198 * @param value 199 */ 200 public void addAttributeValue( Object value ) 201 { 202 if ( value instanceof String ) 203 { 204 currentAttributeValue.add( ( String ) value ); 205 } 206 else 207 { 208 currentAttributeValue.add( ( byte[] ) value ); 209 } 210 } 211 212 213 /** 214 * Compute the SearchResultEntry length 215 * 216 * SearchResultEntry : 217 * <pre> 218 * 0x64 L1 219 * | 220 * +--> 0x04 L2 objectName 221 * +--> 0x30 L3 (attributes) 222 * | 223 * +--> 0x30 L4-1 (partial attributes list) 224 * | | 225 * | +--> 0x04 L5-1 type 226 * | +--> 0x31 L6-1 (values) 227 * | | 228 * | +--> 0x04 L7-1-1 value 229 * | +--> ... 230 * | +--> 0x04 L7-1-n value 231 * | 232 * +--> 0x30 L4-2 (partial attributes list) 233 * | | 234 * | +--> 0x04 L5-2 type 235 * | +--> 0x31 L6-2 (values) 236 * | | 237 * | +--> 0x04 L7-2-1 value 238 * | +--> ... 239 * | +--> 0x04 L7-2-n value 240 * | 241 * +--> ... 242 * | 243 * +--> 0x30 L4-m (partial attributes list) 244 * | 245 * +--> 0x04 L5-m type 246 * +--> 0x31 L6-m (values) 247 * | 248 * +--> 0x04 L7-m-1 value 249 * +--> ... 250 * +--> 0x04 L7-m-n value 251 * </pre> 252 */ 253 protected int computeLengthProtocolOp() 254 { 255 objectNameBytes = StringTools.getBytesUtf8( entry.getDn().getName() ); 256 257 // The entry 258 searchResultEntryLength = 1 + TLV.getNbBytes( objectNameBytes.length ) + objectNameBytes.length; 259 260 // The attributes sequence 261 attributesLength = 0; 262 263 if ( ( entry != null ) && ( entry.size() != 0 ) ) 264 { 265 attributeLength = new LinkedList<Integer>(); 266 valsLength = new LinkedList<Integer>(); 267 268 // Compute the attributes length 269 for ( EntryAttribute attribute : entry ) 270 { 271 int localAttributeLength = 0; 272 int localValuesLength = 0; 273 274 // Get the type length 275 int idLength = attribute.getId().getBytes().length; 276 localAttributeLength = 1 + TLV.getNbBytes( idLength ) + idLength; 277 278 if ( attribute.size() != 0 ) 279 { 280 // The values 281 if ( attribute.size() > 0 ) 282 { 283 localValuesLength = 0; 284 285 for ( org.apache.directory.shared.ldap.entry.Value<?> value : attribute ) 286 { 287 byte[] binaryValue = value.getBytes(); 288 localValuesLength += 1 + TLV.getNbBytes( binaryValue.length ) + binaryValue.length; 289 } 290 291 localAttributeLength += 1 + TLV.getNbBytes( localValuesLength ) + localValuesLength; 292 } 293 else 294 { 295 // We have to deal with the special wase where 296 // we don't have a value. 297 // It will be encoded as an empty OCTETSTRING, 298 // so it will be two byte slong (0x04 0x00) 299 localAttributeLength += 1 + 1; 300 } 301 } 302 else 303 { 304 // We have no values. We will just have an empty SET OF : 305 // 0x31 0x00 306 localAttributeLength += 1 + 1; 307 } 308 309 // add the attribute length to the attributes length 310 attributesLength += 1 + TLV.getNbBytes( localAttributeLength ) + localAttributeLength; 311 312 attributeLength.add( localAttributeLength ); 313 valsLength.add( localValuesLength ); 314 } 315 } 316 317 searchResultEntryLength += 1 + TLV.getNbBytes( attributesLength ) + attributesLength; 318 319 // Return the result. 320 return 1 + TLV.getNbBytes( searchResultEntryLength ) + searchResultEntryLength; 321 } 322 323 324 /** 325 * Encode the SearchResultEntry message to a PDU. 326 * 327 * SearchResultEntry : 328 * <pre> 329 * 0x64 LL 330 * 0x04 LL objectName 331 * 0x30 LL attributes 332 * 0x30 LL partialAttributeList 333 * 0x04 LL type 334 * 0x31 LL vals 335 * 0x04 LL attributeValue 336 * ... 337 * 0x04 LL attributeValue 338 * ... 339 * 0x30 LL partialAttributeList 340 * 0x04 LL type 341 * 0x31 LL vals 342 * 0x04 LL attributeValue 343 * ... 344 * 0x04 LL attributeValue 345 * </pre> 346 * @param buffer The buffer where to put the PDU 347 * @return The PDU. 348 */ 349 protected void encodeProtocolOp( ByteBuffer buffer ) throws EncoderException 350 { 351 try 352 { 353 // The SearchResultEntry Tag 354 buffer.put( LdapConstants.SEARCH_RESULT_ENTRY_TAG ); 355 buffer.put( TLV.getBytes( searchResultEntryLength ) ); 356 357 // The objectName 358 Value.encode( buffer, objectNameBytes ); 359 360 // The attributes sequence 361 buffer.put( UniversalTag.SEQUENCE_TAG ); 362 buffer.put( TLV.getBytes( attributesLength ) ); 363 364 // The partial attribute list 365 if ( ( entry != null ) && ( entry.size() != 0 ) ) 366 { 367 int attributeNumber = 0; 368 369 // Compute the attributes length 370 for ( EntryAttribute attribute : entry ) 371 { 372 // The partial attribute list sequence 373 buffer.put( UniversalTag.SEQUENCE_TAG ); 374 int localAttributeLength = attributeLength.get( attributeNumber ); 375 buffer.put( TLV.getBytes( localAttributeLength ) ); 376 377 // The attribute type 378 Value.encode( buffer, Asn1StringUtils.asciiStringToByte( attribute.getUpId() ) ); 379 380 // The values 381 buffer.put( UniversalTag.SET_TAG ); 382 int localValuesLength = valsLength.get( attributeNumber ); 383 buffer.put( TLV.getBytes( localValuesLength ) ); 384 385 if ( attribute.size() != 0 ) 386 { 387 if ( attribute.size() > 0 ) 388 { 389 for ( org.apache.directory.shared.ldap.entry.Value<?> value : attribute ) 390 { 391 if ( !value.isBinary() ) 392 { 393 Value.encode( buffer, value.getString() ); 394 } 395 else 396 { 397 Value.encode( buffer, value.getBytes() ); 398 } 399 } 400 } 401 } 402 403 // Go to the next attribute number; 404 attributeNumber++; 405 } 406 } 407 } 408 catch ( BufferOverflowException boe ) 409 { 410 throw new EncoderException( I18n.err( I18n.ERR_04005 ) ); 411 } 412 } 413 414 415 /** 416 * Returns the Search Result Entry string 417 * 418 * @return The Search Result Entry string 419 */ 420 public String toString() 421 { 422 423 StringBuilder sb = new StringBuilder(); 424 425 sb.append( " Search Result Entry\n" ); 426 sb.append( " entry\n" ); 427 428 if ( ( entry == null ) || ( entry.size() == 0 ) ) 429 { 430 sb.append( " No entry\n" ); 431 } 432 else 433 { 434 sb.append( entry ); 435 } 436 437 return sb.toString(); 438 } 439 440 441 /** 442 * @return Returns the currentAttributeValue. 443 */ 444 public String getCurrentAttributeValueType() 445 { 446 return currentAttributeValue.getId(); 447 } 448 }