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