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