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