package fitnesse.slim.converters;
import java.util.HashMap;
import java.util.Map;
import fitnesse.html.HtmlTag;
import fitnesse.html.HtmlUtil;
import fitnesse.slim.Converter;
import org.htmlparser.Node;
import org.htmlparser.Parser;
import org.htmlparser.filters.TagNameFilter;
import org.htmlparser.tags.CompositeTag;
import org.htmlparser.util.NodeList;
import org.htmlparser.util.ParserException;
public class MapConverter implements Converter<Map> {
private NodeList nodes;
private NodeList tables;
@Override
public String toString(Map hash) {
if (hash == null) {
return NULL_VALUE;
}
HtmlTag table = createTag(hash);
return table.html().trim();
}
protected HtmlTag createTag(Map<?, ?> hash) {
// Use HtmlTag, same as we do for fitnesse.wikitext.parser.HashTable.
HtmlTag table = new HtmlTag("table");
table.addAttribute("class", "hash_table");
for (Map.Entry<?, ?> entry : hash.entrySet()) {
HtmlTag row = new HtmlTag("tr");
row.addAttribute("class", "hash_row");
table.add(row);
HtmlTag keyCell = new HtmlTag("td");
addCellContent(keyCell, entry.getKey());
keyCell.addAttribute("class", "hash_key");
row.add(keyCell);
HtmlTag valueCell = new HtmlTag("td");
addCellContent(valueCell, entry.getValue());
valueCell.addAttribute("class", "hash_value");
row.add(valueCell);
}
return table;
}
protected void addCellContent(HtmlTag valueCell, Object cellValue) {
String valueToAdd = ElementConverterHelper.elementToString(cellValue);
if (!HtmlUtil.isValidTableCellContent(valueToAdd))
valueToAdd = HtmlUtil.escapeHTML(valueToAdd);
valueCell.add(valueToAdd.trim());
}
@Override
public Map<String, String> fromString(String possibleTable) {
Map<String, String> map = new HashMap<>();
if (tableIsValid(possibleTable))
extractRowsIntoMap(map, tables);
return map;
}
private boolean tableIsValid(String possibleTable) {
if (isValidHtml(possibleTable)) {
return hasOneTable();
} else {
return false;
}
}
private boolean hasOneTable() {
TagNameFilter tableFilter = new TagNameFilter("table");
tables = nodes.extractAllNodesThatMatch(tableFilter);
return tables.size() == 1;
}
private boolean isValidHtml(String possibleTable) {
nodes = parseHtml(possibleTable);
return nodes != null;
}
private void extractRowsIntoMap(Map<String, String> map, NodeList tables) {
extractRows(map, getRows(tables));
}
private void extractRows(Map<String, String> map, NodeList rows) {
for (int rowIndex = 0; rowIndex < rows.size(); rowIndex++) {
extractRow(map, rows, rowIndex);
}
}
private void extractRow(Map<String, String> map, NodeList rows, int rowIndex) {
Node row = rows.elementAt(rowIndex);
if (row != null)
extractColumns(map, row);
}
private void extractColumns(Map<String, String> map, Node row) {
TagNameFilter tdFilter = new TagNameFilter("td");
if (row.getChildren() != null) {
NodeList cols = row.getChildren().extractAllNodesThatMatch(tdFilter);
if (cols.size() == 2)
addColsToMap(map, cols);
}
}
private void addColsToMap(Map<String, String> map, NodeList cols) {
String key = getText(cols.elementAt(0));
String value = getText(cols.elementAt(1));
map.put(key, value);
}
private NodeList getRows(NodeList tables) {
TagNameFilter trFilter = new TagNameFilter("tr");
Node table = tables.elementAt(0);
if (table.getChildren() != null)
return table.getChildren().extractAllNodesThatMatch(trFilter);
return new NodeList();
}
private String getText(Node compositeNode) {
return HtmlUtil.unescapeHTML(((CompositeTag) compositeNode).getChildrenHTML());
}
private NodeList parseHtml(String possibleTable) {
try {
Parser parser = new Parser(possibleTable);
return parser.parse(null);
} catch (ParserException e) {
return null;
}
}
}