com.oracle.tools.table.Table Maven / Gradle / Ivy
/*
* File: Table.java
*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* The contents of this file are subject to the terms and conditions of
* the Common Development and Distribution License 1.0 (the "License").
*
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the License by consulting the LICENSE.txt file
* distributed with this file, or by consulting https://oss.oracle.com/licenses/CDDL
*
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file LICENSE.txt.
*
* MODIFICATIONS:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*/
package com.oracle.tools.table;
import com.oracle.tools.Option;
import com.oracle.tools.Options;
import static com.oracle.tools.lang.StringHelper.trimTrailingWhiteSpace;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
/**
* A {@link Table} represents a collection of zero or more vertically arranged
* {@link Row}s, each {@link Row} consisting of zero or more horizontally arranged
* {@link Cell}s.
*
* Copyright (c) 2015. All Rights Reserved. Oracle Corporation.
* Oracle is a registered trademark of Oracle Corporation and/or its affiliates.
*
* @author Brian Oliver
*/
public class Table implements Iterable
{
/**
* The {@link Row}s in the {@link Table}.
*/
private ArrayList rows;
/**
* The formatting {@link Options} for the {@link Table}.
*/
private Options options;
/**
* Constructs a {@link Table} given a collection of {@link Row}s.
*
* @param rows the {@link Row}s in the {@link Table}
*/
public Table(Row... rows)
{
this.rows = new ArrayList<>();
if (rows != null)
{
for (Row row : rows)
{
this.rows.add(row);
}
}
this.options = new Options();
}
/**
* Obtains a {@link Row} {@link Row.Comparator} that uses the specific
* {@link Cell} for comparison (uses natural ordering of the {@link Cell}
* content to order the {@link Row}).
*
* @param column the index of the {@link Cell} in the {@link Row} to compare {@link Row}s
*
* @return the {@link Row} {@link Row.Comparator}
*/
public static Row.Comparator orderByColumn(final int column)
{
return new Row.Comparator()
{
@Override
public int compare(Row row1,
Row row2)
{
return row1.getCell(column).getLine(0).compareTo(row2.getCell(column).getLine(0));
}
};
}
/**
* Obtain the formatting {@link Options} for the {@link Table}.
*
* @return the formatting {@link Options} for the {@link Table}
*/
public Options getOptions()
{
return options;
}
/**
* Adds a {@link Row} to the {@link Table}
*
* @param row the {@link Row} to add to the {@link Table}
*
* @return the {@link Table}
*/
public Table addRow(Row row)
{
rows.add(row);
return this;
}
/**
* Adds a {@link Row} consisting of a collection of {@link Cell}s to the {@link Table}
*
* @param cells the {@link Cell}s in the {@link Row}
*
* @return the {@link Table}
*/
public Table addRow(Cell... cells)
{
return addRow(new Row(cells));
}
/**
* Adds a {@link Row} consisting of a collection of {@link Cell} content to the {@link Table}
*
* @param cells the {@link Cell} content in the {@link Row}
*
* @return the {@link Table}
*/
public Table addRow(String... cells)
{
if (cells == null)
{
return this;
}
else
{
Row row = new Row();
for (String cell : cells)
{
row.addCell(cell);
}
return addRow(row);
}
}
/**
* Obtains the {@link Row} at the specified index, commencing at 0.
*
* @param index the index of the {@link Row} to obtain
*
* @return the {@link Row} at the specified index or null
* if the index is out-of-bounds
*/
public Row getRow(int index)
{
if (index < 0 || index >= rows.size())
{
return null;
}
else
{
return rows.get(index);
}
}
/**
* Obtains the number of {@link Row}s in the {@link Table}
*
* @return the number of {@link Row}s in the {@link Table}
*/
public int size()
{
return rows.size();
}
@Override
public Iterator iterator()
{
return rows.iterator();
}
@Override
public String toString()
{
// determine the Cell Separator
Cell.Separator cellSeparator = options.get(Cell.Separator.class, Cell.Separator.standard());
// -----------------------------------
// determine the maximum widths of the cells each row in the table
ArrayList cellWidths = new ArrayList<>();
for (Row row : this)
{
// ensure we have enough cells for the current row
int rowWidth = row.width();
while (cellWidths.size() < rowWidth)
{
cellWidths.add(0);
}
// adjust the existing cell widths based on the current row
int i = 0;
for (Cell cell : row)
{
int currentWidth = cellWidths.get(i);
// determine the width of the cell
// (use the width is defined by the cell, then the row, then the table)
Cell.Width width = options.get(Cell.Width.class,
row.getOptions().get(Cell.Width.class,
this.getOptions().get(Cell.Width.class,
Cell.Width.autodetect())));
int cellWidth;
if (width.isAutoDetect())
{
cellWidth = cell.width();
}
else
{
cellWidth = width.getCharacters();
}
if (cellWidth > currentWidth)
{
cellWidths.set(i, cellWidth);
}
i++;
}
}
// -----------------------------------
// sort the rows (when a Row.Comparator has been provided)
Row.Comparator comparator = options.get(Row.Comparator.class);
Row[] orderedRows = new Row[rows.size()];
rows.toArray(orderedRows);
if (comparator != null)
{
Arrays.sort(orderedRows, comparator);
}
// -----------------------------------
// generate the table
StringBuilder builder = new StringBuilder();
for (int rowIndex = 0; rowIndex < orderedRows.length; rowIndex++)
{
// grab the next row
Row row = orderedRows[rowIndex];
// append a row separator when it's not the first row
if (rowIndex > 0)
{
builder.append("\n");
}
int line = 0;
int rowHeight;
do
{
rowHeight = 0;
int rowWidth = row.width();
for (int cellIndex = 0; cellIndex < rowWidth; cellIndex++)
{
Cell cell = row.getCell(cellIndex);
// determine the cell justification
// (use the justification defined by the cell, then the row, then the table)
Cell.Justification justification = cell.getOptions().get(Cell.Justification.class,
row.getOptions()
.get(Cell.Justification.class,
this.getOptions()
.get(Cell
.Justification.class, Cell
.Justification.LEFT)));
int cellHeight = cell.height();
rowHeight = cellHeight > rowHeight ? cellHeight : rowHeight;
String justifiedContent;
if (cell.isEmpty() || line >= cellHeight)
{
// output a cell separator?
if (cellIndex > 0)
{
builder.append(" ");
builder.append(cellSeparator.getSeparator());
builder.append(" ");
}
// justify the cell content
justifiedContent = justification.format("", cellWidths.get(cellIndex));
}
else
{
// output a cell separator?
if (cellIndex > 0)
{
builder.append(" ");
builder.append(cellSeparator.getSeparator());
builder.append(" ");
}
// justify the cell content
String content = cell.getLine(line);
justifiedContent = justification.format(content, cellWidths.get(cellIndex));
}
// ensure the last column doesn't have any unnecessary white space
if (cellIndex == rowWidth - 1)
{
justifiedContent = trimTrailingWhiteSpace(justifiedContent);
}
// output the justified cell content
builder.append(justifiedContent);
}
line++;
if (line < rowHeight)
{
builder.append("\n");
}
}
while (line < rowHeight);
}
return builder.toString();
}
}