/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package gobblin.cli;
import java.util.ArrayList;
import java.util.List;
import com.google.common.base.Preconditions;
import com.google.common.primitives.Ints;
/**
* A format helper for CLI output. Unfortunately it only supports strings, so
* values need to be converted previous to passing in. This is done in order to
* support table-like formatting.
* <p/>
* It's recommended that this class is built using the inner {@link Builder} class.
*
* @author ahollenbach@nerdwallet.com
*/
public class CliTablePrinter {
/**
* Labels for each columns
*/
private List<String> labels;
/**
* A list of sprintf-style flag strings (corresponding to each column)
*/
private List<String> flags;
/**
* Overall indentation of a table
*/
private int indentation;
/**
* Number of spaces to place between columns
*/
private int delimiterWidth;
/**
* Table of data to print
*/
private List<List<String>> data;
/**
* The row format (generated by the constructor).
*/
private String rowFormat;
public CliTablePrinter(List<String> labels, List<String> flags, int indentation, int delimiterWidth,
List<List<String>> data) {
Preconditions.checkArgument(data.size() > 0);
Preconditions.checkArgument(data.get(0).size() > 0);
if (labels != null) {
Preconditions.checkArgument(data.get(0).size() == labels.size());
}
if (flags != null) {
Preconditions.checkArgument(data.get(0).size() == flags.size());
}
this.labels = labels;
this.flags = flags;
this.indentation = indentation;
this.delimiterWidth = delimiterWidth;
this.data = data;
this.rowFormat = getRowFormat(getColumnMaxWidths());
}
/**
* Used to build a {@link CliTablePrinter} object.
*/
public static final class Builder {
private List<String> labels;
private List<String> flags;
private int indentation;
private int delimiterWidth;
private List<List<String>> data;
public Builder() {
// Set defaults
this.delimiterWidth = 1;
}
public Builder labels(List<String> labels) {
this.labels = labels;
return this;
}
public Builder data(List<List<String>> data) {
this.data = data;
return this;
}
public Builder indentation(int indentation) {
this.indentation = indentation;
return this;
}
public Builder delimiterWidth(int delimiterWidth) {
this.delimiterWidth = delimiterWidth;
return this;
}
public Builder flags(List<String> flags) {
this.flags = flags;
return this;
}
public CliTablePrinter build() {
return new CliTablePrinter(this.labels, this.flags, this.indentation, this.delimiterWidth, this.data);
}
}
/**
* Prints the table of data
*/
public void printTable() {
if (this.labels != null) {
System.out.printf(this.rowFormat, this.labels.toArray());
}
for (List<String> row : this.data) {
System.out.printf(this.rowFormat, row.toArray());
}
}
/**
* A function for determining the max widths of columns, accounting for labels and data.
*
* @return An array of maximum widths for the strings in each column
*/
private List<Integer> getColumnMaxWidths() {
int numCols = data.get(0).size();
int[] widths = new int[numCols];
if (this.labels != null) {
for (int i=0; i<numCols; i++) {
widths[i] = this.labels.get(i).length();
}
}
for (List<String> row : this.data) {
for (int i=0;i<row.size(); i++) {
if (row.get(i) == null) {
widths[i] = Math.max(widths[i], 4);
} else {
widths[i] = Math.max(widths[i], row.get(i).length());
}
}
}
return Ints.asList(widths);
}
/**
* Generates a simple row format string given a set of widths
*
* @param widths A list of widths for each column in the table
* @return A row format for each row in the table
*/
private String getRowFormat(List<Integer> widths) {
StringBuilder rowFormat = new StringBuilder(spaces(this.indentation));
for (int i=0; i< widths.size(); i++) {
rowFormat.append("%");
rowFormat.append(this.flags != null ? this.flags.get(i) : "");
rowFormat.append(widths.get(i).toString());
rowFormat.append("s");
rowFormat.append(spaces(this.delimiterWidth));
}
rowFormat.append("\n");
return rowFormat.toString();
}
private static String spaces(int numSpaces) {
StringBuilder sb = new StringBuilder();
for (int i=0; i<numSpaces; i++) {
sb.append(" ");
}
return sb.toString();
}
}