001/*
002 * ====================================================================
003 * Licensed to the Apache Software Foundation (ASF) under one
004 * or more contributor license agreements.  See the NOTICE file
005 * distributed with this work for additional information
006 * regarding copyright ownership.  The ASF licenses this file
007 * to you under the Apache License, Version 2.0 (the
008 * "License"); you may not use this file except in compliance
009 * with the License.  You may obtain a copy of the License at
010 *
011 *   http://www.apache.org/licenses/LICENSE-2.0
012 *
013 * Unless required by applicable law or agreed to in writing,
014 * software distributed under the License is distributed on an
015 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
016 * KIND, either express or implied.  See the License for the
017 * specific language governing permissions and limitations
018 * under the License.
019 * ====================================================================
020 *
021 * This software consists of voluntary contributions made by many
022 * individuals on behalf of the Apache Software Foundation.  For more
023 * information on the Apache Software Foundation, please see
024 * <http://www.apache.org/>.
025 *
026 */
027
028package org.apache.http.util;
029
030import java.io.Serializable;
031import java.nio.CharBuffer;
032
033import org.apache.http.protocol.HTTP;
034
035/**
036 * A resizable char array.
037 *
038 * @since 4.0
039 */
040public final class CharArrayBuffer implements CharSequence, Serializable {
041
042    private static final long serialVersionUID = -6208952725094867135L;
043
044    private char[] buffer;
045    private int len;
046
047    /**
048     * Creates an instance of {@link CharArrayBuffer} with the given initial
049     * capacity.
050     *
051     * @param capacity the capacity
052     */
053    public CharArrayBuffer(final int capacity) {
054        super();
055        Args.notNegative(capacity, "Buffer capacity");
056        this.buffer = new char[capacity];
057    }
058
059    private void expand(final int newlen) {
060        final char newbuffer[] = new char[Math.max(this.buffer.length << 1, newlen)];
061        System.arraycopy(this.buffer, 0, newbuffer, 0, this.len);
062        this.buffer = newbuffer;
063    }
064
065    /**
066     * Appends {@code len} chars to this buffer from the given source
067     * array starting at index {@code off}. The capacity of the buffer
068     * is increased, if necessary, to accommodate all {@code len} chars.
069     *
070     * @param   b        the chars to be appended.
071     * @param   off      the index of the first char to append.
072     * @param   len      the number of chars to append.
073     * @throws IndexOutOfBoundsException if {@code off} is out of
074     * range, {@code len} is negative, or
075     * {@code off} + {@code len} is out of range.
076     */
077    public void append(final char[] b, final int off, final int len) {
078        if (b == null) {
079            return;
080        }
081        if ((off < 0) || (off > b.length) || (len < 0) ||
082                ((off + len) < 0) || ((off + len) > b.length)) {
083            throw new IndexOutOfBoundsException("off: "+off+" len: "+len+" b.length: "+b.length);
084        }
085        if (len == 0) {
086            return;
087        }
088        final int newlen = this.len + len;
089        if (newlen > this.buffer.length) {
090            expand(newlen);
091        }
092        System.arraycopy(b, off, this.buffer, this.len, len);
093        this.len = newlen;
094    }
095
096    /**
097     * Appends chars of the given string to this buffer. The capacity of the
098     * buffer is increased, if necessary, to accommodate all chars.
099     *
100     * @param str    the string.
101     */
102    public void append(final String str) {
103        final String s = str != null ? str : "null";
104        final int strlen = s.length();
105        final int newlen = this.len + strlen;
106        if (newlen > this.buffer.length) {
107            expand(newlen);
108        }
109        s.getChars(0, strlen, this.buffer, this.len);
110        this.len = newlen;
111    }
112
113    /**
114     * Appends {@code len} chars to this buffer from the given source
115     * buffer starting at index {@code off}. The capacity of the
116     * destination buffer is increased, if necessary, to accommodate all
117     * {@code len} chars.
118     *
119     * @param   b        the source buffer to be appended.
120     * @param   off      the index of the first char to append.
121     * @param   len      the number of chars to append.
122     * @throws IndexOutOfBoundsException if {@code off} is out of
123     * range, {@code len} is negative, or
124     * {@code off} + {@code len} is out of range.
125     */
126    public void append(final CharArrayBuffer b, final int off, final int len) {
127        if (b == null) {
128            return;
129        }
130        append(b.buffer, off, len);
131    }
132
133    /**
134     * Appends all chars to this buffer from the given source buffer starting
135     * at index {@code 0}. The capacity of the destination buffer is
136     * increased, if necessary, to accommodate all {@link #length()} chars.
137     *
138     * @param   b        the source buffer to be appended.
139     */
140    public void append(final CharArrayBuffer b) {
141        if (b == null) {
142            return;
143        }
144        append(b.buffer,0, b.len);
145    }
146
147    /**
148     * Appends {@code ch} char to this buffer. The capacity of the buffer
149     * is increased, if necessary, to accommodate the additional char.
150     *
151     * @param   ch        the char to be appended.
152     */
153    public void append(final char ch) {
154        final int newlen = this.len + 1;
155        if (newlen > this.buffer.length) {
156            expand(newlen);
157        }
158        this.buffer[this.len] = ch;
159        this.len = newlen;
160    }
161
162    /**
163     * Appends {@code len} bytes to this buffer from the given source
164     * array starting at index {@code off}. The capacity of the buffer
165     * is increased, if necessary, to accommodate all {@code len} bytes.
166     * <p>
167     * The bytes are converted to chars using simple cast.
168     *
169     * @param   b        the bytes to be appended.
170     * @param   off      the index of the first byte to append.
171     * @param   len      the number of bytes to append.
172     * @throws IndexOutOfBoundsException if {@code off} is out of
173     * range, {@code len} is negative, or
174     * {@code off} + {@code len} is out of range.
175     */
176    public void append(final byte[] b, final int off, final int len) {
177        if (b == null) {
178            return;
179        }
180        if ((off < 0) || (off > b.length) || (len < 0) ||
181                ((off + len) < 0) || ((off + len) > b.length)) {
182            throw new IndexOutOfBoundsException("off: "+off+" len: "+len+" b.length: "+b.length);
183        }
184        if (len == 0) {
185            return;
186        }
187        final int oldlen = this.len;
188        final int newlen = oldlen + len;
189        if (newlen > this.buffer.length) {
190            expand(newlen);
191        }
192        for (int i1 = off, i2 = oldlen; i2 < newlen; i1++, i2++) {
193            this.buffer[i2] = (char) (b[i1] & 0xff);
194        }
195        this.len = newlen;
196    }
197
198    /**
199     * Appends {@code len} bytes to this buffer from the given source
200     * array starting at index {@code off}. The capacity of the buffer
201     * is increased, if necessary, to accommodate all {@code len} bytes.
202     * <p>
203     * The bytes are converted to chars using simple cast.
204     *
205     * @param   b        the bytes to be appended.
206     * @param   off      the index of the first byte to append.
207     * @param   len      the number of bytes to append.
208     * @throws IndexOutOfBoundsException if {@code off} is out of
209     * range, {@code len} is negative, or
210     * {@code off} + {@code len} is out of range.
211     */
212    public void append(final ByteArrayBuffer b, final int off, final int len) {
213        if (b == null) {
214            return;
215        }
216        append(b.buffer(), off, len);
217    }
218
219    /**
220     * Appends chars of the textual representation of the given object to this
221     * buffer. The capacity of the buffer is increased, if necessary, to
222     * accommodate all chars.
223     *
224     * @param obj    the object.
225     */
226    public void append(final Object obj) {
227        append(String.valueOf(obj));
228    }
229
230    /**
231     * Clears content of the buffer. The underlying char array is not resized.
232     */
233    public void clear() {
234        this.len = 0;
235    }
236
237    /**
238     * Converts the content of this buffer to an array of chars.
239     *
240     * @return char array
241     */
242    public char[] toCharArray() {
243        final char[] b = new char[this.len];
244        if (this.len > 0) {
245            System.arraycopy(this.buffer, 0, b, 0, this.len);
246        }
247        return b;
248    }
249
250    /**
251     * Returns the {@code char} value in this buffer at the specified
252     * index. The index argument must be greater than or equal to
253     * {@code 0}, and less than the length of this buffer.
254     *
255     * @param      i   the index of the desired char value.
256     * @return     the char value at the specified index.
257     * @throws     IndexOutOfBoundsException  if {@code index} is
258     *             negative or greater than or equal to {@link #length()}.
259     */
260    @Override
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     */
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     */
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     */
290    @Override
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}
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}.
317     *
318     * @param      len   the new length
319     * @throws     IndexOutOfBoundsException  if the
320     *               {@code len} argument is greater than the current
321     *               capacity of the buffer or less than {@code 0}.
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} if this buffer is empty, that is, its
332     * {@link #length()} is equal to {@code 0}.
333     * @return {@code true} if this buffer is empty, {@code false}
334     *   otherwise.
335     */
336    public boolean isEmpty() {
337        return this.len == 0;
338    }
339
340    /**
341     * Returns {@code true} if this buffer is full, that is, its
342     * {@link #length()} is equal to its {@link #capacity()}.
343     * @return {@code true} if this buffer is full, {@code false}
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} and finishing at {@code endIndex}.
354     * If no such character occurs in this buffer within the specified bounds,
355     * {@code -1} is returned.
356     * <p>
357     * There is no restriction on the value of {@code beginIndex} and
358     * {@code endIndex}. If {@code beginIndex} is negative,
359     * it has the same effect as if it were zero. If {@code endIndex} is
360     * greater than {@link #length()}, it has the same effect as if it were
361     * {@link #length()}. If the {@code beginIndex} is greater than
362     * the {@code endIndex}, {@code -1} 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} 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} and finishing
394     * at {@link #length()}. If no such character occurs in this buffer within
395     * those bounds, {@code -1} 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} 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} and extends to the character at index
408     * {@code endIndex - 1}.
409     *
410     * @param      beginIndex   the beginning index, inclusive.
411     * @param      endIndex     the ending index, exclusive.
412     * @return     the specified substring.
413     * @throws  StringIndexOutOfBoundsException  if the
414     *             {@code beginIndex} is negative, or
415     *             {@code endIndex} is larger than the length of this
416     *             buffer, or {@code beginIndex} is larger than
417     *             {@code endIndex}.
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} and extends to the last
436     * non-whitespace character with the index lesser than
437     * {@code endIndex}.
438     *
439     * @param      beginIndex   the beginning index, inclusive.
440     * @param      endIndex     the ending index, exclusive.
441     * @return     the specified substring.
442     * @throws  IndexOutOfBoundsException  if the
443     *             {@code beginIndex} is negative, or
444     *             {@code endIndex} is larger than the length of this
445     *             buffer, or {@code beginIndex} is larger than
446     *             {@code endIndex}.
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}