mirror of
https://github.com/tiennm99/tjahzi.git
synced 2026-10-14 00:17:02 +00:00
Re #37: Implemented resettable text buffer to replace TextBuilder from javolution.
This commit is contained in:
1 parent
883d3b220d
commit
3b5b07daa5
4 files changed
+297
-638
No files matched your search
@@ -1,7 +1,7 @@
|
||||
package pl.tkowalcz.tjahzi.http;
|
||||
|
||||
import io.netty.util.concurrent.FastThreadLocal;
|
||||
import pl.tkowalcz.tjahzi.javolution.text.TextBuilder;
|
||||
import pl.tkowalcz.tjahzi.utils.TextBuilder;
|
||||
|
||||
public class TextBuilders {
|
||||
|
||||
|
||||
@@ -1,637 +0,0 @@
|
||||
/*
|
||||
* Javolution - Java(TM) Solution for Real-Time and Embedded Systems
|
||||
* Copyright (C) 2012 - Javolution (http://javolution.org/)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software is
|
||||
* freely granted, provided that this notice is preserved.
|
||||
*/
|
||||
package pl.tkowalcz.tjahzi.javolution.text;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* <p> An {@link Appendable} text whose capacity expands
|
||||
* gently without incurring expensive resize/copy operations ever.</p>
|
||||
*
|
||||
* <p> This class is not intended for large documents manipulations.
|
||||
*
|
||||
* @author <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a>
|
||||
* @version 5.3, January 20, 2008
|
||||
*/
|
||||
public class TextBuilder implements Appendable, CharSequence, Serializable {
|
||||
|
||||
private static final long serialVersionUID = 0x600L; // Version.
|
||||
// We do a full resize (and copy) only when the capacity is less than C1.
|
||||
// For large collections, multi-dimensional arrays are employed.
|
||||
private static final int B0 = 5; // Initial capacity in bits.
|
||||
private static final int C0 = 1 << B0; // Initial capacity (32)
|
||||
private static final int B1 = 10; // Low array maximum capacity in bits.
|
||||
private static final int C1 = 1 << B1; // Low array maximum capacity (1024).
|
||||
private static final int M1 = C1 - 1; // Mask.
|
||||
// Resizes up to 1024 maximum (32, 64, 128, 256, 512, 1024).
|
||||
private char[] _low = new char[C0];
|
||||
// For larger capacity use multi-dimensional array.
|
||||
private char[][] _high = new char[1][];
|
||||
|
||||
/**
|
||||
* Holds the current length.
|
||||
*/
|
||||
private int _length;
|
||||
|
||||
/**
|
||||
* Holds current capacity.
|
||||
*/
|
||||
private int _capacity = C0;
|
||||
|
||||
/**
|
||||
* Creates a text builder of small initial capacity.
|
||||
*/
|
||||
public TextBuilder() {
|
||||
_high[0] = _low;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a text builder holding the specified <code>String</code>
|
||||
* (convenience method).
|
||||
*
|
||||
* @param str the initial string content of this text builder.
|
||||
*/
|
||||
public TextBuilder(String str) {
|
||||
this();
|
||||
append(str);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a text builder of specified initial capacity.
|
||||
* Unless the text length exceeds the specified capacity, operations
|
||||
* on this text builder will not allocate memory.
|
||||
*
|
||||
* @param capacity the initial capacity.
|
||||
*/
|
||||
public TextBuilder(int capacity) {
|
||||
this();
|
||||
while (capacity > _capacity) {
|
||||
increaseCapacity();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the length (character count) of this text builder.
|
||||
*
|
||||
* @return the number of characters (16-bits Unicode).
|
||||
*/
|
||||
public final int length() {
|
||||
return _length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the character at the specified index.
|
||||
*
|
||||
* @param index the index of the character.
|
||||
* @return the character at the specified index.
|
||||
* @throws IndexOutOfBoundsException if
|
||||
* <code>(index < 0) || (index >= this.length())</code>.
|
||||
*/
|
||||
public final char charAt(int index) {
|
||||
if (index >= _length)
|
||||
throw new IndexOutOfBoundsException();
|
||||
return index < C1 ? _low[index] : _high[index >> B1][index & M1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the character at the specified position.
|
||||
*
|
||||
* @param index the index of the character to modify.
|
||||
* @param c the new character.
|
||||
* @throws IndexOutOfBoundsException if <code>(index < 0) ||
|
||||
* (index >= this.length())</code>
|
||||
*/
|
||||
public final void setCharAt(int index, char c) {
|
||||
if ((index < 0) || (index >= _length))
|
||||
throw new IndexOutOfBoundsException();
|
||||
_high[index >> B1][index & M1] = c;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method equivalent to {@link #setLength(int, char)
|
||||
* setLength(newLength, '\u0000')}.
|
||||
*
|
||||
* @param newLength the new length of this builder.
|
||||
* @throws IndexOutOfBoundsException if <code>(newLength < 0)</code>
|
||||
*/
|
||||
public final void setLength(int newLength) {
|
||||
setLength(newLength, '\u0000');
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the length of this character builder.
|
||||
* If the length is greater than the current length; the
|
||||
* specified character is inserted.
|
||||
*
|
||||
* @param newLength the new length of this builder.
|
||||
* @param fillChar the character to be appended if required.
|
||||
* @throws IndexOutOfBoundsException if <code>(newLength < 0)</code>
|
||||
*/
|
||||
public final void setLength(int newLength, char fillChar) {
|
||||
if (newLength < 0)
|
||||
throw new IndexOutOfBoundsException();
|
||||
if (newLength <= _length)
|
||||
_length = newLength;
|
||||
else
|
||||
for (int i = _length; i++ < newLength;) {
|
||||
append(fillChar);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link java.lang.CharSequence} corresponding
|
||||
* to the character sequence between the specified indexes.
|
||||
*
|
||||
* @param start the index of the first character inclusive.
|
||||
* @param end the index of the last character exclusive.
|
||||
* @return a character sequence.
|
||||
* @throws IndexOutOfBoundsException if <code>(start < 0) || (end < 0) ||
|
||||
* (start > end) || (end > this.length())</code>
|
||||
*/
|
||||
public final java.lang.CharSequence subSequence(int start, int end) {
|
||||
if ((start < 0) || (end < 0) || (start > end) || (end > _length))
|
||||
throw new IndexOutOfBoundsException();
|
||||
return new String(_low, start, end - start);
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends the specified character.
|
||||
*
|
||||
* @param c the character to append.
|
||||
* @return <code>this</code>
|
||||
*/
|
||||
public final TextBuilder append(char c) {
|
||||
if (_length >= _capacity)
|
||||
increaseCapacity();
|
||||
_high[_length >> B1][_length & M1] = c;
|
||||
_length++;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends the specified character sequence. If the specified character
|
||||
* sequence is <code>null</code> this method is equivalent to
|
||||
* <code>append("null")</code>.
|
||||
*
|
||||
* @param csq the character sequence to append or <code>null</code>.
|
||||
* @return <code>this</code>
|
||||
*/
|
||||
public final TextBuilder append(CharSequence csq) {
|
||||
return (csq == null) ? append("null") : append(csq, 0, csq.length());
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a subsequence of the specified character sequence.
|
||||
* If the specified character sequence is <code>null</code> this method
|
||||
* is equivalent to <code>append("null")</code>.
|
||||
*
|
||||
* @param csq the character sequence to append or <code>null</code>.
|
||||
* @param start the index of the first character to append.
|
||||
* @param end the index after the last character to append.
|
||||
* @return <code>this</code>
|
||||
* @throws IndexOutOfBoundsException if <code>(start < 0) || (end < 0)
|
||||
* || (start > end) || (end > csq.length())</code>
|
||||
*/
|
||||
public final TextBuilder append(CharSequence csq, int start,
|
||||
int end) {
|
||||
if (csq == null)
|
||||
return append("null");
|
||||
if ((start < 0) || (end < 0) || (start > end) || (end > csq.length()))
|
||||
throw new IndexOutOfBoundsException();
|
||||
for (int i = start; i < end;) {
|
||||
append(csq.charAt(i++));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends the specified string to this text builder.
|
||||
* If the specified string is <code>null</code> this method
|
||||
* is equivalent to <code>append("null")</code>.
|
||||
*
|
||||
* @param str the string to append or <code>null</code>.
|
||||
* @return <code>this</code>
|
||||
*/
|
||||
public final TextBuilder append(String str) {
|
||||
return (str == null) ? append("null") : append(str, 0, str.length());
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a subsequence of the specified string.
|
||||
* If the specified character sequence is <code>null</code> this method
|
||||
* is equivalent to <code>append("null")</code>.
|
||||
*
|
||||
* @param str the string to append or <code>null</code>.
|
||||
* @param start the index of the first character to append.
|
||||
* @param end the index after the last character to append.
|
||||
* @return <code>this</code>
|
||||
* @throws IndexOutOfBoundsException if <code>(start < 0) || (end < 0)
|
||||
* || (start > end) || (end > str.length())</code>
|
||||
*/
|
||||
public final TextBuilder append(String str, int start, int end) {
|
||||
if (str == null)
|
||||
return append("null");
|
||||
if ((start < 0) || (end < 0) || (start > end) || (end > str.length()))
|
||||
throw new IndexOutOfBoundsException("start: " + start + ", end: "
|
||||
+ end + ", str.length(): " + str.length());
|
||||
int newLength = _length + end - start;
|
||||
while (_capacity < newLength) {
|
||||
increaseCapacity();
|
||||
}
|
||||
for (int i = start, j = _length; i < end;) {
|
||||
char[] chars = _high[j >> B1];
|
||||
int dstBegin = j & M1;
|
||||
int inc = Math.min(C1 - dstBegin, end - i);
|
||||
str.getChars(i, (i += inc), chars, dstBegin);
|
||||
j += inc;
|
||||
}
|
||||
_length = newLength;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends the characters from the char array argument.
|
||||
*
|
||||
* @param chars the character array source.
|
||||
* @return <code>this</code>
|
||||
*/
|
||||
public final TextBuilder append(char chars[]) {
|
||||
append(chars, 0, chars.length);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends the characters from a subarray of the char array argument.
|
||||
*
|
||||
* @param chars the character array source.
|
||||
* @param offset the index of the first character to append.
|
||||
* @param length the number of character to append.
|
||||
* @return <code>this</code>
|
||||
* @throws IndexOutOfBoundsException if <code>(offset < 0) ||
|
||||
* (length < 0) || ((offset + length) > chars.length)</code>
|
||||
*/
|
||||
public final TextBuilder append(char chars[], int offset, int length) {
|
||||
final int end = offset + length;
|
||||
if ((offset < 0) || (length < 0) || (end > chars.length))
|
||||
throw new IndexOutOfBoundsException();
|
||||
int newLength = _length + length;
|
||||
while (_capacity < newLength) {
|
||||
increaseCapacity();
|
||||
}
|
||||
for (int i = offset, j = _length; i < end;) {
|
||||
char[] dstChars = _high[j >> B1];
|
||||
int dstBegin = j & M1;
|
||||
int inc = Math.min(C1 - dstBegin, end - i);
|
||||
System.arraycopy(chars, i, dstChars, dstBegin, inc);
|
||||
i += inc;
|
||||
j += inc;
|
||||
}
|
||||
_length = newLength;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends the textual representation of the specified <code>boolean</code>
|
||||
* argument.
|
||||
*
|
||||
* @param b the <code>boolean</code> to format.
|
||||
* @return <code>this</code>
|
||||
*/
|
||||
public final TextBuilder append(boolean b) {
|
||||
return b ? append("true") : append("false");
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends the decimal representation of the specified <code>int</code>
|
||||
* argument.
|
||||
*
|
||||
* @param i the <code>int</code> to format.
|
||||
* @return <code>this</code>
|
||||
*/
|
||||
public final TextBuilder append(int i) {
|
||||
if (i <= 0) {
|
||||
if (i == 0)
|
||||
return append("0");
|
||||
if (i == Integer.MIN_VALUE) // Negation would overflow.
|
||||
return append("-2147483648");
|
||||
append('-');
|
||||
i = -i;
|
||||
}
|
||||
int digits = digitLength(i);
|
||||
if (_capacity < _length + digits)
|
||||
increaseCapacity();
|
||||
_length += digits;
|
||||
for (int index = _length - 1;; index--) {
|
||||
int j = i / 10;
|
||||
_high[index >> B1][index & M1] = (char) ('0' + i - (j * 10));
|
||||
if (j == 0)
|
||||
return this;
|
||||
i = j;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends the radix representation of the specified <code>int</code>
|
||||
* argument.
|
||||
*
|
||||
* @param i the <code>int</code> to format.
|
||||
* @param radix the radix (e.g. <code>16</code> for hexadecimal).
|
||||
* @return <code>this</code>
|
||||
*/
|
||||
public final TextBuilder append(int i, int radix) {
|
||||
if (radix == 10)
|
||||
return append(i); // Faster.
|
||||
if (radix < 2 || radix > 36)
|
||||
throw new IllegalArgumentException("radix: " + radix);
|
||||
if (i < 0) {
|
||||
append('-');
|
||||
if (i == Integer.MIN_VALUE) { // Negative would overflow.
|
||||
appendPositive(-(i / radix), radix);
|
||||
return (TextBuilder) append(DIGIT_TO_CHAR[-(i % radix)]);
|
||||
}
|
||||
i = -i;
|
||||
}
|
||||
appendPositive(i, radix);
|
||||
return this;
|
||||
}
|
||||
|
||||
private void appendPositive(int l1, int radix) {
|
||||
if (l1 >= radix) {
|
||||
int l2 = l1 / radix;
|
||||
// appendPositive(l2, radix);
|
||||
if (l2 >= radix) {
|
||||
int l3 = l2 / radix;
|
||||
// appendPositive(l3, radix);
|
||||
if (l3 >= radix) {
|
||||
int l4 = l3 / radix;
|
||||
appendPositive(l4, radix);
|
||||
append(DIGIT_TO_CHAR[l3 - (l4 * radix)]);
|
||||
} else
|
||||
append(DIGIT_TO_CHAR[l3]);
|
||||
append(DIGIT_TO_CHAR[l2 - (l3 * radix)]);
|
||||
} else
|
||||
append(DIGIT_TO_CHAR[l2]);
|
||||
append(DIGIT_TO_CHAR[l1 - (l2 * radix)]);
|
||||
} else
|
||||
append(DIGIT_TO_CHAR[l1]);
|
||||
}
|
||||
|
||||
private final static char[] DIGIT_TO_CHAR = { '0', '1', '2', '3', '4', '5',
|
||||
'6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i',
|
||||
'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
|
||||
'w', 'x', 'y', 'z' };
|
||||
|
||||
/**
|
||||
* Appends the decimal representation of the specified <code>long</code>
|
||||
* argument.
|
||||
*
|
||||
* @param l the <code>long</code> to format.
|
||||
* @return <code>this</code>
|
||||
*/
|
||||
public final TextBuilder append(long l) {
|
||||
if (l <= 0) {
|
||||
if (l == 0)
|
||||
return append("0");
|
||||
if (l == Long.MIN_VALUE) // Negation would overflow.
|
||||
return append("-9223372036854775808");
|
||||
append('-');
|
||||
l = -l;
|
||||
}
|
||||
if (l <= Integer.MAX_VALUE)
|
||||
return append((int) l);
|
||||
append(l / 1000000000);
|
||||
int i = (int) (l % 1000000000);
|
||||
int digits = digitLength(i);
|
||||
append("000000000", 0, 9 - digits);
|
||||
return append(i);
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends the radix representation of the specified <code>long</code>
|
||||
* argument.
|
||||
*
|
||||
* @param l the <code>long</code> to format.
|
||||
* @param radix the radix (e.g. <code>16</code> for hexadecimal).
|
||||
* @return <code>this</code>
|
||||
*/
|
||||
public final TextBuilder append(long l, int radix) {
|
||||
if (radix == 10)
|
||||
return append(l); // Faster.
|
||||
if (radix < 2 || radix > 36)
|
||||
throw new IllegalArgumentException("radix: " + radix);
|
||||
if (l < 0) {
|
||||
append('-');
|
||||
if (l == Long.MIN_VALUE) { // Negative would overflow.
|
||||
appendPositive(-(l / radix), radix);
|
||||
return (TextBuilder) append(DIGIT_TO_CHAR[(int) -(l % radix)]);
|
||||
}
|
||||
l = -l;
|
||||
}
|
||||
appendPositive(l, radix);
|
||||
return this;
|
||||
}
|
||||
|
||||
private void appendPositive(long l1, int radix) {
|
||||
if (l1 >= radix) {
|
||||
long l2 = l1 / radix;
|
||||
// appendPositive(l2, radix);
|
||||
if (l2 >= radix) {
|
||||
long l3 = l2 / radix;
|
||||
// appendPositive(l3, radix);
|
||||
if (l3 >= radix) {
|
||||
long l4 = l3 / radix;
|
||||
appendPositive(l4, radix);
|
||||
append(DIGIT_TO_CHAR[(int) (l3 - (l4 * radix))]);
|
||||
} else
|
||||
append(DIGIT_TO_CHAR[(int) l3]);
|
||||
append(DIGIT_TO_CHAR[(int) (l2 - (l3 * radix))]);
|
||||
} else
|
||||
append(DIGIT_TO_CHAR[(int) l2]);
|
||||
append(DIGIT_TO_CHAR[(int) (l1 - (l2 * radix))]);
|
||||
} else
|
||||
append(DIGIT_TO_CHAR[(int) l1]);
|
||||
}
|
||||
|
||||
private static final long[] POW10_LONG = new long[] { 1L, 10L, 100L, 1000L,
|
||||
10000L, 100000L, 1000000L, 10000000L, 100000000L, 1000000000L,
|
||||
10000000000L, 100000000000L, 1000000000000L, 10000000000000L,
|
||||
100000000000000L, 1000000000000000L, 10000000000000000L,
|
||||
100000000000000000L, 1000000000000000000L };
|
||||
|
||||
/**
|
||||
* Inserts the specified character sequence at the specified location.
|
||||
*
|
||||
* @param index the insertion position.
|
||||
* @param csq the character sequence being inserted.
|
||||
* @return <code>this</code>
|
||||
* @throws IndexOutOfBoundsException if <code>(index < 0) ||
|
||||
* (index > this.length())</code>
|
||||
*/
|
||||
public final TextBuilder insert(int index, java.lang.CharSequence csq) {
|
||||
if ((index < 0) || (index > _length))
|
||||
throw new IndexOutOfBoundsException("index: " + index);
|
||||
final int shift = csq.length();
|
||||
int newLength = _length + shift;
|
||||
while (newLength >= _capacity) {
|
||||
increaseCapacity();
|
||||
}
|
||||
_length = newLength;
|
||||
for (int i = _length - shift; --i >= index;) {
|
||||
this.setCharAt(i + shift, this.charAt(i));
|
||||
}
|
||||
for (int i = csq.length(); --i >= 0;) {
|
||||
this.setCharAt(index + i, csq.charAt(i));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all the characters of this text builder
|
||||
* (equivalent to <code>this.delete(start, this.length())</code>).
|
||||
*
|
||||
* @return <code>this.delete(0, this.length())</code>
|
||||
*/
|
||||
public final TextBuilder clear() {
|
||||
_length = 0;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the characters between the specified indices.
|
||||
*
|
||||
* @param start the beginning index, inclusive.
|
||||
* @param end the ending index, exclusive.
|
||||
* @return <code>this</code>
|
||||
* @throws IndexOutOfBoundsException if <code>(start < 0) || (end < 0)
|
||||
* || (start > end) || (end > this.length())</code>
|
||||
*/
|
||||
public final TextBuilder delete(int start, int end) {
|
||||
if ((start < 0) || (end < 0) || (start > end) || (end > this.length()))
|
||||
throw new IndexOutOfBoundsException();
|
||||
for (int i = end, j = start; i < _length;) {
|
||||
this.setCharAt(j++, this.charAt(i++));
|
||||
}
|
||||
_length -= end - start;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverses this character sequence.
|
||||
*
|
||||
* @return <code>this</code>
|
||||
*/
|
||||
public final TextBuilder reverse() {
|
||||
final int n = _length - 1;
|
||||
for (int j = (n - 1) >> 1; j >= 0;) {
|
||||
char c = charAt(j);
|
||||
setCharAt(j, charAt(n - j));
|
||||
setCharAt(n - j--, c);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the hash code for this text builder.
|
||||
*
|
||||
* @return the hash code value.
|
||||
*/
|
||||
@Override
|
||||
public final int hashCode() {
|
||||
int h = 0;
|
||||
for (int i = 0; i < _length;) {
|
||||
h = 31 * h + charAt(i++);
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares this text builder against the specified object for equality.
|
||||
* Returns <code>true</code> if the specified object is a text builder
|
||||
* having the same character content.
|
||||
*
|
||||
* @param obj the object to compare with or <code>null</code>.
|
||||
* @return <code>true</code> if that is a text builder with the same
|
||||
* character content as this text; <code>false</code> otherwise.
|
||||
*/
|
||||
@Override
|
||||
public final boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (!(obj instanceof TextBuilder))
|
||||
return false;
|
||||
TextBuilder that = (TextBuilder) obj;
|
||||
if (this._length != that._length)
|
||||
return false;
|
||||
for (int i = 0; i < _length;) {
|
||||
if (this.charAt(i) != that.charAt(i++))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this text builder has the same character content as the
|
||||
* specified character sequence.
|
||||
*
|
||||
* @param csq the character sequence to compare with.
|
||||
* @return <code>true</code> if the specified character sequence has the
|
||||
* same character content as this text; <code>false</code> otherwise.
|
||||
*/
|
||||
public final boolean contentEquals(java.lang.CharSequence csq) {
|
||||
if (csq.length() != _length)
|
||||
return false;
|
||||
for (int i = 0; i < _length;) {
|
||||
char c = _high[i >> B1][i & M1];
|
||||
if (csq.charAt(i++) != c)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Increases this text builder capacity.
|
||||
*/
|
||||
private void increaseCapacity() {
|
||||
if (_capacity < C1) { // For small capacity, resize.
|
||||
_capacity <<= 1;
|
||||
char[] tmp = new char[_capacity];
|
||||
System.arraycopy(_low, 0, tmp, 0, _length);
|
||||
_low = tmp;
|
||||
_high[0] = tmp;
|
||||
} else { // Add a new low block of 1024 elements.
|
||||
int j = _capacity >> B1;
|
||||
if (j >= _high.length) { // Resizes _high.
|
||||
char[][] tmp = new char[_high.length * 2][];
|
||||
System.arraycopy(_high, 0, tmp, 0, _high.length);
|
||||
_high = tmp;
|
||||
}
|
||||
_high[j] = new char[C1];
|
||||
_capacity += C1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of digits of the decimal representation of the
|
||||
* specified <code>int</code> value, excluding the sign character if any.
|
||||
*
|
||||
* @param i the <code>int</code> value for which the digit length is returned.
|
||||
* @return <code>String.valueOf(i).length()</code> for zero or positive values;
|
||||
* <code>String.valueOf(i).length() - 1</code> for negative values.
|
||||
*/
|
||||
private static int digitLength(int i) {
|
||||
if (i >= 0)
|
||||
return (i >= 100000) ? (i >= 10000000) ? (i >= 1000000000) ? 10
|
||||
: (i >= 100000000) ? 9 : 8 : (i >= 1000000) ? 7 : 6
|
||||
: (i >= 100) ? (i >= 10000) ? 5 : (i >= 1000) ? 4 : 3
|
||||
: (i >= 10) ? 2 : 1;
|
||||
if (i == Integer.MIN_VALUE)
|
||||
return 10; // "2147483648".length()
|
||||
return digitLength(-i); // No overflow possible.
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package pl.tkowalcz.tjahzi.utils;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
|
||||
public class TextBuilder implements Appendable, CharSequence {
|
||||
|
||||
private static final int INITIAL_SIZE = 8;
|
||||
private static final int CAPACITY_INCREMENT = 64;
|
||||
|
||||
private char[] chars = new char[INITIAL_SIZE];
|
||||
private int length;
|
||||
|
||||
public TextBuilder clear() {
|
||||
length = 0;
|
||||
return this;
|
||||
}
|
||||
|
||||
public TextBuilder append(CharSequence string) {
|
||||
ensureCapacity(string.length());
|
||||
|
||||
for (int i = 0; i < string.length(); i++) {
|
||||
chars[length++] = string.charAt(i);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextBuilder append(CharSequence sequence, int start, int end) {
|
||||
Objects.checkFromToIndex(start, end, sequence.length());
|
||||
ensureCapacity(sequence.length());
|
||||
|
||||
for (int i = start; i < end; i++) {
|
||||
chars[length++] = sequence.charAt(i);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextBuilder append(char c) {
|
||||
ensureCapacity(1);
|
||||
chars[length++] = c;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int length() {
|
||||
return length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public char charAt(int index) {
|
||||
Objects.checkIndex(index, length);
|
||||
return chars[index];
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence subSequence(int start, int end) {
|
||||
Objects.checkFromToIndex(start, end, length);
|
||||
return new String(chars, start, end - start);
|
||||
}
|
||||
|
||||
public TextBuilder setCharAt(int index, char c) {
|
||||
Objects.checkIndex(index, length);
|
||||
chars[index] = c;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new String(chars, 0, length);
|
||||
}
|
||||
|
||||
private void ensureCapacity(int requiredCapacity) {
|
||||
if (length + requiredCapacity >= chars.length) {
|
||||
int missingCapacity = length + requiredCapacity - chars.length;
|
||||
int expandedSize = chars.length + Math.max(CAPACITY_INCREMENT, missingCapacity);
|
||||
chars = Arrays.copyOf(chars, expandedSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,212 @@
|
||||
package pl.tkowalcz.tjahzi.utils;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
class TextBuilderTest {
|
||||
|
||||
@Test
|
||||
void shouldTrackLength() {
|
||||
// Given
|
||||
TextBuilder textBuilder = new TextBuilder();
|
||||
|
||||
// When
|
||||
textBuilder.append("abcd");
|
||||
textBuilder.append('f');
|
||||
textBuilder.append("1234567890", 3, 7);
|
||||
textBuilder.setCharAt(3, 'n');
|
||||
|
||||
// Then
|
||||
assertThat(textBuilder.length()).isEqualTo(9);
|
||||
assertThat(textBuilder.toString()).isEqualTo("abcnf4567");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldClearBuilder() {
|
||||
// Given
|
||||
TextBuilder textBuilder = new TextBuilder();
|
||||
textBuilder.append("abcd");
|
||||
|
||||
// When
|
||||
textBuilder.clear();
|
||||
|
||||
// Then
|
||||
assertThat(textBuilder.length()).isZero();
|
||||
assertThat(textBuilder.toString()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldSetCharAtIndex() {
|
||||
// Given
|
||||
TextBuilder textBuilder = new TextBuilder();
|
||||
textBuilder.append("1234567890");
|
||||
|
||||
// When
|
||||
textBuilder.setCharAt(3, 'f');
|
||||
|
||||
// Then
|
||||
assertThat(textBuilder.length()).isEqualTo(10);
|
||||
assertThat(textBuilder.toString()).isEqualTo("123f567890");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotAllowSettingCharAtWrongIndex() {
|
||||
// Given
|
||||
TextBuilder textBuilder = new TextBuilder();
|
||||
textBuilder.append("1234567890");
|
||||
|
||||
// When & Then
|
||||
assertThrows(
|
||||
IndexOutOfBoundsException.class,
|
||||
() -> textBuilder.setCharAt(-10, 'f')
|
||||
);
|
||||
|
||||
assertThrows(
|
||||
IndexOutOfBoundsException.class,
|
||||
() -> textBuilder.setCharAt(11, 'f')
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnCharAt() {
|
||||
// Given
|
||||
TextBuilder textBuilder = new TextBuilder();
|
||||
textBuilder.append("1234567890");
|
||||
|
||||
// When
|
||||
char actual = textBuilder.charAt(6);
|
||||
|
||||
// Then
|
||||
assertThat(actual).isEqualTo('7');
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotAllowReturningCharAtWrongIndex() {
|
||||
// Given
|
||||
TextBuilder textBuilder = new TextBuilder();
|
||||
textBuilder.append("1234567890");
|
||||
|
||||
// When & Then
|
||||
assertThrows(
|
||||
IndexOutOfBoundsException.class,
|
||||
() -> textBuilder.charAt(-10)
|
||||
);
|
||||
|
||||
assertThrows(
|
||||
IndexOutOfBoundsException.class,
|
||||
() -> textBuilder.charAt(11)
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnSubstring() {
|
||||
// Given
|
||||
TextBuilder textBuilder = new TextBuilder();
|
||||
textBuilder.append("1234567890");
|
||||
|
||||
// When
|
||||
CharSequence actual = textBuilder.subSequence(3, 8);
|
||||
|
||||
// Then
|
||||
assertThat(actual).isEqualTo("45678");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldVerifySubstringArguments() {
|
||||
// Given
|
||||
TextBuilder textBuilder = new TextBuilder();
|
||||
textBuilder.append("1234567890");
|
||||
|
||||
// When & Then
|
||||
assertThrows(
|
||||
IndexOutOfBoundsException.class,
|
||||
() -> textBuilder.subSequence(-1, 8)
|
||||
);
|
||||
|
||||
assertThrows(
|
||||
IndexOutOfBoundsException.class,
|
||||
() -> textBuilder.subSequence(50, 8)
|
||||
);
|
||||
|
||||
assertThrows(
|
||||
IndexOutOfBoundsException.class,
|
||||
() -> textBuilder.subSequence(3, -1)
|
||||
);
|
||||
|
||||
assertThrows(
|
||||
IndexOutOfBoundsException.class,
|
||||
() -> textBuilder.subSequence(3, 120)
|
||||
);
|
||||
|
||||
assertThrows(
|
||||
IndexOutOfBoundsException.class,
|
||||
() -> textBuilder.subSequence(6, 5)
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnStringRepresentation() {
|
||||
// Given
|
||||
TextBuilder textBuilder = new TextBuilder();
|
||||
textBuilder.append("1234567890");
|
||||
|
||||
// When
|
||||
CharSequence actual = textBuilder.toString();
|
||||
|
||||
// Then
|
||||
assertThat(actual).isEqualTo("1234567890");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldConstructJSON() {
|
||||
// Given
|
||||
TextBuilder textBuilder = new TextBuilder();
|
||||
|
||||
// When
|
||||
textBuilder.append("{ ");
|
||||
textBuilder.append("aaaa=bbb");
|
||||
textBuilder.append(',');
|
||||
textBuilder.append("foo=bar");
|
||||
textBuilder.append(',');
|
||||
textBuilder.setCharAt(textBuilder.length() - 1, '}');
|
||||
|
||||
// Then
|
||||
CharSequence actual = textBuilder.toString();
|
||||
assertThat(actual).isEqualTo("{ aaaa=bbb,foo=bar}");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldConstructEmptyJSON() {
|
||||
// Given
|
||||
TextBuilder textBuilder = new TextBuilder();
|
||||
|
||||
// When
|
||||
textBuilder.append("{ ");
|
||||
textBuilder.setCharAt(textBuilder.length() - 1, '}');
|
||||
|
||||
// Then
|
||||
CharSequence actual = textBuilder.toString();
|
||||
assertThat(actual).isEqualTo("{}");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldGrowBackendStorage() {
|
||||
// Given
|
||||
String expected = StringUtils.repeat("abcd", 121);
|
||||
TextBuilder textBuilder = new TextBuilder();
|
||||
|
||||
// When
|
||||
int i;
|
||||
for (i = 0; i < 121; i++) {
|
||||
textBuilder.append('a');
|
||||
textBuilder.append("bcd");
|
||||
}
|
||||
|
||||
// Then
|
||||
assertThat(textBuilder.length()).isEqualTo(expected.length());
|
||||
assertThat(textBuilder.toString()).isEqualTo(expected);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user