TestPrint.java

package internal.utils;

import space.sunqian.annotation.Nonnull;
import space.sunqian.annotation.Nullable;

import java.io.PrintStream;
import java.util.HashMap;
import java.util.Map;

/**
 * This interface provides methods for printing test info. The default printer is {@link System#out}.
 *
 * @author sunqian
 */
public interface TestPrint {

    /**
     * Sets the printer for current thread. The printer may be {@code null}, int this case the printer will be set to
     * {@link System#out}, and it is the default printer.
     *
     * @param printer the printer
     */
    default void setPrinter(@Nullable PrintStream printer) {
        if (printer == null) {
            Local.remove(Local.Key.PRINTER);
        } else {
            Local.set(Local.Key.PRINTER, printer);
        }
    }

    /**
     * Prints the given message. Each element of the message array will be concatenated as one to print.
     *
     * @param message the given message
     */
    default void print(@Nullable Object @Nonnull ... message) {
        StringBuilder sb = new StringBuilder();
        for (Object o : message) {
            sb.append(o);
        }
        PrintStream printer = Local.get(Local.Key.PRINTER);
        printer = printer == null ? System.out : printer;
        printer.print(sb);
        printer.flush();
    }

    /**
     * Prints the given message and adds a line-separator at the tail. Each element of the message array will be
     * concatenated as one to print.
     *
     * @param message the given message
     */
    default void println(@Nullable Object @Nonnull ... message) {
        StringBuilder sb = new StringBuilder();
        for (Object o : message) {
            sb.append(o);
        }
        PrintStream printer = Local.get(Local.Key.PRINTER);
        printer = printer == null ? System.out : printer;
        printer.println(sb);
        printer.flush();
    }

    /**
     * Prints the content in this format: {@code title: message}, and adds a line-separator at the tail. Each element of
     * the message array will be concatenated as one to print.
     *
     * @param title   the given title
     * @param message the given message
     */
    default void printFor(@Nonnull String title, @Nullable Object @Nonnull ... message) {
        Object[] msg = new Object[message.length + 1];
        msg[0] = title + ": ";
        System.arraycopy(message, 0, msg, 1, message.length);
        println(msg);
    }

    final class Local {

        private static final @Nonnull ThreadLocal<Map<Key, Object>> localMap = new ThreadLocal<Map<Key, Object>>() {
            @Override
            protected @Nonnull Map<Key, Object> initialValue() {
                return new HashMap<>();
            }
        };

        private Local() {
        }

        static void set(@Nonnull TestPrint.Local.Key key, @Nonnull Object value) {
            localMap.get().put(key, value);
        }

        @SuppressWarnings("unchecked")
        static <T> T get(@Nonnull TestPrint.Local.Key key) {
            return (T) localMap.get().get(key);
        }

        static void remove(@Nonnull TestPrint.Local.Key key) {
            localMap.get().remove(key);
        }

        enum Key {

            PRINTER,
            ;
        }
    }
}