1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.asn1new.primitives;
18
19 import java.io.Serializable;
20
21 import org.apache.asn1.codec.DecoderException;
22
23
24 /***
25 * Implement the Bit String primitive type.
26 *
27 * A BitString is internally stored as an array of int.
28 *
29 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
30 */
31 public class BitString implements Serializable
32 {
33 private static final long serialVersionUID = 1L;
34
35
36
37 /*** A null MutableString */
38 public static final BitString EMPTY_STRING = new BitString();
39
40 /*** A flag to mark the OctetString as Streamed (for OctetString larger than 1024 chars) */
41
42 public static final boolean STREAMED = true;
43
44 /*** The default length of an BitString */
45 private static final int DEFAULT_LENGTH = 8;
46
47
48
49 /*** The number of unused ints */
50 private int nbUnusedBits;
51
52 /*** Tells if the OctetString is streamed or not */
53 private boolean isStreamed;
54
55 /*** The string is stored in a byte array */
56 private byte[] bytes;
57
58 /*** Actual length of the byte array */
59 private int nbBytes;
60
61 /*** Actual length of the bit string */
62 private int nbBits;
63
64
65
66 /***
67 * Creates a BitString, with a default length.
68 */
69 public BitString()
70 {
71 bytes = new byte[DEFAULT_LENGTH];
72 nbBytes = 0;
73 isStreamed = false;
74 nbUnusedBits = 0;
75 nbBits = 0;
76 }
77
78 /***
79 * Creates a BitString with a specific length (length is the number
80 * of bytes).
81 *
82 * @param length The BitString length (it's a number of bits)
83 */
84 public BitString( int length )
85 {
86 nbBits = length;
87
88
89 nbBytes = ( length / 8 ) + ( ( ( length % 8 ) != 0 ) ? 1 : 0 );
90
91 nbUnusedBits = length % 8;
92
93 if ( nbBytes > DEFAULT_LENGTH )
94 {
95
96
97 isStreamed = true;
98 bytes = new byte[nbBytes];
99 }
100 else
101 {
102 isStreamed = false;
103 bytes = new byte[nbBytes];
104 }
105 }
106
107 /***
108 * Creates a streamed BitString with a specific length.
109 * Actually, it's just a simple BitString.
110 * TODO Implement streaming.
111 *
112 * @param length The BitString length, in number of bits
113 * @param isStreamed Tells if the BitString must be streamed or not
114 */
115 public BitString( int length, boolean isStreamed )
116 {
117 nbBits = length;
118 this.isStreamed = isStreamed;
119 nbBytes = ( length / 8 ) + ( ( ( length % 8 ) != 0 ) ? 1 : 0 );
120
121 nbUnusedBits = length % 8;
122
123 if ( isStreamed )
124 {
125
126
127 bytes = new byte[nbBytes];
128 }
129 else
130 {
131 bytes = new byte[nbBytes];
132 }
133 }
134
135 /***
136 * Creates a BitString with a value.
137 *
138 * @param bytes The value to store. The first byte contains the number
139 * of unused bits
140 */
141 public BitString( byte[] bytes )
142 {
143 nbBytes = bytes.length - 1;
144
145 if ( nbBytes > DEFAULT_LENGTH )
146 {
147 isStreamed = true;
148
149
150
151 bytes = new byte[nbBytes];
152 }
153 else
154 {
155 isStreamed = false;
156
157 bytes = new byte[nbBytes];
158 }
159
160 setBytes( bytes, nbBytes );
161 }
162
163
164
165 /***
166 * Set the value into the bytes.
167 * @param bytes The bytes to copy
168 * @param nbBytes Number of bytes to copy
169 */
170 private void setBytes( byte[] bytes, int nbBytes )
171 {
172
173
174 nbUnusedBits = bytes[0] & 0x07;
175 nbBits = ( nbBytes * 8 ) - nbUnusedBits;
176
177
178 for ( int i = 0; i < nbBytes; i++ )
179 {
180 this.bytes[i] = bytes[i + 1];
181 }
182 }
183
184 /***
185 * Set a new BitString in the BitString. It will replace the old BitString,
186 * and reset the current length with the new one.
187 *
188 * @param bytes The string to store
189 */
190 public void setData( byte[] bytes )
191 {
192
193 if ( ( bytes == null ) || ( bytes.length == 0 ) )
194 {
195 nbBits = -1;
196 return;
197 }
198
199 int nbBytes = bytes.length - 1;
200
201 if ( ( nbBytes > DEFAULT_LENGTH ) && ( bytes.length < nbBytes ) )
202 {
203
204
205
206
207 bytes = new byte[nbBytes];
208 }
209
210 setBytes( bytes, nbBytes );
211 }
212
213 /***
214 * Get the representation of a BitString
215 * @return A byte array which represent the BitString
216 */
217 public byte[] getData()
218 {
219 return bytes;
220 }
221
222 /***
223 * Get the number of unused bits
224 * @return A byte which represent the number of unused bits
225 */
226 public byte getUnusedBits()
227 {
228 return (byte)nbUnusedBits;
229 }
230
231 /***
232 * Get the bit stored into the BitString at a specific position? The position start at 0,
233 * which is on the left :
234 * With '1001 000x', where x is an unused bit,
235 * ^ ^ ^^
236 * | | ||
237 * | | |+---- getBit(7) -> DecoderException
238 * | | +----- getBit(6) = 0
239 * | +---------- getBit(2) = 0
240 * +------------ getBit(0) = 1
241 * @param pos The position of the requested bit.
242 * @return <code>true</code> if the bit is set, <code>false</code> otherwise
243 */
244 public boolean getBit( int pos ) throws DecoderException
245 {
246
247 if ( pos > nbBits )
248 {
249 throw new DecoderException(
250 "Cannot get a bit at position " + pos + " when the BitString contains only " +
251 nbBits + " ints" );
252 }
253
254 int posInt = pos / 8;
255
256 int bitNumber = 7 - ( pos % 8 );
257 int res = bytes[posInt] & ( 1 << bitNumber );
258 return res != 0;
259 }
260
261 /***
262 * Return a native String representation of the BitString.
263 * @return A String representing the BitString
264 */
265 public String toString()
266 {
267
268 StringBuffer sb = new StringBuffer();
269
270 try
271 {
272
273 for ( int i = 0; i < nbBits; i++ )
274 {
275
276 if ( getBit( i ) )
277 {
278 sb.append( '1' );
279 }
280 else
281 {
282 sb.append( '0' );
283 }
284 }
285 }
286 catch ( DecoderException de )
287 {
288 return "Invalid BitString";
289 }
290
291 return sb.toString();
292 }
293
294 /***
295 * Tells if the OctetString is streamed or not
296 * @return <code>true</code> if the OctetString is streamed.
297 */
298 public boolean isStreamed()
299 {
300 return isStreamed;
301 }
302 }