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