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.annotation.NotThreadSafe;
33 import ch.boye.httpclientandroidlib.protocol.HTTP;
34 
35 /**
36  * A resizable char array.
37  *
38  * @since 4.0
39  */
40 @NotThreadSafe
41 public final class CharArrayBuffer implements Serializable {
42 
43     private static final long serialVersionUID = -6208952725094867135L;
44 
45     private char[] buffer;
46     private int len;
47 
48     /**
49      * Creates an instance of {@link CharArrayBuffer} with the given initial
50      * capacity.
51      *
52      * @param capacity the capacity
53      */
CharArrayBuffer(final int capacity)54     public CharArrayBuffer(final int capacity) {
55         super();
56         Args.notNegative(capacity, "Buffer capacity");
57         this.buffer = new char[capacity];
58     }
59 
expand(final int newlen)60     private void expand(final int newlen) {
61         final 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      */
append(final char[] b, final int off, final int len)78     public void append(final char[] b, final int off, final 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         final 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      */
append(final String str)103     public void append(final String str) {
104         final String s = str != null ? str : "null";
105         final int strlen = s.length();
106         final int newlen = this.len + strlen;
107         if (newlen > this.buffer.length) {
108             expand(newlen);
109         }
110         s.getChars(0, strlen, this.buffer, this.len);
111         this.len = newlen;
112     }
113 
114     /**
115      * Appends <code>len</code> chars to this buffer from the given source
116      * buffer starting at index <code>off</code>. The capacity of the
117      * destination buffer is increased, if necessary, to accommodate all
118      * <code>len</code> chars.
119      *
120      * @param   b        the source buffer to be appended.
121      * @param   off      the index of the first char to append.
122      * @param   len      the number of chars to append.
123      * @throws IndexOutOfBoundsException if <code>off</code> is out of
124      * range, <code>len</code> is negative, or
125      * <code>off</code> + <code>len</code> is out of range.
126      */
append(final CharArrayBuffer b, final int off, final int len)127     public void append(final CharArrayBuffer b, final int off, final int len) {
128         if (b == null) {
129             return;
130         }
131         append(b.buffer, off, len);
132     }
133 
134     /**
135      * Appends all chars to this buffer from the given source buffer starting
136      * at index <code>0</code>. The capacity of the destination buffer is
137      * increased, if necessary, to accommodate all {@link #length()} chars.
138      *
139      * @param   b        the source buffer to be appended.
140      */
append(final CharArrayBuffer b)141     public void append(final CharArrayBuffer b) {
142         if (b == null) {
143             return;
144         }
145         append(b.buffer,0, b.len);
146     }
147 
148     /**
149      * Appends <code>ch</code> char to this buffer. The capacity of the buffer
150      * is increased, if necessary, to accommodate the additional char.
151      *
152      * @param   ch        the char to be appended.
153      */
append(final char ch)154     public void append(final char ch) {
155         final int newlen = this.len + 1;
156         if (newlen > this.buffer.length) {
157             expand(newlen);
158         }
159         this.buffer[this.len] = ch;
160         this.len = newlen;
161     }
162 
163     /**
164      * Appends <code>len</code> bytes to this buffer from the given source
165      * array starting at index <code>off</code>. The capacity of the buffer
166      * is increased, if necessary, to accommodate all <code>len</code> bytes.
167      * <p>
168      * The bytes are converted to chars using simple cast.
169      *
170      * @param   b        the bytes to be appended.
171      * @param   off      the index of the first byte to append.
172      * @param   len      the number of bytes to append.
173      * @throws IndexOutOfBoundsException if <code>off</code> is out of
174      * range, <code>len</code> is negative, or
175      * <code>off</code> + <code>len</code> is out of range.
176      */
append(final byte[] b, final int off, final int len)177     public void append(final byte[] b, final int off, final int len) {
178         if (b == null) {
179             return;
180         }
181         if ((off < 0) || (off > b.length) || (len < 0) ||
182                 ((off + len) < 0) || ((off + len) > b.length)) {
183             throw new IndexOutOfBoundsException("off: "+off+" len: "+len+" b.length: "+b.length);
184         }
185         if (len == 0) {
186             return;
187         }
188         final int oldlen = this.len;
189         final int newlen = oldlen + len;
190         if (newlen > this.buffer.length) {
191             expand(newlen);
192         }
193         for (int i1 = off, i2 = oldlen; i2 < newlen; i1++, i2++) {
194             this.buffer[i2] = (char) (b[i1] & 0xff);
195         }
196         this.len = newlen;
197     }
198 
199     /**
200      * Appends <code>len</code> bytes to this buffer from the given source
201      * array starting at index <code>off</code>. The capacity of the buffer
202      * is increased, if necessary, to accommodate all <code>len</code> bytes.
203      * <p>
204      * The bytes are converted to chars using simple cast.
205      *
206      * @param   b        the bytes to be appended.
207      * @param   off      the index of the first byte to append.
208      * @param   len      the number of bytes to append.
209      * @throws IndexOutOfBoundsException if <code>off</code> is out of
210      * range, <code>len</code> is negative, or
211      * <code>off</code> + <code>len</code> is out of range.
212      */
append(final ByteArrayBuffer b, final int off, final int len)213     public void append(final ByteArrayBuffer b, final int off, final int len) {
214         if (b == null) {
215             return;
216         }
217         append(b.buffer(), off, len);
218     }
219 
220     /**
221      * Appends chars of the textual representation of the given object to this
222      * buffer. The capacity of the buffer is increased, if necessary, to
223      * accommodate all chars.
224      *
225      * @param obj    the object.
226      */
append(final Object obj)227     public void append(final Object obj) {
228         append(String.valueOf(obj));
229     }
230 
231     /**
232      * Clears content of the buffer. The underlying char array is not resized.
233      */
clear()234     public void clear() {
235         this.len = 0;
236     }
237 
238     /**
239      * Converts the content of this buffer to an array of chars.
240      *
241      * @return char array
242      */
toCharArray()243     public char[] toCharArray() {
244         final char[] b = new char[this.len];
245         if (this.len > 0) {
246             System.arraycopy(this.buffer, 0, b, 0, this.len);
247         }
248         return b;
249     }
250 
251     /**
252      * Returns the <code>char</code> value in this buffer at the specified
253      * index. The index argument must be greater than or equal to
254      * <code>0</code>, and less than the length of this buffer.
255      *
256      * @param      i   the index of the desired char value.
257      * @return     the char value at the specified index.
258      * @throws     IndexOutOfBoundsException  if <code>index</code> is
259      *             negative or greater than or equal to {@link #length()}.
260      */
charAt(final int i)261     public char charAt(final int i) {
262         return this.buffer[i];
263     }
264 
265     /**
266      * Returns reference to the underlying char array.
267      *
268      * @return the char array.
269      */
buffer()270     public char[] buffer() {
271         return this.buffer;
272     }
273 
274     /**
275      * Returns the current capacity. The capacity is the amount of storage
276      * available for newly appended chars, beyond which an allocation will
277      * occur.
278      *
279      * @return  the current capacity
280      */
capacity()281     public int capacity() {
282         return this.buffer.length;
283     }
284 
285     /**
286      * Returns the length of the buffer (char count).
287      *
288      * @return  the length of the buffer
289      */
length()290     public int length() {
291         return this.len;
292     }
293 
294     /**
295      * Ensures that the capacity is at least equal to the specified minimum.
296      * If the current capacity is less than the argument, then a new internal
297      * array is allocated with greater capacity. If the <code>required</code>
298      * argument is non-positive, this method takes no action.
299      *
300      * @param   required   the minimum required capacity.
301      */
ensureCapacity(final int required)302     public void ensureCapacity(final int required) {
303         if (required <= 0) {
304             return;
305         }
306         final int available = this.buffer.length - this.len;
307         if (required > available) {
308             expand(this.len + required);
309         }
310     }
311 
312     /**
313      * Sets the length of the buffer. The new length value is expected to be
314      * less than the current capacity and greater than or equal to
315      * <code>0</code>.
316      *
317      * @param      len   the new length
318      * @throws     IndexOutOfBoundsException  if the
319      *               <code>len</code> argument is greater than the current
320      *               capacity of the buffer or less than <code>0</code>.
321      */
setLength(final int len)322     public void setLength(final int len) {
323         if (len < 0 || len > this.buffer.length) {
324             throw new IndexOutOfBoundsException("len: "+len+" < 0 or > buffer len: "+this.buffer.length);
325         }
326         this.len = len;
327     }
328 
329     /**
330      * Returns <code>true</code> if this buffer is empty, that is, its
331      * {@link #length()} is equal to <code>0</code>.
332      * @return <code>true</code> if this buffer is empty, <code>false</code>
333      *   otherwise.
334      */
isEmpty()335     public boolean isEmpty() {
336         return this.len == 0;
337     }
338 
339     /**
340      * Returns <code>true</code> if this buffer is full, that is, its
341      * {@link #length()} is equal to its {@link #capacity()}.
342      * @return <code>true</code> if this buffer is full, <code>false</code>
343      *   otherwise.
344      */
isFull()345     public boolean isFull() {
346         return this.len == this.buffer.length;
347     }
348 
349     /**
350      * Returns the index within this buffer of the first occurrence of the
351      * specified character, starting the search at the specified
352      * <code>beginIndex</code> and finishing at <code>endIndex</code>.
353      * If no such character occurs in this buffer within the specified bounds,
354      * <code>-1</code> is returned.
355      * <p>
356      * There is no restriction on the value of <code>beginIndex</code> and
357      * <code>endIndex</code>. If <code>beginIndex</code> is negative,
358      * it has the same effect as if it were zero. If <code>endIndex</code> is
359      * greater than {@link #length()}, it has the same effect as if it were
360      * {@link #length()}. If the <code>beginIndex</code> is greater than
361      * the <code>endIndex</code>, <code>-1</code> is returned.
362      *
363      * @param   ch     the char to search for.
364      * @param   from   the index to start the search from.
365      * @param   to     the index to finish the search at.
366      * @return  the index of the first occurrence of the character in the buffer
367      *   within the given bounds, or <code>-1</code> if the character does
368      *   not occur.
369      */
indexOf(final int ch, final int from, final int to)370     public int indexOf(final int ch, final int from, final int to) {
371         int beginIndex = from;
372         if (beginIndex < 0) {
373             beginIndex = 0;
374         }
375         int endIndex = to;
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      */
indexOf(final int ch)400     public int indexOf(final 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      */
substring(final int beginIndex, final int endIndex)418     public String substring(final int beginIndex, final 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      from   the beginning index, inclusive.
430      * @param      to     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      */
substringTrimmed(final int from, final int to)438     public String substringTrimmed(final int from, final int to) {
439         int beginIndex = from;
440         int endIndex = to;
441         if (beginIndex < 0) {
442             throw new IndexOutOfBoundsException("Negative beginIndex: "+beginIndex);
443         }
444         if (endIndex > this.len) {
445             throw new IndexOutOfBoundsException("endIndex: "+endIndex+" > length: "+this.len);
446         }
447         if (beginIndex > endIndex) {
448             throw new IndexOutOfBoundsException("beginIndex: "+beginIndex+" > endIndex: "+endIndex);
449         }
450         while (beginIndex < endIndex && HTTP.isWhitespace(this.buffer[beginIndex])) {
451             beginIndex++;
452         }
453         while (endIndex > beginIndex && HTTP.isWhitespace(this.buffer[endIndex - 1])) {
454             endIndex--;
455         }
456         return new String(this.buffer, beginIndex, endIndex - beginIndex);
457     }
458 
459     @Override
toString()460     public String toString() {
461         return new String(this.buffer, 0, this.len);
462     }
463 
464 }
465