package fitnesse.wikitext.parser;
public class Table extends SymbolType implements Rule, Translation {
public static final Table symbolType = new Table();
public static final SymbolType tableRow = new SymbolType("TableRow");
public static final SymbolType tableCell = new SymbolType("TableCell");
public Table() {
super("Table");
wikiMatcher(new Matcher().startLine().string("|"));
wikiMatcher(new Matcher().startLine().string("!|"));
wikiMatcher(new Matcher().startLine().string("-|"));
wikiMatcher(new Matcher().startLine().string("-!|"));
wikiRule(this);
htmlTranslation(this);
}
@Override
public Maybe<Symbol> parse(Symbol current, Parser parser) {
String content = current.getContent();
if (content.charAt(0) == '-') current.putProperty("hideFirst", "");
boolean endOfTable = false;
while (!endOfTable) {
Symbol row = new Symbol(tableRow);
row.setStartOffset(parser.getOffset());
current.add(row);
while (true) {
int offset = parser.getOffset();
Symbol cell = parseCell(parser, content);
if (parser.getOffset() == offset) {
endOfTable = true;
break;
}
if (parser.atEnd()) return Symbol.nothing;
if (containsNewLine(cell)) return Symbol.nothing;
row.add(cell);
if (endsRow(parser.getCurrent())) break;
}
row.setEndOffset(parser.getOffset());
if (!startsRow(parser.getCurrent())) break;
}
return new Maybe<>(current);
}
private Symbol parseCell(Parser parser, String content) {
Symbol cell = (content.contains("!"))
? parser.parseToWithSymbols(SymbolType.EndCell, SymbolProvider.literalTableProvider, ParseSpecification.tablePriority)
: parser.parseToWithSymbols(SymbolType.EndCell, SymbolProvider.tableParsingProvider, ParseSpecification.tablePriority);
cell.setType(tableCell);
return cell;
}
private boolean containsNewLine(Symbol cell) {
for (Symbol child : cell.getChildren()) {
if (child.isType(SymbolType.Newline)) return true;
}
return false;
}
private boolean endsRow(Symbol symbol) {
return symbol.getContent().indexOf("\n") > 0;
}
private boolean startsRow(Symbol symbol) {
return symbol.getContent().contains("\n|");
}
@Override
public String toTarget(Translator translator, Symbol symbol) {
HtmlWriter writer = new HtmlWriter();
writer.startTag("table");
if (symbol.hasProperty("class")) {
writer.putAttribute("class", symbol.getProperty("class"));
}
int longestRow = longestRow(symbol);
int rowCount = 0;
for (Symbol child : symbol.getChildren()) {
rowCount++;
writer.startTag("tr");
if (rowCount == 1 && symbol.hasProperty("hideFirst")) {
writer.putAttribute("class", "hidden");
}
int extraColumnSpan = longestRow - rowLength(child);
int column = 1;
for (Symbol grandChild : child.getChildren()) {
String body = translateCellBody(translator, grandChild);
writer.startTag("td");
if (extraColumnSpan > 0 && column == rowLength(child))
writer.putAttribute("colspan", Integer.toString(extraColumnSpan + 1));
writer.putText(body);
writer.endTag();
column++;
}
writer.endTag();
}
writer.endTag();
return writer.toHtml();
}
protected String translateCellBody(Translator translator, Symbol cell) {
final String literalDelimiter = new String(new char[]{255, 1, 255});
cell.walkPreOrder(new SymbolTreeWalker() {
@Override
public boolean visit(Symbol node) {
if (node.isType(Literal.symbolType)) {
node.setContent(literalDelimiter + node.getContent() + literalDelimiter);
}
return true;
}
@Override
public boolean visitChildren(Symbol node) {
return true;
}
});
return translator.translate(cell).trim().replace(literalDelimiter, "");
}
protected int longestRow(Symbol table) {
int longest = 0;
for (Symbol row : table.getChildren()) {
int length = rowLength(row);
if (length > longest) longest = length;
}
return longest;
}
protected int rowLength(Symbol row) {
return row.getChildren().size();
}
}