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.LdapCodecConstants; 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 codec The LDAP service instance 079 * @param decoratedMessage the decorated SearchResultEntry 080 */ 081 public SearchResultEntryDecorator( LdapApiService codec, SearchResultEntry decoratedMessage ) 082 { 083 super( codec, decoratedMessage ); 084 } 085 086 087 /** 088 * @return The current attribute 089 */ 090 public Attribute getCurrentAttribute() 091 { 092 return currentAttribute; 093 } 094 095 096 /** 097 * Create a new attribute 098 * 099 * @param type The attribute's type 100 * @throws LdapException If the value is invalid 101 */ 102 public void addAttribute( String type ) throws LdapException 103 { 104 currentAttribute = new DefaultAttribute( type ); 105 106 getDecorated().getEntry().put( currentAttribute ); 107 } 108 109 110 /** 111 * Create a new attribute 112 * 113 * @param type The attribute's type 114 * @throws LdapException If the value is invalid 115 */ 116 public void addAttribute( byte[] type ) throws LdapException 117 { 118 currentAttribute = new DefaultAttribute( type ); 119 120 getDecorated().getEntry().put( currentAttribute ); 121 } 122 123 124 /** 125 * Add a new value to the current attribute 126 * 127 * @param value The added value 128 * @throws LdapException If the value is invalid 129 */ 130 public void addAttributeValue( Object value ) throws LdapException 131 { 132 if ( value instanceof String ) 133 { 134 currentAttribute.add( ( String ) value ); 135 } 136 else 137 { 138 currentAttribute.add( ( byte[] ) value ); 139 } 140 } 141 142 143 //------------------------------------------------------------------------- 144 // The IntermediateResponse methods 145 //------------------------------------------------------------------------- 146 147 /** 148 * {@inheritDoc} 149 */ 150 @Override 151 public Dn getObjectName() 152 { 153 return getDecorated().getObjectName(); 154 } 155 156 157 /** 158 * {@inheritDoc} 159 */ 160 @Override 161 public void setObjectName( Dn objectName ) 162 { 163 getDecorated().setObjectName( objectName ); 164 } 165 166 167 /** 168 * {@inheritDoc} 169 */ 170 @Override 171 public Entry getEntry() 172 { 173 return getDecorated().getEntry(); 174 } 175 176 177 /** 178 * {@inheritDoc} 179 */ 180 @Override 181 public void setEntry( Entry entry ) 182 { 183 getDecorated().setEntry( entry ); 184 } 185 186 187 //------------------------------------------------------------------------- 188 // The Decorator methods 189 //------------------------------------------------------------------------- 190 191 /** 192 * Compute the SearchResultEntry length 193 * <br> 194 * SearchResultEntry : 195 * <pre> 196 * 0x64 L1 197 * | 198 * +--> 0x04 L2 objectName 199 * +--> 0x30 L3 (attributes) 200 * | 201 * +--> 0x30 L4-1 (partial attributes list) 202 * | | 203 * | +--> 0x04 L5-1 type 204 * | +--> 0x31 L6-1 (values) 205 * | | 206 * | +--> 0x04 L7-1-1 value 207 * | +--> ... 208 * | +--> 0x04 L7-1-n value 209 * | 210 * +--> 0x30 L4-2 (partial attributes list) 211 * | | 212 * | +--> 0x04 L5-2 type 213 * | +--> 0x31 L6-2 (values) 214 * | | 215 * | +--> 0x04 L7-2-1 value 216 * | +--> ... 217 * | +--> 0x04 L7-2-n value 218 * | 219 * +--> ... 220 * | 221 * +--> 0x30 L4-m (partial attributes list) 222 * | 223 * +--> 0x04 L5-m type 224 * +--> 0x31 L6-m (values) 225 * | 226 * +--> 0x04 L7-m-1 value 227 * +--> ... 228 * +--> 0x04 L7-m-n value 229 * </pre> 230 */ 231 @Override 232 public int computeLength() 233 { 234 Dn dn = getObjectName(); 235 236 objectNameBytes = Strings.getBytesUtf8Ascii( dn.getName() ); 237 238 // The entry 239 searchResultEntryLength = 1 + TLV.getNbBytes( objectNameBytes.length ) + objectNameBytes.length; 240 241 // The attributes sequence 242 attributesLength = 0; 243 244 Entry entry = getEntry(); 245 246 if ( ( entry != null ) && ( entry.size() != 0 ) ) 247 { 248 attributeLength = new LinkedList<>(); 249 attributeIds = new LinkedList<>(); 250 valuesLength = new LinkedList<>(); 251 252 // Store those lists in the object 253 valuesLength = new LinkedList<>(); 254 255 // Compute the attributes length 256 for ( Attribute attribute : entry ) 257 { 258 int localAttributeLength; 259 int localValuesLength = 0; 260 261 // Get the type length 262 byte[] attributeIdBytes = Strings.getBytesUtf8Ascii( attribute.getUpId() ); 263 attributeIds.add( attributeIdBytes ); 264 int idLength = attributeIdBytes.length; 265 localAttributeLength = 1 + TLV.getNbBytes( idLength ) + idLength; 266 267 if ( attribute.size() != 0 ) 268 { 269 // The values 270 if ( attribute.size() > 0 ) 271 { 272 localValuesLength = 0; 273 274 for ( org.apache.directory.api.ldap.model.entry.Value<?> value : attribute ) 275 { 276 byte[] binaryValue = value.getBytes(); 277 localValuesLength += 1 + TLV.getNbBytes( binaryValue.length ) + binaryValue.length; 278 } 279 280 localAttributeLength += 1 + TLV.getNbBytes( localValuesLength ) + localValuesLength; 281 } 282 else 283 { 284 // We have to deal with the special case where 285 // we don't have a value. 286 // It will be encoded as an empty OCTETSTRING, 287 // so it will be two bytes long (0x04 0x00) 288 localAttributeLength += 1 + 1; 289 } 290 } 291 else 292 { 293 // We have no values. We will just have an empty SET OF : 294 // 0x31 0x00 295 localAttributeLength += 1 + 1; 296 } 297 298 // add the attribute length to the attributes length 299 attributesLength += 1 + TLV.getNbBytes( localAttributeLength ) + localAttributeLength; 300 301 // Store the lengths of the encoded attributes and values 302 attributeLength.add( localAttributeLength ); 303 valuesLength.add( localValuesLength ); 304 } 305 } 306 307 searchResultEntryLength += 1 + TLV.getNbBytes( attributesLength ) + attributesLength; 308 309 // Return the result. 310 return 1 + TLV.getNbBytes( searchResultEntryLength ) + searchResultEntryLength; 311 } 312 313 314 /** 315 * Encode the SearchResultEntry message to a PDU. 316 * <br> 317 * SearchResultEntry : 318 * <pre> 319 * 0x64 LL 320 * 0x04 LL objectName 321 * 0x30 LL attributes 322 * 0x30 LL partialAttributeList 323 * 0x04 LL type 324 * 0x31 LL vals 325 * 0x04 LL attributeValue 326 * ... 327 * 0x04 LL attributeValue 328 * ... 329 * 0x30 LL partialAttributeList 330 * 0x04 LL type 331 * 0x31 LL vals 332 * 0x04 LL attributeValue 333 * ... 334 * 0x04 LL attributeValue 335 * </pre> 336 * 337 * @param buffer The buffer where to put the PDU 338 * @return The PDU. 339 */ 340 @Override 341 public ByteBuffer encode( ByteBuffer buffer ) throws EncoderException 342 { 343 try 344 { 345 // The SearchResultEntry Tag 346 buffer.put( LdapCodecConstants.SEARCH_RESULT_ENTRY_TAG ); 347 buffer.put( TLV.getBytes( searchResultEntryLength ) ); 348 349 // The objectName 350 BerValue.encode( buffer, objectNameBytes ); 351 352 // The attributes sequence 353 buffer.put( UniversalTag.SEQUENCE.getValue() ); 354 buffer.put( TLV.getBytes( attributesLength ) ); 355 356 // The partial attribute list 357 Entry entry = getEntry(); 358 359 if ( ( entry != null ) && ( entry.size() != 0 ) ) 360 { 361 int attributeNumber = 0; 362 363 // Compute the attributes length 364 for ( Attribute attribute : entry ) 365 { 366 // The partial attribute list sequence 367 buffer.put( UniversalTag.SEQUENCE.getValue() ); 368 int localAttributeLength = attributeLength.get( attributeNumber ); 369 buffer.put( TLV.getBytes( localAttributeLength ) ); 370 371 // The attribute type 372 BerValue.encode( buffer, attributeIds.get( attributeNumber ) ); 373 374 // The values 375 buffer.put( UniversalTag.SET.getValue() ); 376 int localValuesLength = valuesLength.get( attributeNumber ); 377 buffer.put( TLV.getBytes( localValuesLength ) ); 378 379 if ( attribute.size() > 0 ) 380 { 381 for ( Value<?> value : attribute ) 382 { 383 BerValue.encode( buffer, value.getBytes() ); 384 } 385 } 386 387 // Go to the next attribute number 388 attributeNumber++; 389 } 390 } 391 } 392 catch ( BufferOverflowException boe ) 393 { 394 throw new EncoderException( I18n.err( I18n.ERR_04005 ), boe ); 395 } 396 397 return buffer; 398 } 399}