View Javadoc
1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    * 
10   *    http://www.apache.org/licenses/LICENSE-2.0
11   * 
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License.
18   * 
19   */
20  package org.apache.directory.api.ldap.codec.decorators;
21  
22  
23  import java.nio.BufferOverflowException;
24  import java.nio.ByteBuffer;
25  import java.util.LinkedList;
26  import java.util.List;
27  
28  import org.apache.directory.api.asn1.EncoderException;
29  import org.apache.directory.api.asn1.ber.tlv.BerValue;
30  import org.apache.directory.api.asn1.ber.tlv.TLV;
31  import org.apache.directory.api.asn1.ber.tlv.UniversalTag;
32  import org.apache.directory.api.i18n.I18n;
33  import org.apache.directory.api.ldap.codec.api.LdapApiService;
34  import org.apache.directory.api.ldap.codec.api.LdapConstants;
35  import org.apache.directory.api.ldap.codec.api.MessageDecorator;
36  import org.apache.directory.api.ldap.model.entry.Attribute;
37  import org.apache.directory.api.ldap.model.entry.DefaultAttribute;
38  import org.apache.directory.api.ldap.model.entry.Entry;
39  import org.apache.directory.api.ldap.model.entry.Value;
40  import org.apache.directory.api.ldap.model.exception.LdapException;
41  import org.apache.directory.api.ldap.model.message.SearchResultEntry;
42  import org.apache.directory.api.ldap.model.name.Dn;
43  import org.apache.directory.api.util.Strings;
44  
45  
46  /**
47   * A decorator for the SearchResultEntry message
48   *
49   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
50   */
51  public class SearchResultEntryDecorator extends MessageDecorator<SearchResultEntry> implements SearchResultEntry
52  {
53      /** A temporary storage for the byte[] representing the objectName */
54      private byte[] objectNameBytes;
55  
56      /** The search result entry length */
57      private int searchResultEntryLength;
58  
59      /** The partial attributes length */
60      private int attributesLength;
61  
62      /** The list of all attributes length */
63      private List<Integer> attributeLength;
64  
65      /** The list of all attributes Id bytes */
66      private List<byte[]> attributeIds;
67  
68      /** The list of all values length */
69      private List<Integer> valuesLength;
70  
71      /** The current attribute being processed */
72      private Attribute currentAttribute;
73  
74  
75      /**
76       * Makes a SearchResultEntry encodable.
77       *
78       * @param decoratedMessage the decorated SearchResultEntry
79       */
80      public SearchResultEntryDecorator( LdapApiService codec, SearchResultEntry decoratedMessage )
81      {
82          super( codec, decoratedMessage );
83      }
84  
85  
86      /**
87       * Gets the distinguished name bytes of the entry object returned.
88       *
89       * @return the Dn bytes of the entry returned.
90       */
91      public byte[] getObjectNameBytes()
92      {
93          return objectNameBytes;
94      }
95  
96  
97      /**
98       * Sets the distinguished name bytes of the entry object returned.
99       *
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 }