|
1 /* |
|
2 * ==================================================================== |
|
3 * Licensed to the Apache Software Foundation (ASF) under one |
|
4 * or more contributor license agreements. See the NOTICE file |
|
5 * distributed with this work for additional information |
|
6 * regarding copyright ownership. The ASF licenses this file |
|
7 * to you under the Apache License, Version 2.0 (the |
|
8 * "License"); you may not use this file except in compliance |
|
9 * with the License. You may obtain a copy of the License at |
|
10 * |
|
11 * http://www.apache.org/licenses/LICENSE-2.0 |
|
12 * |
|
13 * Unless required by applicable law or agreed to in writing, |
|
14 * software distributed under the License is distributed on an |
|
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
|
16 * KIND, either express or implied. See the License for the |
|
17 * specific language governing permissions and limitations |
|
18 * under the License. |
|
19 * ==================================================================== |
|
20 * |
|
21 * This software consists of voluntary contributions made by many |
|
22 * individuals on behalf of the Apache Software Foundation. For more |
|
23 * information on the Apache Software Foundation, please see |
|
24 * <http://www.apache.org/>. |
|
25 * |
|
26 */ |
|
27 |
|
28 package ch.boye.httpclientandroidlib.util; |
|
29 |
|
30 import java.io.Serializable; |
|
31 |
|
32 import ch.boye.httpclientandroidlib.protocol.HTTP; |
|
33 |
|
34 /** |
|
35 * A resizable char array. |
|
36 * |
|
37 * @since 4.0 |
|
38 */ |
|
39 public final class CharArrayBuffer implements Serializable { |
|
40 |
|
41 private static final long serialVersionUID = -6208952725094867135L; |
|
42 |
|
43 private char[] buffer; |
|
44 private int len; |
|
45 |
|
46 /** |
|
47 * Creates an instance of {@link CharArrayBuffer} with the given initial |
|
48 * capacity. |
|
49 * |
|
50 * @param capacity the capacity |
|
51 */ |
|
52 public CharArrayBuffer(int capacity) { |
|
53 super(); |
|
54 if (capacity < 0) { |
|
55 throw new IllegalArgumentException("Buffer capacity may not be negative"); |
|
56 } |
|
57 this.buffer = new char[capacity]; |
|
58 } |
|
59 |
|
60 private void expand(int newlen) { |
|
61 char newbuffer[] = new char[Math.max(this.buffer.length << 1, newlen)]; |
|
62 System.arraycopy(this.buffer, 0, newbuffer, 0, this.len); |
|
63 this.buffer = newbuffer; |
|
64 } |
|
65 |
|
66 /** |
|
67 * Appends <code>len</code> chars to this buffer from the given source |
|
68 * array starting at index <code>off</code>. The capacity of the buffer |
|
69 * is increased, if necessary, to accommodate all <code>len</code> chars. |
|
70 * |
|
71 * @param b the chars to be appended. |
|
72 * @param off the index of the first char to append. |
|
73 * @param len the number of chars to append. |
|
74 * @throws IndexOutOfBoundsException if <code>off</code> is out of |
|
75 * range, <code>len</code> is negative, or |
|
76 * <code>off</code> + <code>len</code> is out of range. |
|
77 */ |
|
78 public void append(final char[] b, int off, int len) { |
|
79 if (b == null) { |
|
80 return; |
|
81 } |
|
82 if ((off < 0) || (off > b.length) || (len < 0) || |
|
83 ((off + len) < 0) || ((off + len) > b.length)) { |
|
84 throw new IndexOutOfBoundsException("off: "+off+" len: "+len+" b.length: "+b.length); |
|
85 } |
|
86 if (len == 0) { |
|
87 return; |
|
88 } |
|
89 int newlen = this.len + len; |
|
90 if (newlen > this.buffer.length) { |
|
91 expand(newlen); |
|
92 } |
|
93 System.arraycopy(b, off, this.buffer, this.len, len); |
|
94 this.len = newlen; |
|
95 } |
|
96 |
|
97 /** |
|
98 * Appends chars of the given string to this buffer. The capacity of the |
|
99 * buffer is increased, if necessary, to accommodate all chars. |
|
100 * |
|
101 * @param str the string. |
|
102 */ |
|
103 public void append(String str) { |
|
104 if (str == null) { |
|
105 str = "null"; |
|
106 } |
|
107 int strlen = str.length(); |
|
108 int newlen = this.len + strlen; |
|
109 if (newlen > this.buffer.length) { |
|
110 expand(newlen); |
|
111 } |
|
112 str.getChars(0, strlen, this.buffer, this.len); |
|
113 this.len = newlen; |
|
114 } |
|
115 |
|
116 /** |
|
117 * Appends <code>len</code> chars to this buffer from the given source |
|
118 * buffer starting at index <code>off</code>. The capacity of the |
|
119 * destination buffer is increased, if necessary, to accommodate all |
|
120 * <code>len</code> chars. |
|
121 * |
|
122 * @param b the source buffer to be appended. |
|
123 * @param off the index of the first char to append. |
|
124 * @param len the number of chars to append. |
|
125 * @throws IndexOutOfBoundsException if <code>off</code> is out of |
|
126 * range, <code>len</code> is negative, or |
|
127 * <code>off</code> + <code>len</code> is out of range. |
|
128 */ |
|
129 public void append(final CharArrayBuffer b, int off, int len) { |
|
130 if (b == null) { |
|
131 return; |
|
132 } |
|
133 append(b.buffer, off, len); |
|
134 } |
|
135 |
|
136 /** |
|
137 * Appends all chars to this buffer from the given source buffer starting |
|
138 * at index <code>0</code>. The capacity of the destination buffer is |
|
139 * increased, if necessary, to accommodate all {@link #length()} chars. |
|
140 * |
|
141 * @param b the source buffer to be appended. |
|
142 */ |
|
143 public void append(final CharArrayBuffer b) { |
|
144 if (b == null) { |
|
145 return; |
|
146 } |
|
147 append(b.buffer,0, b.len); |
|
148 } |
|
149 |
|
150 /** |
|
151 * Appends <code>ch</code> char to this buffer. The capacity of the buffer |
|
152 * is increased, if necessary, to accommodate the additional char. |
|
153 * |
|
154 * @param ch the char to be appended. |
|
155 */ |
|
156 public void append(char ch) { |
|
157 int newlen = this.len + 1; |
|
158 if (newlen > this.buffer.length) { |
|
159 expand(newlen); |
|
160 } |
|
161 this.buffer[this.len] = ch; |
|
162 this.len = newlen; |
|
163 } |
|
164 |
|
165 /** |
|
166 * Appends <code>len</code> bytes to this buffer from the given source |
|
167 * array starting at index <code>off</code>. The capacity of the buffer |
|
168 * is increased, if necessary, to accommodate all <code>len</code> bytes. |
|
169 * <p> |
|
170 * The bytes are converted to chars using simple cast. |
|
171 * |
|
172 * @param b the bytes to be appended. |
|
173 * @param off the index of the first byte to append. |
|
174 * @param len the number of bytes to append. |
|
175 * @throws IndexOutOfBoundsException if <code>off</code> is out of |
|
176 * range, <code>len</code> is negative, or |
|
177 * <code>off</code> + <code>len</code> is out of range. |
|
178 */ |
|
179 public void append(final byte[] b, int off, int len) { |
|
180 if (b == null) { |
|
181 return; |
|
182 } |
|
183 if ((off < 0) || (off > b.length) || (len < 0) || |
|
184 ((off + len) < 0) || ((off + len) > b.length)) { |
|
185 throw new IndexOutOfBoundsException("off: "+off+" len: "+len+" b.length: "+b.length); |
|
186 } |
|
187 if (len == 0) { |
|
188 return; |
|
189 } |
|
190 int oldlen = this.len; |
|
191 int newlen = oldlen + len; |
|
192 if (newlen > this.buffer.length) { |
|
193 expand(newlen); |
|
194 } |
|
195 for (int i1 = off, i2 = oldlen; i2 < newlen; i1++, i2++) { |
|
196 this.buffer[i2] = (char) (b[i1] & 0xff); |
|
197 } |
|
198 this.len = newlen; |
|
199 } |
|
200 |
|
201 /** |
|
202 * Appends <code>len</code> bytes to this buffer from the given source |
|
203 * array starting at index <code>off</code>. The capacity of the buffer |
|
204 * is increased, if necessary, to accommodate all <code>len</code> bytes. |
|
205 * <p> |
|
206 * The bytes are converted to chars using simple cast. |
|
207 * |
|
208 * @param b the bytes to be appended. |
|
209 * @param off the index of the first byte to append. |
|
210 * @param len the number of bytes to append. |
|
211 * @throws IndexOutOfBoundsException if <code>off</code> is out of |
|
212 * range, <code>len</code> is negative, or |
|
213 * <code>off</code> + <code>len</code> is out of range. |
|
214 */ |
|
215 public void append(final ByteArrayBuffer b, int off, int len) { |
|
216 if (b == null) { |
|
217 return; |
|
218 } |
|
219 append(b.buffer(), off, len); |
|
220 } |
|
221 |
|
222 /** |
|
223 * Appends chars of the textual representation of the given object to this |
|
224 * buffer. The capacity of the buffer is increased, if necessary, to |
|
225 * accommodate all chars. |
|
226 * |
|
227 * @param obj the object. |
|
228 */ |
|
229 public void append(final Object obj) { |
|
230 append(String.valueOf(obj)); |
|
231 } |
|
232 |
|
233 /** |
|
234 * Clears content of the buffer. The underlying char array is not resized. |
|
235 */ |
|
236 public void clear() { |
|
237 this.len = 0; |
|
238 } |
|
239 |
|
240 /** |
|
241 * Converts the content of this buffer to an array of chars. |
|
242 * |
|
243 * @return char array |
|
244 */ |
|
245 public char[] toCharArray() { |
|
246 char[] b = new char[this.len]; |
|
247 if (this.len > 0) { |
|
248 System.arraycopy(this.buffer, 0, b, 0, this.len); |
|
249 } |
|
250 return b; |
|
251 } |
|
252 |
|
253 /** |
|
254 * Returns the <code>char</code> value in this buffer at the specified |
|
255 * index. The index argument must be greater than or equal to |
|
256 * <code>0</code>, and less than the length of this buffer. |
|
257 * |
|
258 * @param i the index of the desired char value. |
|
259 * @return the char value at the specified index. |
|
260 * @throws IndexOutOfBoundsException if <code>index</code> is |
|
261 * negative or greater than or equal to {@link #length()}. |
|
262 */ |
|
263 public char charAt(int i) { |
|
264 return this.buffer[i]; |
|
265 } |
|
266 |
|
267 /** |
|
268 * Returns reference to the underlying char array. |
|
269 * |
|
270 * @return the char array. |
|
271 */ |
|
272 public char[] buffer() { |
|
273 return this.buffer; |
|
274 } |
|
275 |
|
276 /** |
|
277 * Returns the current capacity. The capacity is the amount of storage |
|
278 * available for newly appended chars, beyond which an allocation will |
|
279 * occur. |
|
280 * |
|
281 * @return the current capacity |
|
282 */ |
|
283 public int capacity() { |
|
284 return this.buffer.length; |
|
285 } |
|
286 |
|
287 /** |
|
288 * Returns the length of the buffer (char count). |
|
289 * |
|
290 * @return the length of the buffer |
|
291 */ |
|
292 public int length() { |
|
293 return this.len; |
|
294 } |
|
295 |
|
296 /** |
|
297 * Ensures that the capacity is at least equal to the specified minimum. |
|
298 * If the current capacity is less than the argument, then a new internal |
|
299 * array is allocated with greater capacity. If the <code>required</code> |
|
300 * argument is non-positive, this method takes no action. |
|
301 * |
|
302 * @param required the minimum required capacity. |
|
303 */ |
|
304 public void ensureCapacity(int required) { |
|
305 if (required <= 0) { |
|
306 return; |
|
307 } |
|
308 int available = this.buffer.length - this.len; |
|
309 if (required > available) { |
|
310 expand(this.len + required); |
|
311 } |
|
312 } |
|
313 |
|
314 /** |
|
315 * Sets the length of the buffer. The new length value is expected to be |
|
316 * less than the current capacity and greater than or equal to |
|
317 * <code>0</code>. |
|
318 * |
|
319 * @param len the new length |
|
320 * @throws IndexOutOfBoundsException if the |
|
321 * <code>len</code> argument is greater than the current |
|
322 * capacity of the buffer or less than <code>0</code>. |
|
323 */ |
|
324 public void setLength(int len) { |
|
325 if (len < 0 || len > this.buffer.length) { |
|
326 throw new IndexOutOfBoundsException("len: "+len+" < 0 or > buffer len: "+this.buffer.length); |
|
327 } |
|
328 this.len = len; |
|
329 } |
|
330 |
|
331 /** |
|
332 * Returns <code>true</code> if this buffer is empty, that is, its |
|
333 * {@link #length()} is equal to <code>0</code>. |
|
334 * @return <code>true</code> if this buffer is empty, <code>false</code> |
|
335 * otherwise. |
|
336 */ |
|
337 public boolean isEmpty() { |
|
338 return this.len == 0; |
|
339 } |
|
340 |
|
341 /** |
|
342 * Returns <code>true</code> if this buffer is full, that is, its |
|
343 * {@link #length()} is equal to its {@link #capacity()}. |
|
344 * @return <code>true</code> if this buffer is full, <code>false</code> |
|
345 * otherwise. |
|
346 */ |
|
347 public boolean isFull() { |
|
348 return this.len == this.buffer.length; |
|
349 } |
|
350 |
|
351 /** |
|
352 * Returns the index within this buffer of the first occurrence of the |
|
353 * specified character, starting the search at the specified |
|
354 * <code>beginIndex</code> and finishing at <code>endIndex</code>. |
|
355 * If no such character occurs in this buffer within the specified bounds, |
|
356 * <code>-1</code> is returned. |
|
357 * <p> |
|
358 * There is no restriction on the value of <code>beginIndex</code> and |
|
359 * <code>endIndex</code>. If <code>beginIndex</code> is negative, |
|
360 * it has the same effect as if it were zero. If <code>endIndex</code> is |
|
361 * greater than {@link #length()}, it has the same effect as if it were |
|
362 * {@link #length()}. If the <code>beginIndex</code> is greater than |
|
363 * the <code>endIndex</code>, <code>-1</code> is returned. |
|
364 * |
|
365 * @param ch the char to search for. |
|
366 * @param beginIndex the index to start the search from. |
|
367 * @param endIndex the index to finish the search at. |
|
368 * @return the index of the first occurrence of the character in the buffer |
|
369 * within the given bounds, or <code>-1</code> if the character does |
|
370 * not occur. |
|
371 */ |
|
372 public int indexOf(int ch, int beginIndex, int endIndex) { |
|
373 if (beginIndex < 0) { |
|
374 beginIndex = 0; |
|
375 } |
|
376 if (endIndex > this.len) { |
|
377 endIndex = this.len; |
|
378 } |
|
379 if (beginIndex > endIndex) { |
|
380 return -1; |
|
381 } |
|
382 for (int i = beginIndex; i < endIndex; i++) { |
|
383 if (this.buffer[i] == ch) { |
|
384 return i; |
|
385 } |
|
386 } |
|
387 return -1; |
|
388 } |
|
389 |
|
390 /** |
|
391 * Returns the index within this buffer of the first occurrence of the |
|
392 * specified character, starting the search at <code>0</code> and finishing |
|
393 * at {@link #length()}. If no such character occurs in this buffer within |
|
394 * those bounds, <code>-1</code> is returned. |
|
395 * |
|
396 * @param ch the char to search for. |
|
397 * @return the index of the first occurrence of the character in the |
|
398 * buffer, or <code>-1</code> if the character does not occur. |
|
399 */ |
|
400 public int indexOf(int ch) { |
|
401 return indexOf(ch, 0, this.len); |
|
402 } |
|
403 |
|
404 /** |
|
405 * Returns a substring of this buffer. The substring begins at the specified |
|
406 * <code>beginIndex</code> and extends to the character at index |
|
407 * <code>endIndex - 1</code>. |
|
408 * |
|
409 * @param beginIndex the beginning index, inclusive. |
|
410 * @param endIndex the ending index, exclusive. |
|
411 * @return the specified substring. |
|
412 * @exception StringIndexOutOfBoundsException if the |
|
413 * <code>beginIndex</code> is negative, or |
|
414 * <code>endIndex</code> is larger than the length of this |
|
415 * buffer, or <code>beginIndex</code> is larger than |
|
416 * <code>endIndex</code>. |
|
417 */ |
|
418 public String substring(int beginIndex, int endIndex) { |
|
419 return new String(this.buffer, beginIndex, endIndex - beginIndex); |
|
420 } |
|
421 |
|
422 /** |
|
423 * Returns a substring of this buffer with leading and trailing whitespace |
|
424 * omitted. The substring begins with the first non-whitespace character |
|
425 * from <code>beginIndex</code> and extends to the last |
|
426 * non-whitespace character with the index lesser than |
|
427 * <code>endIndex</code>. |
|
428 * |
|
429 * @param beginIndex the beginning index, inclusive. |
|
430 * @param endIndex the ending index, exclusive. |
|
431 * @return the specified substring. |
|
432 * @exception IndexOutOfBoundsException if the |
|
433 * <code>beginIndex</code> is negative, or |
|
434 * <code>endIndex</code> is larger than the length of this |
|
435 * buffer, or <code>beginIndex</code> is larger than |
|
436 * <code>endIndex</code>. |
|
437 */ |
|
438 public String substringTrimmed(int beginIndex, int endIndex) { |
|
439 if (beginIndex < 0) { |
|
440 throw new IndexOutOfBoundsException("Negative beginIndex: "+beginIndex); |
|
441 } |
|
442 if (endIndex > this.len) { |
|
443 throw new IndexOutOfBoundsException("endIndex: "+endIndex+" > length: "+this.len); |
|
444 } |
|
445 if (beginIndex > endIndex) { |
|
446 throw new IndexOutOfBoundsException("beginIndex: "+beginIndex+" > endIndex: "+endIndex); |
|
447 } |
|
448 while (beginIndex < endIndex && HTTP.isWhitespace(this.buffer[beginIndex])) { |
|
449 beginIndex++; |
|
450 } |
|
451 while (endIndex > beginIndex && HTTP.isWhitespace(this.buffer[endIndex - 1])) { |
|
452 endIndex--; |
|
453 } |
|
454 return new String(this.buffer, beginIndex, endIndex - beginIndex); |
|
455 } |
|
456 |
|
457 public String toString() { |
|
458 return new String(this.buffer, 0, this.len); |
|
459 } |
|
460 |
|
461 } |