From d387e01d387a12e04f7301fa4f84010e05e58760 Mon Sep 17 00:00:00 2001
From: Gaetan Boismal <gaetan.boismal@forgerock.com>
Date: Mon, 08 Feb 2016 09:44:30 +0000
Subject: [PATCH] OPENDJ-2469 Improve performance metric tools
---
opendj-sdk/opendj-cli/src/main/java/com/forgerock/opendj/cli/MultiColumnPrinter.java | 831 ++++++++++++++++++++++++++++++++---------------------------
1 files changed, 449 insertions(+), 382 deletions(-)
diff --git a/opendj-sdk/opendj-cli/src/main/java/com/forgerock/opendj/cli/MultiColumnPrinter.java b/opendj-sdk/opendj-cli/src/main/java/com/forgerock/opendj/cli/MultiColumnPrinter.java
index 53bba32..277f57c 100644
--- a/opendj-sdk/opendj-cli/src/main/java/com/forgerock/opendj/cli/MultiColumnPrinter.java
+++ b/opendj-sdk/opendj-cli/src/main/java/com/forgerock/opendj/cli/MultiColumnPrinter.java
@@ -22,441 +22,508 @@
*
*
* Copyright 2010 Sun Microsystems, Inc.
- * Portions copyright 2012-2015 ForgeRock AS.
+ * Portions copyright 2012-2016 ForgeRock AS.
*/
package com.forgerock.opendj.cli;
-import java.util.Arrays;
+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.Vector;
+import java.util.Locale;
+
+import org.forgerock.util.Reject;
/**
- * Utility class for printing aligned columns of text.
- * <P>
- * This class allows you to specify:
- * <UL>
- * <LI>The number of columns in the output. This will determine the dimension of
- * the string arrays passed to add(String[]) or addTitle(String[]).
- * <LI>spacing/gap between columns
- * <LI>character to use for title border (null means no border)
- * <LI>column alignment. Only LEFT/CENTER is supported for now.
- * </UL>
- * <P>
- * Example usage:
+ * Utility class for printing columns of data.
+ * <p>
+ * This printer can be used to print data in formatted table or in csv format.
+ * <p>
+ * Regarding the formatting table feature, this class allows you to specify for each {@link Column}s:
+ * <ul>
+ * <li>A unique identifier</li>
+ * <li>The column title which will be printed by a call to {@link MultiColumnPrinter#printTitleLine()}</li>
+ * <li>The size (if a cell's data is bigger than the predefined size, then the data will not be truncated,
+ * i.e. it will overflow)</li>
+ * <li>The number of digits to keep (for {@link Double} data)</li>
+ * </ul>
+ * <p>
+ * Code to write data is independent of the {@link MultiColumnPrinter} configuration:
+ * <pre>
+ * 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"}
+ * };
*
- * <PRE>
- * MyPrinter mp = new MyPrinter(3, 2, "-");
- * String oneRow[] = new String[3];
- * oneRow[0] = "User Name";
- * oneRow[1] = "Email Address";
- * oneRow[2] = "Phone Number";
- * mp.addTitle(oneRow);
- * oneRow[0] = "Bob";
- * oneRow[1] = "bob@foo.com";
- * oneRow[2] = "123-4567";
- * mp.add(oneRow);
- * oneRow[0] = "John";
- * oneRow[1] = "john@foo.com";
- * oneRow[2] = "456-7890";
- * mp.add(oneRow);
- * mp.print();
- * </PRE>
- * <P>
- * The above would print:
- * <P>
- *
- * <PRE>
- * --------------------------------------
- * User Name Email Address Phone Number
- * --------------------------------------
- * Bob bob@foo.com 123-4567
- * John john@foo.com 456-7890
- * </PRE>
- * <P>
- * This class also supports multi-row titles and having title strings spanning
- * multiple columns. Example usage:
- *
- * <PRE>
- * TestPrinter tp = new TestPrinter(4, 2, "-");
- * String oneRow[] = new String[4];
- * int[] span = new int[4];
- * span[0] = 2; // spans 2 columns
- * span[1] = 0; // spans 0 columns
- * span[2] = 2; // spans 2 columns
- * span[3] = 0; // spans 0 columns
- * tp.setTitleAlign(CENTER);
- * oneRow[0] = "Name";
- * oneRow[1] = "";
- * oneRow[2] = "Contact";
- * oneRow[3] = "";
- * tp.addTitle(oneRow, span);
- * oneRow[0] = "First";
- * oneRow[1] = "Last";
- * oneRow[2] = "Email";
- * oneRow[3] = "Phone";
- * tp.addTitle(oneRow);
- * oneRow[0] = "Bob";
- * oneRow[1] = "Jones";
- * oneRow[2] = "bob@foo.com";
- * oneRow[3] = "123-4567";
- * tp.add(oneRow);
- * oneRow[0] = "John";
- * oneRow[1] = "Doe";
- * oneRow[2] = "john@foo.com";
- * oneRow[3] = "456-7890";
- * tp.add(oneRow);
- * tp.println();
- * </PRE>
- * <P>
- * The above would print:
- * <P>
- *
- * <PRE>
- * ------------------------------------
- * Name Contact
- * First Last Email Phone
- * ------------------------------------
- * Bob Jones bob@foo.com 123-4567
- * John Doe john@foo.com 456-7890
- * </PRE>
+ * int i;
+ * for (String[] countryData : myData) {
+ * i = 0;
+ * for (final MultiColumnPrinter.Column column : printer.getColumns()) {
+ * printer.printData(countryData[i++]);
+ * }
+ * }
+ * }
+ * </pre>
+ * <p>
+ * The following code sample presents how to create a {@link MultiColumnPrinter} to write CSV data:
+ * <pre>
+ * final List<MultiColumnPrinter.Column> 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);
+ * </pre>
+ * <p>
+ * The code above would print:
+ * <pre>
+ * 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
+ * </pre>
+ * <p>
+ * The following code sample presents how to configure a {@link MultiColumnPrinter}
+ * to print the same data on console with some title headers.
+ * <pre>
+ * final List<MultiColumnPrinter.Column> 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();
+ * </pre>
+ * <p>
+ * The code above would print:
+ * <pre>
+ * --------------------------------------------------
+ * | 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 |
+ * --------------------------------------------------
+ * </pre>
*/
public final class MultiColumnPrinter {
- /** Left ID. */
- public static final int LEFT = 0;
- /** Center ID. */
- public static final int CENTER = 1;
- /** Right ID. */
- public static final int RIGHT = 2;
+ /** The data alignment. */
+ public enum Alignment {
+ /** Data will be left-aligned. */
+ LEFT,
+ /** Data will be centered. */
+ CENTER,
+ /** Data will be right-aligned. */
+ RIGHT
+ }
- private int numCol = 2;
- private int gap = 4;
-
- private int align = CENTER;
- private int titleAlign = CENTER;
-
- private String border;
- private final List<String[]> titleTable = new Vector<>();
- private final List<int[]> titleSpanTable = new Vector<>();
- private final int[] curLength;
-
- private final ConsoleApplication app;
+ private static final String SEPARATOR_ID = "separator";
+ private static int separatorIdNumber;
/**
- * Creates a sorted new MultiColumnPrinter class using LEFT alignment and
- * with no title border.
+ * Returns a new separator {@link Column}.
+ * <p>
+ * This kind of {@link Column} can be used to separate data sections.
*
- * @param numCol
- * number of columns
- * @param gap
- * gap between each column
- * @param app
- * the console application to use for outputting data
+ * @return A new separator {@link Column}.
*/
- public MultiColumnPrinter(final int numCol, final int gap, final ConsoleApplication app) {
- this(numCol, gap, null, LEFT, app);
+ public static Column separatorColumn() {
+ return new Column(SEPARATOR_ID + separatorIdNumber++, "", 1, 0);
}
/**
- * Creates a sorted new MultiColumnPrinter class using LEFT alignment.
+ * Creates a new {@link Column} with the provided arguments.
*
- * @param numCol
- * number of columns
- * @param gap
- * gap between each column
- * @param border
- * character used to frame the titles
- * @param app
- * the console application to use for outputting data
+ * @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 MultiColumnPrinter(final int numCol, final int gap, final String border,
- final ConsoleApplication app) {
- this(numCol, gap, border, LEFT, app);
+ public static Column column(final String id, final String title, final int doublePrecision) {
+ return new Column(id, title, 1, doublePrecision);
}
/**
- * Creates a new MultiColumnPrinter class.
+ * Creates a new Column with the provided arguments.
*
- * @param numCol
- * number of columns
- * @param gap
- * gap between each column
- * @param border
- * character used to frame the titles
- * @param align
- * type of alignment within columns
- * @param app
- * the console application to use for outputting data
+ * @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 MultiColumnPrinter(final int numCol, final int gap, final String border, final int align,
- final ConsoleApplication app) {
- curLength = new int[numCol];
-
- this.numCol = numCol;
- this.gap = gap;
- this.border = border;
- this.align = align;
- this.titleAlign = LEFT;
-
- this.app = app;
+ 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);
}
/**
- * Adds to the row of strings to be used as the title for the table.
- *
- * @param row
- * Array of strings to print in one row of title.
+ * This class describes a Column of data used in the {@link MultiColumnPrinter}.
+ * <p>
+ * A column consists in the following fields:
+ * <ul>
+ * <li>An identifier for the associated data.
+ * <li>A title which is printed when {@link MultiColumnPrinter#printTitleLine()} is called.
+ * <li>A width which is the max width for this column's data.
+ * This information will only be used if the associated {@link MultiColumnPrinter}
+ * is configure to apply formatting.See {@link Builder#format(boolean)}.
+ * <li>A double precision which is the number of decimal to print for numeric data.
+ * See {@link MultiColumnPrinter#printData(Double)}.
+ * </ul>
*/
- public void addTitle(final String[] row) {
- if (row == null) {
- return;
+ public static final class Column {
+ private final String id;
+ private final String title;
+ private final int width;
+ private final int doublePrecision;
+
+ private Column(final String id, final String title, final int width, final int doublePrecision) {
+ this.id = id;
+ this.title = title;
+ this.width = Math.max(width, title.length());
+ this.doublePrecision = doublePrecision;
}
- final int[] span = new int[row.length];
- for (int i = 0; i < row.length; i++) {
- span[i] = 1;
+ /**
+ * Returns this {@link Column} identifier.
+ *
+ * @return This {@link Column} identifier.
+ */
+ public String getId() {
+ return id;
+ }
+ }
+
+ /**
+ * Creates a new {@link Builder} to build a {@link MultiColumnPrinter}.
+ *
+ * @param stream
+ * The {@link PrintStream} to use to print data.
+ * @param columns
+ * The {@link List} of {@link Column} data to print.
+ * @return
+ * A new {@link Builder} to build a {@link MultiColumnPrinter}.
+ */
+ public static Builder builder(final PrintStream stream, final List<Column> columns) {
+ return new Builder(stream, columns);
+ }
+
+ /** A fluent API for incrementally constructing {@link MultiColumnPrinter}. */
+ public static final class Builder {
+ private final PrintStream stream;
+ private final List<Column> columns;
+
+ private Alignment titleAlignment = Alignment.RIGHT;
+ private String columnSeparator = " ";
+ private boolean format;
+
+ private Builder(final PrintStream stream, final List<Column> columns) {
+ Reject.ifNull(stream);
+ this.stream = stream;
+ this.columns = columns;
}
- addTitle(row, span);
+ /**
+ * Sets whether the {@link MultiColumnPrinter} needs to apply formatting.
+ * <br>
+ * Default value is {@code false}.
+ *
+ * @param format
+ * {@code true} if the {@link MultiColumnPrinter} needs to apply formatting.
+ * @return This builder.
+ */
+ public Builder format(final boolean format) {
+ this.format = format;
+ return this;
+ }
+
+ /**
+ * Sets the alignment for title elements which will be printed by the {@link MultiColumnPrinter}.
+ * <p>
+ * This is used only if the printer is configured to
+ * apply formatting, see {@link Builder#format(boolean)}.
+ * <br>
+ * 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.
+ * <p>
+ * 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<Column> columns;
+ private final boolean format;
+ private final Alignment titleAlignment;
+ private final String columnSeparator;
+
+ private List<Column> printableColumns;
+ private final int lineLength;
+ private Iterator<Column> columnIterator;
+ private Column currentColumn;
+
+ private MultiColumnPrinter(final Builder builder) {
+ this.stream = builder.stream;
+ this.columns = Collections.unmodifiableList(builder.columns);
+ this.format = builder.format;
+ this.columnSeparator = builder.columnSeparator;
+ this.titleAlignment = builder.titleAlignment;
+ this.lineLength = computeLineLength();
+ resetIterator();
+ computePrintableColumns();
+ }
+
+ /** Prints a dashed line. */
+ public void printDashedLine() {
+ startNewLineIfNeeded();
+ for (int i = 0; i < lineLength; i++) {
+ stream.print('-');
+ }
+ stream.println();
}
/**
- * Adds to the row of strings to be used as the title for the table. Also
- * allows for certain title strings to span multiple columns The span
- * parameter is an array of integers which indicate how many columns the
- * corresponding title string will occupy. For a row that is 4 columns
- * wide, it is possible to have some title strings in a row to 'span'
- * multiple columns:
- * <P>
+ * Formats and prints the provided text data.
+ * Merge the provided text over the provided number of column.
+ * <p>
+ * Separator columns between merged columns will not be printed.
*
- * <PRE>
- * ------------------------------------
- * Name Contact
- * First Last Email Phone
- * ------------------------------------
- * Bob Jones bob@foo.com 123-4567
- * John Doe john@foo.com 456-7890
- * </PRE>
- *
- * In the example above, the title row has a string 'Name' that spans 2
- * columns. The string 'Contact' also spans 2 columns. The above is done
- * by passing in to addTitle() an array that contains:
- *
- * <PRE>
- * span[0] = 2; // spans 2 columns
- * span[1] = 0; // spans 0 columns, ignore
- * span[2] = 2; // spans 2 columns
- * span[3] = 0; // spans 0 columns, ignore
- * </PRE>
- * <P>
- * A span value of 1 is the default. The method addTitle(String[] row)
- * basically does:
- *
- * <PRE>
- * int[] span = new int[row.length];
- * for (int i = 0; i < row.length; i++) {
- * span[i] = 1;
- * }
- * addTitle(row, span);
- * </PRE>
- *
- * @param row
- * Array of strings to print in one row of title.
- * @param span
- * Array of integers that reflect the number of columns the
- * corresponding title string will occupy.
+ * @param data
+ * The section title to print.
+ * @param rowSpan
+ * Specifies the number of rows a cell should span.
*/
- public void addTitle(final String[] row, final int[] span) {
- // Need to create a new instance of it, otherwise the new values
- // will always overwrite the old values.
- titleTable.add(Arrays.copyOf(row, row.length));
- titleSpanTable.add(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);
+ }
}
/**
- * Clears title strings.
+ * Prints the provided {@link Double} value on the current column.
+ * <p>
+ * 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}.
+ * <br>
+ * See {@link MultiColumnPrinter#column(String, String, int, int)} for more details.
+ *
+ * @param value
+ * The double value to print.
*/
- public void clearTitle() {
- titleTable.clear();
- titleSpanTable.clear();
+ public void printData(final Double value) {
+ passFirstSeparatorColumn();
+ printData(value.isNaN() ? "-"
+ : String.format(Locale.ENGLISH, "%." + currentColumn.doublePrecision + "f", value));
}
/**
- * Adds one row of text to output.
+ * Prints the provided text data on the current column.
*
- * @param row
- * Array of strings to print in one row.
+ * @param data
+ * The text data to print.
*/
- public void printRow(final String... row) {
- for (int i = 0; i < numCol; i++) {
- if (titleAlign == RIGHT) {
- final int spaceBefore = curLength[i] - row[i].length();
- printSpaces(spaceBefore);
- app.getOutputStream().print(row[i]);
- if (i < numCol - 1) {
- printSpaces(gap);
- }
- } else if (align == CENTER) {
- int space1, space2;
- space1 = (curLength[i] - row[i].length()) / 2;
- space2 = curLength[i] - row[i].length() - space1;
+ public void printData(final String data) {
+ passFirstSeparatorColumn();
+ printCell(data, Alignment.RIGHT);
+ }
- printSpaces(space1);
- app.getOutputStream().print(row[i]);
- printSpaces(space2);
- if (i < numCol - 1) {
- printSpaces(gap);
- }
- } else {
- app.getOutputStream().print(row[i]);
- if (i < numCol - 1) {
- printSpaces(curLength[i] - row[i].length() + gap);
- }
+ /**
+ * Returns the data {@link Column} list of this {@link MultiColumnPrinter}.
+ * <p>
+ * Separator columns are filtered out.
+ *
+ * @return The {@link Column} list of this {@link MultiColumnPrinter}.
+ */
+ public List<Column> getColumns() {
+ return printableColumns;
+ }
+
+ private void printCell(final String data, final Alignment alignment) {
+ String toPrint = format ? align(data, alignment, currentColumn.width) : data;
+ if (columnIterator.hasNext()) {
+ toPrint += columnSeparator;
+ }
+ stream.print(toPrint);
+ nextLineOnEOLOrNextColumn();
+ }
+
+ /** Provided the provided string data according to the provided width and the provided alignment. */
+ private String align(final String data, final Alignment alignment, final int width) {
+ final String rawData = data.trim();
+ final int padding = width - rawData.length();
+
+ if (padding <= 0) {
+ return rawData;
+ }
+
+ switch (alignment) {
+ case RIGHT:
+ return pad(padding, rawData, 0);
+ case LEFT:
+ return pad(0, rawData, padding);
+ case CENTER:
+ final int paddingBefore = padding / 2;
+ return pad(paddingBefore, rawData, padding - paddingBefore);
+ default:
+ return "";
+ }
+ }
+
+ private String pad(final int leftPad, final String s, final int rightPad) {
+ return new StringBuilder().append(repeat(' ', leftPad))
+ .append(s)
+ .append(repeat(' ', rightPad))
+ .toString();
+ }
+
+ private void passFirstSeparatorColumn() {
+ if (cursorOnLineStart()) {
+ consumeSeparatorColumn();
+ }
+ }
+
+ private void consumeSeparatorColumn() {
+ if (isSeparatorColumn(currentColumn)) {
+ stream.print('|' + columnSeparator);
+ nextLineOnEOLOrNextColumn();
+ }
+ }
+
+ private void startNewLineIfNeeded() {
+ if (!cursorOnLineStart()) {
+ nextLine();
+ }
+ }
+
+ private void nextLineOnEOLOrNextColumn() {
+ if (columnIterator.hasNext()) {
+ currentColumn = columnIterator.next();
+ consumeSeparatorColumn();
+ } else {
+ nextLine();
+ }
+ }
+
+ private void nextLine() {
+ stream.println();
+ resetIterator();
+ }
+
+ private void resetIterator() {
+ columnIterator = columns.iterator();
+ currentColumn = columnIterator.next();
+ }
+
+ private boolean cursorOnLineStart() {
+ return currentColumn == columns.get(0);
+ }
+
+ private boolean isSeparatorColumn(final Column column) {
+ return column.id.startsWith(SEPARATOR_ID);
+ }
+
+ private void computePrintableColumns() {
+ printableColumns = new ArrayList<>(columns);
+ final Iterator<Column> it = printableColumns.iterator();
+
+ while (it.hasNext()) {
+ if (isSeparatorColumn(it.next())) {
+ it.remove();
}
}
- app.getOutputStream().println("");
}
- /**
- * Prints the table title.
- */
- public void printTitle() {
- // Get the longest string for each column and store in curLength[]
-
- // Scan through title rows
- Iterator<int[]> spanEnum = titleSpanTable.iterator();
- for (String[] row : titleTable) {
- final int[] curSpan = spanEnum.next();
-
- for (int i = 0; i < numCol; i++) {
- // None of the fields should be null, but if it
- // happens to be so, replace it with "-".
- if (row[i] == null) {
- row[i] = "-";
- }
-
- int len = row[i].length();
-
- /*
- * If a title string spans multiple columns, then the space it
- * occupies in each column is at most len/span (since we have
- * gap to take into account as well).
- */
- final int span = curSpan[i];
- int rem = 0;
- if (span > 1) {
- rem = len % span;
- len = len / span;
- }
-
- if (curLength[i] < len) {
- curLength[i] = len;
-
- if ((span > 1) && ((i + span) <= numCol)) {
- for (int j = i + 1; j < (i + span); ++j) {
- curLength[j] = len;
- }
-
- /*
- * Add remainder to last column in span to avoid
- * round-off errors.
- */
- curLength[(i + span) - 1] += rem;
- }
- }
- }
+ private int computeLineLength() {
+ int lineLength = 0;
+ final int separatorLength = this.columnSeparator.length();
+ for (final Column column : this.columns) {
+ lineLength += column.width + separatorLength;
}
-
- printBorder();
-
- spanEnum = titleSpanTable.iterator();
- for (String[] row : titleTable) {
- final int[] curSpan = spanEnum.next();
-
- for (int i = 0; i < numCol; i++) {
- int availableSpace = 0;
- final int span = curSpan[i];
-
- if (span == 0) {
- continue;
- }
-
- availableSpace = curLength[i];
-
- if ((span > 1) && ((i + span) <= numCol)) {
- for (int j = i + 1; j < (i + span); ++j) {
- availableSpace += gap;
- availableSpace += curLength[j];
- }
- }
-
- if (titleAlign == RIGHT) {
- final int spaceBefore = availableSpace - row[i].length();
- printSpaces(spaceBefore);
- app.getOutputStream().print(row[i]);
- if (i < numCol - 1) {
- printSpaces(gap);
- }
- } else if (titleAlign == CENTER) {
- int spaceBefore, spaceAfter;
- spaceBefore = (availableSpace - row[i].length()) / 2;
- spaceAfter = availableSpace - row[i].length() - spaceBefore;
-
- printSpaces(spaceBefore);
- app.getOutputStream().print(row[i]);
- printSpaces(spaceAfter);
- if (i < numCol - 1) {
- printSpaces(gap);
- }
- } else {
- app.getOutputStream().print(row[i]);
- if (i < numCol - 1) {
- printSpaces(availableSpace - row[i].length() + gap);
- }
- }
-
- }
- app.getOutputStream().println("");
- }
- printBorder();
- }
-
- /**
- * Set alignment for title strings.
- *
- * @param titleAlign
- * The alignment which should be one of {@code LEFT},
- * {@code RIGHT}, or {@code CENTER}.
- */
- public void setTitleAlign(final int titleAlign) {
- this.titleAlign = titleAlign;
- }
-
- private void printBorder() {
- if (border == null) {
- return;
- }
-
- // For the value in each column
- for (int i = 0; i < numCol; i++) {
- for (int j = 0; j < curLength[i]; j++) {
- app.getOutputStream().print(border);
- }
- }
-
- // For the gap between each column
- for (int i = 0; i < numCol - 1; i++) {
- for (int j = 0; j < gap; j++) {
- app.getOutputStream().print(border);
- }
- }
- app.getOutputStream().println("");
- }
-
- private void printSpaces(final int count) {
- for (int i = 0; i < count; ++i) {
- app.getOutputStream().print(" ");
- }
+ return lineLength - separatorLength;
}
}
--
Gitblit v1.10.0