|
@@ -9,6 +9,12 @@ package net.ranides.assira.text;
|
|
|
import java.io.Reader;
|
|
import java.io.Reader;
|
|
|
import java.io.Serializable;
|
|
import java.io.Serializable;
|
|
|
import java.io.Writer;
|
|
import java.io.Writer;
|
|
|
|
|
+import java.util.ArrayDeque;
|
|
|
|
|
+import java.util.Deque;
|
|
|
|
|
+import java.util.EmptyStackException;
|
|
|
|
|
+import java.util.Iterator;
|
|
|
|
|
+import java.util.function.Function;
|
|
|
|
|
+import java.util.function.IntFunction;
|
|
|
import net.ranides.assira.collection.arrays.ArrayAllocator;
|
|
import net.ranides.assira.collection.arrays.ArrayAllocator;
|
|
|
import net.ranides.assira.collection.arrays.ArrayUtils;
|
|
import net.ranides.assira.collection.arrays.ArrayUtils;
|
|
|
import net.ranides.assira.collection.arrays.NativeArray;
|
|
import net.ranides.assira.collection.arrays.NativeArray;
|
|
@@ -24,6 +30,8 @@ public final class StrBuilder implements CharSequence, Appendable, Serializable
|
|
|
|
|
|
|
|
private static final long serialVersionUID = 1L;
|
|
private static final long serialVersionUID = 1L;
|
|
|
|
|
|
|
|
|
|
+ private static final StrBuilderOptions OPTIONS = new StrBuilderOptions();
|
|
|
|
|
+
|
|
|
protected static final char[] EMPTY = new char[0];
|
|
protected static final char[] EMPTY = new char[0];
|
|
|
|
|
|
|
|
protected static final int CAPACITY = 32;
|
|
protected static final int CAPACITY = 32;
|
|
@@ -32,9 +40,7 @@ public final class StrBuilder implements CharSequence, Appendable, Serializable
|
|
|
|
|
|
|
|
private int size;
|
|
private int size;
|
|
|
|
|
|
|
|
- private String blank;
|
|
|
|
|
-
|
|
|
|
|
- private String endl;
|
|
|
|
|
|
|
+ Deque<BuilderState> options = new ArrayDeque<>(); // StrBuilderOptions.DEFAULT;
|
|
|
|
|
|
|
|
public StrBuilder() {
|
|
public StrBuilder() {
|
|
|
this(CAPACITY);
|
|
this(CAPACITY);
|
|
@@ -42,6 +48,11 @@ public final class StrBuilder implements CharSequence, Appendable, Serializable
|
|
|
|
|
|
|
|
public StrBuilder(int initialCapacity) {
|
|
public StrBuilder(int initialCapacity) {
|
|
|
buffer = new char[initialCapacity];
|
|
buffer = new char[initialCapacity];
|
|
|
|
|
+ options.push(new BuilderState(OPTIONS));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public StrBuilderOptions options() {
|
|
|
|
|
+ return options.peek();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
@Override
|
|
@@ -103,6 +114,9 @@ public final class StrBuilder implements CharSequence, Appendable, Serializable
|
|
|
|
|
|
|
|
public StrBuilder clear() {
|
|
public StrBuilder clear() {
|
|
|
size = 0;
|
|
size = 0;
|
|
|
|
|
+ BuilderState state = options.peek().copy();
|
|
|
|
|
+ options.clear();
|
|
|
|
|
+ options.push(state);
|
|
|
return this;
|
|
return this;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -115,7 +129,7 @@ public final class StrBuilder implements CharSequence, Appendable, Serializable
|
|
|
NativeArrayUtils.reverse(size, NativeArray.wrap(buffer));
|
|
NativeArrayUtils.reverse(size, NativeArray.wrap(buffer));
|
|
|
return this;
|
|
return this;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
public char[] getChars() {
|
|
public char[] getChars() {
|
|
|
return getChars(0, size);
|
|
return getChars(0, size);
|
|
|
}
|
|
}
|
|
@@ -179,22 +193,48 @@ public final class StrBuilder implements CharSequence, Appendable, Serializable
|
|
|
return new SReader();
|
|
return new SReader();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- public StrBuilder endl() {
|
|
|
|
|
- return append(ValueUtils.or(endl, StringUtils.ENDL));
|
|
|
|
|
|
|
+ public StrBuilder open() {
|
|
|
|
|
+ options.push(options.peek().derive());
|
|
|
|
|
+ return append(options.peek().open());
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- public StrBuilder endl(String value) {
|
|
|
|
|
- this.endl = value;
|
|
|
|
|
- return this;
|
|
|
|
|
|
|
+
|
|
|
|
|
+ public StrBuilder open(String open) {
|
|
|
|
|
+ options.push(options.peek().derive().open(open));
|
|
|
|
|
+ return append(open);
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- public <T> StrBuilder blank(String value) {
|
|
|
|
|
- this.blank = value;
|
|
|
|
|
|
|
+
|
|
|
|
|
+ public StrBuilder open(String open, String close) {
|
|
|
|
|
+ options.push(options.peek().derive().open(open).close(close));
|
|
|
|
|
+ return append(open);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public StrBuilder open(String open, String close, String separator) {
|
|
|
|
|
+ options.push(options.peek().derive().open(open).close(close).separator(separator));
|
|
|
|
|
+ return append(open);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public StrBuilder close() {
|
|
|
|
|
+ if(options.size() <= 1) {
|
|
|
|
|
+ throw new IllegalStateException("There is not block to close.");
|
|
|
|
|
+ }
|
|
|
|
|
+ return append(options.pop().close());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public StrBuilder item() {
|
|
|
|
|
+ BuilderState state = options.peek();
|
|
|
|
|
+ if(0 != state.counter++) {
|
|
|
|
|
+ append(state.separator());
|
|
|
|
|
+ }
|
|
|
return this;
|
|
return this;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ public StrBuilder endl() {
|
|
|
|
|
+ return append(options.peek().endl());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
public StrBuilder append() {
|
|
public StrBuilder append() {
|
|
|
- return null == blank ? this : append(blank);
|
|
|
|
|
|
|
+ String value = options.peek().blank();
|
|
|
|
|
+ return null == value ? this : append(value);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
@Override
|
|
@@ -317,6 +357,67 @@ public final class StrBuilder implements CharSequence, Appendable, Serializable
|
|
|
return append(String.valueOf(value));
|
|
return append(String.valueOf(value));
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ public StrBuilder append(Iterable<?> values) {
|
|
|
|
|
+ return append(values.iterator(), Object::toString);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public <T> StrBuilder append(Iterable<? extends T> values, Function<T,String> function) {
|
|
|
|
|
+ return append(values.iterator(), function);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public <T> StrBuilder append(Iterator<? extends T> values) {
|
|
|
|
|
+ return append(values, Object::toString);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public <T> StrBuilder append(Iterator<? extends T> values, Function<T,String> function) {
|
|
|
|
|
+ if ( !values.hasNext() ) {
|
|
|
|
|
+ return this;
|
|
|
|
|
+ }
|
|
|
|
|
+ String separator = options.peek().separator();
|
|
|
|
|
+ for (;;) {
|
|
|
|
|
+ T item = values.next();
|
|
|
|
|
+ append(function.apply(item));
|
|
|
|
|
+ if ( !values.hasNext() ) {
|
|
|
|
|
+ return this;
|
|
|
|
|
+ }
|
|
|
|
|
+ append(separator);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public <T> StrBuilder append(T[] values) {
|
|
|
|
|
+ return append(values, Object::toString);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public <T> StrBuilder append(T[] values, Function<T,String> function) {
|
|
|
|
|
+ if ( 0==values.length ) {
|
|
|
|
|
+ return this;
|
|
|
|
|
+ }
|
|
|
|
|
+ String separator = options.peek().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> StrBuilder append(NativeArray values) {
|
|
|
|
|
+ return append(values, i -> String.valueOf(values.get(i)));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public <T> StrBuilder append(NativeArray array, IntFunction<String> function) {
|
|
|
|
|
+ int n = array.size();
|
|
|
|
|
+ if ( 0==n ) {
|
|
|
|
|
+ return this;
|
|
|
|
|
+ }
|
|
|
|
|
+ String separator = options.peek().separator();
|
|
|
|
|
+ append(function.apply(0));
|
|
|
|
|
+ for (int i=1; i<n; i++) {
|
|
|
|
|
+ append(separator).append(function.apply(i));
|
|
|
|
|
+ }
|
|
|
|
|
+ return this;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
private final class SReader extends Reader {
|
|
private final class SReader extends Reader {
|
|
|
|
|
|
|
|
private int pos;
|
|
private int pos;
|
|
@@ -421,4 +522,102 @@ public final class StrBuilder implements CharSequence, Appendable, Serializable
|
|
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ protected final class BuilderState extends StrBuilderOptions {
|
|
|
|
|
+
|
|
|
|
|
+ private final StrBuilderOptions parent;
|
|
|
|
|
+
|
|
|
|
|
+ protected int counter;
|
|
|
|
|
+
|
|
|
|
|
+ private String blank;
|
|
|
|
|
+
|
|
|
|
|
+ private String endl;
|
|
|
|
|
+
|
|
|
|
|
+ private String separator;
|
|
|
|
|
+
|
|
|
|
|
+ private String open;
|
|
|
|
|
+
|
|
|
|
|
+ private String close;
|
|
|
|
|
+
|
|
|
|
|
+ public BuilderState(StrBuilderOptions parent) {
|
|
|
|
|
+ this.parent = parent;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public BuilderState derive() {
|
|
|
|
|
+ return new BuilderState(this);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public BuilderState copy() {
|
|
|
|
|
+ BuilderState state = new BuilderState(OPTIONS);
|
|
|
|
|
+
|
|
|
|
|
+ state.blank(this.blank());
|
|
|
|
|
+ state.endl(this.blank());
|
|
|
|
|
+ state.separator(this.separator());
|
|
|
|
|
+ state.open(this.open());
|
|
|
|
|
+ state.close(this.close());
|
|
|
|
|
+
|
|
|
|
|
+ return state;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public StrBuilder builder() {
|
|
|
|
|
+ return StrBuilder.this;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public String blank() {
|
|
|
|
|
+ return ValueUtils.or(blank, parent.blank());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public BuilderState blank(String value) {
|
|
|
|
|
+ this.blank = value;
|
|
|
|
|
+ return this;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public String endl() {
|
|
|
|
|
+ return ValueUtils.or(endl, parent.endl());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public BuilderState endl(String value) {
|
|
|
|
|
+ this.endl = value;
|
|
|
|
|
+ return this;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public String separator() {
|
|
|
|
|
+ return ValueUtils.or(separator, parent.separator());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public BuilderState separator(String value) {
|
|
|
|
|
+ this.separator = value;
|
|
|
|
|
+ return this;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public String open() {
|
|
|
|
|
+ return ValueUtils.or(open, parent.open());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public BuilderState open(String value) {
|
|
|
|
|
+ this.open = value;
|
|
|
|
|
+ return this;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public String close() {
|
|
|
|
|
+ return ValueUtils.or(close, parent.close());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public BuilderState close(String value) {
|
|
|
|
|
+ this.close = value;
|
|
|
|
|
+ return this;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
}
|
|
}
|