ImMathanR
4/25/2014 - 12:47 AM

Extremely simple wrapper around SpannableStringBuilder to make the API more logical and less awful. Apache 2 licensed.

Extremely simple wrapper around SpannableStringBuilder to make the API more logical and less awful. Apache 2 licensed.

import android.text.SpannableStringBuilder;
import java.util.ArrayDeque;
import java.util.Deque;

import static android.text.Spanned.SPAN_INCLUSIVE_EXCLUSIVE;

/** A {@link SpannableStringBuilder} wrapper whose API doesn't make me want to stab my eyes out. */
public class Truss {
  private final SpannableStringBuilder builder;
  private final Deque<Span> stack;

  public Truss() {
    builder = new SpannableStringBuilder();
    stack = new ArrayDeque<>();
  }

  public Truss append(String string) {
    builder.append(string);
    return this;
  }

  public Truss append(CharSequence charSequence) {
    builder.append(charSequence);
    return this;
  }

  public Truss append(char c) {
    builder.append(c);
    return this;
  }

  public Truss append(int number) {
    builder.append(String.valueOf(number));
    return this;
  }

  /** Starts {@code span} at the current position in the builder. */
  public Truss pushSpan(Object span) {
    stack.addLast(new Span(builder.length(), span));
    return this;
  }

  /** End the most recently pushed span at the current position in the builder. */
  public Truss popSpan() {
    Span span = stack.removeLast();
    builder.setSpan(span.span, span.start, builder.length(), SPAN_INCLUSIVE_EXCLUSIVE);
    return this;
  }

  /** Create the final {@link CharSequence}, popping any remaining spans. */
  public CharSequence build() {
    while (!stack.isEmpty()) {
      popSpan();
    }
    return builder; // TODO make immutable copy?
  }

  private static final class Span {
    final int start;
    final Object span;

    public Span(int start, Object span) {
      this.start = start;
      this.span = span;
    }
  }
}