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.model.entry.Attribute;
036import org.apache.directory.api.ldap.model.entry.DefaultAttribute;
037import org.apache.directory.api.ldap.model.entry.Entry;
038import org.apache.directory.api.ldap.model.entry.Value;
039import org.apache.directory.api.ldap.model.exception.LdapException;
040import org.apache.directory.api.ldap.model.message.AddRequest;
041import org.apache.directory.api.ldap.model.message.Control;
042import org.apache.directory.api.ldap.model.name.Dn;
043import org.apache.directory.api.util.Strings;
044
045
046/**
047 * A decorator for the AddRequest message
048 *
049 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
050 */
051public final class AddRequestDecorator extends SingleReplyRequestDecorator<AddRequest> implements
052    AddRequest
053{
054    /** The add request length */
055    private int addRequestLength;
056
057    /** The Entry length */
058    private int entryLength;
059
060    /** The list of all attributes length */
061    private List<Integer> attributesLength;
062
063    /** The list of all attributes Id bytes */
064    private List<byte[]> attributeIds;
065
066    /** The list of all vals length */
067    private List<Integer> valuesLength;
068
069    /** The current attribute being decoded */
070    private Attribute currentAttribute;
071
072    /** The bytes containing the Dn */
073    private byte[] dnBytes;
074
075
076    /**
077     * Makes a AddRequest a MessageDecorator.
078     *
079     * @param decoratedMessage the decorated AddRequest
080     */
081    public AddRequestDecorator( LdapApiService codec, AddRequest decoratedMessage )
082    {
083        super( codec, decoratedMessage );
084    }
085
086
087    /**
088     * Stores the encoded length for the AddRequest
089     * @param addRequestLength The encoded length
090     */
091    public void setAddRequestLength( int addRequestLength )
092    {
093        this.addRequestLength = addRequestLength;
094    }
095
096
097    /**
098     * @return The encoded AddRequest's length
099     */
100    public int getAddRequestLength()
101    {
102        return addRequestLength;
103    }
104
105
106    /**
107     * Stores the encoded length for the Entry
108     * @param entryLength The encoded length
109     */
110    public void setEntryLength( int entryLength )
111    {
112        this.entryLength = entryLength;
113    }
114
115
116    /**
117     * @return The encoded Entry's length
118     */
119    public int getEntryLength()
120    {
121        return entryLength;
122    }
123
124
125    /**
126     * Stores the encoded length for the attributes
127     * @param attributesLength The encoded length
128     */
129    public void setAttributesLength( List<Integer> attributesLength )
130    {
131        this.attributesLength = attributesLength;
132    }
133
134
135    /**
136     * @return The encoded values length
137     */
138    public List<Integer> getAttributesLength()
139    {
140        return attributesLength;
141    }
142
143
144    /**
145     * Stores the encoded length for the values
146     * @param valuesLength The encoded length
147     */
148    public void setValuesLength( List<Integer> valuesLength )
149    {
150        this.valuesLength = valuesLength;
151    }
152
153
154    /**
155     * @return The encoded values length
156     */
157    public List<Integer> getValuesLength()
158    {
159        return valuesLength;
160    }
161
162
163    /**
164     * {@inheritDoc}
165     */
166    public AddRequest setMessageId( int messageId )
167    {
168        super.setMessageId( messageId );
169
170        return this;
171    }
172
173
174    /**
175     * {@inheritDoc}
176     */
177    public AddRequest addControl( Control control )
178    {
179        return ( AddRequest ) super.addControl( control );
180    }
181
182
183    /**
184     * {@inheritDoc}
185     */
186    public AddRequest addAllControls( Control[] controls )
187    {
188        return ( AddRequest ) super.addAllControls( controls );
189    }
190
191
192    /**
193     * {@inheritDoc}
194     */
195    public AddRequest removeControl( Control control )
196    {
197        return ( AddRequest ) super.removeControl( control );
198    }
199
200
201    //-------------------------------------------------------------------------
202    // The AddRequest methods
203    //-------------------------------------------------------------------------
204
205    /**
206     * {@inheritDoc}
207     */
208    public Dn getEntryDn()
209    {
210        return getDecorated().getEntryDn();
211    }
212
213
214    /**
215     * {@inheritDoc}
216     */
217    public AddRequest setEntryDn( Dn entry )
218    {
219        getDecorated().setEntryDn( entry );
220
221        return this;
222    }
223
224
225    /**
226     * {@inheritDoc}
227     */
228    public Entry getEntry()
229    {
230        return getDecorated().getEntry();
231    }
232
233
234    /**
235     * {@inheritDoc}
236     */
237    public AddRequest setEntry( Entry entry )
238    {
239        getDecorated().setEntry( entry );
240
241        return this;
242    }
243
244
245    /**
246     * Create a new attributeValue
247     * 
248     * @param type The attribute's name (called 'type' in the grammar)
249     */
250    public void addAttributeType( String type ) throws LdapException
251    {
252        // do not create a new attribute if we have seen this attributeType before
253        if ( getDecorated().getEntry().get( type ) != null )
254        {
255            currentAttribute = getDecorated().getEntry().get( type );
256            return;
257        }
258
259        // fix this to use AttributeImpl(type.getString().toLowerCase())
260        currentAttribute = new DefaultAttribute( type );
261        getDecorated().getEntry().put( currentAttribute );
262    }
263
264
265    /**
266     * @return Returns the currentAttribute type.
267     */
268    public String getCurrentAttributeType()
269    {
270        return currentAttribute.getUpId();
271    }
272
273
274    /**
275     * Add a new value to the current attribute
276     * 
277     * @param value The value to add
278     */
279    public void addAttributeValue( String value ) throws LdapException
280    {
281        currentAttribute.add( value );
282    }
283
284
285    /**
286     * Add a new value to the current attribute
287     * 
288     * @param value The value to add
289     */
290    public void addAttributeValue( Value<?> value ) throws LdapException
291    {
292        currentAttribute.add( value );
293    }
294
295
296    /**
297     * Add a new value to the current attribute
298     * 
299     * @param value The value to add
300     */
301    public void addAttributeValue( byte[] value ) throws LdapException
302    {
303        currentAttribute.add( value );
304    }
305
306
307    //-------------------------------------------------------------------------
308    // The Decorator methods
309    //-------------------------------------------------------------------------
310    /**
311     * Compute the AddRequest length
312     * 
313     * AddRequest :
314     * 
315     * 0x68 L1
316     *  |
317     *  +--> 0x04 L2 entry
318     *  +--> 0x30 L3 (attributes)
319     *        |
320     *        +--> 0x30 L4-1 (attribute)
321     *        |     |
322     *        |     +--> 0x04 L5-1 type
323     *        |     +--> 0x31 L6-1 (values)
324     *        |           |
325     *        |           +--> 0x04 L7-1-1 value
326     *        |           +--> ...
327     *        |           +--> 0x04 L7-1-n value
328     *        |
329     *        +--> 0x30 L4-2 (attribute)
330     *        |     |
331     *        |     +--> 0x04 L5-2 type
332     *        |     +--> 0x31 L6-2 (values)
333     *        |           |
334     *        |           +--> 0x04 L7-2-1 value
335     *        |           +--> ...
336     *        |           +--> 0x04 L7-2-n value
337     *        |
338     *        +--> ...
339     *        |
340     *        +--> 0x30 L4-m (attribute)
341     *              |
342     *              +--> 0x04 L5-m type
343     *              +--> 0x31 L6-m (values)
344     *                    |
345     *                    +--> 0x04 L7-m-1 value
346     *                    +--> ...
347     *                    +--> 0x04 L7-m-n value
348     */
349    public int computeLength()
350    {
351        AddRequest addRequest = getDecorated();
352        Entry entry = addRequest.getEntry();
353
354        if ( entry == null )
355        {
356            throw new IllegalArgumentException( I18n.err( I18n.ERR_04481_ENTRY_NULL_VALUE ) );
357        }
358
359        dnBytes = Strings.getBytesUtf8( entry.getDn().getName() );
360        int dnLen = dnBytes.length;
361
362        // The entry Dn
363        int addRequestLength = 1 + TLV.getNbBytes( dnLen ) + dnLen;
364
365        // The attributes sequence
366        int entryLength = 0;
367
368        if ( entry.size() != 0 )
369        {
370            attributesLength = new LinkedList<Integer>();
371            attributeIds = new LinkedList<byte[]>();
372            valuesLength = new LinkedList<Integer>();
373
374            // Compute the attributes length
375            for ( Attribute attribute : entry )
376            {
377                int localAttributeLength = 0;
378                int localValuesLength = 0;
379
380                // Get the type length
381                byte[] attributeIdBytes = Strings.getBytesUtf8( attribute.getUpId() );
382                attributeIds.add( attributeIdBytes );
383
384                int idLength = attributeIdBytes.length;
385                localAttributeLength = 1 + TLV.getNbBytes( idLength ) + idLength;
386
387                // The values
388                if ( attribute.size() != 0 )
389                {
390                    localValuesLength = 0;
391
392                    for ( Value<?> value : attribute )
393                    {
394                        if ( value.getBytes() == null )
395                        {
396                            localValuesLength += 1 + 1;
397                        }
398                        else
399                        {
400                            int valueLength = value.getBytes().length;
401                            localValuesLength += 1 + TLV.getNbBytes( valueLength ) + valueLength;
402                        }
403                    }
404
405                    localAttributeLength += 1 + TLV.getNbBytes( localValuesLength ) + localValuesLength;
406                }
407                else
408                {
409                    // No value : we still have to store the encapsulating Sequence
410                    localValuesLength = 1 + 1;
411                    localAttributeLength += 1 + 1 + localValuesLength;
412                }
413
414                // add the attribute length to the attributes length
415                entryLength += 1 + TLV.getNbBytes( localAttributeLength ) + localAttributeLength;
416
417                attributesLength.add( localAttributeLength );
418                valuesLength.add( localValuesLength );
419            }
420
421            setAttributesLength( attributesLength );
422            setValuesLength( valuesLength );
423            setEntryLength( entryLength );
424        }
425
426        addRequestLength += 1 + TLV.getNbBytes( entryLength ) + entryLength;
427        setAddRequestLength( addRequestLength );
428
429        // Return the result.
430        return 1 + TLV.getNbBytes( addRequestLength ) + addRequestLength;
431    }
432
433
434    /**
435     * Encode the AddRequest message to a PDU.
436     * 
437     * AddRequest :
438     * 
439     * 0x68 LL
440     *   0x04 LL entry
441     *   0x30 LL attributesList
442     *     0x30 LL attributeList
443     *       0x04 LL attributeDescription
444     *       0x31 LL attributeValues
445     *         0x04 LL attributeValue
446     *         ...
447     *         0x04 LL attributeValue
448     *     ...
449     *     0x30 LL attributeList
450     *       0x04 LL attributeDescription
451     *       0x31 LL attributeValue
452     *         0x04 LL attributeValue
453     *         ...
454     *         0x04 LL attributeValue
455     * 
456     * @param buffer The buffer where to put the PDU
457     */
458    public ByteBuffer encode( ByteBuffer buffer ) throws EncoderException
459    {
460        try
461        {
462            // The AddRequest Tag
463            buffer.put( LdapConstants.ADD_REQUEST_TAG );
464            buffer.put( TLV.getBytes( getAddRequestLength() ) );
465
466            // The entry
467            BerValue.encode( buffer, dnBytes );
468
469            // The attributes sequence
470            buffer.put( UniversalTag.SEQUENCE.getValue() );
471            buffer.put( TLV.getBytes( getEntryLength() ) );
472
473            // The partial attribute list
474            Entry entry = getEntry();
475
476            if ( entry.size() != 0 )
477            {
478                int attributeNumber = 0;
479
480                // Compute the attributes length
481                for ( Attribute attribute : entry )
482                {
483                    // The attributes list sequence
484                    buffer.put( UniversalTag.SEQUENCE.getValue() );
485                    int localAttributeLength = attributesLength.get( attributeNumber );
486                    buffer.put( TLV.getBytes( localAttributeLength ) );
487
488                    // The attribute type
489                    BerValue.encode( buffer, attributeIds.get( attributeNumber ) );
490
491                    // The values
492                    buffer.put( UniversalTag.SET.getValue() );
493                    int localValuesLength = valuesLength.get( attributeNumber );
494                    buffer.put( TLV.getBytes( localValuesLength ) );
495
496                    if ( attribute.size() != 0 )
497                    {
498                        for ( Value<?> value : attribute )
499                        {
500                            BerValue.encode( buffer, value.getBytes() );
501                        }
502                    }
503                    else
504                    {
505                        BerValue.encode( buffer, Strings.EMPTY_BYTES );
506                    }
507
508                    // Go to the next attribute number;
509                    attributeNumber++;
510                }
511            }
512
513            return buffer;
514        }
515        catch ( BufferOverflowException boe )
516        {
517            throw new EncoderException( "The PDU buffer size is too small !" );
518        }
519    }
520}