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.Collection;
026import java.util.LinkedList;
027import java.util.List;
028
029import org.apache.directory.api.asn1.EncoderException;
030import org.apache.directory.api.asn1.ber.tlv.BerValue;
031import org.apache.directory.api.asn1.ber.tlv.TLV;
032import org.apache.directory.api.asn1.ber.tlv.UniversalTag;
033import org.apache.directory.api.i18n.I18n;
034import org.apache.directory.api.ldap.codec.api.LdapApiService;
035import org.apache.directory.api.ldap.codec.api.LdapCodecConstants;
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.DefaultModification;
039import org.apache.directory.api.ldap.model.entry.Modification;
040import org.apache.directory.api.ldap.model.entry.ModificationOperation;
041import org.apache.directory.api.ldap.model.entry.Value;
042import org.apache.directory.api.ldap.model.exception.LdapException;
043import org.apache.directory.api.ldap.model.message.Control;
044import org.apache.directory.api.ldap.model.message.ModifyRequest;
045import org.apache.directory.api.ldap.model.name.Dn;
046
047
048/**
049 * A decorator for the ModifyRequest message
050 *
051 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
052 */
053public class ModifyRequestDecorator extends SingleReplyRequestDecorator<ModifyRequest>
054    implements ModifyRequest
055{
056    /** The modify request length */
057    private int modifyRequestLength;
058
059    /** The changes length */
060    private int changesLength;
061
062    /** The list of all change lengths */
063    private List<Integer> changeLength;
064
065    /** The list of all the modification lengths */
066    private List<Integer> modificationLength;
067
068    /** The list of all the value lengths */
069    private List<Integer> valuesLength;
070
071    /** The current attribute being decoded */
072    private Attribute currentAttribute;
073
074    /** A local storage for the operation */
075    private ModificationOperation currentOperation;
076
077
078    /**
079     * Makes a ModifyRequest encodable.
080     *
081     * @param codec The LDAP service instance
082     * @param decoratedMessage the decorated ModifyRequest
083     */
084    public ModifyRequestDecorator( LdapApiService codec, ModifyRequest decoratedMessage )
085    {
086        super( codec, decoratedMessage );
087    }
088
089
090    /**
091     * Store the current operation
092     * 
093     * @param currentOperation The currentOperation to set.
094     */
095    public void setCurrentOperation( int currentOperation )
096    {
097        this.currentOperation = ModificationOperation.getOperation( currentOperation );
098    }
099
100
101    /**
102     * Add a new attributeTypeAndValue
103     * 
104     * @param type The attribute's name
105     */
106    public void addAttributeTypeAndValues( String type )
107    {
108        currentAttribute = new DefaultAttribute( type );
109
110        Modification modification = new DefaultModification( currentOperation, currentAttribute );
111        getDecorated().addModification( modification );
112    }
113
114
115    /**
116     * @return the current attribute's type
117     */
118    public String getCurrentAttributeType()
119    {
120        return currentAttribute.getUpId();
121    }
122
123
124    /**
125     * Add a new value to the current attribute
126     * 
127     * @param value The value to add
128     * @throws LdapException If teh value is invalid
129     */
130    public void addAttributeValue( byte[] value ) throws LdapException
131    {
132        currentAttribute.add( value );
133    }
134
135
136    /**
137     * Add a new value to the current attribute
138     * 
139     * @param value The value to add
140     * @throws LdapException If teh value is invalid
141     */
142    public void addAttributeValue( String value ) throws LdapException
143    {
144        currentAttribute.add( value );
145    }
146
147
148    //-------------------------------------------------------------------------
149    // The ModifyRequest methods
150    //-------------------------------------------------------------------------
151
152    /**
153     * {@inheritDoc}
154     */
155    @Override
156    public Dn getName()
157    {
158        return getDecorated().getName();
159    }
160
161
162    /**
163     * {@inheritDoc}
164     */
165    @Override
166    public ModifyRequest setName( Dn name )
167    {
168        getDecorated().setName( name );
169
170        return this;
171    }
172
173
174    /**
175     * {@inheritDoc}
176     */
177    @Override
178    public Collection<Modification> getModifications()
179    {
180        return getDecorated().getModifications();
181    }
182
183
184    /**
185     * {@inheritDoc}
186     */
187    @Override
188    public ModifyRequest addModification( Modification mod )
189    {
190        getDecorated().addModification( mod );
191
192        return this;
193    }
194
195
196    /**
197     * {@inheritDoc}
198     */
199    @Override
200    public ModifyRequest removeModification( Modification mod )
201    {
202        getDecorated().removeModification( mod );
203
204        return this;
205    }
206
207
208    /**
209     * {@inheritDoc}
210     */
211    @Override
212    public ModifyRequest remove( String attributeName, String... attributeValue )
213    {
214        getDecorated().remove( attributeName, attributeValue );
215
216        return this;
217    }
218
219
220    /**
221     * {@inheritDoc}
222     */
223    public ModifyRequest remove( String attributeName, byte[]... attributeValue )
224    {
225        getDecorated().remove( attributeName, attributeValue );
226
227        return this;
228    }
229
230
231    /**
232     * {@inheritDoc}
233     */
234    @Override
235    public ModifyRequest remove( Attribute attr )
236    {
237        getDecorated().remove( attr );
238
239        return this;
240    }
241
242
243    /**
244     * {@inheritDoc}
245     */
246    @Override
247    public ModifyRequest remove( String attributeName )
248    {
249        getDecorated().remove( attributeName );
250
251        return this;
252    }
253
254
255    /**
256     * {@inheritDoc}
257     */
258    @Override
259    public ModifyRequest addModification( Attribute attr, ModificationOperation modOp )
260    {
261        getDecorated().addModification( attr, modOp );
262
263        return this;
264    }
265
266
267    /**
268     * {@inheritDoc}
269     */
270    @Override
271    public ModifyRequest add( String attributeName, String... attributeValue )
272    {
273        getDecorated().add( attributeName, attributeValue );
274
275        return this;
276    }
277
278
279    /**
280     * {@inheritDoc}
281     */
282    public ModifyRequest add( String attributeName, byte[]... attributeValue )
283    {
284        getDecorated().add( attributeName, attributeValue );
285
286        return this;
287    }
288
289
290    /**
291     * {@inheritDoc}
292     */
293    @Override
294    public ModifyRequest add( Attribute attr )
295    {
296        getDecorated().add( attr );
297
298        return this;
299    }
300
301
302    /**
303     * {@inheritDoc}
304     */
305    @Override
306    public ModifyRequest replace( String attributeName )
307    {
308        getDecorated().replace( attributeName );
309
310        return this;
311    }
312
313
314    /**
315     * {@inheritDoc}
316     */
317    @Override
318    public ModifyRequest replace( String attributeName, String... attributeValue )
319    {
320        getDecorated().replace( attributeName, attributeValue );
321
322        return this;
323    }
324
325
326    /**
327     * {@inheritDoc}
328     */
329    public ModifyRequest replace( String attributeName, byte[]... attributeValue )
330    {
331        getDecorated().replace( attributeName, attributeValue );
332
333        return this;
334    }
335
336
337    /**
338     * {@inheritDoc}
339     */
340    @Override
341    public ModifyRequest replace( Attribute attr )
342    {
343        getDecorated().replace( attr );
344
345        return this;
346    }
347
348
349    /**
350     * {@inheritDoc}
351     */
352    @Override
353    public ModifyRequest setMessageId( int messageId )
354    {
355        super.setMessageId( messageId );
356
357        return this;
358    }
359
360
361    /**
362     * {@inheritDoc}
363     */
364    @Override
365    public ModifyRequest addControl( Control control )
366    {
367        return ( ModifyRequest ) super.addControl( control );
368    }
369
370
371    /**
372     * {@inheritDoc}
373     */
374    @Override
375    public ModifyRequest addAllControls( Control[] controls )
376    {
377        return ( ModifyRequest ) super.addAllControls( controls );
378    }
379
380
381    /**
382     * {@inheritDoc}
383     */
384    @Override
385    public ModifyRequest removeControl( Control control )
386    {
387        return ( ModifyRequest ) super.removeControl( control );
388    }
389
390
391    //-------------------------------------------------------------------------
392    // The Decorator methods
393    //-------------------------------------------------------------------------
394
395    /**
396     * Compute the ModifyRequest length 
397     * <br>
398     * ModifyRequest :
399     * <pre>
400     * 0x66 L1
401     *  |
402     *  +--&gt; 0x04 L2 object
403     *  +--&gt; 0x30 L3 modifications
404     *        |
405     *        +--&gt; 0x30 L4-1 modification sequence
406     *        |     |
407     *        |     +--&gt; 0x0A 0x01 (0..2) operation
408     *        |     +--&gt; 0x30 L5-1 modification
409     *        |           |
410     *        |           +--&gt; 0x04 L6-1 type
411     *        |           +--&gt; 0x31 L7-1 vals
412     *        |                 |
413     *        |                 +--&gt; 0x04 L8-1-1 attributeValue
414     *        |                 +--&gt; 0x04 L8-1-2 attributeValue
415     *        |                 +--&gt; ...
416     *        |                 +--&gt; 0x04 L8-1-i attributeValue
417     *        |                 +--&gt; ...
418     *        |                 +--&gt; 0x04 L8-1-n attributeValue
419     *        |
420     *        +--&gt; 0x30 L4-2 modification sequence
421     *        .     |
422     *        .     +--&gt; 0x0A 0x01 (0..2) operation
423     *        .     +--&gt; 0x30 L5-2 modification
424     *                    |
425     *                    +--&gt; 0x04 L6-2 type
426     *                    +--&gt; 0x31 L7-2 vals
427     *                          |
428     *                          +--&gt; 0x04 L8-2-1 attributeValue
429     *                          +--&gt; 0x04 L8-2-2 attributeValue
430     *                          +--&gt; ...
431     *                          +--&gt; 0x04 L8-2-i attributeValue
432     *                          +--&gt; ...
433     *                          +--&gt; 0x04 L8-2-n attributeValue
434     * </pre>
435     */
436    @Override
437    public int computeLength()
438    {
439        // Initialized with name
440        modifyRequestLength = 1 + TLV.getNbBytes( Dn.getNbBytes( getName() ) )
441            + Dn.getNbBytes( getName() );
442
443        // All the changes length
444        changesLength = 0;
445
446        Collection<Modification> modifications = getModifications();
447
448        if ( ( modifications != null ) && ( !modifications.isEmpty() ) )
449        {
450            changeLength = new LinkedList<>();
451            modificationLength = new LinkedList<>();
452            valuesLength = new LinkedList<>();
453
454            for ( Modification modification : modifications )
455            {
456                // Modification sequence length initialized with the operation
457                int localModificationSequenceLength = 1 + 1 + 1;
458                int localValuesLength = 0;
459
460                // Modification length initialized with the type
461                int typeLength = modification.getAttribute().getUpId().length();
462                int localModificationLength = 1 + TLV.getNbBytes( typeLength ) + typeLength;
463
464                // Get all the values
465                if ( modification.getAttribute().size() != 0 )
466                {
467                    for ( Value<?> value : modification.getAttribute() )
468                    {
469                        localValuesLength += 1 + TLV.getNbBytes( value.getBytes().length ) + value.getBytes().length;
470                    }
471                }
472
473                localModificationLength += 1 + TLV.getNbBytes( localValuesLength ) + localValuesLength;
474
475                // Compute the modificationSequenceLength
476                localModificationSequenceLength += 1 + TLV.getNbBytes( localModificationLength )
477                    + localModificationLength;
478
479                // Add the tag and the length
480                changesLength += 1 + TLV.getNbBytes( localModificationSequenceLength )
481                    + localModificationSequenceLength;
482
483                // Store the arrays of values
484                valuesLength.add( localValuesLength );
485                modificationLength.add( localModificationLength );
486                changeLength.add( localModificationSequenceLength );
487            }
488
489            // Add the modifications length to the modificationRequestLength
490            modifyRequestLength += 1 + TLV.getNbBytes( changesLength ) + changesLength;
491        }
492
493        return 1 + TLV.getNbBytes( modifyRequestLength ) + modifyRequestLength;
494    }
495
496
497    /**
498     * Encode the ModifyRequest message to a PDU. 
499     * <br>
500     * ModifyRequest : 
501     * <pre>
502     * 0x66 LL
503     *   0x04 LL object
504     *   0x30 LL modifiations
505     *     0x30 LL modification sequence
506     *       0x0A 0x01 operation
507     *       0x30 LL modification
508     *         0x04 LL type
509     *         0x31 LL vals
510     *           0x04 LL attributeValue
511     *           ... 
512     *           0x04 LL attributeValue
513     *     ... 
514     *     0x30 LL modification sequence
515     *       0x0A 0x01 operation
516     *       0x30 LL modification
517     *         0x04 LL type
518     *         0x31 LL vals
519     *           0x04 LL attributeValue
520     *           ... 
521     *           0x04 LL attributeValue
522     * </pre>
523     * 
524     * @param buffer The buffer where to put the PDU
525     * @return The PDU.
526     */
527    @Override
528    public ByteBuffer encode( ByteBuffer buffer ) throws EncoderException
529    {
530        try
531        {
532            // The AddRequest Tag
533            buffer.put( LdapCodecConstants.MODIFY_REQUEST_TAG );
534            buffer.put( TLV.getBytes( modifyRequestLength ) );
535
536            // The entry
537            BerValue.encode( buffer, Dn.getBytes( getName() ) );
538
539            // The modifications sequence
540            buffer.put( UniversalTag.SEQUENCE.getValue() );
541            buffer.put( TLV.getBytes( changesLength ) );
542
543            // The modifications list
544            Collection<Modification> modifications = getModifications();
545
546            if ( ( modifications != null ) && ( !modifications.isEmpty() ) )
547            {
548                int modificationNumber = 0;
549
550                // Compute the modifications length
551                for ( Modification modification : modifications )
552                {
553                    // The modification sequence
554                    buffer.put( UniversalTag.SEQUENCE.getValue() );
555                    int localModificationSequenceLength = changeLength.get( modificationNumber );
556                    buffer.put( TLV.getBytes( localModificationSequenceLength ) );
557
558                    // The operation. The value has to be changed, it's not
559                    // the same value in DirContext and in RFC 2251.
560                    buffer.put( UniversalTag.ENUMERATED.getValue() );
561                    buffer.put( ( byte ) 1 );
562                    buffer.put( ( byte ) modification.getOperation().getValue() );
563
564                    // The modification
565                    buffer.put( UniversalTag.SEQUENCE.getValue() );
566                    int localModificationLength = modificationLength.get( modificationNumber );
567                    buffer.put( TLV.getBytes( localModificationLength ) );
568
569                    // The modification type
570                    BerValue.encode( buffer, modification.getAttribute().getUpId() );
571
572                    // The values
573                    buffer.put( UniversalTag.SET.getValue() );
574                    int localValuesLength = valuesLength.get( modificationNumber );
575                    buffer.put( TLV.getBytes( localValuesLength ) );
576
577                    if ( modification.getAttribute().size() != 0 )
578                    {
579                        for ( org.apache.directory.api.ldap.model.entry.Value<?> value : modification.getAttribute() )
580                        {
581                            if ( value.isHumanReadable() )
582                            {
583                                BerValue.encode( buffer, value.getString() );
584                            }
585                            else
586                            {
587                                BerValue.encode( buffer, value.getBytes() );
588                            }
589                        }
590                    }
591
592                    // Go to the next modification number
593                    modificationNumber++;
594                }
595            }
596        }
597        catch ( BufferOverflowException boe )
598        {
599            throw new EncoderException( I18n.err( I18n.ERR_04005 ), boe );
600        }
601
602        return buffer;
603    }
604}