If you'd like to print this:
One Two Three Four
1 2 3 4
using this:
new Columns()
.withColumnSeparator(" ")
.withPad(" ")
// .alignLeft()
.alignCenter()
// .alignRight()
.addLine("One", "Two", "Three", "Four")
.addLine("1", "2", "3", "4")
.print()
;
all you need is this:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
import java.util.function.UnaryOperator;
public class Columns {
private List<List<String>> lines = new ArrayList<>();
private List<Integer> maxLengths = new ArrayList<>();
private String columnSeparator = " ";
private String pad = " ";
private Function<String, Function<String, UnaryOperator<String>>> align;
{
alignLeft(); // set default alignment
}
public Columns addLine(String... line) {
if (maxLengths.size() == 0){
for(int column = 0; column < line.length; column++) {
maxLengths.add(0);
}
}
if ( maxLengths.size() != line.length ) {
throw new IllegalArgumentException();
}
for(int column = 0; column < line.length; column++) {
int length = Math
.max(
maxLengths.get(column),
line[column].length()
)
;
maxLengths.set( column, length );
}
lines.add( Arrays.asList(line) );
return this;
}
public Columns print(){
System.out.println( toString() );
return this;
}
public String toString(){
int totalLength = 0;
for(List<String> line : lines) {
for(int column = 0; column < line.size(); column++) {
totalLength += maxLengths.get(column);
if (column < line.size() - 1) {
totalLength += columnSeparator.length();
}
}
totalLength += System.lineSeparator().length();
}
StringBuilder result = new StringBuilder(totalLength);
for(List<String> line : lines) {
for(int column = 0; column < line.size(); column++) {
result.append(
padCell(
line.get(column),
maxLengths.get(column)
)
);
if (column < line.size() - 1) {
result.append(columnSeparator);
}
}
result.append( System.lineSeparator() );
}
return result.toString();
}
private String padCell(String word, int newLength){
int padCount = newLength - word.length();
int leftCount = padCount / 2;
int rightCount = padCount - leftCount;
String left = new String(new char[leftCount]).replace("\0", pad);
String right = new String(new char[rightCount]).replace("\0", pad);
return align.apply(left).apply(word).apply(right);
}
public Columns separateColumnsWith(String columnSeparator){
this.columnSeparator = columnSeparator;
return this;
}
public Columns padWith(String pad){
this.pad = pad;
return this;
}
public Columns alignLeft(){
align = left -> word -> right -> word + left + right;
return this;
}
public Columns alignCenter(){
align = left -> word -> right -> left + word + right;
return this;
}
public Columns alignRight(){
align = left -> word -> right -> left + right + word;
return this;
}
}
By waiting until all the lines have been added before printing it can figure out the width each column needs.
Looking for a code review to reveal problems. Anything from better names to fewer bugs to better ideas.
Along with some minor fixes the string concatenation has been modified which simplified the alignment lambdas (and lengthened padCell()
and toString()
). Micro-optimization?
/**
comments would be welcome. LGTM, ship it! \$\endgroup\$