package org.basex.util;
import static org.basex.core.Text.*;
import static org.basex.util.Token.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Scanner;
import org.basex.util.list.BoolList;
import org.basex.util.list.IntList;
import org.basex.util.list.TokenList;
/**
* This is a table representation for textual table output.
* It should be guaranteed that the {@link #header} object has the
* same number of entries as all {@link #contents} string arrays.
*
* @author BaseX Team 2005-12, BSD License
* @author Christian Gruen
*/
public final class Table {
/** Distance between table columns. */
private static final int DIST = 2;
/** Table header. */
public final TokenList header = new TokenList();
/** Alignment (false: left, true: right alignment). */
public final BoolList align = new BoolList();
/** Table contents. */
public final ArrayList<TokenList> contents = new ArrayList<TokenList>();
/** Data description; if available, will be added as footer. */
public String description;
/**
* Default constructor.
*/
public Table() { }
/**
* Constructor with table input.
* @param in textual table input
*/
public Table(final String in) {
if(in.isEmpty()) return;
// parse table header
final Scanner scan = new Scanner(in);
byte[] line = token(scan.nextLine());
final IntList il = new IntList();
int i = 0;
while(i < line.length) {
il.add(i);
// find next two spaces
while(++i + 1 < line.length && (line[i] != ' ' || line[i + 1] != ' '));
header.add(substring(line, il.get(il.size() - 1), i));
while(++i < line.length && line[i] == ' ');
}
il.add(i);
// skip delimiter
scan.nextLine();
// parse table entries
final int s = il.size() - 1;
while((line = token(scan.nextLine())).length != 0) {
final TokenList entry = new TokenList();
for(int e = 0; e < s; ++e) {
entry.add(trim(substring(line, il.get(e), il.get(e + 1))));
}
contents.add(entry);
}
}
/**
* Sorts the table by the first column.
*/
public void sort() {
Collections.sort(contents, new Comparator<TokenList>() {
@Override
public int compare(final TokenList tl1, final TokenList tl2) {
return diff(lc(tl1.get(0)), lc(tl2.get(0)));
}
});
}
/**
* Returns the value for the specified table position.
* @param r row
* @param c column
* @return value
*/
public String value(final int r, final int c) {
return string(contents.get(r).get(c));
}
/**
* Returns the number of rows.
* @return number of rows
*/
public int rows() {
return contents.size();
}
/**
* Returns the number of columns.
* @return number of columns
*/
public int cols() {
return header.size();
}
/**
* Moves the specified string to top.
* @param top entry to be moved to the top
*/
public void toTop(final byte[] top) {
for(int i = 0; i < contents.size(); ++i) {
if(eq(top, contents.get(i).get(0))) {
contents.add(0, contents.remove(i));
return;
}
}
}
/**
* Returns a textual representation of the table.
* @return text
*/
public byte[] finish() {
final int[] ind = new int[header.size()];
final int sz = header.size();
for(int s = 0; s < sz; ++s) {
for(final TokenList e : contents) {
ind[s] = Math.max(ind[s], e.get(s).length);
}
ind[s] = Math.max(ind[s], header.get(s).length);
}
final TokenBuilder tb = new TokenBuilder();
for(int u = 0; u < sz; ++u) {
final byte[] s = header.get(u);
final int is = ind[u] - s.length + DIST;
tb.add(s);
for(int i = 0; i < is; ++i) tb.add(' ');
}
tb.add(NL);
for(int u = 0; u < sz; ++u) {
for(int i = 0; i < ind[u] + (u + 1 == sz ? 0 : DIST); ++i) tb.add('-');
}
tb.add(NL);
for(final TokenList e : contents) {
for(int u = 0; u < sz; ++u) {
final byte[] s = e.get(u);
final int is = ind[u] - s.length;
if(u < align.size() && align.get(u)) {
for(int i = 0; i < is; ++i) tb.add(' ');
tb.add(s);
} else {
tb.add(s);
for(int i = 0; i < is; ++i) tb.add(' ');
}
for(int i = 0; i < DIST; ++i) tb.add(' ');
}
tb.add(NL);
}
if(description != null) {
tb.add(NL).add(contents.size() + " " + description + DOT);
}
return tb.finish();
}
@Override
public String toString() {
final TokenBuilder tb = new TokenBuilder();
for(final byte[] b : header) {
tb.add(b);
tb.add('\t');
}
tb.add(NL);
for(final TokenList e : contents) {
for(final byte[] b : e) {
tb.add(b);
tb.add('\t');
}
}
return tb.toString();
}
}