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 javax.naming.NamingException;
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.entry.Entry;
037    import org.apache.directory.shared.ldap.entry.EntryAttribute;
038    import org.apache.directory.shared.ldap.entry.client.DefaultClientAttribute;
039    import org.apache.directory.shared.ldap.entry.client.DefaultClientEntry;
040    import org.apache.directory.shared.ldap.name.LdapDN;
041    import org.apache.directory.shared.ldap.util.StringTools;
042    
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: 798550 $, $Date: 2009-07-28 16:54:01 +0200 (Mar, 28 jul 2009) $, 
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 int getMessageType()
117        {
118            return LdapConstants.ADD_REQUEST;
119        }
120    
121    
122        /**
123         * Initialize the Entry.
124         */
125        public void initEntry()
126        {
127            entry = new DefaultClientEntry();
128        }
129    
130    
131        /**
132         * Get the entry to be added
133         * 
134         * @return Returns the entry.
135         */
136        public Entry getEntry()
137        {
138            return entry;
139        }
140    
141    
142        /**
143         * Sets the entry.
144         *
145         * @param entry
146         *      the entry
147         */
148        public void setEntry( Entry entry )
149        {
150            this.entry = entry;
151        }
152    
153    
154        /**
155         * Create a new attributeValue
156         * 
157         * @param type The attribute's name (called 'type' in the grammar)
158         */
159        public void addAttributeType( String type ) throws NamingException
160        {
161            // do not create a new attribute if we have seen this attributeType before
162            if ( entry.get( type ) != null )
163            {
164                currentAttribute = entry.get( type );
165                return;
166            }
167    
168            // fix this to use AttributeImpl(type.getString().toLowerCase())
169            currentAttribute = new DefaultClientAttribute( type );
170            entry.put( currentAttribute );
171        }
172    
173    
174        /**
175         * Add a new value to the current attribute
176         * 
177         * @param value The value to add
178         */
179        public void addAttributeValue( String value )
180        {
181            currentAttribute.add( value );
182        }
183    
184    
185        /**
186         * Add a new value to the current attribute
187         * 
188         * @param value The value to add
189         */
190        public void addAttributeValue( org.apache.directory.shared.ldap.entry.Value<?> value )
191        {
192            currentAttribute.add( value );
193        }
194    
195    
196        /**
197         * Add a new value to the current attribute
198         * 
199         * @param value The value to add
200         */
201        public void addAttributeValue( byte[] value )
202        {
203            currentAttribute.add( value );
204        }
205    
206    
207        /**
208         * Get the added DN
209         * 
210         * @return Returns the entry DN.
211         */
212        public LdapDN getEntryDn()
213        {
214            return entry.getDn();
215        }
216    
217    
218        /**
219         * Set the added DN.
220         * 
221         * @param entry The DN to set.
222         */
223        public void setEntryDn( LdapDN entryDn )
224        {
225            entry.setDn( entryDn );
226        }
227    
228    
229        /**
230         * Compute the AddRequest length
231         * 
232         * AddRequest :
233         * 
234         * 0x68 L1
235         *  |
236         *  +--> 0x04 L2 entry
237         *  +--> 0x30 L3 (attributes)
238         *        |
239         *        +--> 0x30 L4-1 (attribute)
240         *        |     |
241         *        |     +--> 0x04 L5-1 type
242         *        |     +--> 0x31 L6-1 (values)
243         *        |           |
244         *        |           +--> 0x04 L7-1-1 value
245         *        |           +--> ...
246         *        |           +--> 0x04 L7-1-n value
247         *        |
248         *        +--> 0x30 L4-2 (attribute)
249         *        |     |
250         *        |     +--> 0x04 L5-2 type
251         *        |     +--> 0x31 L6-2 (values)
252         *        |           |
253         *        |           +--> 0x04 L7-2-1 value
254         *        |           +--> ...
255         *        |           +--> 0x04 L7-2-n value
256         *        |
257         *        +--> ...
258         *        |
259         *        +--> 0x30 L4-m (attribute)
260         *              |
261         *              +--> 0x04 L5-m type
262         *              +--> 0x31 L6-m (values)
263         *                    |
264         *                    +--> 0x04 L7-m-1 value
265         *                    +--> ...
266         *                    +--> 0x04 L7-m-n value
267         */
268        public int computeLength()
269        {
270            // The entry
271            addRequestLength = 1 + TLV.getNbBytes( LdapDN.getNbBytes( entry.getDn() ) ) + LdapDN.getNbBytes( entry.getDn() );
272    
273            // The attributes sequence
274            attributesLength = 0;
275    
276            if ( ( entry != null ) && ( entry.size() != 0 ) )
277            {
278                attributeLength = new LinkedList<Integer>();
279                valuesLength = new LinkedList<Integer>();
280    
281                // Compute the attributes length
282                for ( EntryAttribute attribute : entry )
283                {
284                    int localAttributeLength = 0;
285                    int localValuesLength = 0;
286    
287                    // Get the type length
288                    int idLength = attribute.getId().getBytes().length;
289                    localAttributeLength = 1 + TLV.getNbBytes( idLength ) + idLength;
290    
291                    // The values
292                    if ( attribute.size() != 0 )
293                    {
294                        localValuesLength = 0;
295    
296                        for ( org.apache.directory.shared.ldap.entry.Value<?> value : attribute )
297                        {
298                            int valueLength = value.getBytes().length;
299                            localValuesLength += 1 + TLV.getNbBytes( valueLength ) + valueLength;
300                        }
301    
302                        localAttributeLength += 1 + TLV.getNbBytes( localValuesLength ) + localValuesLength;
303                    }
304    
305                    // add the attribute length to the attributes length
306                    attributesLength += 1 + TLV.getNbBytes( localAttributeLength ) + localAttributeLength;
307    
308                    attributeLength.add( localAttributeLength );
309                    valuesLength.add( localValuesLength );
310                }
311            }
312    
313            addRequestLength += 1 + TLV.getNbBytes( attributesLength ) + attributesLength;
314    
315            // Return the result.
316            int result = 1 + TLV.getNbBytes( addRequestLength ) + addRequestLength;
317    
318            if ( IS_DEBUG )
319            {
320                log.debug( "AddRequest PDU length = {}", Integer.valueOf( result ) );
321            }
322    
323            return result;
324        }
325    
326    
327        /**
328         * Encode the AddRequest message to a PDU. 
329         * 
330         * AddRequest :
331         * 
332         * 0x68 LL
333         *   0x04 LL entry
334         *   0x30 LL attributesList
335         *     0x30 LL attributeList
336         *       0x04 LL attributeDescription
337         *       0x31 LL attributeValues
338         *         0x04 LL attributeValue
339         *         ... 
340         *         0x04 LL attributeValue
341         *     ... 
342         *     0x30 LL attributeList
343         *       0x04 LL attributeDescription
344         *       0x31 LL attributeValue
345         *         0x04 LL attributeValue
346         *         ... 
347         *         0x04 LL attributeValue 
348         * 
349         * @param buffer The buffer where to put the PDU
350         * @return The PDU.
351         */
352        public ByteBuffer encode( ByteBuffer buffer ) throws EncoderException
353        {
354            if ( buffer == null )
355            {
356                throw new EncoderException( "Cannot put a PDU in a null buffer !" );
357            }
358    
359            try
360            {
361                // The AddRequest Tag
362                buffer.put( LdapConstants.ADD_REQUEST_TAG );
363                buffer.put( TLV.getBytes( addRequestLength ) );
364    
365                // The entry
366                Value.encode( buffer, LdapDN.getBytes( entry.getDn() ) );
367    
368                // The attributes sequence
369                buffer.put( UniversalTag.SEQUENCE_TAG );
370                buffer.put( TLV.getBytes( attributesLength ) );
371    
372                // The partial attribute list
373                if ( ( entry != null ) && ( entry.size() != 0 ) )
374                {
375                    int attributeNumber = 0;
376    
377                    // Compute the attributes length
378                    for ( EntryAttribute attribute : entry )
379                    {
380                        // The attributes list sequence
381                        buffer.put( UniversalTag.SEQUENCE_TAG );
382                        int localAttributeLength = attributeLength.get( attributeNumber );
383                        buffer.put( TLV.getBytes( localAttributeLength ) );
384    
385                        // The attribute type
386                        Value.encode( buffer, attribute.getId() );
387    
388                        // The values
389                        buffer.put( UniversalTag.SET_TAG );
390                        int localValuesLength = valuesLength.get( attributeNumber );
391                        buffer.put( TLV.getBytes( localValuesLength ) );
392    
393                        if ( attribute.size() != 0 )
394                        {
395                            for ( org.apache.directory.shared.ldap.entry.Value<?> value : attribute )
396                            {
397                                if ( value.isBinary() )
398                                {
399                                    Value.encode( buffer, value.getBytes() );
400                                }
401                                else
402                                {
403                                    Value.encode( buffer, value.getString() );
404                                }
405                            }
406                        }
407    
408                        // Go to the next attribute number;
409                        attributeNumber++;
410                    }
411                }
412            }
413            catch ( BufferOverflowException boe )
414            {
415                throw new EncoderException( "The PDU buffer size is too small !" );
416            }
417    
418            if ( IS_DEBUG )
419            {
420                log.debug( "AddRequest encoding : {}", StringTools.dumpBytes( buffer.array() ) );
421                log.debug( "AddRequest initial value : {}", toString() );
422            }
423    
424            return buffer;
425        }
426    
427    
428        /**
429         * Return a String representing an AddRequest
430         * 
431         * @return A String representing the AddRequest
432         */
433        public String toString()
434        {
435    
436            StringBuilder sb = new StringBuilder();
437    
438            sb.append( "    Add Request\n" );
439            sb.append( "        Attributes\n" );
440    
441            if ( entry == null )
442            {
443                sb.append( "            No attributes\n" );
444            }
445            else
446            {
447                sb.append( entry );
448            }
449    
450            return sb.toString();
451        }
452    
453    
454        /**
455         * @return Returns the currentAttribute type.
456         */
457        public String getCurrentAttributeType()
458        {
459            return currentAttribute.getId();
460        }
461    }