TextComponentWriter.java

package de.slothsoft.sprintsim.io;

import java.util.Arrays;
import java.util.Locale;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import de.slothsoft.sprintsim.io.CellFormat.Alignment;

public class TextComponentWriter implements ComponentWriter {

	private Logger logger;

	private int columnSize = 15;
	private TableInfo tableInfo;
	private String emptyColumn = createEmptyColumn(this.columnSize);

	private final CellFormat cellFormat = new CellFormat(Locale.ENGLISH);

	public TextComponentWriter() {
		this(System.out::println);
	}

	public TextComponentWriter(Logger logger) {
		this.logger = Objects.requireNonNull(logger);
	}

	@Override
	public void writeTitle(String string) {
		writeLine(string);
		writeLine(string.replaceAll(".", "=")); //$NON-NLS-1$ //$NON-NLS-2$
		writeEmpty();
	}

	@Override
	public void writeLine(String string) {
		this.logger.log(string);
	}

	@Override
	public void startTable(TableInfo newInfo) {
		this.tableInfo = newInfo;
		this.emptyColumn = createEmptyColumn(this.columnSize);
	}

	@Override
	public void endTable() {
		this.tableInfo = null;
		this.emptyColumn = createEmptyColumn(this.columnSize);
	}

	@Override
	public void writeTableHeader(String... headers) {
		final StringBuilder headersString = new StringBuilder();
		for (int i = 0; i < headers.length; i++) {
			headersString.append(createTableCenterAligned(i, headers[i]));
		}

		this.logger.log(headersString.toString());
		writeTableSeparatorLine(headers.length);
	}

	String createTableCenterAligned(int index, String value) {
		if (value == null) return this.emptyColumn;
		final int indentWidth = Math.max(0, calculateColumnSize(index) - value.length());
		return this.emptyColumn.substring(0, indentWidth / 2) + value
				+ this.emptyColumn.substring(0, indentWidth - indentWidth / 2);
	}

	@Override
	public void writeTableSeparatorLine(int columnCount) {
		final String separatorLine = IntStream.range(0, columnCount)
				.mapToObj(i -> this.emptyColumn.substring(0, calculateColumnSize(i))).collect(Collectors.joining());
		this.logger.log(separatorLine.replaceAll(".", "="));
	}

	@Override
	public void writeTableLine(Object... cells) {
		final StringBuilder cellsString = new StringBuilder();
		for (int i = 0; i < cells.length; i++) {
			cellsString.append(createCellString(i, cells[i]));
		}
		this.logger.log(cellsString.toString());
	}

	String createCellString(int index, Object cell) {
		final Alignment aligment = this.cellFormat.getAlignment(cell);

		switch (aligment) {
			case BEGINNING :
				return createTableLeftAligned(index, this.cellFormat.stringify(cell));
			case CENTER :
				return createTableCenterAligned(index, this.cellFormat.stringify(cell));
			case END :
				return createTableRightAligned(index, this.cellFormat.stringify(cell));
			default :
				throw new IllegalArgumentException("Do not know alignment " + aligment);
		}
	}

	String createTableLeftAligned(int index, String value) {
		return value + this.emptyColumn.substring(0, Math.max(0, calculateColumnSize(index) - value.length()));
	}

	private int calculateColumnSize(int index) {
		final double ratio = this.tableInfo == null || this.tableInfo.columnRatios == null
				? 1.0
				: this.tableInfo.columnRatios[index];
		return (int) Math.round(this.columnSize * ratio);
	}

	String createTableRightAligned(int index, String value) {
		return this.emptyColumn.substring(0, Math.max(0, calculateColumnSize(index) - value.length())) + value;
	}

	public int getColumnSize() {
		return this.columnSize;
	}

	public TextComponentWriter columnSize(int newColumnSize) {
		setColumnSize(newColumnSize);
		return this;
	}

	public void setColumnSize(int columnSize) {
		this.columnSize = columnSize;
		this.emptyColumn = createEmptyColumn(columnSize);
	}

	private String createEmptyColumn(int targetSize) {
		final double maxRatio = this.tableInfo == null || this.tableInfo.columnRatios == null
				? 1.0
				: Arrays.stream(this.tableInfo.columnRatios).max().getAsDouble();
		return IntStream.range(0, (int) Math.round(targetSize * maxRatio)).mapToObj(i -> " ") //$NON-NLS-1$
				.collect(Collectors.joining());
	}

	public Logger getLogger() {
		return this.logger;
	}

	public TextComponentWriter logger(Logger newLogger) {
		setLogger(newLogger);
		return this;
	}

	public void setLogger(Logger logger) {
		this.logger = Objects.requireNonNull(logger);
	}
}