|
|
@@ -0,0 +1,541 @@
|
|
|
+/*
|
|
|
+ * @author Ranides Atterwim <ranides@gmail.com>
|
|
|
+ * @copyright Ranides Atterwim
|
|
|
+ * @license WTFPL
|
|
|
+ * @url http://ranides.net/projects/assira
|
|
|
+ */
|
|
|
+
|
|
|
+package net.ranides.assira.text;
|
|
|
+
|
|
|
+import java.io.Reader;
|
|
|
+import java.io.Serializable;
|
|
|
+import java.io.Writer;
|
|
|
+import java.util.Iterator;
|
|
|
+import java.util.function.Function;
|
|
|
+import java.util.function.IntFunction;
|
|
|
+import net.ranides.assira.collection.arrays.ArrayUtils;
|
|
|
+import net.ranides.assira.collection.arrays.NativeArray;
|
|
|
+import net.ranides.assira.collection.arrays.NativeArrayUtils;
|
|
|
+
|
|
|
+/**
|
|
|
+ *
|
|
|
+ * @author Ranides Atterwim <ranides@gmail.com>
|
|
|
+ */
|
|
|
+@SuppressWarnings({
|
|
|
+ "PMD.TooManyPublicMethods",
|
|
|
+ "PMD.TooManyDifferentMethods"
|
|
|
+})
|
|
|
+public class StrBuffer implements Appendable, CharSequence, Serializable {
|
|
|
+
|
|
|
+ private static final long serialVersionUID = 1L;
|
|
|
+
|
|
|
+ protected static final char[] EMPTY = new char[0];
|
|
|
+
|
|
|
+ protected char[] buffer;
|
|
|
+
|
|
|
+ protected int size;
|
|
|
+
|
|
|
+ private final BuilderState options = new BuilderState();
|
|
|
+
|
|
|
+ public StrBuffer(int capacity) {
|
|
|
+ this.buffer = new char[capacity];
|
|
|
+ }
|
|
|
+
|
|
|
+ public StrBuilderOptions options() {
|
|
|
+ return options;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public int length() {
|
|
|
+ return size;
|
|
|
+ }
|
|
|
+
|
|
|
+ public int capacity() {
|
|
|
+ return buffer.length;
|
|
|
+ }
|
|
|
+
|
|
|
+ public StrBuffer resize(int length, char c) {
|
|
|
+ if (length < 0) {
|
|
|
+ throw new IllegalArgumentException("negative size: " + length);
|
|
|
+ }
|
|
|
+ if (length > size) {
|
|
|
+ throw new IllegalArgumentException(length + " is greater than " + capacity());
|
|
|
+ }
|
|
|
+ size = length;
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+
|
|
|
+ public StrBuffer clear() {
|
|
|
+ size = 0;
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public char charAt(int index) {
|
|
|
+ return buffer[index];
|
|
|
+ }
|
|
|
+
|
|
|
+ public StrBuffer setCharAt(int index, char value) {
|
|
|
+ buffer[index] = value;
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public CharSequence subSequence(int begin, int end) {
|
|
|
+ return StringUtils.wrap(buffer, begin, end);
|
|
|
+ }
|
|
|
+
|
|
|
+ public char[] getChars() {
|
|
|
+ return getChars(0, size);
|
|
|
+ }
|
|
|
+
|
|
|
+ public char[] getChars(int begin, int end) {
|
|
|
+ if (end == begin) {
|
|
|
+ return StrBuffer.EMPTY;
|
|
|
+ }
|
|
|
+ return ArrayUtils.slice(buffer, begin, end);
|
|
|
+ }
|
|
|
+
|
|
|
+ public char[] getChars(char[] target) {
|
|
|
+ return getChars(0, size, target, 0);
|
|
|
+ }
|
|
|
+
|
|
|
+ public char[] getChars(int begin, int end, char target[], int offset) {
|
|
|
+ System.arraycopy(buffer, begin, target, offset, end - begin);
|
|
|
+ return target;
|
|
|
+ }
|
|
|
+
|
|
|
+ public String getRightFragment(int length) {
|
|
|
+ return new String(buffer, size - length, length);
|
|
|
+ }
|
|
|
+
|
|
|
+ public String getLeftFragment(int length) {
|
|
|
+ return new String(buffer, 0, length);
|
|
|
+ }
|
|
|
+
|
|
|
+ public StrBuffer reverse() {
|
|
|
+ NativeArrayUtils.reverse(NativeArray.wrap(buffer), size);
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+
|
|
|
+ public StrBuffer open() {
|
|
|
+ return StrBuffer.this.append(options.reset().open());
|
|
|
+ }
|
|
|
+
|
|
|
+ public StrBuffer open(String open) {
|
|
|
+ options.open(open);
|
|
|
+ return open();
|
|
|
+ }
|
|
|
+
|
|
|
+ public StrBuffer open(String open, String close) {
|
|
|
+ options.open(open).close(close);
|
|
|
+ return open();
|
|
|
+ }
|
|
|
+
|
|
|
+ public StrBuffer open(String open, String close, String separator) {
|
|
|
+ options.open(open).close(close).separator(separator);
|
|
|
+ return open();
|
|
|
+ }
|
|
|
+
|
|
|
+ public StrBuffer close() {
|
|
|
+ return StrBuffer.this.append(options.close());
|
|
|
+ }
|
|
|
+
|
|
|
+ public StrBuffer item() {
|
|
|
+ if(0 != options.counter++) {
|
|
|
+ StrBuffer.this.append(options.separator());
|
|
|
+ }
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+
|
|
|
+ public StrBuffer endl() {
|
|
|
+ return StrBuffer.this.append(options.endl());
|
|
|
+ }
|
|
|
+
|
|
|
+ public StrBuffer append() {
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public StrBuffer append(CharSequence value) {
|
|
|
+ return append(value, 0, value.length());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public StrBuffer append(CharSequence value, int begin, int end) {
|
|
|
+ for(int i=begin; i<end; i++) {
|
|
|
+ buffer[size++] = value.charAt(i);
|
|
|
+ }
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+
|
|
|
+ public StrBuffer append(String value) {
|
|
|
+ return append(value, 0, value.length());
|
|
|
+ }
|
|
|
+
|
|
|
+ public StrBuffer append(String value, int begin, int end) {
|
|
|
+ int n = end - begin;
|
|
|
+ value.getChars(begin, end, buffer, size);
|
|
|
+ size += n;
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+
|
|
|
+ public StrBuffer append(StringBuffer value) {
|
|
|
+ return append(value, 0, value.length());
|
|
|
+ }
|
|
|
+
|
|
|
+ public StrBuffer append(StringBuffer value, int begin, int end) {
|
|
|
+ int n = end - begin;
|
|
|
+ value.getChars(begin, end, buffer, size);
|
|
|
+ size += n;
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+
|
|
|
+ public StrBuffer append(StringBuilder value) {
|
|
|
+ return append(value, 0, value.length());
|
|
|
+ }
|
|
|
+
|
|
|
+ public StrBuffer append(StringBuilder value, int begin, int end) {
|
|
|
+ int n = end - begin;
|
|
|
+ value.getChars(begin, end, buffer, size);
|
|
|
+ size += n;
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+
|
|
|
+ public StrBuffer append(char[] value) {
|
|
|
+ return append(value, 0, value.length);
|
|
|
+ }
|
|
|
+
|
|
|
+ public StrBuffer append(char[] value, int begin, int end) {
|
|
|
+ int n = end - begin;
|
|
|
+ System.arraycopy(value, begin, buffer, size, n);
|
|
|
+ size += n;
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public StrBuffer append(char c) {
|
|
|
+ buffer[size++] = c;
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+
|
|
|
+ public StrBuffer append(Object value) {
|
|
|
+ return append(value.toString());
|
|
|
+ }
|
|
|
+
|
|
|
+ public StrBuffer append(boolean value) {
|
|
|
+ return append(value ? "true" : "false");
|
|
|
+ }
|
|
|
+
|
|
|
+ public StrBuffer append(int value) {
|
|
|
+ return append(String.valueOf(value));
|
|
|
+ }
|
|
|
+
|
|
|
+ public StrBuffer append(long value) {
|
|
|
+ return append(String.valueOf(value));
|
|
|
+ }
|
|
|
+
|
|
|
+ public StrBuffer append(float value) {
|
|
|
+ return append(String.valueOf(value));
|
|
|
+ }
|
|
|
+
|
|
|
+ public StrBuffer append(double value) {
|
|
|
+ return append(String.valueOf(value));
|
|
|
+ }
|
|
|
+
|
|
|
+ public StrBuffer append(Iterable<?> values) {
|
|
|
+ return append(values.iterator(), Object::toString);
|
|
|
+ }
|
|
|
+
|
|
|
+ public <T> StrBuffer append(Iterable<? extends T> values, Function<T,String> function) {
|
|
|
+ return append(values.iterator(), function);
|
|
|
+ }
|
|
|
+
|
|
|
+ public <T> StrBuffer append(Iterator<? extends T> values) {
|
|
|
+ return append(values, Object::toString);
|
|
|
+ }
|
|
|
+
|
|
|
+ public <T> StrBuffer append(Iterator<? extends T> values, Function<T,String> function) {
|
|
|
+ if ( !values.hasNext() ) {
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+ String separator = options().separator();
|
|
|
+ for (;;) {
|
|
|
+ T item = values.next();
|
|
|
+ append(function.apply(item));
|
|
|
+ if ( !values.hasNext() ) {
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+ append(separator);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public <T> StrBuffer append(T[] values) {
|
|
|
+ return append(values, Object::toString);
|
|
|
+ }
|
|
|
+
|
|
|
+ public <T> StrBuffer append(T[] values, Function<T,String> function) {
|
|
|
+ if ( 0==values.length ) {
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+ String separator = options().separator();
|
|
|
+ append(function.apply(values[0]));
|
|
|
+ for (int i=1; i<values.length; i++) {
|
|
|
+ append(separator).append(function.apply(values[i]));
|
|
|
+ }
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+
|
|
|
+ public <T> StrBuffer append(NativeArray values) {
|
|
|
+ return append(values.size(), i -> String.valueOf(values.get(i)));
|
|
|
+ }
|
|
|
+
|
|
|
+ public <T> StrBuffer append(int size, IntFunction<String> function) {
|
|
|
+ if ( 0==size ) {
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+ String separator = options().separator();
|
|
|
+ append(function.apply(0));
|
|
|
+ for (int i=1; i<size; i++) {
|
|
|
+ append(separator).append(function.apply(i));
|
|
|
+ }
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public boolean equals(Object object) {
|
|
|
+ if (this == object) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ if (object instanceof StrBuffer) {
|
|
|
+ return equalsSB((StrBuffer)object);
|
|
|
+ }
|
|
|
+ if (object instanceof CharSequence) {
|
|
|
+ return equalsCS((CharSequence)object);
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ private boolean equalsSB(StrBuffer other) {
|
|
|
+ if (this.size != other.size) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ for(int i=0; i<size; i++) {
|
|
|
+ if(this.buffer[i] != other.buffer[i]) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ private boolean equalsCS(CharSequence other) {
|
|
|
+ if (this.size != other.length()) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ for(int i=0; i<size; i++) {
|
|
|
+ if(this.buffer[i] != other.charAt(i)) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public int hashCode() {
|
|
|
+ return NativeArray.wrap(buffer).hashCode(0, size);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public String toString() {
|
|
|
+ return new String(buffer, 0, size);
|
|
|
+ }
|
|
|
+
|
|
|
+ public StringBuilder toBuilder() {
|
|
|
+ return new StringBuilder(size).append(buffer, 0, size);
|
|
|
+ }
|
|
|
+
|
|
|
+ public Writer asWriter() {
|
|
|
+ return new SWriter();
|
|
|
+ }
|
|
|
+
|
|
|
+ public Reader asReader() {
|
|
|
+ return new SReader();
|
|
|
+ }
|
|
|
+
|
|
|
+ private final class SReader extends Reader {
|
|
|
+
|
|
|
+ private int pos;
|
|
|
+ private int mark;
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void close() {
|
|
|
+ // do nothing
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public int read() {
|
|
|
+ if (!ready()) {
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ return buffer[pos++];
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public int read(char target[], int offset, int length) {
|
|
|
+ if (pos >= length()) {
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ if (pos + length > length()) {
|
|
|
+ length = length() - pos;
|
|
|
+ }
|
|
|
+ getChars(pos, pos + length, target, offset);
|
|
|
+ pos += length;
|
|
|
+ return length;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public long skip(long n) {
|
|
|
+ if (pos + n > size) {
|
|
|
+ n = size - pos;
|
|
|
+ }
|
|
|
+ if (n < 0) {
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ pos += n;
|
|
|
+ return n;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public boolean ready() {
|
|
|
+ return pos < size;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public boolean markSupported() {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void mark(int readAheadLimit) {
|
|
|
+ mark = pos;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void reset() {
|
|
|
+ pos = mark;
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ private final class SWriter extends Writer {
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void close() {
|
|
|
+ // do nothing
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void flush() {
|
|
|
+ // do nothing
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void write(int c) {
|
|
|
+ StrBuffer.this.append((char) c);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void write(char[] buffer) {
|
|
|
+ StrBuffer.this.append(buffer);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void write(char[] buffer, int offset, int length) {
|
|
|
+ StrBuffer.this.append(buffer, offset, length);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void write(String str) {
|
|
|
+ StrBuffer.this.append(str);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void write(String str, int offset, int length) {
|
|
|
+ StrBuffer.this.append(str, offset, length);
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ private static final class BuilderState extends StrBuilderOptions {
|
|
|
+
|
|
|
+ private static final long serialVersionUID = 1L;
|
|
|
+
|
|
|
+ protected int counter;
|
|
|
+
|
|
|
+ private String endl;
|
|
|
+
|
|
|
+ private String separator;
|
|
|
+
|
|
|
+ private String open;
|
|
|
+
|
|
|
+ private String close;
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public StrBuilder builder() {
|
|
|
+ throw new UnsupportedOperationException();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public String endl() {
|
|
|
+ return endl;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public BuilderState endl(String value) {
|
|
|
+ this.endl = value;
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public String separator() {
|
|
|
+ return separator;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public BuilderState separator(String value) {
|
|
|
+ this.separator = value;
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public String open() {
|
|
|
+ return open;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public BuilderState open(String value) {
|
|
|
+ this.open = value;
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public String close() {
|
|
|
+ return close;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public BuilderState close(String value) {
|
|
|
+ this.close = value;
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+
|
|
|
+ protected BuilderState reset() {
|
|
|
+ this.counter = 0;
|
|
|
+ return this;
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+}
|
|
|
+
|