/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt * or http://forgerock.org/license/CDDLv1.0.html. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at legal-notices/CDDLv1_0.txt. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * * Copyright 2010 Sun Microsystems, Inc. * Portions copyright 2012-2014 ForgeRock AS. */ package com.forgerock.opendj.cli; import java.util.Iterator; import java.util.List; import java.util.Vector; /** * Utility class for printing aligned columns of text. *

* This class allows you to specify: *

*

* Example usage: * *

 * 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();
 * 
*

* The above would print: *

* *

 *  --------------------------------------
 *  User Name  Email Address  Phone Number
 *  --------------------------------------
 *  Bob        bob@foo.com    123-4567
 *  John       john@foo.com   456-7890
 * 
*

* This class also supports multi-row titles and having title strings spanning * multiple columns. Example usage: * *

 * 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();
 * 
*

* The above would print: *

* *

 *      ------------------------------------
 *          Name             Contact
 *      First  Last      Email       Phone
 *      ------------------------------------
 *      Bob    Jones  bob@foo.com   123-4567
 *      John   Doe    john@foo.com  456-7890
 * 
*/ 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; private int numCol = 2; private int gap = 4; private int align = CENTER; private int titleAlign = CENTER; private String border = null; private List titleTable = null; private List titleSpanTable = null; private final int[] curLength; private final ConsoleApplication app; /** * Creates a sorted new MultiColumnPrinter class using LEFT alignment and * with no title border. * * @param numCol * number of columns * @param gap * gap between each column * @param app * the console application to use for outputting data */ public MultiColumnPrinter(final int numCol, final int gap, final ConsoleApplication app) { this(numCol, gap, null, LEFT, app); } /** * Creates a sorted new MultiColumnPrinter class using LEFT alignment. * * @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 */ public MultiColumnPrinter(final int numCol, final int gap, final String border, final ConsoleApplication app) { this(numCol, gap, border, LEFT, app); } /** * Creates a new MultiColumnPrinter class. * * @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 */ public MultiColumnPrinter(final int numCol, final int gap, final String border, final int align, final ConsoleApplication app) { titleTable = new Vector(); titleSpanTable = new Vector(); curLength = new int[numCol]; this.numCol = numCol; this.gap = gap; this.border = border; this.align = align; this.titleAlign = LEFT; this.app = app; } /** * 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. */ public void addTitle(final String[] row) { if (row == null) { return; } final int[] span = new int[row.length]; for (int i = 0; i < row.length; i++) { span[i] = 1; } addTitle(row, span); } /** * 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: *

* *

     * ------------------------------------
     *     Name             Contact
     * First  Last      Email       Phone
     * ------------------------------------
     * Bob    Jones  bob@foo.com   123-4567
     * John   Doe    john@foo.com  456-7890
     * 
* * 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: * *
     * 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
     * 
*

* A span value of 1 is the default. The method addTitle(String[] row) * basically does: * *

     * int[] span = new int[row.length];
     * for (int i = 0; i < row.length; i++) {
     *     span[i] = 1;
     * }
     * addTitle(row, span);
     * 
* * @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. */ 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. final String[] rowInstance = new String[row.length]; for (int i = 0; i < row.length; i++) { rowInstance[i] = row[i]; } titleTable.add(rowInstance); titleSpanTable.add(span); } /** * Clears title strings. */ public void clearTitle() { titleTable.clear(); titleSpanTable.clear(); } /** * Adds one row of text to output. * * @param row * Array of strings to print in one row. */ 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; 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); } } } 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 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; } } } } 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(" "); } } }