View Javadoc

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 org.apache.http.util;
29  
30  import java.io.Serializable;
31  
32  import org.apache.http.annotation.NotThreadSafe;
33  import org.apache.http.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       */
54      public CharArrayBuffer(int capacity) {
55          super();
56          if (capacity < 0) {
57              throw new IllegalArgumentException("Buffer capacity may not be negative");
58          }
59          this.buffer = new char[capacity];
60      }
61  
62      private void expand(int newlen) {
63          char newbuffer[] = new char[Math.max(this.buffer.length << 1, newlen)];
64          System.arraycopy(this.buffer, 0, newbuffer, 0, this.len);
65          this.buffer = newbuffer;
66      }
67  
68      /**
69       * Appends <code>len</code> chars to this buffer from the given source
70       * array starting at index <code>off</code>. The capacity of the buffer
71       * is increased, if necessary, to accommodate all <code>len</code> chars.
72       *
73       * @param   b        the chars to be appended.
74       * @param   off      the index of the first char to append.
75       * @param   len      the number of chars to append.
76       * @throws IndexOutOfBoundsException if <code>off</code> is out of
77       * range, <code>len</code> is negative, or
78       * <code>off</code> + <code>len</code> is out of range.
79       */
80      public void append(final char[] b, int off, int len) {
81          if (b == null) {
82              return;
83          }
84          if ((off < 0) || (off > b.length) || (len < 0) ||
85                  ((off + len) < 0) || ((off + len) > b.length)) {
86              throw new IndexOutOfBoundsException("off: "+off+" len: "+len+" b.length: "+b.length);
87          }
88          if (len == 0) {
89              return;
90          }
91          int newlen = this.len + len;
92          if (newlen > this.buffer.length) {
93              expand(newlen);
94          }
95          System.arraycopy(b, off, this.buffer, this.len, len);
96          this.len = newlen;
97      }
98  
99      /**
100      * Appends chars of the given string to this buffer. The capacity of the
101      * buffer is increased, if necessary, to accommodate all chars.
102      *
103      * @param str    the string.
104      */
105     public void append(String str) {
106         if (str == null) {
107             str = "null";
108         }
109         int strlen = str.length();
110         int newlen = this.len + strlen;
111         if (newlen > this.buffer.length) {
112             expand(newlen);
113         }
114         str.getChars(0, strlen, this.buffer, this.len);
115         this.len = newlen;
116     }
117 
118     /**
119      * Appends <code>len</code> chars to this buffer from the given source
120      * buffer starting at index <code>off</code>. The capacity of the
121      * destination buffer is increased, if necessary, to accommodate all
122      * <code>len</code> chars.
123      *
124      * @param   b        the source buffer to be appended.
125      * @param   off      the index of the first char to append.
126      * @param   len      the number of chars to append.
127      * @throws IndexOutOfBoundsException if <code>off</code> is out of
128      * range, <code>len</code> is negative, or
129      * <code>off</code> + <code>len</code> is out of range.
130      */
131     public void append(final CharArrayBuffer b, int off, int len) {
132         if (b == null) {
133             return;
134         }
135         append(b.buffer, off, len);
136     }
137 
138     /**
139      * Appends all chars to this buffer from the given source buffer starting
140      * at index <code>0</code>. The capacity of the destination buffer is
141      * increased, if necessary, to accommodate all {@link #length()} chars.
142      *
143      * @param   b        the source buffer to be appended.
144      */
145     public void append(final CharArrayBuffer b) {
146         if (b == null) {
147             return;
148         }
149         append(b.buffer,0, b.len);
150     }
151 
152     /**
153      * Appends <code>ch</code> char to this buffer. The capacity of the buffer
154      * is increased, if necessary, to accommodate the additional char.
155      *
156      * @param   ch        the char to be appended.
157      */
158     public void append(char ch) {
159         int newlen = this.len + 1;
160         if (newlen > this.buffer.length) {
161             expand(newlen);
162         }
163         this.buffer[this.len] = ch;
164         this.len = newlen;
165     }
166 
167     /**
168      * Appends <code>len</code> bytes to this buffer from the given source
169      * array starting at index <code>off</code>. The capacity of the buffer
170      * is increased, if necessary, to accommodate all <code>len</code> bytes.
171      * <p>
172      * The bytes are converted to chars using simple cast.
173      *
174      * @param   b        the bytes to be appended.
175      * @param   off      the index of the first byte to append.
176      * @param   len      the number of bytes to append.
177      * @throws IndexOutOfBoundsException if <code>off</code> is out of
178      * range, <code>len</code> is negative, or
179      * <code>off</code> + <code>len</code> is out of range.
180      */
181     public void append(final byte[] b, int off, int len) {
182         if (b == null) {
183             return;
184         }
185         if ((off < 0) || (off > b.length) || (len < 0) ||
186                 ((off + len) < 0) || ((off + len) > b.length)) {
187             throw new IndexOutOfBoundsException("off: "+off+" len: "+len+" b.length: "+b.length);
188         }
189         if (len == 0) {
190             return;
191         }
192         int oldlen = this.len;
193         int newlen = oldlen + len;
194         if (newlen > this.buffer.length) {
195             expand(newlen);
196         }
197         for (int i1 = off, i2 = oldlen; i2 < newlen; i1++, i2++) {
198             this.buffer[i2] = (char) (b[i1] & 0xff);
199         }
200         this.len = newlen;
201     }
202 
203     /**
204      * Appends <code>len</code> bytes to this buffer from the given source
205      * array starting at index <code>off</code>. The capacity of the buffer
206      * is increased, if necessary, to accommodate all <code>len</code> bytes.
207      * <p>
208      * The bytes are converted to chars using simple cast.
209      *
210      * @param   b        the bytes to be appended.
211      * @param   off      the index of the first byte to append.
212      * @param   len      the number of bytes to append.
213      * @throws IndexOutOfBoundsException if <code>off</code> is out of
214      * range, <code>len</code> is negative, or
215      * <code>off</code> + <code>len</code> is out of range.
216      */
217     public void append(final ByteArrayBuffer b, int off, int len) {
218         if (b == null) {
219             return;
220         }
221         append(b.buffer(), off, len);
222     }
223 
224     /**
225      * Appends chars of the textual representation of the given object to this
226      * buffer. The capacity of the buffer is increased, if necessary, to
227      * accommodate all chars.
228      *
229      * @param obj    the object.
230      */
231     public void append(final Object obj) {
232         append(String.valueOf(obj));
233     }
234 
235     /**
236      * Clears content of the buffer. The underlying char array is not resized.
237      */
238     public void clear() {
239         this.len = 0;
240     }
241 
242     /**
243      * Converts the content of this buffer to an array of chars.
244      *
245      * @return char array
246      */
247     public char[] toCharArray() {
248         char[] b = new char[this.len];
249         if (this.len > 0) {
250             System.arraycopy(this.buffer, 0, b, 0, this.len);
251         }
252         return b;
253     }
254 
255     /**
256      * Returns the <code>char</code> value in this buffer at the specified
257      * index. The index argument must be greater than or equal to
258      * <code>0</code>, and less than the length of this buffer.
259      *
260      * @param      i   the index of the desired char value.
261      * @return     the char value at the specified index.
262      * @throws     IndexOutOfBoundsException  if <code>index</code> is
263      *             negative or greater than or equal to {@link #length()}.
264      */
265     public char charAt(int i) {
266         return this.buffer[i];
267     }
268 
269     /**
270      * Returns reference to the underlying char array.
271      *
272      * @return the char array.
273      */
274     public char[] buffer() {
275         return this.buffer;
276     }
277 
278     /**
279      * Returns the current capacity. The capacity is the amount of storage
280      * available for newly appended chars, beyond which an allocation will
281      * occur.
282      *
283      * @return  the current capacity
284      */
285     public int capacity() {
286         return this.buffer.length;
287     }
288 
289     /**
290      * Returns the length of the buffer (char count).
291      *
292      * @return  the length of the buffer
293      */
294     public int length() {
295         return this.len;
296     }
297 
298     /**
299      * Ensures that the capacity is at least equal to the specified minimum.
300      * If the current capacity is less than the argument, then a new internal
301      * array is allocated with greater capacity. If the <code>required</code>
302      * argument is non-positive, this method takes no action.
303      *
304      * @param   required   the minimum required capacity.
305      */
306     public void ensureCapacity(int required) {
307         if (required <= 0) {
308             return;
309         }
310         int available = this.buffer.length - this.len;
311         if (required > available) {
312             expand(this.len + required);
313         }
314     }
315 
316     /**
317      * Sets the length of the buffer. The new length value is expected to be
318      * less than the current capacity and greater than or equal to
319      * <code>0</code>.
320      *
321      * @param      len   the new length
322      * @throws     IndexOutOfBoundsException  if the
323      *               <code>len</code> argument is greater than the current
324      *               capacity of the buffer or less than <code>0</code>.
325      */
326     public void setLength(int len) {
327         if (len < 0 || len > this.buffer.length) {
328             throw new IndexOutOfBoundsException("len: "+len+" < 0 or > buffer len: "+this.buffer.length);
329         }
330         this.len = len;
331     }
332 
333     /**
334      * Returns <code>true</code> if this buffer is empty, that is, its
335      * {@link #length()} is equal to <code>0</code>.
336      * @return <code>true</code> if this buffer is empty, <code>false</code>
337      *   otherwise.
338      */
339     public boolean isEmpty() {
340         return this.len == 0;
341     }
342 
343     /**
344      * Returns <code>true</code> if this buffer is full, that is, its
345      * {@link #length()} is equal to its {@link #capacity()}.
346      * @return <code>true</code> if this buffer is full, <code>false</code>
347      *   otherwise.
348      */
349     public boolean isFull() {
350         return this.len == this.buffer.length;
351     }
352 
353     /**
354      * Returns the index within this buffer of the first occurrence of the
355      * specified character, starting the search at the specified
356      * <code>beginIndex</code> and finishing at <code>endIndex</code>.
357      * If no such character occurs in this buffer within the specified bounds,
358      * <code>-1</code> is returned.
359      * <p>
360      * There is no restriction on the value of <code>beginIndex</code> and
361      * <code>endIndex</code>. If <code>beginIndex</code> is negative,
362      * it has the same effect as if it were zero. If <code>endIndex</code> is
363      * greater than {@link #length()}, it has the same effect as if it were
364      * {@link #length()}. If the <code>beginIndex</code> is greater than
365      * the <code>endIndex</code>, <code>-1</code> is returned.
366      *
367      * @param   ch          the char to search for.
368      * @param   beginIndex   the index to start the search from.
369      * @param   endIndex   the index to finish the search at.
370      * @return  the index of the first occurrence of the character in the buffer
371      *   within the given bounds, or <code>-1</code> if the character does
372      *   not occur.
373      */
374     public int indexOf(int ch, int beginIndex, int endIndex) {
375         if (beginIndex < 0) {
376             beginIndex = 0;
377         }
378         if (endIndex > this.len) {
379             endIndex = this.len;
380         }
381         if (beginIndex > endIndex) {
382             return -1;
383         }
384         for (int i = beginIndex; i < endIndex; i++) {
385             if (this.buffer[i] == ch) {
386                 return i;
387             }
388         }
389         return -1;
390     }
391 
392     /**
393      * Returns the index within this buffer of the first occurrence of the
394      * specified character, starting the search at <code>0</code> and finishing
395      * at {@link #length()}. If no such character occurs in this buffer within
396      * those bounds, <code>-1</code> is returned.
397      *
398      * @param   ch          the char to search for.
399      * @return  the index of the first occurrence of the character in the
400      *   buffer, or <code>-1</code> if the character does not occur.
401      */
402     public int indexOf(int ch) {
403         return indexOf(ch, 0, this.len);
404     }
405 
406     /**
407      * Returns a substring of this buffer. The substring begins at the specified
408      * <code>beginIndex</code> and extends to the character at index
409      * <code>endIndex - 1</code>.
410      *
411      * @param      beginIndex   the beginning index, inclusive.
412      * @param      endIndex     the ending index, exclusive.
413      * @return     the specified substring.
414      * @exception  StringIndexOutOfBoundsException  if the
415      *             <code>beginIndex</code> is negative, or
416      *             <code>endIndex</code> is larger than the length of this
417      *             buffer, or <code>beginIndex</code> is larger than
418      *             <code>endIndex</code>.
419      */
420     public String substring(int beginIndex, int endIndex) {
421         return new String(this.buffer, beginIndex, endIndex - beginIndex);
422     }
423 
424     /**
425      * Returns a substring of this buffer with leading and trailing whitespace
426      * omitted. The substring begins with the first non-whitespace character
427      * from <code>beginIndex</code> and extends to the last
428      * non-whitespace character with the index lesser than
429      * <code>endIndex</code>.
430      *
431      * @param      beginIndex   the beginning index, inclusive.
432      * @param      endIndex     the ending index, exclusive.
433      * @return     the specified substring.
434      * @exception  IndexOutOfBoundsException  if the
435      *             <code>beginIndex</code> is negative, or
436      *             <code>endIndex</code> is larger than the length of this
437      *             buffer, or <code>beginIndex</code> is larger than
438      *             <code>endIndex</code>.
439      */
440     public String substringTrimmed(int beginIndex, int endIndex) {
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
460     public String toString() {
461         return new String(this.buffer, 0, this.len);
462     }
463 
464 }