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.LdapConstants;
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 decoratedMessage the decorated SearchResultEntry
079     */
080    public SearchResultEntryDecorator( LdapApiService codec, SearchResultEntry decoratedMessage )
081    {
082        super( codec, decoratedMessage );
083    }
084
085
086    /**
087     * Gets the distinguished name bytes of the entry object returned.
088     *
089     * @return the Dn bytes of the entry returned.
090     */
091    public byte[] getObjectNameBytes()
092    {
093        return objectNameBytes;
094    }
095
096
097    /**
098     * Sets the distinguished name bytes of the entry object returned.
099     *
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}