/**
* Copyright (C) 2015 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.strata.report.framework.format;
import static com.opengamma.strata.collect.Guavate.toImmutableList;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Optional;
import java.util.stream.IntStream;
import com.google.common.collect.ImmutableList;
import com.opengamma.strata.collect.Unchecked;
import com.opengamma.strata.collect.io.AsciiTable;
import com.opengamma.strata.collect.io.AsciiTableAlignment;
import com.opengamma.strata.collect.io.CsvOutput;
import com.opengamma.strata.report.Report;
/**
* Common base class for formatting reports into ASCII tables or CSV format.
*
* @param <R> the report type
*/
public abstract class ReportFormatter<R extends Report> {
/**
* The default format settings, used if there are no settings for a data type.
*/
private final FormatSettings<Object> defaultSettings;
/**
* The format settings provider.
*/
private final FormatSettingsProvider formatSettingsProvider = FormatSettingsProvider.INSTANCE;
/**
* Creates a new formatter with a set of default format settings.
*
* @param defaultSettings default format settings, used if there are no settings for a data type.
*/
protected ReportFormatter(FormatSettings<Object> defaultSettings) {
this.defaultSettings = defaultSettings;
}
//-------------------------------------------------------------------------
/**
* Outputs the report table in CSV format.
*
* @param report the report
* @param out the output stream to write to
*/
@SuppressWarnings("resource")
public void writeCsv(R report, OutputStream out) {
OutputStreamWriter outputWriter = new OutputStreamWriter(out, StandardCharsets.UTF_8);
CsvOutput csvOut = new CsvOutput(outputWriter);
csvOut.writeLine(report.getColumnHeaders());
IntStream.range(0, report.getRowCount())
.mapToObj(rowIdx -> formatRow(report, rowIdx, ReportOutputFormat.CSV))
.forEach(csvOut::writeLine);
Unchecked.wrap(outputWriter::flush);
}
/**
* Outputs the report as an ASCII table.
*
* @param report the report
* @param out the output stream to write to
*/
public void writeAsciiTable(R report, OutputStream out) {
List<Class<?>> columnTypes = getColumnTypes(report);
List<AsciiTableAlignment> alignments = IntStream.range(0, columnTypes.size())
.mapToObj(i -> calculateAlignment(columnTypes.get(i)))
.collect(toImmutableList());
List<String> headers = report.getColumnHeaders();
ImmutableList<ImmutableList<String>> cells = formatAsciiTable(report);
String asciiTable = AsciiTable.generate(headers, alignments, cells);
PrintWriter pw = new PrintWriter(new OutputStreamWriter(out, StandardCharsets.UTF_8));
pw.println(asciiTable);
pw.flush();
}
// calculates the alignment to use
private AsciiTableAlignment calculateAlignment(Class<?> columnType) {
FormatSettings<Object> formatSettings = formatSettingsProvider.settings(columnType, defaultSettings);
boolean isNumeric =
formatSettings.getCategory() == FormatCategory.NUMERIC || formatSettings.getCategory() == FormatCategory.DATE;
return isNumeric ? AsciiTableAlignment.RIGHT : AsciiTableAlignment.LEFT;
}
// formats the ASCII table
private ImmutableList<ImmutableList<String>> formatAsciiTable(R report) {
ImmutableList.Builder<ImmutableList<String>> table = ImmutableList.builder();
for (int rowIdx = 0; rowIdx < report.getRowCount(); rowIdx++) {
table.add(formatRow(report, rowIdx, ReportOutputFormat.ASCII_TABLE));
}
return table.build();
}
// formats a single row
private ImmutableList<String> formatRow(R report, int rowIdx, ReportOutputFormat format) {
ImmutableList.Builder<String> tableRow = ImmutableList.builder();
for (int colIdx = 0; colIdx < report.getColumnCount(); colIdx++) {
tableRow.add(formatData(report, rowIdx, colIdx, format));
}
return tableRow.build();
}
//-------------------------------------------------------------------------
/**
* Gets the type of the data in each report column.
* <p>
* If every value in a column is a failure the type will be {@code Object.class}.
*
* @param report the report
* @return a list of column types
*/
protected abstract List<Class<?>> getColumnTypes(R report);
/**
* Formats a piece of data for display.
*
* @param report the report containing the data
* @param rowIdx the row index of the data
* @param colIdx the column index of the data
* @param format the report output format
* @return the formatted data
*/
protected abstract String formatData(R report, int rowIdx, int colIdx, ReportOutputFormat format);
//-------------------------------------------------------------------------
/**
* Formats a value into a string.
*
* @param value the value
* @param format the format that controls how the value is formatted
* @return the formatted value
*/
@SuppressWarnings("unchecked")
protected String formatValue(Object value, ReportOutputFormat format) {
Object formatValue = value instanceof Optional ? ((Optional<?>) value).orElse(null) : value;
if (formatValue == null) {
return "";
}
FormatSettings<Object> formatSettings = formatSettingsProvider.settings(formatValue.getClass(), defaultSettings);
ValueFormatter<Object> formatter = formatSettings.getFormatter();
return format == ReportOutputFormat.CSV ?
formatter.formatForCsv(formatValue) :
formatter.formatForDisplay(formatValue);
}
}