Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,7 @@ target

pom.xml.*
release.properties

.settings/
.project/
.classpath/
45 changes: 45 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,52 @@ System.out.println(FlipTableConverters.fromObjects(headers, data));
╚════════════╧═══════════╧═════╧═════════╝
```

Column wrapping (optional)
--------------------------

**Fixed Table width**
```java
String[] headers = { "First Name", "Last Name", "Details" };
String[][] data = {
{ "One One One One", "Two Two Two:Two", "Three Three.Three,Three" },
{ "Joe", "Smith", "Hello" }
};
System.out.println(FlipTable.of(headers, data, FixedWidth.withWidth(30)));

```
```
╔════════════╤═══════════╤═════════════╗
║ First Name │ Last Name │ Details ║
╠════════════╪═══════════╪═════════════╣
║ One One │ Two Two │ Three Three ║
║ One One │ Two:Two │ .Three, ║
║ │ │ Three ║
╟────────────┼───────────┼─────────────╢
║ Joe │ Smith │ Hello ║
╚════════════╧═══════════╧═════════════╝
```

**Custom Column widths**
```java
String[] headers = { "First", "Last", "Det" };
String[][] data = {
{ "One One One One", "Two Two Two:Two", "Fifteen four on on on on on five" },
{ "Joe", "Boe", "Hello" }
};
System.out.println(FlipTable.of(headers, data, CustomColumnWidth.withColumnWidths(new int[] {5, 5, 8})));
```
```
╔═══════╤═══════╤══════════╗
║ First │ Last │ Det ║
╠═══════╪═══════╪══════════╣
║ One │ Two │ Fifteen ║
║ One │ Two │ four on ║
║ One │ Two: │ on on on ║
║ One │ Two │ on five ║
╟───────┼───────┼──────────╢
║ Joe │ Boe │ Hello ║
╚═══════╧═══════╧══════════╝
```

Download
--------
Expand Down
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
Expand Down
22 changes: 16 additions & 6 deletions src/main/java/com/jakewharton/fliptables/FlipTable.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@
*/
package com.jakewharton.fliptables;

import com.jakewharton.fliptables.format.wrap.ColumnWrapFormat;
import com.jakewharton.fliptables.format.wrap.DefaultWrapFormat;
import com.jakewharton.fliptables.format.wrap.WrappedTableData;

/**
* <pre>
* ╔═════════════╤════════════════════════════╤══════════════╗
Expand All @@ -30,10 +34,13 @@ public final class FlipTable {

/** Create a new table with the specified headers and row data. */
public static String of(String[] headers, String[][] data) {
return of(headers, data, new DefaultWrapFormat());
}
public static String of(String[] headers, String[][] data, ColumnWrapFormat columnWrapFormat) {
if (headers == null) throw new NullPointerException("headers == null");
if (headers.length == 0) throw new IllegalArgumentException("Headers must not be empty.");
if (data == null) throw new NullPointerException("data == null");
return new FlipTable(headers, data).toString();
return new FlipTable(headers, data, columnWrapFormat).toString();
}

private final String[] headers;
Expand All @@ -42,12 +49,10 @@ public static String of(String[] headers, String[][] data) {
private final int[] columnWidths;
private final int emptyWidth;

private FlipTable(String[] headers, String[][] data) {
this.headers = headers;
this.data = data;

private FlipTable(String[] headers, String[][] data, ColumnWrapFormat columnWrapFormat) {

columns = headers.length;
columnWidths = new int[columns];
int[] columnWidths = new int[columns];
for (int row = -1; row < data.length; row++) {
String[] rowData = (row == -1) ? headers : data[row]; // Hack to parse headers too.
if (rowData.length != columns) {
Expand All @@ -61,6 +66,11 @@ private FlipTable(String[] headers, String[][] data) {
}
}
}

WrappedTableData wrappedTableData = columnWrapFormat.adjustData(headers, data, columnWidths);
this.headers = wrappedTableData.getHeaders();
this.data = wrappedTableData.getData();
this.columnWidths = wrappedTableData.getColumnWidths();

int emptyWidth = 3 * (columns - 1); // Account for column dividers and their spacing.
for (int columnWidth : columnWidths) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package com.jakewharton.fliptables.format.wrap;

/**
* Provides a mechanism to wrap the text of the columns.
* Line breaks are added to break long texts without breaking the words
*/
public abstract class ColumnWrapFormat {

private static final String SPLITTER_REGEX = "((?=:|,|\\.|\\s)|(?<=:|,|\\.|\\s))";
private static final String LINE_BREAK = "\n";

public abstract WrappedTableData adjustData(String[] headers, String[][] data, int[] columnWidths);

protected String[][] adjustedData(String[][] data, int[] adjustedWidths) {
String[][] adjustedData = new String[data.length][adjustedWidths.length];
for(int row = 0; row < data.length; row++) {
for(int col = 0; col < adjustedWidths.length; col++) {
adjustedData[row][col] = adjustFieldData(data[row][col], adjustedWidths[col]);
}
}
return adjustedData;
}

protected String adjustFieldData(String field, int desiredWidth) {
if(null == field || field.isEmpty() || field.length() <= desiredWidth) {
return field;
}
return withLineBreaks(field, desiredWidth);
}

/**
* Adds line breaks on maxLineLength boundaries without breaking the words
*/
private String withLineBreaks(String input, int maxLineLength) {
String[] components = input.split(SPLITTER_REGEX);
StringBuilder builder = new StringBuilder();
int lineLength = 0, iterator = 0;
do {
String component = components[iterator];
if(lineLength == 0 || (lineLength + component.length() <= maxLineLength)) {
builder.append(component);
lineLength += component.length();
iterator++;
}
else {
builder.append(LINE_BREAK);
lineLength = 0;
}
} while(iterator < components.length);

return builder.toString();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.jakewharton.fliptables.format.wrap;

/**
* Adjusting the columns of the table to the user provided column widths
* A column is never made narrower than the length of the header
*/
public class CustomColumnWidth extends ColumnWrapFormat {

private int[] desiredColumnWidths;

public static CustomColumnWidth withColumnWidths(int[] desiredColumnWidths) {
return new CustomColumnWidth(desiredColumnWidths);
}

private CustomColumnWidth(int[] desiredColumnWidths) {
this.desiredColumnWidths = desiredColumnWidths;
}

@Override
public WrappedTableData adjustData(String[] headers, String[][] data, int[] columnWidths) {
if (desiredColumnWidths.length != headers.length) {
throw new IllegalArgumentException("Length of the array of the desired columns does not match the number of columns");
}

int[] adjustedWidths = new int[columnWidths.length];
for(int col = 0; col < columnWidths.length; col++) {
adjustedWidths[col] = Math.max(headers[col].length(), desiredColumnWidths[col]);
}
return new WrappedTableData(headers, adjustedData(data, adjustedWidths), adjustedWidths);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.jakewharton.fliptables.format.wrap;

public class DefaultWrapFormat extends ColumnWrapFormat {

@Override
public WrappedTableData adjustData(String[] headers, String[][] data, int[] columnWidths) {
return new WrappedTableData(headers, data, columnWidths);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.jakewharton.fliptables.format.wrap;

import java.util.Arrays;

/**
* Reduces the individual column widths proportional to the table width.
* A column is never made narrower than the length of the header
*/
public class FixedWidth extends ColumnWrapFormat {

private int maxWidth;

public static FixedWidth withWidth(int tableWidth) {
return new FixedWidth(tableWidth);
}

private FixedWidth(int width) {
this.maxWidth = width;
}

@Override
public WrappedTableData adjustData(String[] headers, String[][] data, int[] columnWidths) {
int currentLength = Arrays.stream(columnWidths).sum();
double ratio = maxWidth / (1.0 * currentLength);
if(ratio > 1.0d) {
return new WrappedTableData(headers, data, columnWidths);
}
int[] adjustedWidths = adjustedColumnWidths(headers, columnWidths, ratio);
return new WrappedTableData(headers, adjustedData(data, adjustedWidths), adjustedWidths);
}

private int[] adjustedColumnWidths(String headers[], int[] columnWidths, double ratio) {
int[] adjustedWidths = headers.length == 1 ? new int[] { maxWidth }: new int[headers.length];
int index = 0, sum = 0;
for(; index < headers.length - 1; index++) {
adjustedWidths[index] = Math.max((int) (ratio * columnWidths[index]), headers[index].length());
sum += adjustedWidths[index];
}
adjustedWidths[index] = Math.max(maxWidth - sum, headers[index].length());
return adjustedWidths;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.jakewharton.fliptables.format.wrap;

/**
* Class to hold the adjusted (wrapped) data out
*/
public class WrappedTableData {
String[] headers;
String[][] data;
int[] columnWidths;

public WrappedTableData() {}

public WrappedTableData(String[] headers, String[][] data, int[] columnWidths) {
this.headers = headers;
this.data = data;
this.columnWidths = columnWidths;
}

public String[] getHeaders() {
return this.headers;
}

public String[][] getData() {
return this.data;
}

public int[] getColumnWidths() {
return this.columnWidths;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package com.jakewharton.fliptables.format.wrap;

import static org.assertj.core.api.Assertions.assertThat;

import java.io.IOException;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import org.junit.Test;

import com.jakewharton.fliptables.FlipTable;
import com.jakewharton.fliptables.util.ResourceReader;

public class ColumnWrapTest {

private String[] headers = new String[] {"Feather", "Weather", "Fizz", "Buzz"};
private String[][] data = new String[3][headers.length];

public ColumnWrapTest() {
for(int idx = 0; idx < 3; idx++) {
data[idx][0] = columnData("a", 6, 10);
data[idx][1] = columnData("b", 6, 5);
data[idx][2] = columnData("c", 6, 15);
data[idx][3] = columnData("d", 6, 25);
}
data[1][2] = "some long words somelongwords somelongerwords someevenlongerwords someevenmorelongerwords";
}

private static String columnData(String base, int wordSize, int words) {
return IntStream.range(0, words)
.mapToObj(wordIndex -> IntStream.range(0, wordSize).mapToObj(charIndex -> base).collect(Collectors.joining()))
.collect(Collectors.joining(" "));
}

@Test
public void testFixedWidthWrapping() throws IOException {
String read = ResourceReader.readFromResourceFile("fixed-width-wrapping.txt");
String flipTable = FlipTable.of(headers, data, FixedWidth.withWidth(120));
assertThat(flipTable).isEqualTo(read);
}

@Test
public void testCustomWidthWrapping() throws IOException {
String read = ResourceReader.readFromResourceFile("custom-width-wrapping.txt");
String flipTable = FlipTable.of(headers, data, CustomColumnWidth.withColumnWidths(new int[] {30, 30, 30, 30}));
assertThat(flipTable).isEqualTo(read);
}

public static void main(String args[]) {
String[] headers = { "First", "Last", "Det" };
String[][] data = {
{ "One One One One", "Two Two Two:Two", "Fifteen four on on on on on five" },
{ "Joe", "Boe", "Hello" }
};
System.out.println(FlipTable.of(headers, data, CustomColumnWidth.withColumnWidths(new int[] {5, 5, 8})));
}

}
26 changes: 26 additions & 0 deletions src/test/java/com/jakewharton/fliptables/util/ResourceReader.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.jakewharton.fliptables.util;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;

public class ResourceReader {

public static String readFromResourceFile(String filePath) throws IOException {

try(InputStream inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(filePath)) {
StringBuilder textBuilder = new StringBuilder();
try (Reader reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName(StandardCharsets.UTF_8.name())))) {
int c = 0;
while ((c = reader.read()) != -1) {
textBuilder.append((char) c);
}
}
return textBuilder.toString();
}
}
}
Loading