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 02:31:36 +0200 (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 }