/*
* @(#)VMMetrics.java 1.3 04/01/05
*
* Copyright (c) 2000-2003 Sun Microsystems, Inc. All Rights Reserved.
*
* Sun grants you ("Licensee") a non-exclusive, royalty free, license to use,
* modify and redistribute this software in source and binary code form,
* provided that i) this copyright notice and license appear on all copies of
* the software; and ii) Licensee does not utilize the software in a manner
* which is disparaging to Sun.
*
* This software is provided "AS IS," without a warranty of any kind. ALL
* EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
* IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
* NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE
* LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
* OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS
* LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
* INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
* CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF
* OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* This software is not designed or intended for use in on-line control of
* aircraft, air traffic, aircraft navigation or aircraft communications; or in
* the design, construction, operation or maintenance of any nuclear
* facility. Licensee represents and warrants that it will not use or
* redistribute the Software for such purposes.
*/
/*
* @(#)MultiColumnPrinter.java 1.3 04/09/15
*
* Copyright 2003 Sun Microsystems, Inc. All Rights Reserved
* SUN PROPRIETARY/CONFIDENTIAL
* Use is subject to license terms.
*
*/
import java.util.Enumeration;
import java.util.Vector;
import java.util.Map;
import java.util.TreeMap;
import java.util.Iterator;
/**
* Utility class for printing aligned collumns of text. How/where
* the text is printed is determined by the abstract methods
* doPrint(String) and doPrintln(String). The typical case is to
* create a subclass and make these methods print the string to standard
* out or error.
* <P>
* This class allows you to specify:
* <UL>
* <LI>The number of collumns 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:
* <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 collumns. Example usage:
* <PRE>
* TestPrinter tp = new TestPrinter(4, 2, "-");
* String oneRow[] = new String [ 4 ];
* int[] span = new int[ 4 ];
*
* span[0] = 2; // spans 2 collumns
* span[1] = 0; // spans 0 collumns
* span[2] = 2; // spans 2 collumns
* span[3] = 0; // spans 0 collumns
*
* 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>
*
*/
public abstract class MultiColumnPrinter {
final public static int LEFT = 0;
final public static int CENTER = 1;
/*
* Sets the default sorting behavior.
* When set to true, the table entries are sorted unless otherwise
* specified in a constructor.
*/
final private static boolean DEFAULT_SORT = true;
private int numCol = 2;
private int gap = 4;
private int align = CENTER;
private int titleAlign = CENTER;
private String border = null;
private Vector table = null;
private Vector titleTable = null;
private Vector titleSpanTable = null;
private int curLength[];
private boolean sortNeeded = DEFAULT_SORT;
private int[] keyCriteria = null;
/**
* 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 sort true if the output is sorted; false otherwise
*
* REVISIT: Possibly adding another argument that specifies which ones can
* be truncated (xxx...)
*/
public MultiColumnPrinter(int numCol, int gap, String border,
int align, boolean sort) {
table = new Vector();
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.sortNeeded = sort;
}
/**
* Creates a new sorted 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
*/
public MultiColumnPrinter(int numCol, int gap, String border, int align) {
this(numCol, gap, border, align, DEFAULT_SORT);
}
/**
* 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
*/
public MultiColumnPrinter(int numCol, int gap, String border) {
this(numCol, gap, border, LEFT);
}
/**
* 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
*/
public MultiColumnPrinter(int numCol, int gap) {
this(numCol, gap, null, LEFT);
}
/**
* 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(String[] row) {
if (row == null)
return;
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 collumns
* The span parameter is an array of integers which indicate how
* many collumns the corresponding title string will occupy.
* For a row that is 4 collumns wide, it is possible to have some
* title strings in a row to 'span' multiple collumns:
*
* <P>
* <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 collumns. The string 'Contact' also spans 2 collumns.
* The above is done by passing in to addTitle() an array that
* contains:
*
* <PRE>
* span[0] = 2; // spans 2 collumns
* span[1] = 0; // spans 0 collumns, ignore
* span[2] = 2; // spans 2 collumns
* span[3] = 0; // spans 0 collumns, 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 collumns
* the corresponding title string will occupy.
*/
public void addTitle(String[] row, int span[]) {
// Need to create a new instance of it, otherwise the new values will
// always overwrite the old values.
String[] rowInstance = new String[(row.length)];
for (int i = 0; i < row.length; i++) {
rowInstance[i] = row[i];
}
titleTable.addElement(rowInstance);
titleSpanTable.addElement(span);
}
/**
* Set alignment for title strings
*
* @param titleAlign
*/
public void setTitleAlign(int titleAlign) {
this.titleAlign = titleAlign;
}
/**
* Adds one row of text to output.
*
* @param row Array of strings to print in one row.
*/
public void add(String[] row) {
// Need to create a new instance of it, otherwise the new values will
// always overwrite the old values.
String[] rowInstance = new String[(row.length)];
for (int i = 0; i < row.length; i++) {
rowInstance[i] = row[i];
}
table.addElement(rowInstance);
}
/**
* Clears title strings.
*/
public void clearTitle() {
titleTable.clear();
titleSpanTable.clear();
}
/**
* Clears strings.
*/
public void clear() {
table.clear();
if (curLength != null) {
for (int i = 0; i < curLength.length; ++i) {
curLength[i] = 0;
}
}
}
/**
* Prints the multi-column table, including the title.
*/
public void print() {
print(true);
}
/**
* Prints the multi-column table.
*
* @param printTitle Specifies if the title rows should be printed.
*/
public void print(boolean printTitle) {
// REVISIT:
// Make sure you take care of curLength and row being null value cases.
// Get the longest string for each column and store in curLength[]
// Scan through title rows
Enumeration elm = titleTable.elements();
Enumeration spanEnum = titleSpanTable.elements();
int rowNum = 0;
while (elm.hasMoreElements()) {
String[] row = (String[])elm.nextElement();
int[] curSpan = (int[])spanEnum.nextElement();
for (int i = 0; i < numCol; i++) {
// Fix for 4627901: NullPtrException seen when
// execute 'jmqcmd list dur'
// This happens when a field to be listed is null.
// 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 collumns, then
* the space it occupies in each collumn is at most
* len/span (since we have gap to take into account
* as well).
*/
int span = curSpan[i], 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 collumn in span
* to avoid round-off errors.
*/
curLength[(i+span)-1] += rem;
}
}
}
++rowNum;
}
// Scan through rest of rows
elm = table.elements();
while (elm.hasMoreElements()) {
String[] row = (String[])elm.nextElement();
for (int i = 0; i < numCol; i++) {
// Fix for 4627901: NullPtrException seen when
// execute 'jmqcmd list dur'
// This happens when a field to be listed is null.
// None of the fields should be null, but if it
// happens to be so, replace it with "-".
if (row[i] == null)
row[i] = "-";
if (curLength[i] < row[i].length())
curLength[i] = row[i].length();
}
}
/*
* Print title
*/
if (printTitle) {
printBorder();
elm = titleTable.elements();
spanEnum = titleSpanTable.elements();
while (elm.hasMoreElements()) {
String[] row = (String[])elm.nextElement();
int[] curSpan = (int[])spanEnum.nextElement();
for (int i = 0; i < numCol; i++) {
int availableSpace = 0, 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 == CENTER) {
int space_before, space_after;
space_before = (availableSpace-row[i].length())/2;
space_after = availableSpace-row[i].length() - space_before;
printSpaces(space_before);
doPrint(row[i]);
printSpaces(space_after);
if (i < numCol-1) printSpaces(gap);
} else {
doPrint(row[i]);
if (i < numCol-1) printSpaces(availableSpace-row[i].length()+gap);
}
}
doPrintln("");
}
printBorder();
}
if (sortNeeded)
printSortedTable();
else
printUnsortedTable();
}
/*
* Prints the table entries in the sorted order.
*/
private void printSortedTable() {
// Sort the table entries
TreeMap sortedTable = new TreeMap();
Enumeration elm = table.elements();
while (elm.hasMoreElements()) {
String[] row = (String[])elm.nextElement();
// If keyCriteria contains valid info use that
// to create the key; otherwise, use the default row[0]
// for the key.
if (keyCriteria != null && keyCriteria.length > 0) {
String key = getKey(row);
if (key != null)
sortedTable.put(key, row);
else
sortedTable.put(row[0], row);
} else {
sortedTable.put(row[0], row);
}
}
// Iterate through the table entries
Iterator iterator = sortedTable.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry entry = (Map.Entry)iterator.next();
String[] row = ((String[])entry.getValue());
printRow(row);
}
}
/*
* Creates the key for the current row based on the
* criteria specified by setKeyCriteria().
* If key cannot be created by the criteria, it simply returns
* null.
*
* Examples:
* String[] row = {"foo", "bar", "hello");
*
* int[] keyCriteria = {0};
* key = "foo";
*
* int[] keyCriteria = {0, 1};
* key = "foobar";
*
* int[] keyCriteria = {2, 1};
* key = "hellobar";
*
* int[] keyCriteria = {4};
* key = null;
*/
private String getKey(String[] row) {
String key = "";
for (int i = 0; i < keyCriteria.length; i++) {
int content = keyCriteria[i];
try {
key = key + row[content];
} catch (ArrayIndexOutOfBoundsException ae) {
// Happens when keyCriteria[] contains an index that
// does not exist in 'row'.
return null;
}
}
return key;
}
/*
* Prints the table entries in the order they were entered.
*/
private void printUnsortedTable() {
Enumeration elm = table.elements();
while (elm.hasMoreElements()) {
String[] row = (String[])elm.nextElement();
printRow(row);
}
}
private void printRow(String[] row) {
for (int i = 0; i < numCol; i++) {
if (align == CENTER) {
int space1, space2;
space1 = (curLength[i]-row[i].length())/2;
space2 = curLength[i]-row[i].length() - space1;
printSpaces(space1);
doPrint(row[i]);
printSpaces(space2);
if (i < numCol-1) printSpaces(gap);
} else {
doPrint(row[i]);
if (i < numCol-1) printSpaces(curLength[i]-row[i].length()+gap);
}
}
doPrintln("");
}
/**
* Prints the multi-column table, with a carriage return.
*/
public void println() {
print();
doPrintln("");
}
private void printSpaces(int count) {
for (int i = 0; i < count; ++i) {
doPrint(" ");
}
}
private void printBorder() {
int colNum = 1;
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++) {
doPrint(border);
}
}
// For the gap between each column
for (int i = 0; i < numCol-1; i++) {
for (int j = 0; j < gap; j++) {
doPrint(border);
}
}
doPrintln("");
}
/*
* Sets the criteria for the key.
* new int[] {0, 1} means use the first and the second
* elements of the array.
*/
public void setKeyCriteria(int[] criteria) {
this.keyCriteria = criteria;
}
/**
* Method that does the actual printing. Override this method to
* print to your destination of choice (stdout, stream, etc).
*
* @param str String to print.
*/
public abstract void doPrint(String str);
/**
* Method that does the actual printing. Override this method to
* print to your destination of choice (stdout, stream, etc).
*
* This method also prints a newline at the end of the string.
*
* @param str String to print.
*/
public abstract void doPrintln(String str);
}
|