/**
* Copyright 2014 Alexey Ragozin
*
* Licensed 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 org.gridkit.util.formating;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* @author Alexey Ragozin (alexey.ragozin@gmail.com)
*/
public class TextTable {
private List<String[]> rows = new ArrayList<String[]>();
private int colCount;
public void transpone() {
int rc = rows.size();
int cc = colCount;
List<String[]> nrows = new ArrayList<String[]>();
for(int i = 0; i != cc; ++i) {
String[] nrow = new String[cc];
for(int j = 0; j != rc; ++i) {
nrow[j] = rows.get(j)[i];
}
nrows.add(nrow);
}
rows = nrows;
colCount = rc;
}
public void addRow(String... row) {
addRow(row, false);
}
public void addRow(List<String> row) {
addRow(row.toArray(new String[row.size()]), false);
}
public void addRow(List<String> row, boolean autoGrow) {
addRow(row.toArray(new String[row.size()]), autoGrow);
}
public void addRow(String[] row, boolean autoGrow) {
if (rows.size() == 0) {
colCount = row.length;
}
if (row.length > colCount) {
if (autoGrow) {
extendRows(row.length);
}
else {
throw new IllegalArgumentException("Row is longer than table");
}
}
rows.add(Arrays.copyOf(row, colCount));
}
private void extendRows(int length) {
for(int i = 0; i != rows.size(); ++i) {
rows.set(i, Arrays.copyOf(rows.get(i), length));
}
colCount = length;
}
public void addColumnRight(List<String> col) {
addColumnRight(col.toArray(new String[col.size()]));
}
public void addColumnRight(String... col) {
if (col.length > rows.size()) {
throw new IllegalArgumentException("Column is taller than table");
}
colCount += 1;
for(int i = 0; i != rows.size(); ++i) {
String[] row = rows.get(i);
row = Arrays.copyOf(row, colCount);
if (col.length > i) {
row[colCount - 1] = col[i];
}
rows.set(i, row);
}
}
public void addColumnLeft(List<String> col) {
addColumnLeft(col.toArray(new String[col.size()]));
}
public void addColumnLeft(String[] col) {
if (col.length > rows.size()) {
throw new IllegalArgumentException("Column is taller than table");
}
colCount += 1;
for(int i = 0; i != rows.size(); ++i) {
String[] row = rows.get(i);
String[] nrow = new String[colCount];
System.arraycopy(row, 0, nrow, 1, row.length);
if (col.length > i) {
nrow[0] = col[i];
}
rows.set(i, nrow);
}
}
public String formatTextTable(int maxCellWidth) {
return formatTable(rows, maxCellWidth, true);
}
public String formatToCSV() {
try {
StringWriter sw = new StringWriter();
CSVWriter writer = new CSVWriter(sw, ',', '"', '"', "\n");
for(String[] row: rows) {
writer.writeNext(row);
}
writer.close();
return sw.toString();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public String formatTextTableUnbordered(int maxCellWidth) {
return formatTable(rows, maxCellWidth, false);
}
private String formatTable(List<String[]> content, int maxCell, boolean table) {
int[] width = new int[content.get(0).length];
for(String[] row: content) {
for(int i = 0; i != row.length; ++i) {
width[i] = Math.min(Math.max(width[i], measureCell(row[i])), maxCell);
}
}
StringBuilder sb = new StringBuilder();
boolean header = table;
for(String[] row: content) {
for(int i = 0; i != width.length; ++i) {
renderCell(sb, row[i], width[i]);
if (table) {
sb.append('|');
}
}
if (table) {
sb.setLength(sb.length() - 1);
}
trimTail(sb);
sb.append('\n');
if (header) {
header = false;
for(int n: width) {
for(int i = 0; i != n; ++i) {
sb.append('-');
}
sb.append('+');
}
sb.setLength(sb.length() - 1);
trimTail(sb);
sb.append('\n');
}
}
return sb.toString();
}
protected int measureCell(String cell) {
if (cell == null) {
return 0;
}
else {
int len = 0;
for(int i = 0; i != cell.length(); ++i) {
if (cell.charAt(i) != '\t') {
len++;
}
}
return len;
}
}
protected void renderCell(StringBuilder sb, String rawCell, int width) {
String cell = rawCell == null ? "" : rawCell;
int tabs = 0;
for(int i = 0; i != cell.length(); ++i) {
if (cell.charAt(i) == '\t') {
tabs++;
}
}
if (cell.length() - tabs > width) {
int n = width - 3;
for(int i = 0; i != cell.length(); ++i) {
if (cell.charAt(i) != '\t') {
sb.append(cell.charAt(i));
if (0 == --n) {
break;
}
}
}
sb.append("...");
}
else {
if (tabs == 0) {
sb.append(cell);
for(int s = 0; s != width - cell.length(); ++s) {
sb.append(' ');
}
}
else {
int gap = width - cell.length() + tabs;
for(int i = 0; i != cell.length(); ++i) {
if (cell.charAt(i) == '\t') {
int fill = (gap + tabs - 1) / tabs;
for(int j = 0; j != fill; ++j) {
sb.append(' ');
}
gap -= fill;
tabs--;
}
else {
sb.append(cell.charAt(i));
}
}
}
}
}
private static void trimTail(StringBuilder sb) {
while(sb.length() > 0 && sb.charAt(sb.length() - 1) == ' ') {
sb.setLength(sb.length() - 1);
}
}
class CSVWriter {
private Writer rawWriter;
private PrintWriter pw;
private char separator;
private char quoteChar;
private char quoteEscapeChar;
private String lineEnd;
public CSVWriter(Writer writer, char separator, char quotechar, char escapechar, String lineEnd) {
this.rawWriter = writer;
this.pw = new PrintWriter(writer);
this.separator = separator;
this.quoteChar = quotechar;
this.quoteEscapeChar = escapechar;
this.lineEnd = lineEnd;
}
public void writeNext(String[] nextLine) {
if (nextLine == null)
return;
StringBuilder sb = new StringBuilder();
for (int i = 0; i < nextLine.length; i++) {
if (i != 0) {
sb.append(separator);
}
String nextElement = nextLine[i];
if (nextElement == null)
continue;
if (quoteChar != 0)
sb.append(quoteChar);
sb.append(stringContainsSpecialCharacters(nextElement) ? processLine(nextElement) : nextElement);
if (quoteChar != 0)
sb.append(quoteChar);
}
sb.append(lineEnd);
pw.write(sb.toString());
}
private boolean stringContainsSpecialCharacters(String line) {
return line.indexOf(quoteChar) != -1 || line.indexOf(quoteEscapeChar) != -1;
}
protected StringBuilder processLine(String nextElement)
{
StringBuilder sb = new StringBuilder();
for (int j = 0; j < nextElement.length(); j++) {
char nextChar = nextElement.charAt(j);
if (quoteEscapeChar != 0 && nextChar == quoteChar) {
sb.append(quoteEscapeChar).append(nextChar);
} else if (quoteEscapeChar != 0 && nextChar == quoteEscapeChar) {
sb.append(quoteEscapeChar).append(nextChar);
} else {
sb.append(nextChar);
}
}
return sb;
}
public void flush() throws IOException {
pw.flush();
}
public void close() throws IOException {
flush();
pw.close();
rawWriter.close();
}
public boolean checkError() {
return pw.checkError();
}
}
}