| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145 |
- package org.leumasjaffe.format;
- import java.util.HashMap;
- import java.util.Objects;
- import java.util.regex.Matcher;
- import java.util.regex.Pattern;
- import lombok.AccessLevel;
- import lombok.experimental.FieldDefaults;
- @FieldDefaults(level=AccessLevel.PRIVATE, makeFinal=true)
- public class StringFormatter {
- private static final class NameMap extends HashMap<String, Object> {
- /**
- *
- */
- private static final long serialVersionUID = -2243574068000774442L;
- NameMap(Object... args) {
- for (Object arg : args) {
- Objects.requireNonNull(arg);
- if (arg instanceof Named) {
- put(((Named) arg).getName(), arg);
- }
- }
- }
- }
-
- private static final class FmtStateMachine {
- final StringBuilder str;
-
- String fmt;
- Object[] args;
- int currentIdx;
- final NameMap named;
- int lastCh, endPos = 0;
- 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 = named;
- this.lastCh = lastCh;
- }
-
- FmtStateMachine formatMain() {
- int lpos = 0;
- 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);
- if (fmt.charAt(pos+1) == '{') {
- // Literal '{'
- str.append('{');
- epos = pos + 1;
- } else if (epos == pos+1) {
- // Unmarked '{}' -> get the next argument
- str.append(getToken(null));
- } else {
- epos = formatToken(pos, epos);
- }
- pos = epos;
- }
- str.append(remaining(lpos).replaceAll("}}", "}"));
- return this;
- }
- 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 starts with '%', make it a c-format expression
- if (token.indexOf('?') != -1) {
- final int b1 = fmt.indexOf('?', pos)+1;
- boolean _if = getConditionToken(token, 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 boolean getConditionToken(final String token, final int end) {
- final String cond = token.substring(0, end);
- Matcher matcher = Pattern.compile("^(\\d*|[a-zA-Z_]+)(>=?|<=?|!=|==?)(\\d+)$").matcher(cond);
- if (matcher.matches()) {
- final int cmp = ((Integer) getToken(matcher.group(1))).compareTo(Integer.valueOf(matcher.group(3)));
- switch (matcher.group(2)) {
- case "=" : return cmp == 0;
- case "==": return cmp == 0;
- case "!=": return cmp != 0;
- case "<" : return cmp < 0;
- case "<=": return cmp <= 0;
- case ">" : return cmp > 0;
- case ">=": return cmp >= 0;
- default: throw new IllegalStateException();
- }
- } else {
- return (Boolean) getToken(cond);
- }
- }
- 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 {
- return named.get(token);
- }
- }
-
- public String toString() {
- return str.toString();
- }
- }
-
- String fmt;
-
- public StringFormatter(String logFmtString) {
- Objects.requireNonNull(logFmtString);
- this.fmt = logFmtString;
- }
-
- public String format(Object... args) {
- return new FmtStateMachine(fmt, args).formatMain().toString();
- }
- }
|