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;
021    
022    
023    import java.nio.BufferOverflowException;
024    import java.nio.ByteBuffer;
025    
026    import org.apache.directory.shared.asn1.AbstractAsn1Object;
027    import org.apache.directory.shared.asn1.Asn1Object;
028    import org.apache.directory.shared.asn1.ber.tlv.TLV;
029    import org.apache.directory.shared.asn1.ber.tlv.UniversalTag;
030    import org.apache.directory.shared.asn1.ber.tlv.Value;
031    import org.apache.directory.shared.asn1.codec.EncoderException;
032    import org.apache.directory.shared.ldap.util.StringTools;
033    
034    
035    /**
036     * A Asn1Object to store a Control.
037     * 
038     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
039     * @version $Rev: 764131 $, $Date: 2009-04-11 03:03:00 +0200 (Sam, 11 avr 2009) $, 
040     */
041    public class ControlCodec extends AbstractAsn1Object
042    {
043        // ~ Instance fields
044        // ----------------------------------------------------------------------------
045    
046        /** The control type */
047        private String controlType;
048    
049        /** The criticality (default value is false) */
050        private boolean criticality = false;
051    
052        /** Optionnal control value */
053        private Object controlValue;
054    
055        /** Optionnal control value in encoded form */
056        private byte[] encodedValue;
057    
058        /** The control length */
059        private int controlLength;
060    
061        // ~ Methods
062        // ------------------------------------------------------------------------------------
063    
064        /**
065         * Default constructor.
066         */
067        public ControlCodec()
068        {
069            super();
070        }
071    
072        /**
073         * Get the control type
074         * 
075         * @return A string which represent the control type
076         */
077        public String getControlType()
078        {
079            return controlType == null ? "" : controlType;
080        }
081    
082    
083        /**
084         * Set the control type
085         * 
086         * @param controlType The OID to be stored
087         */
088        public void setControlType( String controlType )
089        {
090            this.controlType = controlType;
091        }
092    
093    
094        /**
095         * Get the control value
096         * 
097         * @return The control value
098         */
099        public Object getControlValue()
100        {
101            if ( controlValue == null )
102            {
103                return StringTools.EMPTY_BYTES;
104            }
105            else if ( controlValue instanceof String )
106            {
107                return StringTools.getBytesUtf8( ( String ) controlValue );
108            }
109            else
110            {
111                return controlValue;
112            }
113        }
114    
115    
116        /**
117         * Set the encoded control value
118         * 
119         * @param encodedValue The encoded control value to store
120         */
121        public void setEncodedValue( byte[] encodedValue )
122        {
123            if ( encodedValue != null )
124            {
125                this.encodedValue = new byte[ encodedValue.length ];
126                System.arraycopy( encodedValue, 0, this.encodedValue, 0, encodedValue.length );
127            } else {
128                this.encodedValue = null;
129            }
130        }
131    
132    
133        /**
134         * Get the raw control encoded bytes
135         * 
136         * @return the encoded bytes for the control
137         */
138        public byte[] getEncodedValue()
139        {
140            if ( encodedValue == null )
141            {
142                return StringTools.EMPTY_BYTES;
143            }
144    
145            final byte[] copy = new byte[ encodedValue.length ];
146            System.arraycopy( encodedValue, 0, copy, 0, encodedValue.length );
147            return copy;
148        }
149    
150    
151        /**
152         * Set the control value
153         * 
154         * @param controlValue The control value to store
155         */
156        public void setControlValue( Object controlValue )
157        {
158            this.controlValue = controlValue;
159        }
160    
161    
162        /**
163         * Get the criticality
164         * 
165         * @return <code>true</code> if the criticality flag is true.
166         */
167        public boolean getCriticality()
168        {
169            return criticality;
170        }
171    
172    
173        /**
174         * Set the criticality
175         * 
176         * @param criticality The criticality value
177         */
178        public void setCriticality( boolean criticality )
179        {
180            this.criticality = criticality;
181        }
182    
183    
184        /**
185         * Compute the Control length 
186         * Control :
187         * 
188         * 0x30 L1
189         *  |
190         *  +--> 0x04 L2 controlType
191         * [+--> 0x01 0x01 criticality]
192         * [+--> 0x04 L3 controlValue] 
193         * 
194         * Control length = Length(0x30) + length(L1) 
195         *                  + Length(0x04) + Length(L2) + L2
196         *                  [+ Length(0x01) + 1 + 1]
197         *                  [+ Length(0x04) + Length(L3) + L3]
198         */
199        public int computeLength()
200        {
201            // The controlType
202            int controlTypeLengh = StringTools.getBytesUtf8( controlType ).length;
203            controlLength = 1 + TLV.getNbBytes( controlTypeLengh ) + controlTypeLengh;
204    
205            // The criticality, only if true
206            if ( criticality )
207            {
208                controlLength += 1 + 1 + 1; // Always 3 for a boolean
209            }
210    
211            // The control value, if any
212            if ( controlValue != null )
213            {
214                byte[] controlBytes;
215                if ( controlValue instanceof byte[] )
216                {
217                    controlBytes = ( byte[] ) controlValue;
218                    controlLength += 1 + TLV.getNbBytes( controlBytes.length ) + controlBytes.length;
219                }
220                else if ( controlValue instanceof String )
221                {
222                    controlBytes = StringTools.getBytesUtf8( ( String ) controlValue );
223                    controlLength += 1 + TLV.getNbBytes( controlBytes.length ) + controlBytes.length;
224                }
225                else if ( controlValue instanceof Asn1Object )
226                {
227                    int length = ( ( Asn1Object ) controlValue ).computeLength();
228                    controlLength += 1 + TLV.getNbBytes( length ) + length;
229                }
230                else
231                {
232                    throw new IllegalStateException( "Don't know how to handle control value class "
233                        + controlValue.getClass() );
234                }
235            }
236    
237            return 1 + TLV.getNbBytes( controlLength ) + controlLength;
238        }
239    
240    
241        /**
242         * Generate the PDU which contains the Control. 
243         * Control : 
244         * 0x30 LL
245         *   0x04 LL type 
246         *   [0x01 0x01 criticality]
247         *   [0x04 LL value]
248         * 
249         * @param buffer The encoded PDU
250         * @return A ByteBuffer that contaons the PDU
251         * @throws EncoderException If anything goes wrong.
252         */
253        public ByteBuffer encode( ByteBuffer buffer ) throws EncoderException
254        {
255            try
256            {
257                // The LdapMessage Sequence
258                buffer.put( UniversalTag.SEQUENCE_TAG );
259    
260                // The length has been calculated by the computeLength method
261                buffer.put( TLV.getBytes( controlLength ) );
262            }
263            catch ( BufferOverflowException boe )
264            {
265                throw new EncoderException( "The PDU buffer size is too small !" );
266            }
267    
268            // The control type
269            Value.encode( buffer, getControlType().getBytes() );
270    
271            // The control criticality, if true
272            if ( criticality )
273            {
274                Value.encode( buffer, criticality );
275            }
276    
277            // The control value, if any
278            if ( controlValue != null )
279            {
280                byte[] controlBytes;
281                if ( controlValue instanceof byte[] )
282                {
283                    controlBytes = ( byte[] ) controlValue;
284                    encodedValue = controlBytes;
285                }
286                else if ( controlValue instanceof String )
287                {
288                    controlBytes = StringTools.getBytesUtf8( ( String ) controlValue );
289                    encodedValue = controlBytes;
290                }
291                else if ( controlValue instanceof Asn1Object )
292                {
293                    controlBytes = ( ( Asn1Object ) controlValue ).encode( null ).array();
294                    encodedValue = controlBytes;
295                }
296                else
297                {
298                    throw new IllegalStateException( "Don't know how to handle control value class "
299                        + controlValue.getClass() );
300                }
301    
302                Value.encode( buffer, controlBytes );
303            }
304    
305            return buffer;
306        }
307    
308    
309        /**
310         * Return a String representing a Control
311         */
312        public String toString()
313        {
314            StringBuffer sb = new StringBuffer();
315    
316            sb.append( "    Control\n" );
317            sb.append( "        Control type : '" ).append( controlType ).append(
318                "'\n" );
319            sb.append( "        Criticality : '" ).append( criticality ).append( "'\n" );
320    
321            if ( controlValue != null )
322            {
323                if ( controlValue instanceof byte[] )
324                {
325                    sb.append( "        Control value : '" ).append( StringTools.dumpBytes( ( byte[] ) controlValue ) )
326                        .append( "'\n" );
327                }
328                else
329                {
330                    sb.append( "        Control value : '" ).append( controlValue ).append( "'\n" );
331                }
332            }
333    
334            return sb.toString();
335        }
336    }