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.LdapCodecConstants;
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 codec The LDAP service instance
79       * @param decoratedMessage the decorated SearchResultEntry
80       */
81      public SearchResultEntryDecorator( LdapApiService codec, SearchResultEntry decoratedMessage )
82      {
83          super( codec, decoratedMessage );
84      }
85  
86  
87      /**
88       * @return The current attribute
89       */
90      public Attribute getCurrentAttribute()
91      {
92          return currentAttribute;
93      }
94  
95  
96      /**
97       * Create a new attribute
98       * 
99       * @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      *  +--&gt; 0x04 L2 objectName
199      *  +--&gt; 0x30 L3 (attributes)
200      *        |
201      *        +--&gt; 0x30 L4-1 (partial attributes list)
202      *        |     |
203      *        |     +--&gt; 0x04 L5-1 type
204      *        |     +--&gt; 0x31 L6-1 (values)
205      *        |           |
206      *        |           +--&gt; 0x04 L7-1-1 value
207      *        |           +--&gt; ...
208      *        |           +--&gt; 0x04 L7-1-n value
209      *        |
210      *        +--&gt; 0x30 L4-2 (partial attributes list)
211      *        |     |
212      *        |     +--&gt; 0x04 L5-2 type
213      *        |     +--&gt; 0x31 L6-2 (values)
214      *        |           |
215      *        |           +--&gt; 0x04 L7-2-1 value
216      *        |           +--&gt; ...
217      *        |           +--&gt; 0x04 L7-2-n value
218      *        |
219      *        +--&gt; ...
220      *        |
221      *        +--&gt; 0x30 L4-m (partial attributes list)
222      *              |
223      *              +--&gt; 0x04 L5-m type
224      *              +--&gt; 0x31 L6-m (values)
225      *                    |
226      *                    +--&gt; 0x04 L7-m-1 value
227      *                    +--&gt; ...
228      *                    +--&gt; 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 }