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.LdapCodecConstants;
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 codec The LDAP service instance
080     * @param decoratedMessage the decorated AddRequest
081     */
082    public AddRequestDecorator( LdapApiService codec, AddRequest decoratedMessage )
083    {
084        super( codec, decoratedMessage );
085    }
086
087
088    /**
089     * {@inheritDoc}
090     */
091    @Override
092    public AddRequest setMessageId( int messageId )
093    {
094        super.setMessageId( messageId );
095
096        return this;
097    }
098
099
100    /**
101     * {@inheritDoc}
102     */
103    @Override
104    public AddRequest addControl( Control control )
105    {
106        return ( AddRequest ) super.addControl( control );
107    }
108
109
110    /**
111     * {@inheritDoc}
112     */
113    @Override
114    public AddRequest addAllControls( Control[] controls )
115    {
116        return ( AddRequest ) super.addAllControls( controls );
117    }
118
119
120    /**
121     * {@inheritDoc}
122     */
123    @Override
124    public AddRequest removeControl( Control control )
125    {
126        return ( AddRequest ) super.removeControl( control );
127    }
128
129
130    //-------------------------------------------------------------------------
131    // The AddRequest methods
132    //-------------------------------------------------------------------------
133
134    /**
135     * {@inheritDoc}
136     */
137    @Override
138    public Dn getEntryDn()
139    {
140        return getDecorated().getEntryDn();
141    }
142
143
144    /**
145     * {@inheritDoc}
146     */
147    @Override
148    public AddRequest setEntryDn( Dn entry )
149    {
150        getDecorated().setEntryDn( entry );
151
152        return this;
153    }
154
155
156    /**
157     * {@inheritDoc}
158     */
159    @Override
160    public Entry getEntry()
161    {
162        return getDecorated().getEntry();
163    }
164
165
166    /**
167     * {@inheritDoc}
168     */
169    @Override
170    public AddRequest setEntry( Entry entry )
171    {
172        getDecorated().setEntry( entry );
173
174        return this;
175    }
176
177
178    /**
179     * Create a new attributeValue
180     * 
181     * @param type The attribute's name (called 'type' in the grammar)
182     * @throws LdapException If the value is invalid
183     */
184    public void addAttributeType( String type ) throws LdapException
185    {
186        // do not create a new attribute if we have seen this attributeType before
187        if ( getDecorated().getEntry().get( type ) != null )
188        {
189            currentAttribute = getDecorated().getEntry().get( type );
190            return;
191        }
192
193        // fix this to use AttributeImpl(type.getString().toLowerCase())
194        currentAttribute = new DefaultAttribute( type );
195        getDecorated().getEntry().put( currentAttribute );
196    }
197
198
199    /**
200     * @return Returns the currentAttribute type.
201     */
202    public String getCurrentAttributeType()
203    {
204        return currentAttribute.getUpId();
205    }
206
207
208    /**
209     * Add a new value to the current attribute
210     * 
211     * @param value The value to add
212     * @throws LdapException If the value is invalid
213     */
214    public void addAttributeValue( String value ) throws LdapException
215    {
216        currentAttribute.add( value );
217    }
218
219
220    /**
221     * Add a new value to the current attribute
222     * 
223     * @param value The value to add
224     * @throws LdapException If the value is invalid
225     */
226    public void addAttributeValue( Value<?> value ) throws LdapException
227    {
228        currentAttribute.add( value );
229    }
230
231
232    /**
233     * Add a new value to the current attribute
234     * 
235     * @param value The value to add
236     * @throws LdapException If the value is invalid
237     */
238    public void addAttributeValue( byte[] value ) throws LdapException
239    {
240        currentAttribute.add( value );
241    }
242
243
244    //-------------------------------------------------------------------------
245    // The Decorator methods
246    //-------------------------------------------------------------------------
247    /**
248     * Compute the AddRequest length
249     * <br>
250     * AddRequest :
251     * <pre>
252     * 0x68 L1
253     *  |
254     *  +--&gt; 0x04 L2 entry
255     *  +--&gt; 0x30 L3 (attributes)
256     *        |
257     *        +--&gt; 0x30 L4-1 (attribute)
258     *        |     |
259     *        |     +--&gt; 0x04 L5-1 type
260     *        |     +--&gt; 0x31 L6-1 (values)
261     *        |           |
262     *        |           +--&gt; 0x04 L7-1-1 value
263     *        |           +--&gt; ...
264     *        |           +--&gt; 0x04 L7-1-n value
265     *        |
266     *        +--&gt; 0x30 L4-2 (attribute)
267     *        |     |
268     *        |     +--&gt; 0x04 L5-2 type
269     *        |     +--&gt; 0x31 L6-2 (values)
270     *        |           |
271     *        |           +--&gt; 0x04 L7-2-1 value
272     *        |           +--&gt; ...
273     *        |           +--&gt; 0x04 L7-2-n value
274     *        |
275     *        +--&gt; ...
276     *        |
277     *        +--&gt; 0x30 L4-m (attribute)
278     *              |
279     *              +--&gt; 0x04 L5-m type
280     *              +--&gt; 0x31 L6-m (values)
281     *                    |
282     *                    +--&gt; 0x04 L7-m-1 value
283     *                    +--&gt; ...
284     *                    +--&gt; 0x04 L7-m-n value
285     * </pre>
286     */
287    @Override
288    public int computeLength()
289    {
290        AddRequest addRequest = getDecorated();
291        Entry entry = addRequest.getEntry();
292
293        if ( entry == null )
294        {
295            throw new IllegalArgumentException( I18n.err( I18n.ERR_04481_ENTRY_NULL_VALUE ) );
296        }
297
298        dnBytes = Strings.getBytesUtf8( entry.getDn().getName() );
299        int dnLen = dnBytes.length;
300
301        // The entry Dn
302        addRequestLength = 1 + TLV.getNbBytes( dnLen ) + dnLen;
303
304        // The attributes sequence
305        entryLength = 0;
306
307        if ( entry.size() != 0 )
308        {
309            attributesLength = new LinkedList<>();
310            attributeIds = new LinkedList<>();
311            valuesLength = new LinkedList<>();
312
313            // Compute the attributes length
314            for ( Attribute attribute : entry )
315            {
316                int localAttributeLength;
317                int localValuesLength;
318
319                // Get the type length
320                byte[] attributeIdBytes = Strings.getBytesUtf8( attribute.getUpId() );
321                attributeIds.add( attributeIdBytes );
322
323                int idLength = attributeIdBytes.length;
324                localAttributeLength = 1 + TLV.getNbBytes( idLength ) + idLength;
325
326                // The values
327                if ( attribute.size() != 0 )
328                {
329                    localValuesLength = 0;
330
331                    for ( Value<?> value : attribute )
332                    {
333                        if ( value.getBytes() == null )
334                        {
335                            localValuesLength += 1 + 1;
336                        }
337                        else
338                        {
339                            int valueLength = value.getBytes().length;
340                            localValuesLength += 1 + TLV.getNbBytes( valueLength ) + valueLength;
341                        }
342                    }
343
344                    localAttributeLength += 1 + TLV.getNbBytes( localValuesLength ) + localValuesLength;
345                }
346                else
347                {
348                    // No value : we still have to store the encapsulating Sequence
349                    localValuesLength = 1 + 1;
350                    localAttributeLength += 1 + 1 + localValuesLength;
351                }
352
353                // add the attribute length to the attributes length
354                entryLength += 1 + TLV.getNbBytes( localAttributeLength ) + localAttributeLength;
355
356                attributesLength.add( localAttributeLength );
357                valuesLength.add( localValuesLength );
358            }
359        }
360
361        addRequestLength += 1 + TLV.getNbBytes( entryLength ) + entryLength;
362
363        // Return the result.
364        return 1 + TLV.getNbBytes( addRequestLength ) + addRequestLength;
365    }
366
367
368    /**
369     * Encode the AddRequest message to a PDU.
370     * <br>
371     * AddRequest :
372     * <pre>
373     * 0x68 LL
374     *   0x04 LL entry
375     *   0x30 LL attributesList
376     *     0x30 LL attributeList
377     *       0x04 LL attributeDescription
378     *       0x31 LL attributeValues
379     *         0x04 LL attributeValue
380     *         ...
381     *         0x04 LL attributeValue
382     *     ...
383     *     0x30 LL attributeList
384     *       0x04 LL attributeDescription
385     *       0x31 LL attributeValue
386     *         0x04 LL attributeValue
387     *         ...
388     *         0x04 LL attributeValue
389     * </pre>
390     * 
391     * @param buffer The buffer where to put the PDU
392     */
393    @Override
394    public ByteBuffer encode( ByteBuffer buffer ) throws EncoderException
395    {
396        try
397        {
398            // The AddRequest Tag
399            buffer.put( LdapCodecConstants.ADD_REQUEST_TAG );
400            buffer.put( TLV.getBytes( addRequestLength ) );
401
402            // The entry
403            BerValue.encode( buffer, dnBytes );
404
405            // The attributes sequence
406            buffer.put( UniversalTag.SEQUENCE.getValue() );
407            buffer.put( TLV.getBytes( entryLength ) );
408
409            // The partial attribute list
410            Entry entry = getEntry();
411
412            if ( entry.size() != 0 )
413            {
414                int attributeNumber = 0;
415
416                // Compute the attributes length
417                for ( Attribute attribute : entry )
418                {
419                    // The attributes list sequence
420                    buffer.put( UniversalTag.SEQUENCE.getValue() );
421                    int localAttributeLength = attributesLength.get( attributeNumber );
422                    buffer.put( TLV.getBytes( localAttributeLength ) );
423
424                    // The attribute type
425                    BerValue.encode( buffer, attributeIds.get( attributeNumber ) );
426
427                    // The values
428                    buffer.put( UniversalTag.SET.getValue() );
429                    int localValuesLength = valuesLength.get( attributeNumber );
430                    buffer.put( TLV.getBytes( localValuesLength ) );
431
432                    if ( attribute.size() != 0 )
433                    {
434                        for ( Value<?> value : attribute )
435                        {
436                            BerValue.encode( buffer, value.getBytes() );
437                        }
438                    }
439                    else
440                    {
441                        BerValue.encode( buffer, Strings.EMPTY_BYTES );
442                    }
443
444                    // Go to the next attribute number
445                    attributeNumber++;
446                }
447            }
448
449            return buffer;
450        }
451        catch ( BufferOverflowException boe )
452        {
453            throw new EncoderException( "The PDU buffer size is too small !", boe );
454        }
455    }
456}