Skip to content

Commit

Permalink
Fix TreeTable declarative support (#16368)
Browse files Browse the repository at this point in the history
Also fix some small issues in Table declarative:
* Write null property values as empty strings instead of NPEing
* Read/write item ids from/to <tr item-id="...">

Change-Id: Ieccc3f49c5021f8a4a50d4ea671f9086ad8f997c
  • Loading branch information
jdahlstrom authored and Vaadin Code Review committed Apr 28, 2015
1 parent 6b3c1c0 commit 9f6bec2
Show file tree
Hide file tree
Showing 7 changed files with 323 additions and 52 deletions.
2 changes: 1 addition & 1 deletion server/src/com/vaadin/ui/AbstractSelect.java
Expand Up @@ -2231,7 +2231,7 @@ protected void readItems(Element design, DesignContext context) {
* if the tag name of the {@code child} element is not
* {@code option}.
*/
protected String readItem(Element child, Set<String> selected,
protected Object readItem(Element child, Set<String> selected,
DesignContext context) {
if (!"option".equals(child.tagName())) {
throw new DesignException("Unrecognized child element in "
Expand Down
4 changes: 2 additions & 2 deletions server/src/com/vaadin/ui/OptionGroup.java
Expand Up @@ -257,9 +257,9 @@ public boolean isHtmlContentAllowed() {
}

@Override
protected String readItem(Element child, Set<String> selected,
protected Object readItem(Element child, Set<String> selected,
DesignContext context) {
String itemId = super.readItem(child, selected, context);
Object itemId = super.readItem(child, selected, context);

if (child.hasAttr("disabled")) {
setItemEnabled(itemId, false);
Expand Down
62 changes: 40 additions & 22 deletions server/src/com/vaadin/ui/Table.java
Expand Up @@ -6042,7 +6042,7 @@ public void readDesign(Element design, DesignContext context) {

readColumns(design);
readHeader(design);
readBody(design);
readBody(design, context);
readFooter(design);
}

Expand Down Expand Up @@ -6150,30 +6150,43 @@ private void readHeaderOrFooter(Element design, boolean header) {
}
}

private void readBody(Element design) {
protected void readBody(Element design, DesignContext context) {
Element tbody = design.select("> table > tbody").first();
if (tbody != null) {
for (Element row : tbody.children()) {
Elements cells = row.children();
if (visibleColumns.size() != cells.size()) {
throw new DesignException(
"Wrong number of columns in a row of a Table. Expected "
+ visibleColumns.size() + ", was "
+ cells.size() + ".");
}
Object[] data = new String[cells.size()];
for (int c = 0; c < cells.size(); ++c) {
data[c] = cells.get(c).html();
}
Object itemId = addItem(data, null);
if (itemId == null) {
throw new DesignException(
"A row of a Table could not be read");
}
}
if (tbody == null) {
return;
}

Set<String> selected = new HashSet<String>();
for (Element tr : tbody.children()) {
readItem(tr, selected, context);
}
}

@Override
protected Object readItem(Element tr, Set<String> selected,
DesignContext context) {
Elements cells = tr.children();
if (visibleColumns.size() != cells.size()) {
throw new DesignException(
"Wrong number of columns in a Table row. Expected "
+ visibleColumns.size() + ", was " + cells.size()
+ ".");
}
Object[] data = new String[cells.size()];
for (int c = 0; c < cells.size(); ++c) {
data[c] = cells.get(c).html();
}

Object itemId = addItem(data,
tr.hasAttr("item-id") ? tr.attr("item-id") : null);

if (itemId == null) {
throw new DesignException("Failed to add a Table row: " + data);
}

return itemId;
}

@Override
public void writeDesign(Element design, DesignContext context) {
Table def = context.getDefaultInstance(this);
Expand Down Expand Up @@ -6260,6 +6273,9 @@ private void writeFooter(Element table) {

@Override
protected void writeItems(Element design, DesignContext context) {
if (getVisibleColumns().length == 0) {
return;
}
Element tbody = design.child(0).appendElement("tbody");
super.writeItems(tbody, context);
}
Expand All @@ -6268,10 +6284,12 @@ protected void writeItems(Element design, DesignContext context) {
protected Element writeItem(Element tbody, Object itemId,
DesignContext context) {
Element tr = tbody.appendElement("tr");
tr.attr("item-id", String.valueOf(itemId));
Item item = getItem(itemId);
for (Object id : getVisibleColumns()) {
Element td = tr.appendElement("td");
td.html(item.getItemProperty(id).getValue().toString());
Object value = item.getItemProperty(id).getValue();
td.html(value != null ? value.toString() : "");
}
return tr;
}
Expand Down
84 changes: 84 additions & 0 deletions server/src/com/vaadin/ui/TreeTable.java
Expand Up @@ -23,9 +23,13 @@
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.jsoup.nodes.Element;

import com.vaadin.data.Collapsible;
import com.vaadin.data.Container;
import com.vaadin.data.Container.Hierarchical;
Expand All @@ -41,6 +45,9 @@
import com.vaadin.ui.Tree.CollapseListener;
import com.vaadin.ui.Tree.ExpandEvent;
import com.vaadin.ui.Tree.ExpandListener;
import com.vaadin.ui.declarative.DesignAttributeHandler;
import com.vaadin.ui.declarative.DesignContext;
import com.vaadin.ui.declarative.DesignException;

/**
* TreeTable extends the {@link Table} component so that it can also visualize a
Expand Down Expand Up @@ -888,4 +895,81 @@ protected List<Object> getItemIds(int firstIndex, int rows) {
}
return itemIds;
}

@Override
protected void readBody(Element design, DesignContext context) {
Element tbody = design.select("> table > tbody").first();
if (tbody == null) {
return;
}

Set<String> selected = new HashSet<String>();
Stack<Object> parents = new Stack<Object>();
int lastDepth = -1;

for (Element tr : tbody.children()) {
int depth = DesignAttributeHandler.readAttribute("depth",
tr.attributes(), 0, int.class);

if (depth < 0 || depth > lastDepth + 1) {
throw new DesignException(
"Malformed TreeTable item hierarchy at " + tr
+ ": last depth was " + lastDepth);
} else if (depth <= lastDepth) {
for (int d = depth; d <= lastDepth; d++) {
parents.pop();
}
}

Object itemId = readItem(tr, selected, context);
setParent(itemId, !parents.isEmpty() ? parents.peek() : null);
parents.push(itemId);
lastDepth = depth;
}
}

@Override
protected Object readItem(Element tr, Set<String> selected,
DesignContext context) {
Object itemId = super.readItem(tr, selected, context);

if (tr.hasAttr("collapsed")) {
boolean collapsed = DesignAttributeHandler.readAttribute(
"collapsed", tr.attributes(), boolean.class);
setCollapsed(itemId, collapsed);
}

return itemId;
}

@Override
protected void writeItems(Element design, DesignContext context) {
if (getVisibleColumns().length == 0) {
return;
}
Element tbody = design.child(0).appendElement("tbody");
writeItems(tbody, rootItemIds(), 0, context);
}

protected void writeItems(Element tbody, Collection<?> itemIds, int depth,
DesignContext context) {
for (Object itemId : itemIds) {
Element tr = writeItem(tbody, itemId, context);
DesignAttributeHandler.writeAttribute("depth", tr.attributes(),
depth, 0, int.class);

if (getChildren(itemId) != null) {
writeItems(tbody, getChildren(itemId), depth + 1, context);
}
}
}

@Override
protected Element writeItem(Element tbody, Object itemId,
DesignContext context) {
Element tr = super.writeItem(tbody, itemId, context);
DesignAttributeHandler.writeAttribute("collapsed", tr.attributes(),
isCollapsed(itemId), true, boolean.class);
return tr;
}
}
Expand Up @@ -36,12 +36,14 @@ public class TableDeclarativeTest extends TableDeclarativeTestBase {
@Test
public void testBasicAttributes() {

String design = "<v-table page-length=30 cache-rate=3 selectable=true editable=true "
String design = "<"
+ getTag()
+ " page-length=30 cache-rate=3 selectable=true editable=true "
+ "sortable=false sort-ascending=false sort-container-property-id=foo "
+ "drag-mode=row multi-select-mode=simple column-header-mode=id row-header-mode=id "
+ "column-reordering-allowed=true column-collapsing-allowed=true />";

Table table = new Table();
Table table = getTable();
table.setPageLength(30);
table.setCacheRate(3);
table.setSelectable(true);
Expand All @@ -65,17 +67,18 @@ public void testBasicAttributes() {

@Test
public void testColumns() {
String design = "<v-table column-collapsing-allowed=true>" //
String design = "<"
+ getTag()
+ " column-collapsing-allowed=true>" //
+ " <table>" //
+ " <colgroup>"
+ " <col property-id='foo' width=300>"
+ " <col property-id='bar' center expand=1 collapsible=false>"
+ " <col property-id='baz' right expand=2 collapsed=true>"
+ " </colgroup>" //
+ " </table>" //
+ "</v-table>";
+ " </table>";

Table table = new Table();
Table table = getTable();
table.setColumnCollapsingAllowed(true);

table.addContainerProperty("foo", String.class, null);
Expand All @@ -98,7 +101,8 @@ public void testColumns() {

@Test
public void testHeadersFooters() {
String design = "<v-table>" //
String design = "<" + getTag()
+ ">" //
+ " <table>" //
+ " <colgroup><col property-id=foo><col property-id=bar></colgroup>" //
+ " <thead>" //
Expand All @@ -107,10 +111,9 @@ public void testHeadersFooters() {
+ " <tfoot>" //
+ " <tr><td>foo<td>bar" //
+ " </tfoot>" //
+ " </table>" //
+ "</v-table>";
+ " </table>";

Table table = new Table();
Table table = getTable();
table.setFooterVisible(true);

table.addContainerProperty("foo", String.class, null);
Expand All @@ -129,36 +132,35 @@ public void testHeadersFooters() {

@Test
public void testInlineData() {
String design = "<v-table> "//
String design = "<"
+ getTag()
+ ">" //
+ " <table>" //
+ " <colgroup>"
+ " <col property-id='foo' />"
+ " <col property-id='bar' />"
+ " <col property-id='baz' />" //
+ " </colgroup>"
+ " <thead>"
+ " </colgroup>" + " <thead>"
+ " <tr><th>Description<th>Milestone<th>Status</tr>"
+ " </thead>"
+ " <tbody>"
+ " <tr><td>r1c1</td><td>r1c2</td><td>r1c3</td>" //
+ " <tr><td>r2c1</td><td>r2c2</td><td>r2c3</td>" //
+ " </thead>" + " <tbody>"
+ " <tr item-id=1><td>r1c1</td><td>r1c2</td><td>r1c3</td>" //
+ " <tr item-id=2><td>r2c1</td><td>r2c2</td><td>r2c3</td>" //
+ " </tbody>" //
+ " <tfoot>" //
+ " <tr><td>F1<td>F2<td>F3</tr>" //
+ " </tfoot>" //
+ " </table>" //
+ "</v-table>";
+ " </table>";

Table table = new Table();
Table table = getTable();
table.addContainerProperty("foo", String.class, null);
table.addContainerProperty("bar", String.class, null);
table.addContainerProperty("baz", String.class, null);
table.setColumnHeaders("Description", "Milestone", "Status");
table.setColumnFooter("foo", "F1");
table.setColumnFooter("bar", "F2");
table.setColumnFooter("baz", "F3");
table.addItem(new Object[] { "r1c1", "r1c2", "r1c3" }, null);
table.addItem(new Object[] { "r2c1", "r2c2", "r2c3" }, null);
table.addItem(new Object[] { "r1c1", "r1c2", "r1c3" }, "1");
table.addItem(new Object[] { "r2c1", "r2c2", "r2c3" }, "2");
table.setFooterVisible(true);

testRead(design, table);
Expand Down
Expand Up @@ -20,7 +20,8 @@
import com.vaadin.tests.design.DeclarativeTestBase;
import com.vaadin.ui.Table;

public class TableDeclarativeTestBase extends DeclarativeTestBase<Table> {
public abstract class TableDeclarativeTestBase extends
DeclarativeTestBase<Table> {

@Override
public Table testRead(String design, Table expected) {
Expand All @@ -30,20 +31,30 @@ public Table testRead(String design, Table expected) {
return read;
}

private void compareBody(Table read, Table expected) {
assertEquals(expected.getItemIds().size(), read.getItemIds().size());
protected Table getTable() {
return new Table();
}

protected String getTag() {
return "v-table";
}

protected void compareBody(Table read, Table expected) {
assertEquals("number of items", expected.getItemIds().size(), read
.getItemIds().size());
for (Object rowId : expected.getItemIds()) {
assertTrue(read.containsId(rowId));
for (Object propertyId : read.getVisibleColumns()) {
Object expectedItem = expected.getContainerProperty(rowId,
propertyId);
Object readItem = read.getContainerProperty(rowId, propertyId);
assertEquals(expectedItem, readItem);
assertEquals("property '" + propertyId + "'", expectedItem,
readItem);
}
}
}

private void compareColumns(Table read, Table expected) {
protected void compareColumns(Table read, Table expected) {
for (Object pid : expected.getVisibleColumns()) {
String col = "column '" + pid + "'";
assertEquals(col + " width", expected.getColumnWidth(pid),
Expand Down

0 comments on commit 9f6bec2

Please sign in to comment.