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