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.shared.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.shared.asn1.EncoderException;
029import org.apache.directory.shared.asn1.ber.tlv.TLV;
030import org.apache.directory.shared.asn1.ber.tlv.UniversalTag;
031import org.apache.directory.shared.asn1.ber.tlv.Value;
032import org.apache.directory.shared.asn1.util.Asn1StringUtils;
033import org.apache.directory.shared.i18n.I18n;
034import org.apache.directory.shared.ldap.codec.api.LdapApiService;
035import org.apache.directory.shared.ldap.codec.api.LdapConstants;
036import org.apache.directory.shared.ldap.codec.api.MessageDecorator;
037import org.apache.directory.shared.ldap.model.entry.DefaultAttribute;
038import org.apache.directory.shared.ldap.model.entry.Entry;
039import org.apache.directory.shared.ldap.model.entry.Attribute;
040import org.apache.directory.shared.ldap.model.exception.LdapException;
041import org.apache.directory.shared.ldap.model.message.SearchResultEntry;
042import org.apache.directory.shared.ldap.model.name.Dn;
043import org.apache.directory.shared.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 vals length */
066    private List<Integer> valsLength;
067    
068    /** The current attribute being processed */
069    private Attribute currentAttribute;
070
071
072    /**
073     * Makes a SearchResultEntry encodable.
074     *
075     * @param decoratedMessage the decorated SearchResultEntry
076     */
077    public SearchResultEntryDecorator( LdapApiService codec, SearchResultEntry decoratedMessage )
078    {
079        super( codec, decoratedMessage );
080    }
081
082
083    /**
084     * Gets the distinguished name bytes of the entry object returned.
085     *
086     * @return the Dn bytes of the entry returned.
087     */
088    public byte[] getObjectNameBytes()
089    {
090        return objectNameBytes;
091    }
092
093
094    /**
095     * Sets the distinguished name bytes of the entry object returned.
096     *
097     * @param objectNameBytes the Dn bytes of the entry returned.
098     */
099    public void setObjectNameBytes( byte[] objectNameBytes )
100    {
101        this.objectNameBytes = objectNameBytes;
102    }
103
104
105    /**
106     * @return The encoded SearchResultEntry's length
107     */
108    public int getSearchResultEntryLength()
109    {
110        return searchResultEntryLength;
111    }
112
113
114    /**
115     * Stores the encoded length for the SearchResultEntry
116     * @param searchResultEntryLength The encoded length
117     */
118    public void setSearchResultEntryLength( int searchResultEntryLength )
119    {
120        this.searchResultEntryLength = searchResultEntryLength;
121    }
122
123
124    /**
125     * @return The encoded PartialAttributeList's length
126     */
127    public int getAttributesLength()
128    {
129        return attributesLength;
130    }
131
132
133    /**
134     * Stores the encoded length for the Attributes
135     * @param attributesLength The list of encoded lengths
136     */
137    public void setAttributesLength( int attributesLength )
138    {
139        this.attributesLength = attributesLength;
140    }
141
142
143    /**
144     * @return The encoded PartialAttributeList's length
145     */
146    public List<Integer> getAttributeLength()
147    {
148        return attributeLength;
149    }
150
151
152    /**
153     * @return The list of encoded Attributes' length
154     */
155    public void setAttributeLength( List<Integer> attributeLength )
156    {
157        this.attributeLength = attributeLength;
158    }
159
160
161    /**
162     * @return The list of encoded values' length
163     */
164    public List<Integer> getValsLength()
165    {
166        return valsLength;
167    }
168
169
170    /**
171     * Stores the list of encoded length for the values
172     * @param valsLength The list of encoded lengths
173     */
174    public void setValsLength( List<Integer> valsLength )
175    {
176        this.valsLength = valsLength;
177    }
178    
179    
180    public Attribute getCurrentAttribute()
181    {
182        return currentAttribute;
183    }
184
185
186    /**
187     * Create a new attribute
188     * 
189     * @param type The attribute's type
190     */
191    public void addAttribute( String type ) throws LdapException
192    {
193        currentAttribute = new DefaultAttribute( type );
194
195        getDecorated().getEntry().put( currentAttribute );
196    }
197
198
199    /**
200     * Add a new value to the current attribute
201     * 
202     * @param value The added value
203     */
204    public void addAttributeValue( Object value ) throws LdapException
205    {
206        if ( value instanceof String )
207        {
208            currentAttribute.add( ( String ) value );
209        }
210        else
211        {
212            currentAttribute.add( ( byte[] ) value );
213        }
214    }
215
216
217    //-------------------------------------------------------------------------
218    // The IntermediateResponse methods
219    //-------------------------------------------------------------------------
220    
221    
222    /**
223     * {@inheritDoc}
224     */
225    public Dn getObjectName()
226    {
227        return getDecorated().getObjectName();
228    }
229
230
231    /**
232     * {@inheritDoc}
233     */
234    public void setObjectName( Dn objectName )
235    {
236        getDecorated().setObjectName( objectName );
237    }
238
239
240    /**
241     * {@inheritDoc}
242     */
243    public Entry getEntry()
244    {
245        return getDecorated().getEntry();
246    }
247
248
249    /**
250     * {@inheritDoc}
251     */
252    public void setEntry( Entry entry )
253    {
254        getDecorated().setEntry( entry );
255    }
256
257
258    //-------------------------------------------------------------------------
259    // The Decorator methods
260    //-------------------------------------------------------------------------
261    
262    
263    /**
264     * Compute the SearchResultEntry length
265     * 
266     * SearchResultEntry :
267     * <pre>
268     * 0x64 L1
269     *  |
270     *  +--> 0x04 L2 objectName
271     *  +--> 0x30 L3 (attributes)
272     *        |
273     *        +--> 0x30 L4-1 (partial attributes list)
274     *        |     |
275     *        |     +--> 0x04 L5-1 type
276     *        |     +--> 0x31 L6-1 (values)
277     *        |           |
278     *        |           +--> 0x04 L7-1-1 value
279     *        |           +--> ...
280     *        |           +--> 0x04 L7-1-n value
281     *        |
282     *        +--> 0x30 L4-2 (partial attributes list)
283     *        |     |
284     *        |     +--> 0x04 L5-2 type
285     *        |     +--> 0x31 L6-2 (values)
286     *        |           |
287     *        |           +--> 0x04 L7-2-1 value
288     *        |           +--> ...
289     *        |           +--> 0x04 L7-2-n value
290     *        |
291     *        +--> ...
292     *        |
293     *        +--> 0x30 L4-m (partial attributes list)
294     *              |
295     *              +--> 0x04 L5-m type
296     *              +--> 0x31 L6-m (values)
297     *                    |
298     *                    +--> 0x04 L7-m-1 value
299     *                    +--> ...
300     *                    +--> 0x04 L7-m-n value
301     * </pre>
302     */
303    public int computeLength()
304    {
305        Dn dn = getObjectName();
306
307        byte[] dnBytes = Strings.getBytesUtf8(dn.getName());
308
309        // The entry
310        int searchResultEntryLength = 1 + TLV.getNbBytes( dnBytes.length ) + dnBytes.length;
311        setObjectNameBytes( dnBytes );
312
313        // The attributes sequence
314        int attributesLength = 0;
315
316        Entry entry = getEntry();
317
318        if ( ( entry != null ) && ( entry.size() != 0 ) )
319        {
320            List<Integer> attributeLength = new LinkedList<Integer>();
321            List<Integer> valsLength = new LinkedList<Integer>();
322
323            // Store those lists in the object
324            setAttributeLength( attributeLength );
325            setValsLength( valsLength );
326
327            // Compute the attributes length
328            for ( Attribute attribute : entry )
329            {
330                int localAttributeLength = 0;
331                int localValuesLength = 0;
332
333                // Get the type length
334                int idLength = attribute.getUpId().getBytes().length;
335                localAttributeLength = 1 + TLV.getNbBytes( idLength ) + idLength;
336
337                if ( attribute.size() != 0 )
338                {
339                    // The values
340                    if ( attribute.size() > 0 )
341                    {
342                        localValuesLength = 0;
343
344                        for ( org.apache.directory.shared.ldap.model.entry.Value<?> value : attribute )
345                        {
346                            byte[] binaryValue = value.getBytes();
347                            localValuesLength += 1 + TLV.getNbBytes( binaryValue.length ) + binaryValue.length;
348                        }
349
350                        localAttributeLength += 1 + TLV.getNbBytes( localValuesLength ) + localValuesLength;
351                    }
352                    else
353                    {
354                        // We have to deal with the special wase where
355                        // we don't have a value.
356                        // It will be encoded as an empty OCTETSTRING,
357                        // so it will be two byte slong (0x04 0x00)
358                        localAttributeLength += 1 + 1;
359                    }
360                }
361                else
362                {
363                    // We have no values. We will just have an empty SET OF :
364                    // 0x31 0x00
365                    localAttributeLength += 1 + 1;
366                }
367
368                // add the attribute length to the attributes length
369                attributesLength += 1 + TLV.getNbBytes( localAttributeLength ) + localAttributeLength;
370
371                // Store the lengths of the encoded attributes and values
372                attributeLength.add( localAttributeLength );
373                valsLength.add( localValuesLength );
374            }
375
376            // Store the lengths of the entry
377            setAttributesLength( attributesLength );
378        }
379
380        searchResultEntryLength += 1 + TLV.getNbBytes( attributesLength ) + attributesLength;
381
382        // Store the length of the response 
383        setSearchResultEntryLength( searchResultEntryLength );
384
385        // Return the result.
386        return 1 + TLV.getNbBytes( searchResultEntryLength ) + searchResultEntryLength;
387    }
388
389
390    /**
391     * Encode the SearchResultEntry message to a PDU.
392     * 
393     * SearchResultEntry :
394     * <pre>
395     * 0x64 LL
396     *   0x04 LL objectName
397     *   0x30 LL attributes
398     *     0x30 LL partialAttributeList
399     *       0x04 LL type
400     *       0x31 LL vals
401     *         0x04 LL attributeValue
402     *         ... 
403     *         0x04 LL attributeValue
404     *     ... 
405     *     0x30 LL partialAttributeList
406     *       0x04 LL type
407     *       0x31 LL vals
408     *         0x04 LL attributeValue
409     *         ... 
410     *         0x04 LL attributeValue 
411     * </pre>
412     * @param buffer The buffer where to put the PDU
413     * @param searchResultEntryDecorator the SearchResultEntry decorator
414     * @return The PDU.
415     */
416    public ByteBuffer encode( ByteBuffer buffer ) throws EncoderException
417    {
418        try
419        {
420            // The SearchResultEntry Tag
421            buffer.put( LdapConstants.SEARCH_RESULT_ENTRY_TAG );
422            buffer.put( TLV.getBytes( getSearchResultEntryLength() ) );
423
424            // The objectName
425            Value.encode( buffer, getObjectNameBytes() );
426
427            // The attributes sequence
428            buffer.put( UniversalTag.SEQUENCE.getValue() );
429            buffer.put( TLV.getBytes( getAttributesLength() ) );
430
431            // The partial attribute list
432            Entry entry = getEntry();
433
434            if ( ( entry != null ) && ( entry.size() != 0 ) )
435            {
436                int attributeNumber = 0;
437
438                // Compute the attributes length
439                for ( Attribute attribute : entry )
440                {
441                    // The partial attribute list sequence
442                    buffer.put( UniversalTag.SEQUENCE.getValue() );
443                    int localAttributeLength = getAttributeLength().get( attributeNumber );
444                    buffer.put( TLV.getBytes( localAttributeLength ) );
445
446                    // The attribute type
447                    Value.encode( buffer, Asn1StringUtils.asciiStringToByte( attribute.getUpId() ) );
448
449                    // The values
450                    buffer.put( UniversalTag.SET.getValue() );
451                    int localValuesLength = getValsLength().get( attributeNumber );
452                    buffer.put( TLV.getBytes( localValuesLength ) );
453
454                    if ( attribute.size() > 0 )
455                    {
456                        for ( org.apache.directory.shared.ldap.model.entry.Value<?> value : attribute )
457                        {
458                            if ( value.isHumanReadable() )
459                            {
460                                Value.encode( buffer, value.getString() );
461                            }
462                            else
463                            {
464                                Value.encode( buffer, value.getBytes() );
465                            }
466                        }
467                    }
468
469                    // Go to the next attribute number;
470                    attributeNumber++;
471                }
472            }
473        }
474        catch ( BufferOverflowException boe )
475        {
476            throw new EncoderException( I18n.err( I18n.ERR_04005 ) );
477        }
478        
479        return buffer;
480    }
481}