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     */
020    package org.apache.directory.shared.ldap.codec.modify;
021    
022    
023    import java.nio.BufferOverflowException;
024    import java.nio.ByteBuffer;
025    import java.util.ArrayList;
026    import java.util.LinkedList;
027    import java.util.List;
028    
029    import org.apache.directory.shared.asn1.ber.tlv.TLV;
030    import org.apache.directory.shared.asn1.ber.tlv.UniversalTag;
031    import org.apache.directory.shared.asn1.ber.tlv.Value;
032    import org.apache.directory.shared.asn1.codec.EncoderException;
033    import org.apache.directory.shared.i18n.I18n;
034    import org.apache.directory.shared.ldap.codec.LdapConstants;
035    import org.apache.directory.shared.ldap.codec.LdapMessageCodec;
036    import org.apache.directory.shared.ldap.codec.MessageTypeEnum;
037    import org.apache.directory.shared.ldap.entry.EntryAttribute;
038    import org.apache.directory.shared.ldap.entry.Modification;
039    import org.apache.directory.shared.ldap.entry.ModificationOperation;
040    import org.apache.directory.shared.ldap.entry.client.ClientModification;
041    import org.apache.directory.shared.ldap.entry.client.DefaultClientAttribute;
042    import org.apache.directory.shared.ldap.name.DN;
043    import org.slf4j.Logger;
044    import org.slf4j.LoggerFactory;
045    
046    
047    /**
048     * A ModifyRequest Message. 
049     * 
050     * Its syntax is : 
051     * 
052     * ModifyRequest ::= [APPLICATION 6] SEQUENCE { 
053     *     object LDAPDN, 
054     *     modification SEQUENCE OF SEQUENCE { 
055     *         operation ENUMERATED { 
056     *             add (0), 
057     *             delete (1), 
058     *             replace (2) 
059     *         }, 
060     *         modification AttributeTypeAndValues 
061     *     } 
062     * } 
063     * 
064     * AttributeTypeAndValues ::= SEQUENCE {
065     *     type AttributeDescription, 
066     *     vals SET OF AttributeValue 
067     * } 
068     * 
069     * AttributeValue ::= OCTET STRING
070     * 
071     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
072     * @version $Rev: 918756 $, $Date: 2010-03-04 00:05:29 +0100 (Thu, 04 Mar 2010) $, 
073     */
074    public class ModifyRequestCodec extends LdapMessageCodec
075    {
076        // ~ Static fields/initializers
077        // -----------------------------------------------------------------
078    
079        /** The logger */
080        private static final Logger LOG = LoggerFactory.getLogger( ModifyRequestCodec.class );
081    
082        // ~ Instance fields
083        // ----------------------------------------------------------------------------
084    
085        /** The DN to be modified. */
086        private DN object;
087    
088        /** The modifications list. This is an array of Modification. */
089        private List<Modification> modifications;
090    
091        /** The current attribute being decoded */
092        private EntryAttribute currentAttribute;
093    
094        /** A local storage for the operation */
095        private ModificationOperation currentOperation;
096    
097        /** The modify request length */
098        private int modifyRequestLength;
099    
100        /** The modifications length */
101        private int modificationsLength;
102    
103        /** The modification sequence length */
104        private List<Integer> modificationSequenceLength;
105    
106        /** The list of all modification length */
107        private List<Integer> modificationLength;
108    
109        /** The list of all vals length */
110        private List<Integer> valuesLength;
111    
112    
113        // ~ Constructors
114        // -------------------------------------------------------------------------------
115    
116        /**
117         * Creates a new ModifyRequest object.
118         */
119        public ModifyRequestCodec()
120        {
121            super();
122        }
123    
124    
125        // ~ Methods
126        // ------------------------------------------------------------------------------------
127    
128        /**
129         * Get the message type
130         * 
131         * @return Returns the type.
132         */
133        public MessageTypeEnum getMessageType()
134        {
135            return MessageTypeEnum.MODIFY_REQUEST;
136        }
137    
138        
139        /**
140         * {@inheritDoc}
141         */
142        public String getMessageTypeName()
143        {
144            return "MODIFY_REQUEST";
145        }
146    
147    
148        /**
149         * Initialize the ArrayList for modifications.
150         */
151        public void initModifications()
152        {
153            modifications = new ArrayList<Modification>();
154        }
155    
156    
157        /**
158         * Get the entry's attributes
159         * 
160         * @return Returns the modifications.
161         */
162        public List<Modification> getModifications()
163        {
164            return modifications;
165        }
166    
167    
168        /**
169         * Add a new modification to the list
170         * 
171         * @param operation The type of operation (add, delete or replace)
172         */
173        public void addModification( int operation )
174        {
175            currentOperation = ModificationOperation.getOperation( operation );
176    
177            if ( currentAttribute == null )
178            {
179                modifications = new ArrayList<Modification>();
180            }
181        }
182    
183    
184        /**
185         * Add a new attributeTypeAndValue
186         * 
187         * @param type The attribute's name
188         */
189        public void addAttributeTypeAndValues( String type )
190        {
191            currentAttribute = new DefaultClientAttribute( type );
192    
193            Modification modification = new ClientModification( currentOperation, currentAttribute );
194            modifications.add( modification );
195        }
196    
197    
198        /**
199         * Add a new value to the current attribute
200         * 
201         * @param value The value to add
202         */
203        public void addAttributeValue( String value )
204        {
205            currentAttribute.add( value );
206        }
207    
208    
209        /**
210         * Add a new value to the current attribute
211         * 
212         * @param value The value to add
213         */
214        public void addAttributeValue( org.apache.directory.shared.ldap.entry.Value<?> value )
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         */
225        public void addAttributeValue( byte[] value )
226        {
227            currentAttribute.add( value );
228        }
229    
230    
231        /**
232         * Return the current attribute's type
233         */
234        public String getCurrentAttributeType()
235        {
236            return currentAttribute.getId();
237        }
238    
239    
240        /**
241         * Get the modification's DN
242         * 
243         * @return Returns the object.
244         */
245        public DN getObject()
246        {
247            return object;
248        }
249    
250    
251        /**
252         * Set the modification DN.
253         * 
254         * @param object The DN to set.
255         */
256        public void setObject( DN object )
257        {
258            this.object = object;
259        }
260    
261    
262        /**
263         * Get the current operation
264         * 
265         * @return Returns the currentOperation.
266         */
267        public int getCurrentOperation()
268        {
269            return currentOperation.getValue();
270        }
271    
272    
273        /**
274         * Store the current operation
275         * 
276         * @param currentOperation The currentOperation to set.
277         */
278        public void setCurrentOperation( int currentOperation )
279        {
280            this.currentOperation = ModificationOperation.getOperation( currentOperation );
281        }
282    
283    
284        /**
285         * Store the current operation
286         * 
287         * @param currentOperation The currentOperation to set.
288         */
289        public void setCurrentOperation( ModificationOperation currentOperation )
290        {
291            this.currentOperation = currentOperation;
292        }
293    
294    
295        /**
296         * sets the modifications
297         * 
298         * @param modifications the list of modifications
299         */
300        public void setModifications( List<Modification> modifications )
301        {
302            this.modifications = modifications;
303        }
304    
305    
306        /**
307         * Compute the ModifyRequest length 
308         * 
309         * ModifyRequest :
310         * 
311         * 0x66 L1
312         *  |
313         *  +--> 0x04 L2 object
314         *  +--> 0x30 L3 modifications
315         *        |
316         *        +--> 0x30 L4-1 modification sequence
317         *        |     |
318         *        |     +--> 0x0A 0x01 (0..2) operation
319         *        |     +--> 0x30 L5-1 modification
320         *        |           |
321         *        |           +--> 0x04 L6-1 type
322         *        |           +--> 0x31 L7-1 vals
323         *        |                 |
324         *        |                 +--> 0x04 L8-1-1 attributeValue
325         *        |                 +--> 0x04 L8-1-2 attributeValue
326         *        |                 +--> ...
327         *        |                 +--> 0x04 L8-1-i attributeValue
328         *        |                 +--> ...
329         *        |                 +--> 0x04 L8-1-n attributeValue
330         *        |
331         *        +--> 0x30 L4-2 modification sequence
332         *        .     |
333         *        .     +--> 0x0A 0x01 (0..2) operation
334         *        .     +--> 0x30 L5-2 modification
335         *                    |
336         *                    +--> 0x04 L6-2 type
337         *                    +--> 0x31 L7-2 vals
338         *                          |
339         *                          +--> 0x04 L8-2-1 attributeValue
340         *                          +--> 0x04 L8-2-2 attributeValue
341         *                          +--> ...
342         *                          +--> 0x04 L8-2-i attributeValue
343         *                          +--> ...
344         *                          +--> 0x04 L8-2-n attributeValue
345         */
346        protected int computeLengthProtocolOp()
347        {
348            // Initialized with object
349            modifyRequestLength = 1 + TLV.getNbBytes( DN.getNbBytes( object ) ) + DN.getNbBytes( object );
350    
351            // Modifications
352            modificationsLength = 0;
353    
354            if ( ( modifications != null ) && ( modifications.size() != 0 ) )
355            {
356                modificationSequenceLength = new LinkedList<Integer>();
357                modificationLength = new LinkedList<Integer>();
358                valuesLength = new LinkedList<Integer>();
359    
360                for ( Modification modification:modifications )
361                {
362                    // Modification sequence length initialized with the operation
363                    int localModificationSequenceLength = 1 + 1 + 1;
364                    int localValuesLength = 0;
365    
366                    // Modification length initialized with the type
367                    int typeLength = modification.getAttribute().getId().length();
368                    int localModificationLength = 1 + TLV.getNbBytes( typeLength ) + typeLength;
369    
370                    // Get all the values
371                    if ( modification.getAttribute().size() != 0 )
372                    {
373                        for ( org.apache.directory.shared.ldap.entry.Value<?> value:modification.getAttribute() )
374                        {
375                            localValuesLength += 1 + TLV.getNbBytes( value.getBytes().length )
376                                + value.getBytes().length;
377                        }
378                    }
379    
380                    localModificationLength += 1 + TLV.getNbBytes( localValuesLength ) + localValuesLength;
381    
382                    // Compute the modificationSequenceLength
383                    localModificationSequenceLength += 1 + TLV.getNbBytes( localModificationLength )
384                        + localModificationLength;
385    
386                    // Add the tag and the length
387                    modificationsLength += 1 + TLV.getNbBytes( localModificationSequenceLength )
388                        + localModificationSequenceLength;
389    
390                    // Store the arrays of values
391                    valuesLength.add( localValuesLength );
392                    modificationLength.add( localModificationLength );
393                    modificationSequenceLength.add( localModificationSequenceLength );
394                }
395    
396                // Add the modifications length to the modificationRequestLength
397                modifyRequestLength += 1 + TLV.getNbBytes( modificationsLength ) + modificationsLength;
398            }
399    
400            return 1 + TLV.getNbBytes( modifyRequestLength ) + modifyRequestLength;
401        }
402    
403    
404        /**
405         * Encode the ModifyRequest message to a PDU. 
406         * 
407         * ModifyRequest : 
408         * <pre>
409         * 0x66 LL
410         *   0x04 LL object
411         *   0x30 LL modifiations
412         *     0x30 LL modification sequence
413         *       0x0A 0x01 operation
414         *       0x30 LL modification
415         *         0x04 LL type
416         *         0x31 LL vals
417         *           0x04 LL attributeValue
418         *           ... 
419         *           0x04 LL attributeValue
420         *     ... 
421         *     0x30 LL modification sequence
422         *       0x0A 0x01 operation
423         *       0x30 LL modification
424         *         0x04 LL type
425         *         0x31 LL vals
426         *           0x04 LL attributeValue
427         *           ... 
428         *           0x04 LL attributeValue
429         * </pre>
430         * 
431         * @param buffer The buffer where to put the PDU
432         * @return The PDU.
433         */
434        protected void encodeProtocolOp( ByteBuffer buffer ) throws EncoderException
435        {
436            try
437            {
438                // The AddRequest Tag
439                buffer.put( LdapConstants.MODIFY_REQUEST_TAG );
440                buffer.put( TLV.getBytes( modifyRequestLength ) );
441    
442                // The entry
443                Value.encode( buffer, DN.getBytes( object ) );
444    
445                // The modifications sequence
446                buffer.put( UniversalTag.SEQUENCE_TAG );
447                buffer.put( TLV.getBytes( modificationsLength ) );
448    
449                // The modifications list
450                if ( ( modifications != null ) && ( modifications.size() != 0 ) )
451                {
452                    int modificationNumber = 0;
453    
454                    // Compute the modifications length
455                    for ( Modification modification:modifications )
456                    {
457                        // The modification sequence
458                        buffer.put( UniversalTag.SEQUENCE_TAG );
459                        int localModificationSequenceLength = modificationSequenceLength
460                            .get( modificationNumber );
461                        buffer.put( TLV.getBytes( localModificationSequenceLength ) );
462    
463                        // The operation. The value has to be changed, it's not
464                        // the same value in DirContext and in RFC 2251.
465                        buffer.put( UniversalTag.ENUMERATED_TAG );
466                        buffer.put( ( byte ) 1 );
467                        buffer.put( ( byte ) modification.getOperation().getValue() );
468    
469                        // The modification
470                        buffer.put( UniversalTag.SEQUENCE_TAG );
471                        int localModificationLength = modificationLength.get( modificationNumber );
472                        buffer.put( TLV.getBytes( localModificationLength ) );
473    
474                        // The modification type
475                        Value.encode( buffer, modification.getAttribute().getId() );
476    
477                        // The values
478                        buffer.put( UniversalTag.SET_TAG );
479                        int localValuesLength = valuesLength.get( modificationNumber );
480                        buffer.put( TLV.getBytes( localValuesLength ) );
481    
482                        if ( modification.getAttribute().size() != 0 )
483                        {
484                            for ( org.apache.directory.shared.ldap.entry.Value<?> value:modification.getAttribute() )
485                            {
486                                if ( !value.isBinary() )
487                                {
488                                    Value.encode( buffer, value.getString() );
489                                }
490                                else
491                                {
492                                    Value.encode( buffer, value.getBytes() );
493                                }
494                            }
495                        }
496    
497                        // Go to the next modification number;
498                        modificationNumber++;
499                    }
500                }
501            }
502            catch ( BufferOverflowException boe )
503            {
504                throw new EncoderException( I18n.err( I18n.ERR_04005 ) );
505            }
506        }
507    
508    
509        /**
510         * Get a String representation of a ModifyRequest
511         * 
512         * @return A ModifyRequest String
513         */
514        public String toString()
515        {
516            StringBuffer sb = new StringBuffer();
517    
518            sb.append( "    Modify Request\n" );
519            sb.append( "        Object : '" ).append( object ).append( "'\n" );
520    
521            if ( modifications != null )
522            {
523                int i = 0;
524                
525                for ( Modification modification:modifications )
526                {
527                    sb.append( "            Modification[" ).append( i ).append( "]\n" );
528                    sb.append( "                Operation : " );
529    
530                    if ( modification != null )
531                    {
532                        switch ( modification.getOperation() )
533                        {
534        
535                            case ADD_ATTRIBUTE:
536                                sb.append( " add\n" );
537                                break;
538        
539                            case REPLACE_ATTRIBUTE:
540                                sb.append( " replace\n" );
541                                break;
542        
543                            case REMOVE_ATTRIBUTE:
544                                sb.append( " delete\n" );
545                                break;
546                        }
547    
548                        sb.append( "                Modification\n" );
549        
550                        EntryAttribute attribute = modification.getAttribute();
551        
552                        if ( attribute != null )
553                        {
554                            sb.append( attribute );
555                        }
556                    }
557                    else
558                    {
559                        sb.append( " unknown modification operation\n" );
560                    }
561    
562                }
563            }
564    
565            return sb.toString();
566        }
567    }