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.add;
021    
022    
023    import java.nio.BufferOverflowException;
024    import java.nio.ByteBuffer;
025    import java.util.LinkedList;
026    import java.util.List;
027    
028    import org.apache.directory.shared.ldap.exception.LdapException;
029    
030    import org.apache.directory.shared.asn1.ber.tlv.TLV;
031    import org.apache.directory.shared.asn1.ber.tlv.UniversalTag;
032    import org.apache.directory.shared.asn1.ber.tlv.Value;
033    import org.apache.directory.shared.asn1.codec.EncoderException;
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.Entry;
038    import org.apache.directory.shared.ldap.entry.EntryAttribute;
039    import org.apache.directory.shared.ldap.entry.client.DefaultClientAttribute;
040    import org.apache.directory.shared.ldap.entry.client.DefaultClientEntry;
041    import org.apache.directory.shared.ldap.name.DN;
042    import org.apache.directory.shared.ldap.util.StringTools;
043    import org.slf4j.Logger;
044    import org.slf4j.LoggerFactory;
045    
046    
047    /**
048     * An AddRequest Message. Its syntax is : 
049     *   AddRequest ::= [APPLICATION 8] SEQUENCE {
050     *              entry           LDAPDN,
051     *              attributes      AttributeList }
052     *
053     *   AttributeList ::= SEQUENCE OF SEQUENCE {
054     *              type    AttributeDescription,
055     *              vals    SET OF AttributeValue }
056     * 
057     *   AttributeValue ::= OCTET STRING
058     * 
059     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
060     * @version $Rev: 923524 $, $Date: 2010-03-16 01:31:36 +0100 (Tue, 16 Mar 2010) $, 
061     */
062    public class AddRequestCodec extends LdapMessageCodec
063    {
064        // ~ Static fields/initializers
065        // -----------------------------------------------------------------
066    
067        /** The logger */
068        private static final Logger log = LoggerFactory.getLogger( AddRequestCodec.class );
069    
070        /** Speedup for logs */
071        private static final boolean IS_DEBUG = log.isDebugEnabled();
072    
073        // ~ Instance fields
074        // ----------------------------------------------------------------------------
075    
076        /** The attributes list. */
077        private Entry entry;
078    
079        /** The current attribute being decoded */
080        private EntryAttribute currentAttribute;
081    
082        /** The add request length */
083        private int addRequestLength;
084    
085        /** The attributes length */
086        private int attributesLength;
087    
088        /** The list of all attributes length */
089        private List<Integer> attributeLength;
090    
091        /** The list of all vals length */
092        private List<Integer> valuesLength;
093    
094    
095        // ~ Constructors
096        // -------------------------------------------------------------------------------
097    
098        /**
099         * Creates a new AddRequest object.
100         */
101        public AddRequestCodec()
102        {
103            super();
104            entry = new DefaultClientEntry();
105        }
106    
107    
108        // ~ Methods
109        // ------------------------------------------------------------------------------------
110    
111        /**
112         * Get the message type
113         * 
114         * @return Returns the type.
115         */
116        public MessageTypeEnum getMessageType()
117        {
118            return MessageTypeEnum.ADD_REQUEST;
119        }
120    
121        
122        /**
123         * {@inheritDoc}
124         */
125        public String getMessageTypeName()
126        {
127            return "ADD_REQUEST";
128        }
129    
130    
131        /**
132         * Initialize the Entry.
133         */
134        public void initEntry()
135        {
136            entry = new DefaultClientEntry();
137        }
138    
139    
140        /**
141         * Get the entry to be added
142         * 
143         * @return Returns the entry.
144         */
145        public Entry getEntry()
146        {
147            return entry;
148        }
149    
150    
151        /**
152         * Sets the entry.
153         *
154         * @param entry
155         *      the entry
156         */
157        public void setEntry( Entry entry )
158        {
159            this.entry = entry;
160        }
161    
162    
163        /**
164         * Create a new attributeValue
165         * 
166         * @param type The attribute's name (called 'type' in the grammar)
167         */
168        public void addAttributeType( String type ) throws LdapException
169        {
170            // do not create a new attribute if we have seen this attributeType before
171            if ( entry.get( type ) != null )
172            {
173                currentAttribute = entry.get( type );
174                return;
175            }
176    
177            // fix this to use AttributeImpl(type.getString().toLowerCase())
178            currentAttribute = new DefaultClientAttribute( type );
179            entry.put( currentAttribute );
180        }
181    
182    
183        /**
184         * Add a new value to the current attribute
185         * 
186         * @param value The value to add
187         */
188        public void addAttributeValue( String value )
189        {
190            currentAttribute.add( value );
191        }
192    
193    
194        /**
195         * Add a new value to the current attribute
196         * 
197         * @param value The value to add
198         */
199        public void addAttributeValue( org.apache.directory.shared.ldap.entry.Value<?> value )
200        {
201            currentAttribute.add( value );
202        }
203    
204    
205        /**
206         * Add a new value to the current attribute
207         * 
208         * @param value The value to add
209         */
210        public void addAttributeValue( byte[] value )
211        {
212            currentAttribute.add( value );
213        }
214    
215    
216        /**
217         * Get the added DN
218         * 
219         * @return Returns the entry DN.
220         */
221        public DN getEntryDn()
222        {
223            return entry.getDn();
224        }
225    
226    
227        /**
228         * Set the added DN.
229         * 
230         * @param entry The DN to set.
231         */
232        public void setEntryDn( DN entryDn )
233        {
234            entry.setDn( entryDn );
235        }
236    
237    
238        /**
239         * Compute the AddRequest length
240         * 
241         * AddRequest :
242         * 
243         * 0x68 L1
244         *  |
245         *  +--> 0x04 L2 entry
246         *  +--> 0x30 L3 (attributes)
247         *        |
248         *        +--> 0x30 L4-1 (attribute)
249         *        |     |
250         *        |     +--> 0x04 L5-1 type
251         *        |     +--> 0x31 L6-1 (values)
252         *        |           |
253         *        |           +--> 0x04 L7-1-1 value
254         *        |           +--> ...
255         *        |           +--> 0x04 L7-1-n value
256         *        |
257         *        +--> 0x30 L4-2 (attribute)
258         *        |     |
259         *        |     +--> 0x04 L5-2 type
260         *        |     +--> 0x31 L6-2 (values)
261         *        |           |
262         *        |           +--> 0x04 L7-2-1 value
263         *        |           +--> ...
264         *        |           +--> 0x04 L7-2-n value
265         *        |
266         *        +--> ...
267         *        |
268         *        +--> 0x30 L4-m (attribute)
269         *              |
270         *              +--> 0x04 L5-m type
271         *              +--> 0x31 L6-m (values)
272         *                    |
273         *                    +--> 0x04 L7-m-1 value
274         *                    +--> ...
275         *                    +--> 0x04 L7-m-n value
276         */
277        protected int computeLengthProtocolOp()
278        {
279            // The entry
280            addRequestLength = 1 + TLV.getNbBytes( DN.getNbBytes( entry.getDn() ) ) + DN.getNbBytes( entry.getDn() );
281    
282            // The attributes sequence
283            attributesLength = 0;
284    
285            if ( ( entry != null ) && ( entry.size() != 0 ) )
286            {
287                attributeLength = new LinkedList<Integer>();
288                valuesLength = new LinkedList<Integer>();
289    
290                // Compute the attributes length
291                for ( EntryAttribute attribute : entry )
292                {
293                    int localAttributeLength = 0;
294                    int localValuesLength = 0;
295    
296                    // Get the type length
297                    int idLength = attribute.getId().getBytes().length;
298                    localAttributeLength = 1 + TLV.getNbBytes( idLength ) + idLength;
299    
300                    // The values
301                    if ( attribute.size() != 0 )
302                    {
303                        localValuesLength = 0;
304    
305                        for ( org.apache.directory.shared.ldap.entry.Value<?> value : attribute )
306                        {
307                            int valueLength = value.getBytes().length;
308                            localValuesLength += 1 + TLV.getNbBytes( valueLength ) + valueLength;
309                        }
310    
311                        localAttributeLength += 1 + TLV.getNbBytes( localValuesLength ) + localValuesLength;
312                    }
313    
314                    // add the attribute length to the attributes length
315                    attributesLength += 1 + TLV.getNbBytes( localAttributeLength ) + localAttributeLength;
316    
317                    attributeLength.add( localAttributeLength );
318                    valuesLength.add( localValuesLength );
319                }
320            }
321    
322            addRequestLength += 1 + TLV.getNbBytes( attributesLength ) + attributesLength;
323    
324            // Return the result.
325            int result = 1 + TLV.getNbBytes( addRequestLength ) + addRequestLength;
326    
327            if ( IS_DEBUG )
328            {
329                log.debug( "AddRequest PDU length = {}", Integer.valueOf( result ) );
330            }
331    
332            return result;
333        }
334    
335    
336        /**
337         * Encode the AddRequest message to a PDU. 
338         * 
339         * AddRequest :
340         * 
341         * 0x68 LL
342         *   0x04 LL entry
343         *   0x30 LL attributesList
344         *     0x30 LL attributeList
345         *       0x04 LL attributeDescription
346         *       0x31 LL attributeValues
347         *         0x04 LL attributeValue
348         *         ... 
349         *         0x04 LL attributeValue
350         *     ... 
351         *     0x30 LL attributeList
352         *       0x04 LL attributeDescription
353         *       0x31 LL attributeValue
354         *         0x04 LL attributeValue
355         *         ... 
356         *         0x04 LL attributeValue 
357         * 
358         * @param buffer The buffer where to put the PDU
359         * @return The PDU.
360         */
361        protected void encodeProtocolOp( ByteBuffer buffer ) throws EncoderException
362        {
363            try
364            {
365                // The AddRequest Tag
366                buffer.put( LdapConstants.ADD_REQUEST_TAG );
367                buffer.put( TLV.getBytes( addRequestLength ) );
368    
369                // The entry
370                Value.encode( buffer, DN.getBytes( entry.getDn() ) );
371    
372                // The attributes sequence
373                buffer.put( UniversalTag.SEQUENCE_TAG );
374                buffer.put( TLV.getBytes( attributesLength ) );
375    
376                // The partial attribute list
377                if ( ( entry != null ) && ( entry.size() != 0 ) )
378                {
379                    int attributeNumber = 0;
380    
381                    // Compute the attributes length
382                    for ( EntryAttribute attribute : entry )
383                    {
384                        // The attributes list sequence
385                        buffer.put( UniversalTag.SEQUENCE_TAG );
386                        int localAttributeLength = attributeLength.get( attributeNumber );
387                        buffer.put( TLV.getBytes( localAttributeLength ) );
388    
389                        // The attribute type
390                        Value.encode( buffer, attribute.getId() );
391    
392                        // The values
393                        buffer.put( UniversalTag.SET_TAG );
394                        int localValuesLength = valuesLength.get( attributeNumber );
395                        buffer.put( TLV.getBytes( localValuesLength ) );
396    
397                        if ( attribute.size() != 0 )
398                        {
399                            for ( org.apache.directory.shared.ldap.entry.Value<?> value : attribute )
400                            {
401                                if ( value.isBinary() )
402                                {
403                                    Value.encode( buffer, value.getBytes() );
404                                }
405                                else
406                                {
407                                    Value.encode( buffer, value.getString() );
408                                }
409                            }
410                        }
411    
412                        // Go to the next attribute number;
413                        attributeNumber++;
414                    }
415                }
416            }
417            catch ( BufferOverflowException boe )
418            {
419                throw new EncoderException( "The PDU buffer size is too small !" );
420            }
421    
422            if ( IS_DEBUG )
423            {
424                log.debug( "AddRequest encoding : {}", StringTools.dumpBytes( buffer.array() ) );
425                log.debug( "AddRequest initial value : {}", toString() );
426            }
427        }
428    
429    
430        /**
431         * @return Returns the currentAttribute type.
432         */
433        public String getCurrentAttributeType()
434        {
435            return currentAttribute.getId();
436        }
437    
438    
439        /**
440         * Return a String representing an AddRequest
441         * 
442         * @return A String representing the AddRequest
443         */
444        public String toString()
445        {
446            StringBuilder sb = new StringBuilder();
447    
448            sb.append( "    Add Request\n" );
449            sb.append( "        Attributes\n" );
450    
451            if ( entry == null )
452            {
453                sb.append( "            No attributes" );
454            }
455            else
456            {
457                sb.append( entry );
458            }
459    
460            return toString( sb.toString() );
461        }
462    }