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.asn1.primitives;
021    
022    
023    import java.io.Serializable;
024    
025    import org.apache.directory.shared.i18n.I18n;
026    
027    
028    /**
029     * Implement the Bit String primitive type. A BitString is internally stored as
030     * an array of int.
031     * 
032     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
033     * @version $Rev: 912399 $, $Date: 2010-02-21 21:52:31 +0100 (Sun, 21 Feb 2010) $
034     */
035    public class BitString implements Serializable
036    {
037        private static final long serialVersionUID = 1L;
038    
039        // ~ Static fields/initializers
040        // -----------------------------------------------------------------
041    
042        /** A null MutableString */
043        public static final BitString EMPTY_STRING = new BitString( 1 );
044    
045        /**
046         * A flag to mark the OctetString as Streamed (for OctetString larger than
047         * 1024 chars)
048         */
049        // TODO implement the streaming...
050        public static final boolean STREAMED = true;
051    
052        /** The default length of a BitString */
053        private static final int DEFAULT_LENGTH = 1024;
054    
055        // ~ Instance fields
056        // ----------------------------------------------------------------------------
057    
058        /** The number of unused ints */
059        private int nbUnusedBits;
060    
061        /** Tells if the OctetString is streamed or not */
062        private boolean isStreamed;
063    
064        /** The string is stored in a byte array */
065        private byte[] bytes;
066    
067        /** Actual length of the byte array */
068        private int nbBytes;
069    
070        /** Actual length of the bit string */
071        private int nbBits;
072    
073    
074        // ~ Constructors
075        // -------------------------------------------------------------------------------*
076        /**
077         * Creates a BitString with a specific length (length is the number of
078         * bits).
079         * 
080         * @param length The BitString length (it's a number of bits)
081         */
082        public BitString( int length ) 
083        {
084            if ( length <= 0 )
085            {
086                // This is not allowed
087                throw new IndexOutOfBoundsException( I18n.err( I18n.ERR_00029 ) );
088            }
089            
090            nbBits = length;
091    
092            // As we store values in bytes, we must divide the length by 8
093            nbBytes = ( length / 8 ) + ( ( ( length % 8 ) != 0 ) ? 1 : 0 );
094    
095            nbUnusedBits = ( 8 - length % 8 ) % 8;
096    
097            if ( nbBytes > DEFAULT_LENGTH )
098            {
099    
100                // TODO : implement the streaming
101                isStreamed = true;
102                bytes = new byte[nbBytes];
103            }
104            else
105            {
106                isStreamed = false;
107                bytes = new byte[nbBytes];
108            }
109        }
110    
111    
112        /**
113         * Creates a streamed BitString with a specific length. Actually, it's just
114         * a simple BitString. TODO Implement streaming.
115         * 
116         * @param length
117         *            The BitString length, in number of bits
118         * @param isStreamed
119         *            Tells if the BitString must be streamed or not
120         */
121        public BitString( int length, boolean isStreamed )
122        {
123            if ( length <= 0 )
124            {
125                // This is not allowed
126                throw new IndexOutOfBoundsException( I18n.err( I18n.ERR_00029 ) );
127            }
128            
129            nbBits = length;
130            this.isStreamed = isStreamed;
131            nbBytes = ( length / 8 ) + ( ( ( length % 8 ) != 0 ) ? 1 : 0 );
132    
133            nbUnusedBits = length % 8;
134    
135            if ( isStreamed )
136            {
137    
138                // TODO : implement the streaming
139                bytes = new byte[nbBytes];
140            }
141            else
142            {
143                bytes = new byte[nbBytes];
144            }
145        }
146    
147    
148        /**
149         * Creates a BitString with a value.
150         * 
151         * @param bytes The value to store. The first byte contains the number of
152         * unused bits
153         */
154        public BitString( byte[] bytes )
155        {
156            nbBytes = bytes.length - 1;
157    
158            if ( nbBytes > DEFAULT_LENGTH )
159            {
160                isStreamed = true;
161    
162                // It will be a streamed OctetString.
163                // TODO : implement the streaming
164                this.bytes = new byte[nbBytes];
165            }
166            else
167            {
168                isStreamed = false;
169    
170                this.bytes = new byte[nbBytes];
171            }
172    
173            setBytes( bytes, nbBytes );
174        }
175    
176    
177        // ~ Methods
178        // ------------------------------------------------------------------------------------
179    
180        /**
181         * Set the value into the bytes.
182         * 
183         * @param bytes The bytes to copy
184         * @param nbBytes Number of bytes to copy
185         */
186        private void setBytes( byte[] bytes, int nbBytes )
187        {
188    
189            // The first byte contains the number of unused ints
190            nbUnusedBits = bytes[0] & 0x07;
191            nbBits = ( nbBytes * 8 ) - nbUnusedBits;
192    
193            // We have to transfer the data from bytes to ints
194            for ( int i = 0; i < nbBytes; i++ )
195            {
196                this.bytes[i] = bytes[i + 1];
197            }
198        }
199    
200    
201        /**
202         * Set a new BitString in the BitString. It will replace the old BitString,
203         * and reset the current length with the new one.
204         * 
205         * @param bytes The string to store
206         */
207        public void setData( byte[] bytes )
208        {
209    
210            if ( ( bytes == null ) || ( bytes.length == 0 ) )
211            {
212                nbBits = -1;
213                return;
214            }
215    
216            int nbb = bytes.length - 1;
217    
218            if ( ( nbb > DEFAULT_LENGTH ) && ( bytes.length < nbb ) )
219            {
220    
221                // The current size is too small.
222                // We have to allocate more space
223                // TODO : implement the streaming
224                bytes = new byte[nbb];
225            }
226    
227            setBytes( bytes, nbb );
228        }
229    
230    
231        /**
232         * Get the representation of a BitString
233         * 
234         * @return A byte array which represent the BitString
235         */
236        public byte[] getData()
237        {
238            return bytes;
239        }
240    
241    
242        /**
243         * Get the number of unused bits
244         * 
245         * @return A byte which represent the number of unused bits
246         */
247        public byte getUnusedBits()
248        {
249            return ( byte ) nbUnusedBits;
250        }
251    
252        /**
253         * Set a bit at a specified position. 
254         * The bits are stored from left to right.
255         * For instance, if we have 10 bits, then they are coded as b0 b1 b2 b3 b4 b5 b6 b7 - b8 b9 x x x x x x
256         * 
257         * @param pos The bit to set
258         */
259        public void setBit( int pos )
260        {
261            if ( ( pos < 0 ) || ( pos > nbBits ) )
262            {
263                throw new IndexOutOfBoundsException( I18n.err( I18n.ERR_00030 ) );
264            }
265            
266            int posInt = nbBytes - 1 - ( ( pos + nbUnusedBits ) >> 3 );
267            int bitNumber = ( pos + nbUnusedBits ) % 8;
268    
269            bytes[posInt] |= ( 1 << bitNumber );
270        }
271        
272        /**
273         * Clear a bit at a specified position. 
274         * The bits are stored from left to right.
275         * For instance, if we have 10 bits, then they are coded 
276         * as b0 b1 b2 b3 b4 b5 b6 b7 - b8 b9 x x x x x x
277         * 
278         * @param pos The bit to clear
279         */
280        public void clearBit( int pos )
281        {
282            if ( ( pos < 0 ) || ( pos > nbBits ) )
283            {
284                throw new IndexOutOfBoundsException( I18n.err( I18n.ERR_00030 ) );
285            }
286            
287            int posInt = nbBytes - 1 - ( ( pos + nbUnusedBits ) >> 3 );
288            int bitNumber = ( pos + nbUnusedBits ) % 8;
289    
290            bytes[posInt] &= ~( 1 << bitNumber );
291        }
292        
293        /**
294         * Get the bit stored into the BitString at a specific position. 
295         * The bits are stored from left to right, the LSB on the right and the
296         * MSB on the left
297         * For instance, if we have 10 bits, then they are coded as 
298         * b9 b8 b7 b6 - b5 b4 b3 b2 - b1 b0 x x - x x x x
299         * 
300         * With '1001 000x', where x is an unused bit, 
301         *       ^ ^    ^ 
302         *       | |    | 
303         *       | |    |  
304         *       | |    +----- getBit(0) = 0 
305         *       | +---------- getBit(4) = 0 
306         *       +------------ getBit(6) = 1
307         *       
308         * @param pos The position of the requested bit.  
309         * 
310         * @return <code>true</code> if the bit is set, <code>false</code>
311         *         otherwise
312         */
313        public boolean getBit( int pos )
314        {
315    
316            if ( pos > nbBits )
317            {
318                throw new IndexOutOfBoundsException( I18n.err( I18n.ERR_00031, pos, nbBits ) );
319            }
320    
321            int posInt = nbBytes - 1 - ( ( pos + nbUnusedBits ) >> 3 );
322            int bitNumber = ( pos + nbUnusedBits ) % 8;
323    
324            int res = bytes[posInt] & ( 1 << bitNumber );
325            return res != 0;
326        }
327        
328        /**
329         * @return The number of bytes used to encode this BitString
330         */
331        public int size()
332        {
333            return nbBytes;
334        }
335    
336    
337        /**
338         * Return a native String representation of the BitString.
339         * 
340         * @return A String representing the BitString
341         */
342        public String toString()
343        {
344    
345            StringBuffer sb = new StringBuffer();
346    
347            for ( int i = nbBits; i > 0; i-- )
348            {
349    
350                if ( getBit( i ) )
351                {
352                    sb.append( '1' );
353                }
354                else
355                {
356                    sb.append( '0' );
357                }
358            }
359    
360            return sb.toString();
361        }
362    
363    
364        /**
365         * Tells if the OctetString is streamed or not
366         * 
367         * @return <code>true</code> if the OctetString is streamed.
368         */
369        public boolean isStreamed()
370        {
371            return isStreamed;
372        }
373    }