/*
 * Copyright 2011 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.ipc.invalidation.util;

import com.google.protobuf.ByteString;

import java.lang.reflect.Field;

/**
 * A {@link TextBuilder} is an abstraction that allows classes to efficiently
 * append their string representations and then use them later for human
 * consumption, e.g., for debugging or logging. It is currently a wrapper
 * around {@link StringBuilder} and {@link Formatter} to give us format and
 * append capabilities together. All append methods return this TextBuilder
 * so that the method calls can be chained.
 *
 */
public class TextBuilder {

  private final StringBuilder builder;
  private final UtilFormatter formatter;

  /**
   * Given an object, outputs all its fields with names to builder
   */
  public static void outputFieldsToBuilder(TextBuilder builder, Object object) {
    // Get all the fields and print them using toCompactString if possible;
    // otherwise, via toString
    Field[] fields = object.getClass().getDeclaredFields();
    for (Field field : fields) {
      try {
        field.setAccessible(true);
        builder.append(field.getName() + " = ");
        Object fieldValue = field.get(object);
        if (fieldValue instanceof InternalBase) {
          ((InternalBase) fieldValue).toCompactString(builder);
        } else {
          builder.append(fieldValue);
        }
        builder.append(", ");
      } catch (IllegalArgumentException e) {
        e.printStackTrace();
      } catch (IllegalAccessException e) {
        e.printStackTrace();
      }
    }
  }

  /**
   * Returns an empty TextBuilder to which various objects' string
   * representations can be added later.
   */
  public TextBuilder() {
   builder = new StringBuilder();
   formatter = new UtilFormatter(builder);
  }

  /**
   * Appends the string representation of {@code c} to this builder.
   *
   * @param c the character being appended
   */
  public TextBuilder append(char c) {
    builder.append(c);
    return this;
  }

  /**
   * Appends the string representation of {@code i} to this builder.
   *
   * @param i the integer being appended
   */
  public TextBuilder append(int i) {
    builder.append(i);
    return this;
  }

  /**
   * Appends the toString representation of {@code object} to this builder.
   */
  public TextBuilder append(Object object) {
    builder.append(object);
    return this;
  }

  /** Appends the {@link Bytes#toString} representation of {@code bytes} to this builder. */
  public TextBuilder append(ByteString bytes) {
    builder.append(Bytes.toString(bytes));
    return this;
  }

  /**
   * Appends the string representation of {@code l} to this builder.
   *
   * @param l the long being appended
   */
  public TextBuilder append(long l) {
    builder.append(l);
    return this;
  }

  /**
   * Appends the string representation of {@code b} to this builder.
   *
   * @param b the boolean being appended
   */
  public TextBuilder append(boolean b) {
    builder.append(b);
    return this;
  }

  /**
   * Appends {@code s} to this builder.
   *
   * @param s the string being appended
   */
  public TextBuilder append(String s) {
    builder.append(s);
    return this;
  }

  /**
   * Writes a formatted string to this using the specified format string and
   * arguments.
   *
   * @param format the format as used in {@link java.util.Formatter}
   * @param args the arguments that are converted to their string form using
   * {@code format}
   */
  public TextBuilder appendFormat(String format, Object... args) {
    formatter.format(format, args);
    return this;
  }

  @Override
  public String toString() {
    return builder.toString();
  }
}
