Refactored table html rendering classes away from TableContainer

TableContainer is deprecated and this has been on the todo list for a while
This commit is contained in:
Risto Lahtela 2020-11-19 16:10:18 +02:00
parent 35ca5a8560
commit d402b1c832
9 changed files with 370 additions and 62 deletions

View File

@ -16,6 +16,8 @@
*/
package com.djrapitops.plan.delivery.rendering.html.icon;
import org.apache.commons.lang3.StringUtils;
import java.util.Optional;
public enum Color {
@ -61,4 +63,8 @@ public enum Color {
public String getHtmlClass() {
return htmlClass;
}
public String getBackgroundColorClass() {
return StringUtils.replace(htmlClass, "col-", "bg-");
}
}

View File

@ -0,0 +1,90 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.delivery.rendering.html.structure;
import com.djrapitops.plan.extension.table.Table;
import java.util.List;
public class DynamicHtmlTable implements HtmlTable {
private final Header[] headers;
private final List<Object[]> rows;
public DynamicHtmlTable(Header[] headers, List<Object[]> rows) {
this.headers = headers;
this.rows = rows;
}
public DynamicHtmlTable(Table table) {
this(HtmlTable.mapToHeaders(table), table.getRows());
}
@Override
public String toHtml() {
return "<div class=\"table-responsive\">" +
"<table class=\"table table-bordered table-striped table-hover player-plugin-table dataTable\">" +
buildTableHeader() +
buildTableBody() +
"</table>" +
"</div>";
}
private String buildTableHeader() {
StringBuilder builtHeader = new StringBuilder("<thead><tr>");
for (Header header : headers) {
builtHeader.append("<th>")
.append(header.getIcon().toHtml())
.append(' ')
.append(header.getText())
.append("</th>");
}
builtHeader.append("</tr></thead>");
return builtHeader.toString();
}
private String buildTableBody() {
StringBuilder builtBody = new StringBuilder();
builtBody.append("<tbody>");
if (rows.isEmpty()) {
appendRow(builtBody, "No Data");
}
for (Object[] row : rows) {
appendRow(builtBody, row);
}
return builtBody.append("</tbody>").toString();
}
private void appendRow(StringBuilder builtBody, Object... row) {
int headerLength = row.length - 1;
builtBody.append("<tr>");
for (int i = 0; i < headers.length; i++) {
try {
if (i > headerLength) {
builtBody.append("<td>-");
} else {
builtBody.append("<td>");
Object value = row[i];
builtBody.append(value != null ? value : '-');
}
builtBody.append("</td>");
} catch (ClassCastException | ArrayIndexOutOfBoundsException e) {
throw new IllegalStateException("Invalid formatter given at index " + i + ": " + e.getMessage(), e);
}
}
builtBody.append("</tr>");
}
}

View File

@ -0,0 +1,74 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.delivery.rendering.html.structure;
import com.djrapitops.plan.delivery.rendering.html.icon.Color;
import com.djrapitops.plan.delivery.rendering.html.icon.Icon;
import com.djrapitops.plan.extension.table.Table;
import java.util.ArrayList;
public interface HtmlTable {
static HtmlTable fromExtensionTable(Table table, com.djrapitops.plan.extension.icon.Color tableColor) {
return fromExtensionTable(table, Color.getByName(tableColor.name()).orElse(Color.NONE));
}
static HtmlTable fromExtensionTable(Table table, Color tableColor) {
if (table.getRows().size() > 50) {
return new DynamicHtmlTable(table);
} else {
return new HtmlTableWithColoredHeader(table, tableColor);
}
}
static Header[] mapToHeaders(Table table) {
ArrayList<Header> headers = new ArrayList<>();
com.djrapitops.plan.extension.icon.Icon[] icons = table.getIcons();
String[] columns = table.getColumns();
for (int i = 0; i < columns.length; i++) {
String column = columns[i];
if (column == null) {
break;
}
headers.add(new Header(Icon.fromExtensionIcon(icons[i]), column));
}
return headers.toArray(new Header[0]);
}
String toHtml();
class Header {
private final Icon icon;
private final String text;
public Header(Icon icon, String text) {
this.icon = icon;
this.text = text;
}
public Icon getIcon() {
return icon;
}
public String getText() {
return text;
}
}
}

View File

@ -0,0 +1,94 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.delivery.rendering.html.structure;
import com.djrapitops.plan.delivery.rendering.html.icon.Color;
import com.djrapitops.plan.extension.table.Table;
import java.util.List;
public class HtmlTableWithColoredHeader implements HtmlTable {
private final Header[] headers;
private final Color headerColor;
private final List<Object[]> rows;
public HtmlTableWithColoredHeader(Header[] headers, Color headerColor, List<Object[]> rows) {
this.headers = headers;
this.headerColor = headerColor;
this.rows = rows;
}
public HtmlTableWithColoredHeader(Table table, Color headerColor) {
this(HtmlTable.mapToHeaders(table), headerColor, table.getRows());
}
@Override
public String toHtml() {
return "<div class=\"scrollbar\">" +
"<table class=\"table table-striped\">" +
buildTableHeader() +
buildTableBody() +
"</table>" +
"</div>";
}
private String buildTableHeader() {
StringBuilder builtHeader = new StringBuilder("<thead class=\"" + headerColor.getBackgroundColorClass() + "\"><tr>");
for (Header header : headers) {
builtHeader.append("<th>")
.append(header.getIcon().toHtml())
.append(' ')
.append(header.getText())
.append("</th>");
}
builtHeader.append("</tr></thead>");
return builtHeader.toString();
}
private String buildTableBody() {
StringBuilder builtBody = new StringBuilder();
builtBody.append("<tbody>");
if (rows.isEmpty()) {
appendRow(builtBody, "No Data");
}
for (Object[] row : rows) {
appendRow(builtBody, row);
}
return builtBody.append("</tbody>").toString();
}
private void appendRow(StringBuilder builtBody, Object... row) {
int headerLength = row.length - 1;
builtBody.append("<tr>");
for (int i = 0; i < headers.length; i++) {
try {
if (i > headerLength) {
builtBody.append("<td>-");
} else {
builtBody.append("<td>");
Object value = row[i];
builtBody.append(value != null ? value : '-');
}
builtBody.append("</td>");
} catch (ClassCastException | ArrayIndexOutOfBoundsException e) {
throw new IllegalStateException("Invalid formatter given at index " + i + ": " + e.getMessage(), e);
}
}
builtBody.append("</tr>");
}
}

View File

@ -185,7 +185,7 @@ public class PlayerPluginTab implements Comparable<PlayerPluginTab> {
if (tableData.isWideTable()) {
hasWideTable = true;
}
builder.append(tableData.getHtmlTable().buildHtml());
builder.append(tableData.getHtmlTable().toHtml());
}
return builder.toString();
}

View File

@ -207,7 +207,7 @@ public class ServerPluginTabs {
private String buildTablesHtml(ExtensionTabData tabData) {
StringBuilder builder = new StringBuilder();
for (ExtensionTableData tableData : tabData.getTableData()) {
builder.append(tableData.getHtmlTable().buildHtml());
builder.append(tableData.getHtmlTable().toHtml());
}
return builder.toString();
}

View File

@ -16,15 +16,10 @@
*/
package com.djrapitops.plan.extension.implementation.results;
import com.djrapitops.plan.data.element.TableContainer;
import com.djrapitops.plan.delivery.rendering.html.structure.HtmlTable;
import com.djrapitops.plan.extension.icon.Color;
import com.djrapitops.plan.extension.icon.Icon;
import com.djrapitops.plan.extension.table.Table;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
/**
@ -44,41 +39,8 @@ public class ExtensionTableData implements Comparable<ExtensionTableData> {
this.tableColor = tableColor;
}
public TableContainer getHtmlTable() {
String[] columns = table.getColumns();
Icon[] icons = table.getIcons();
List<Object[]> rows = table.getRows();
String[] header = buildHeader(columns, icons);
TableContainer htmlTable = new TableContainer(header);
if (rows.size() > 50) {
htmlTable.useJqueryDataTables(); // Use a jQuery data table since there are a lot of rows.
} else {
String colorName = com.djrapitops.plan.delivery.rendering.html.icon.Color.getByName(tableColor.name()).orElse(com.djrapitops.plan.delivery.rendering.html.icon.Color.NONE).getHtmlClass()
.replace("col-", ""); // TODO after PluginData deprecation, change this thing
htmlTable.setColor(colorName);
}
for (Object[] row : rows) {
htmlTable.addRow(Arrays.stream(row).map(value -> value != null ? value.toString() : null).toArray(Serializable[]::new));
}
return htmlTable;
}
private String[] buildHeader(String[] columns, Icon[] icons) {
ArrayList<String> header = new ArrayList<>();
for (int i = 0; i < columns.length; i++) {
String column = columns[i];
if (column == null) {
break;
}
header.add(com.djrapitops.plan.delivery.rendering.html.icon.Icon.fromExtensionIcon(icons[i]).toHtml() + ' ' + column);
}
return header.toArray(new String[0]);
public HtmlTable getHtmlTable() {
return HtmlTable.fromExtensionTable(table, tableColor);
}
@Override

View File

@ -0,0 +1,79 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.delivery.rendering.html.structure;
import com.djrapitops.plan.extension.icon.Color;
import com.djrapitops.plan.extension.icon.Icon;
import com.djrapitops.plan.extension.table.Table;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
/**
* Sanity tests for the functionality of refactored html table rendering code.
*/
class HtmlTableTest {
@Test
void coloredTableProducesSameHtmlAsOldCode() {
// Produced by old code
String expected = "<div class=\"scrollbar\"><table class=\"table table-striped\"><thead class=\"bg-amber\"><tr><th><i class=\" fa fa-test\"></i> Col 1</th><th><i class=\" fa fa-test\"></i> Col 2</th><th><i class=\" fa fa-test\"></i> Col 3</th></tr></thead><tbody><tr><td>1</td><td>2</td><td>three</td></tr></tbody></table></div>";
Icon icon = Icon.called("test").build();
String result = HtmlTable.fromExtensionTable(Table.builder()
.columnOne("Col 1", icon)
.columnTwo("Col 2", icon)
.columnThree("Col 3", icon)
.addRow("1", 2, "three")
.build(), Color.AMBER).toHtml();
assertEquals(expected, result);
}
@Test
void coloredTableProducesSameHtmlAsOldCodeWhenEmpty() {
// Produced by old code
String expected = "<div class=\"scrollbar\"><table class=\"table table-striped\"><thead class=\"bg-amber\"><tr><th><i class=\" fa fa-test\"></i> Col 1</th><th><i class=\" fa fa-test\"></i> Col 2</th><th><i class=\" fa fa-test\"></i> Col 3</th></tr></thead><tbody><tr><td>No Data</td><td>-</td><td>-</td></tr></tbody></table></div>";
Icon icon = Icon.called("test").build();
String result = HtmlTable.fromExtensionTable(Table.builder()
.columnOne("Col 1", icon)
.columnTwo("Col 2", icon)
.columnThree("Col 3", icon)
.build(), Color.AMBER).toHtml();
assertEquals(expected, result);
}
@Test
void dynamicTableProducesSameHtmlAsOldCode() {
// Produced by old code
String expected = "<div class=\"table-responsive\"><table class=\"table table-bordered table-striped table-hover player-plugin-table dataTable\"><thead><tr><th><i class=\" fa fa-test\"></i> Col 1</th><th><i class=\" fa fa-test\"></i> Col 2</th><th><i class=\" fa fa-test\"></i> Col 3</th></tr></thead><tbody><tr><td>1</td><td>2</td><td>three</td></tr><tr><td>1</td><td>2</td><td>three</td></tr><tr><td>1</td><td>2</td><td>three</td></tr><tr><td>1</td><td>2</td><td>three</td></tr><tr><td>1</td><td>2</td><td>three</td></tr><tr><td>1</td><td>2</td><td>three</td></tr><tr><td>1</td><td>2</td><td>three</td></tr><tr><td>1</td><td>2</td><td>three</td></tr><tr><td>1</td><td>2</td><td>three</td></tr><tr><td>1</td><td>2</td><td>three</td></tr><tr><td>1</td><td>2</td><td>three</td></tr><tr><td>1</td><td>2</td><td>three</td></tr><tr><td>1</td><td>2</td><td>three</td></tr><tr><td>1</td><td>2</td><td>three</td></tr><tr><td>1</td><td>2</td><td>three</td></tr><tr><td>1</td><td>2</td><td>three</td></tr><tr><td>1</td><td>2</td><td>three</td></tr><tr><td>1</td><td>2</td><td>three</td></tr><tr><td>1</td><td>2</td><td>three</td></tr><tr><td>1</td><td>2</td><td>three</td></tr><tr><td>1</td><td>2</td><td>three</td></tr><tr><td>1</td><td>2</td><td>three</td></tr><tr><td>1</td><td>2</td><td>three</td></tr><tr><td>1</td><td>2</td><td>three</td></tr><tr><td>1</td><td>2</td><td>three</td></tr><tr><td>1</td><td>2</td><td>three</td></tr><tr><td>1</td><td>2</td><td>three</td></tr><tr><td>1</td><td>2</td><td>three</td></tr><tr><td>1</td><td>2</td><td>three</td></tr><tr><td>1</td><td>2</td><td>three</td></tr><tr><td>1</td><td>2</td><td>three</td></tr><tr><td>1</td><td>2</td><td>three</td></tr><tr><td>1</td><td>2</td><td>three</td></tr><tr><td>1</td><td>2</td><td>three</td></tr><tr><td>1</td><td>2</td><td>three</td></tr><tr><td>1</td><td>2</td><td>three</td></tr><tr><td>1</td><td>2</td><td>three</td></tr><tr><td>1</td><td>2</td><td>three</td></tr><tr><td>1</td><td>2</td><td>three</td></tr><tr><td>1</td><td>2</td><td>three</td></tr><tr><td>1</td><td>2</td><td>three</td></tr><tr><td>1</td><td>2</td><td>three</td></tr><tr><td>1</td><td>2</td><td>three</td></tr><tr><td>1</td><td>2</td><td>three</td></tr><tr><td>1</td><td>2</td><td>three</td></tr><tr><td>1</td><td>2</td><td>three</td></tr><tr><td>1</td><td>2</td><td>three</td></tr><tr><td>1</td><td>2</td><td>three</td></tr><tr><td>1</td><td>2</td><td>three</td></tr><tr><td>1</td><td>2</td><td>three</td></tr><tr><td>1</td><td>2</td><td>three</td></tr><tr><td>1</td><td>2</td><td>three</td></tr><tr><td>1</td><td>2</td><td>three</td></tr><tr><td>1</td><td>2</td><td>three</td></tr><tr><td>1</td><td>2</td><td>three</td></tr><tr><td>1</td><td>2</td><td>three</td></tr><tr><td>1</td><td>2</td><td>three</td></tr><tr><td>1</td><td>2</td><td>three</td></tr><tr><td>1</td><td>2</td><td>three</td></tr><tr><td>1</td><td>2</td><td>three</td></tr></tbody></table></div>";
Icon icon = Icon.called("test").build();
Table.Factory buildingTable = Table.builder()
.columnOne("Col 1", icon)
.columnTwo("Col 2", icon)
.columnThree("Col 3", icon);
for (int i = 0; i < 60; i++) {
buildingTable.addRow("1", 2, "three");
}
String result = HtmlTable.fromExtensionTable(buildingTable.build(), Color.AMBER).toHtml();
assertEquals(expected, result);
}
}

View File

@ -16,7 +16,7 @@
*/
package com.djrapitops.plan.storage.database.queries;
import com.djrapitops.plan.data.element.TableContainer;
import com.djrapitops.plan.delivery.rendering.html.structure.HtmlTable;
import com.djrapitops.plan.extension.CallEvents;
import com.djrapitops.plan.extension.DataExtension;
import com.djrapitops.plan.extension.ExtensionService;
@ -176,8 +176,9 @@ public interface ExtensionsDatabaseTest extends DatabaseTestPreparer {
List<ExtensionTableData> tableData = tabData.getTableData();
assertEquals(1, tableData.size());
TableContainer table = tableData.get(0).getHtmlTable();
assertEquals("<tbody><tr><td>Group</td><td>1</td></tr></tbody>", table.parseBody());
HtmlTable table = tableData.get(0).getHtmlTable();
String result = table.toHtml();
assertTrue(result.contains("<tbody><tr><td>Group</td><td>1</td></tr></tbody>"), result);
}
@Test
@ -305,15 +306,16 @@ public interface ExtensionsDatabaseTest extends DatabaseTestPreparer {
assertEquals(1, tableData.size());
ExtensionTableData table = tableData.get(0);
TableContainer expected = new TableContainer(
"<i class=\" fa fa-gavel\"></i> first",
"<i class=\" fa fa-what\"></i> second",
"<i class=\" fa fa-question\"></i> third"
);
expected.setColor("amber");
expected.addRow("value", 3, 0.5, 400L);
HtmlTable expected = HtmlTable.fromExtensionTable(
Table.builder()
.columnOne("first", Icon.called("gavel").build())
.columnTwo("second", Icon.called("what").build())
.columnThree("third", Icon.called("question").build())
.addRow("value", 3, 0.5, 400L)
.build(),
com.djrapitops.plan.delivery.rendering.html.icon.Color.AMBER);
assertEquals(expected.buildHtml(), table.getHtmlTable().buildHtml());
assertEquals(expected.toHtml(), table.getHtmlTable().toHtml());
}
@Test
@ -338,15 +340,16 @@ public interface ExtensionsDatabaseTest extends DatabaseTestPreparer {
assertEquals(1, tableData.size());
ExtensionTableData table = tableData.get(0);
TableContainer expected = new TableContainer(
"<i class=\" fa fa-gavel\"></i> first",
"<i class=\" fa fa-what\"></i> second",
"<i class=\" fa fa-question\"></i> third"
);
expected.setColor("amber");
expected.addRow("value", 3, 0.5, 400L);
HtmlTable expected = HtmlTable.fromExtensionTable(
Table.builder()
.columnOne("first", Icon.called("gavel").build())
.columnTwo("second", Icon.called("what").build())
.columnThree("third", Icon.called("question").build())
.addRow("value", 3, 0.5, 400L)
.build(),
com.djrapitops.plan.delivery.rendering.html.icon.Color.AMBER);
assertEquals(expected.buildHtml(), table.getHtmlTable().buildHtml());
assertEquals(expected.toHtml(), table.getHtmlTable().toHtml());
}
@PluginInfo(name = "ConditionalExtension")