/* * The contents of this file are subject to the terms of the Common Development and * Distribution License (the License). You may not use this file except in compliance with the * License. * * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the * specific language governing permission and limitations under the License. * * When distributing Covered Software, include this CDDL Header Notice in each file and include * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL * Header, with the fields enclosed by brackets [] replaced by your own identifying * information: "Portions Copyright [year] [name of copyright owner]". * * Copyright 2010 Sun Microsystems, Inc. * Portions copyright 2012-2016 ForgeRock AS. */ package com.forgerock.opendj.cli; import static com.forgerock.opendj.cli.Utils.repeat; import java.io.PrintStream; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Locale; import org.forgerock.util.Reject; /** * Utility class for printing columns of data. *
* This printer can be used to print data in formatted table or in csv format. *
* Regarding the formatting table feature, this class allows you to specify for each {@link Column}s: *
* Code to write data is independent of the {@link MultiColumnPrinter} configuration: *
* void printData(final MultiColumnPrinter printer) {
* String[][] myData = new String[][] {
* new String[]{"U.S.A", "34.2", "40.8", ".us"},
* new String[]{"United Kingdom", "261.1", "31.6", ".uk"},
* new String[]{"France", "98.8", "30.1", ".fr"}
* };
*
* int i;
* for (String[] countryData : myData) {
* i = 0;
* for (final MultiColumnPrinter.Column column : printer.getColumns()) {
* printer.printData(countryData[i++]);
* }
* }
* }
*
* * The following code sample presents how to create a {@link MultiColumnPrinter} to write CSV data: *
* final List*columns = new ArrayList<>(); * columns.add(MultiColumnPrinter.column("CountryNameColumnId", "country_name", 0)); * columns.add(MultiColumnPrinter.column("populationDensityId", "population_density", 1)); * columns.add(MultiColumnPrinter.column("GiniId", "gini", 1)); * columns.add(MultiColumnPrinter.column("internetTLDId", "internet_tld", 0)); * MultiColumnPrinter myCsvPrinter = MultiColumnPrinter.builder(System.out, columns) * .columnSeparator(",") * .build(); * printData(myCsvPrinter); *
* The code above would print: *
* country_name,population_density,gini,internet_tld * U.S.A,34.2,40.8,.us * United Kingdom,261.1,31.6,.uk * France,98.8,30.1,.fr **
* The following code sample presents how to configure a {@link MultiColumnPrinter} * to print the same data on console with some title headers. *
* final List*columns = new ArrayList<>(); * columns.add(MultiColumnPrinter.separatorColumn()); * columns.add(MultiColumnPrinter.column("CountryNameColumnId", "Country Name", 15, 0)); * columns.add(MultiColumnPrinter.column("populationDensityId", "Density", 10, 1)); * columns.add(MultiColumnPrinter.separatorColumn()); * columns.add(MultiColumnPrinter.column("GiniID", "GINI", 5, 1)); * columns.add(MultiColumnPrinter.column("internetTLDID", "TLD", 5, 0)); * columns.add(MultiColumnPrinter.separatorColumn()); * MultiColumnPrinter myPrinter = MultiColumnPrinter.builder(System.out, columns) * .format(true) * .columnSeparator(" ") * .titleAlignment(MultiColumnPrinter.Alignment.CENTER) * .build(); * myPrinter.printDashedLine(); * myPrinter.printTitleSection("General Information", 2); * myPrinter.printTitleSection("Data", 2); * myPrinter.printTitleLine(); * myPrinter.printDashedLine(); * printData(myPrinter); * myPrinter.printDashedLine(); *
* The code above would print: *
* -------------------------------------------------- * | General Information | Data | * | Country Name Density | GINI TLD | * -------------------------------------------------- * | U.S.A 34.2 | 40.8 .us | * | United Kingdom 261.1 | 31.6 .uk | * | France 98.8 | 30.1 .fr | * -------------------------------------------------- **/ public final class MultiColumnPrinter { /** The data alignment. */ public enum Alignment { /** Data will be left-aligned. */ LEFT, /** Data will be centered. */ CENTER, /** Data will be right-aligned. */ RIGHT } private static final String SEPARATOR_ID = "separator"; private static int separatorIdNumber; /** * Returns a new separator {@link Column}. *
* This kind of {@link Column} can be used to separate data sections. * * @return A new separator {@link Column}. */ public static Column separatorColumn() { return new Column(SEPARATOR_ID + separatorIdNumber++, "", 1, 0); } /** * Creates a new {@link Column} with the provided arguments. * * @param id * The column identifier. * @param title * The column title. * @param doublePrecision * The double precision used to print {@link Double} data for this column. * See {@link MultiColumnPrinter#printData(Double)}. * @return * A new Column with the provided arguments. */ public static Column column(final String id, final String title, final int doublePrecision) { return new Column(id, title, 1, doublePrecision); } /** * Creates a new Column with the provided arguments. * * @param id * The column identifier. * @param title * The column title. * @param width * The column width. * This information will only be used if the associated * {@link MultiColumnPrinter} is configured to apply formatting. * See {@link Builder#format(boolean)}. * @param doublePrecision * The double precision to use to print data for this column. * @return * A new Column with the provided arguments. */ public static Column column(final String id, final String title, final int width, final int doublePrecision) { return new Column(id, title, Math.max(width, title.length()), doublePrecision); } /** * This class describes a Column of data used in the {@link MultiColumnPrinter}. *
* A column consists in the following fields: *
* This is used only if the printer is configured to
* apply formatting, see {@link Builder#format(boolean)}.
*
* Default value is {@link Alignment#RIGHT}.
*
* @param titleAlignment
* The title alignment.
* @return This builder.
*/
public Builder titleAlignment(final Alignment titleAlignment) {
this.titleAlignment = titleAlignment;
return this;
}
/**
* Sets the sequence to use to separate column.
*
* Default value is {@code " "}.
*
* @param separator
* The sequence {@link String}.
* @return This builder.
*/
public Builder columnSeparator(final String separator) {
this.columnSeparator = separator;
return this;
}
/**
* Creates a new {@link MultiColumnPrinter} as configured in this {@link Builder}.
*
* @return A new {@link MultiColumnPrinter} as configured in this {@link Builder}.
*/
public MultiColumnPrinter build() {
return new MultiColumnPrinter(this);
}
}
private final PrintStream stream;
private final List
* Separator columns between merged columns will not be printed.
*
* @param data
* The section title to print.
* @param rowSpan
* Specifies the number of rows a cell should span.
*/
public void printTitleSection(final String data, final int rowSpan) {
consumeSeparatorColumn();
int lengthToPad = 0;
int nbColumnMerged = 0;
while (columnIterator.hasNext() && nbColumnMerged < rowSpan) {
lengthToPad += currentColumn.width + columnSeparator.length();
if (!isSeparatorColumn(currentColumn)) {
nbColumnMerged++;
}
currentColumn = columnIterator.next();
}
stream.print(align(data, titleAlignment, lengthToPad));
consumeSeparatorColumn();
if (!columnIterator.hasNext()) {
nextLine();
}
}
/** Prints a line with all column title and separator. */
public void printTitleLine() {
startNewLineIfNeeded();
passFirstSeparatorColumn();
for (final Column column : this.printableColumns) {
printCell(column.title, Alignment.RIGHT);
}
}
/**
* Prints the provided {@link Double} value on the current column.
*
* If this {@link MultiColumnPrinter} is configured to apply formatting,
* the provided value will be truncated according to the decimal
* precision set in the corresponding {@link Column}.
*
* Separator columns are filtered out.
*
* @return The {@link Column} list of this {@link MultiColumnPrinter}.
*/
public List
* See {@link MultiColumnPrinter#column(String, String, int, int)} for more details.
*
* @param value
* The double value to print.
*/
public void printData(final Double value) {
passFirstSeparatorColumn();
printData(value.isNaN() ? "-"
: String.format(Locale.ENGLISH, "%." + currentColumn.doublePrecision + "f", value));
}
/**
* Prints the provided text data on the current column.
*
* @param data
* The text data to print.
*/
public void printData(final String data) {
passFirstSeparatorColumn();
printCell(data, Alignment.RIGHT);
}
/**
* Returns the data {@link Column} list of this {@link MultiColumnPrinter}.
*