|
|
@@ -24,21 +24,31 @@ public class StringFormatter {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- private final class FmtStateMachine {
|
|
|
- final StringBuilder str = new StringBuilder(fmt.length());
|
|
|
+ private static final class FmtStateMachine {
|
|
|
+ final StringBuilder str;
|
|
|
|
|
|
+ String fmt;
|
|
|
Object[] args;
|
|
|
- int currentIdx = 0;
|
|
|
+ int currentIdx;
|
|
|
final NameMap named;
|
|
|
+ int lastCh, endPos = 0;
|
|
|
|
|
|
- FmtStateMachine(Object... args) {
|
|
|
+ FmtStateMachine(String fmt, Object... args) {
|
|
|
+ this(fmt, 0, args, new NameMap(args), '\0');
|
|
|
+ }
|
|
|
+
|
|
|
+ FmtStateMachine(String fmt, int idx, Object[] args, NameMap named, int lastCh) {
|
|
|
+ this.str = new StringBuilder(fmt.length());
|
|
|
+ this.fmt = fmt;
|
|
|
+ this.currentIdx = idx;
|
|
|
this.args = args;
|
|
|
- this.named = new NameMap(args);
|
|
|
+ this.named = named;
|
|
|
+ this.lastCh = lastCh;
|
|
|
}
|
|
|
|
|
|
FmtStateMachine formatMain() {
|
|
|
int lpos = 0;
|
|
|
- for (int pos = fmt.indexOf('{'); pos != -1; lpos = pos+1, pos = fmt.indexOf('{', lpos)) {
|
|
|
+ for (int pos = fmt.indexOf('{'); pos != -1 && hasMore(pos); lpos = pos+1, pos = fmt.indexOf('{', lpos)) {
|
|
|
str.append(fmt.substring(lpos, pos).replaceAll("}}", "}"));
|
|
|
int epos = fmt.indexOf('}', pos);
|
|
|
|
|
|
@@ -48,25 +58,50 @@ public class StringFormatter {
|
|
|
epos = pos + 1;
|
|
|
} else if (epos == pos+1) {
|
|
|
// Unmarked '{}' -> get the next argument
|
|
|
- str.append(args[currentIdx++]);
|
|
|
+ str.append(getToken(null));
|
|
|
} else {
|
|
|
- formatToken(pos, epos);
|
|
|
+ epos = formatToken(pos, epos);
|
|
|
}
|
|
|
pos = epos;
|
|
|
}
|
|
|
- str.append(fmt.substring(lpos).replaceAll("}}", "}"));
|
|
|
+ str.append(remaining(lpos).replaceAll("}}", "}"));
|
|
|
return this;
|
|
|
}
|
|
|
|
|
|
- private void formatToken(int pos, int epos) {
|
|
|
- final String token = fmt.substring(pos+1, epos);
|
|
|
+ private String remaining(int lpos) {
|
|
|
+ this.endPos = fmt.indexOf(lastCh, lpos);
|
|
|
+ return lastCh == '\0' ? fmt.substring(lpos) : fmt.substring(lpos, endPos++);
|
|
|
+ }
|
|
|
+
|
|
|
+ private boolean hasMore(int pos) {
|
|
|
+ return lastCh == '\0' || pos < fmt.indexOf(lastCh);
|
|
|
+ }
|
|
|
+
|
|
|
+ private int formatToken(int pos, int epos) {
|
|
|
+ final String token = fmt.substring(++pos, epos);
|
|
|
// If token contains a '.', use reflection?
|
|
|
- // If token contains a '?', make it an if-expression
|
|
|
// If token starts with '%', make it a c-format expression
|
|
|
- if (Character.isDigit(token.charAt(0))) {
|
|
|
- str.append(args[Integer.parseInt(token)]);
|
|
|
+ if (token.indexOf('?') != -1) {
|
|
|
+ final int b1 = fmt.indexOf('?', pos)+1;
|
|
|
+ boolean _if = (Boolean) getToken(token.substring(0, b1-pos-1));
|
|
|
+ FmtStateMachine then = new FmtStateMachine(fmt.substring(b1), currentIdx, args, named, ':').formatMain();
|
|
|
+ FmtStateMachine els = new FmtStateMachine(fmt.substring(then.endPos+b1), then.currentIdx, args, named, '}').formatMain();
|
|
|
+ currentIdx = els.currentIdx;
|
|
|
+ epos = els.endPos+then.endPos+b1-1;
|
|
|
+ str.append(_if ? then : els);
|
|
|
+ } else {
|
|
|
+ str.append(getToken(token));
|
|
|
+ }
|
|
|
+ return epos;
|
|
|
+ }
|
|
|
+
|
|
|
+ private Object getToken(final String token) {
|
|
|
+ if (token == null || token.isEmpty()) {
|
|
|
+ return args[currentIdx++];
|
|
|
+ } else if (Character.isDigit(token.charAt(0))) {
|
|
|
+ return args[Integer.parseInt(token)];
|
|
|
} else {
|
|
|
- str.append(named.get(token));
|
|
|
+ return named.get(token);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -82,6 +117,6 @@ public class StringFormatter {
|
|
|
}
|
|
|
|
|
|
public String format(Object... args) {
|
|
|
- return this.new FmtStateMachine(args).formatMain().toString();
|
|
|
+ return new FmtStateMachine(fmt, args).formatMain().toString();
|
|
|
}
|
|
|
}
|