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 com.foxinmy.weixin4j.util; 29 30 import java.io.Serializable; 31 import java.nio.CharBuffer; 32 33 import com.foxinmy.weixin4j.http.HTTP; 34 35 36 /** 37 * A resizable char array. 38 * 39 * @since 4.0 40 */ 41 public final class CharArrayBuffer implements CharSequence, 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(final int capacity) { 55 super(); 56 this.buffer = new char[capacity]; 57 } 58 59 private void expand(final int newlen) { 60 final char newbuffer[] = new char[Math.max(this.buffer.length << 1, newlen)]; 61 System.arraycopy(this.buffer, 0, newbuffer, 0, this.len); 62 this.buffer = newbuffer; 63 } 64 65 /** 66 * Appends {@code len} chars to this buffer from the given source 67 * array starting at index {@code off}. The capacity of the buffer 68 * is increased, if necessary, to accommodate all {@code len} chars. 69 * 70 * @param b the chars to be appended. 71 * @param off the index of the first char to append. 72 * @param len the number of chars to append. 73 * @throws IndexOutOfBoundsException if {@code off} is out of 74 * range, {@code len} is negative, or 75 * {@code off} + {@code len} is out of range. 76 */ 77 public void append(final char[] b, final int off, final int len) { 78 if (b == null) { 79 return; 80 } 81 if ((off < 0) || (off > b.length) || (len < 0) || 82 ((off + len) < 0) || ((off + len) > b.length)) { 83 throw new IndexOutOfBoundsException("off: "+off+" len: "+len+" b.length: "+b.length); 84 } 85 if (len == 0) { 86 return; 87 } 88 final int newlen = this.len + len; 89 if (newlen > this.buffer.length) { 90 expand(newlen); 91 } 92 System.arraycopy(b, off, this.buffer, this.len, len); 93 this.len = newlen; 94 } 95 96 /** 97 * Appends chars of the given string to this buffer. The capacity of the 98 * buffer is increased, if necessary, to accommodate all chars. 99 * 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 }