@Connect(com.vaadin.ui.Grid.Column.class)
public class ColumnConnector extends AbstractExtensionConnector {
- private Column<Object, JsonObject> column;
+ static abstract class CustomColumn extends Column<Object, JsonObject> {
+
+ private final String connectorId;
+
+ CustomColumn(String connectorId) {
+ this.connectorId = connectorId;
+ }
+
+ public String getConnectorId() {
+ return connectorId;
+ }
+ }
+
+ private CustomColumn column;
/* This parent is needed because it's no longer available in onUnregister */
private GridConnector parent;
@Override
protected void extend(ServerConnector target) {
parent = getParent();
- String columnId = getState().id;
- column = new Column<Object, JsonObject>() {
+ column = new CustomColumn(getConnectorId()) {
@Override
public Object getValue(JsonObject row) {
final JsonObject rowData = row
.getObject(DataCommunicatorConstants.DATA);
- if (rowData.hasKey(columnId)) {
- final JsonValue columnValue = rowData.get(columnId);
+ if (rowData.hasKey(getConnectorId())) {
+ final JsonValue columnValue = rowData.get(getConnectorId());
return getRendererConnector().decode(columnValue);
}
}
};
column.setRenderer(getRendererConnector().getRenderer());
- getParent().addColumn(column, columnId);
+ getParent().addColumn(column, getState().id);
}
@SuppressWarnings("unchecked")
import com.vaadin.client.WidgetUtil;
import com.vaadin.client.annotations.OnStateChange;
import com.vaadin.client.connectors.AbstractListingConnector;
+import com.vaadin.client.connectors.grid.ColumnConnector.CustomColumn;
import com.vaadin.client.data.DataSource;
import com.vaadin.client.ui.SimpleManagedLayout;
import com.vaadin.client.widget.grid.CellReference;
}
/* Map to keep track of all added columns */
- private Map<Column<?, JsonObject>, String> columnToIdMap = new HashMap<>();
- private Map<String, Column<?, JsonObject>> idToColumn = new HashMap<>();
+ private Map<CustomColumn, String> columnToIdMap = new HashMap<>();
+ private Map<String, CustomColumn> idToColumn = new HashMap<>();
/* Child component list for HasComponentsConnector */
private List<ComponentConnector> childComponents;
* the id of the column to get
* @return the column with the given id
*/
- public Column<?, JsonObject> getColumn(String columnId) {
+ public CustomColumn getColumn(String columnId) {
return idToColumn.get(columnId);
}
}
Column<?, JsonObject> column = cellRef.getColumn();
- if (columnToIdMap.containsKey(column)) {
- String id = columnToIdMap.get(column);
+ if (column instanceof CustomColumn) {
+ String id = ((CustomColumn) column).getConnectorId();
JsonObject cellStyles = row
.getObject(GridState.JSONKEY_CELLSTYLES);
if (cellStyles.hasKey(id)) {
* @param id
* communication id
*/
- public void addColumn(Column<?, JsonObject> column, String id) {
+ public void addColumn(CustomColumn column, String id) {
assert !columnToIdMap.containsKey(column) && !columnToIdMap
.containsValue(id) : "Column with given id already exists.";
getWidget().addColumn(column);
* @param column
* column to remove
*/
- public void removeColumn(Column<?, JsonObject> column) {
+ public void removeColumn(CustomColumn column) {
assert columnToIdMap
.containsKey(column) : "Given Column does not exist.";
getWidget().removeColumn(column);
|| row.hasKey(GridState.JSONKEY_CELLDESCRIPTION))) {
Column<?, JsonObject> column = cell.getColumn();
- if (columnToIdMap.containsKey(column)) {
+ if (column instanceof CustomColumn) {
JsonObject cellDescriptions = row
.getObject(GridState.JSONKEY_CELLDESCRIPTION);
- String id = columnToIdMap.get(column);
+ String id = ((CustomColumn) column).getConnectorId();
if (cellDescriptions != null
&& cellDescriptions.hasKey(id)) {
return new TooltipInfo(cellDescriptions.getString(id));
import com.vaadin.event.EventListener;
import com.vaadin.server.EncodeResult;
import com.vaadin.server.JsonCodec;
-import com.vaadin.server.KeyMapper;
import com.vaadin.server.data.SortOrder;
import com.vaadin.shared.MouseEventDetails;
import com.vaadin.shared.Registration;
import com.vaadin.shared.ui.grid.GridState;
import com.vaadin.shared.ui.grid.HeightMode;
import com.vaadin.shared.ui.grid.SectionState;
+import com.vaadin.shared.util.SharedUtil;
import com.vaadin.ui.components.grid.Header;
import com.vaadin.ui.components.grid.Header.Row;
import com.vaadin.ui.renderers.AbstractRenderer;
public void generateData(T data, JsonObject jsonObject) {
ColumnState state = getState(false);
- String communicationId = state.id;
+ String communicationId = getConnectorId();
assert communicationId != null : "No communication ID set for column "
+ state.caption;
}
};
- private KeyMapper<Column<T, ?>> columnKeys = new KeyMapper<>();
private Set<Column<T, ?>> columnSet = new LinkedHashSet<>();
+ private Map<String, Column<T, ?>> columnKeys = new HashMap<>();
+
private List<SortOrder<Column<T, ?>>> sortOrder = new ArrayList<>();
private DetailsManager<T> detailsManager;
private Set<Component> extensionComponents = new HashSet<>();
private Header header = new HeaderImpl();
+ private int counter = 0;
+
/**
* Constructor for the {@link Grid} component.
*/
}
/**
- * Adds a new column to this {@link Grid} with given header caption, typed
+ * Adds a new column to this {@link Grid} with given identifier, typed
* renderer and value provider.
*
- * @param caption
- * the header caption
+ * @param identifier
+ * the identifier in camel case for the new column
* @param valueProvider
* the value provider
* @param renderer
*
* @see {@link AbstractRenderer}
*/
- public <V> Column<T, V> addColumn(String caption,
+ public <V> Column<T, V> addColumn(String identifier,
Function<T, ? extends V> valueProvider,
AbstractRenderer<? super T, V> renderer) {
- final Column<T, V> column = new Column<>(caption, valueProvider,
+ assert !columnKeys.containsKey(identifier) : "Duplicate identifier: "
+ + identifier;
+
+ final Column<T, V> column = new Column<>(
+ SharedUtil.camelCaseToHumanFriendly(identifier), valueProvider,
renderer);
- addColumn(column);
+ addColumn(identifier, column);
return column;
}
/**
- * Adds a new text column to this {@link Grid} with given header caption
+ * Adds a new text column to this {@link Grid} with given identifier and
* string value provider. The column will use a {@link TextRenderer}.
*
- * @param caption
+ * @param identifier
* the header caption
* @param valueProvider
* the value provider
*
* @return the new column
*/
- public Column<T, String> addColumn(String caption,
+ public Column<T, String> addColumn(String identifier,
Function<T, String> valueProvider) {
- return addColumn(caption, valueProvider, new TextRenderer());
+ return addColumn(identifier, valueProvider, new TextRenderer());
+ }
+
+ /**
+ * Adds a new text column to this {@link Grid} with string value provider.
+ * The column will use a {@link TextRenderer}. Identifier for the column is
+ * generated automatically.
+ *
+ * @param valueProvider
+ * the value provider
+ *
+ * @return the new column
+ */
+ public Column<T, String> addColumn(Function<T, String> valueProvider) {
+ return addColumn(getGeneratedIdentifier(), valueProvider,
+ new TextRenderer());
+ }
+
+ /**
+ * Adds a new column to this {@link Grid} with typed renderer and value
+ * provider. Identifier for the column is generated automatically.
+ *
+ * @param valueProvider
+ * the value provider
+ * @param renderer
+ * the column value class
+ * @param <V>
+ * the column value type
+ *
+ * @return the new column
+ *
+ * @see {@link AbstractRenderer}
+ */
+ public <V> Column<T, V> addColumn(Function<T, ? extends V> valueProvider,
+ AbstractRenderer<? super T, V> renderer) {
+ return addColumn(getGeneratedIdentifier(), valueProvider, renderer);
}
- private void addColumn(Column<T, ?> column) {
+ private void addColumn(String identifier, Column<T, ?> column) {
if (getColumns().contains(column)) {
return;
}
- final String columnId = columnKeys.key(column);
-
column.extend(this);
- column.setId(columnId);
columnSet.add(column);
+ columnKeys.put(identifier, column);
+ column.setId(identifier);
addDataGenerator(column);
- getState().columnOrder.add(columnId);
- getHeader().addColumn(columnId);
+ getState().columnOrder.add(identifier);
+ getHeader().addColumn(identifier);
if (getDefaultHeaderRow() != null) {
- getDefaultHeaderRow().getCell(columnId)
+ getDefaultHeaderRow().getCell(identifier)
.setText(column.getCaption());
}
}
*/
public void removeColumn(Column<T, ?> column) {
if (columnSet.remove(column)) {
- columnKeys.remove(column);
+ columnKeys.remove(column.getId());
removeDataGenerator(column);
getHeader().removeColumn(column.getId());
column.remove();
removeColumns.stream().forEach(this::removeColumn);
addColumns.removeAll(currentColumns);
- addColumns.stream().forEach(this::addColumn);
+ addColumns.stream().forEach(c -> addColumn(getIdentifier(c), c));
setColumnOrder(columns);
}
+ private String getIdentifier(Column<T, ?> column) {
+ return columnKeys.entrySet().stream()
+ .filter(entry -> entry.getValue().equals(column))
+ .map(entry -> entry.getKey()).findFirst()
+ .orElse(getGeneratedIdentifier());
+ }
+
+ private String getGeneratedIdentifier() {
+ String columnId = "generatedColumn" + counter;
+ counter = counter + 1;
+ return columnId;
+ }
+
/**
* Sets a new column order for the grid. All columns which are not ordered
* here will remain in the order they were before as the last columns of
import com.vaadin.shared.ui.grid.HeightMode;
import com.vaadin.ui.Grid;
+import com.vaadin.ui.renderers.NumberRenderer;
public class GridTest {
public void setUp() {
grid = new Grid<>();
grid.addColumn("foo", Function.identity());
+ grid.addColumn(String::length, new NumberRenderer());
+ grid.addColumn("randomColumnId", Function.identity());
}
@Test
@Test(expected = IllegalArgumentException.class)
public void testFrozenColumnCountTooBig() {
- grid.setFrozenColumnCount(2);
+ grid.setFrozenColumnCount(5);
}
@Test(expected = IllegalArgumentException.class)
grid.getFrozenColumnCount());
}
}
+
+ @Test
+ public void testGridColumnIdentifier() {
+ grid.getColumn("foo").setCaption("Bar");
+ assertEquals("Column header not updated correctly", "Bar",
+ grid.getHeaderRow(0).getCell("foo").getText());
+ }
+
+ @Test
+ public void testGridColumnGeneratedIdentifier() {
+ assertEquals("Unexpected caption on a generated Column",
+ "Generated Column0",
+ grid.getColumn("generatedColumn0").getCaption());
+ }
+
+ @Test
+ public void testGridColumnCaptionFromIdentifier() {
+ assertEquals("Unexpected caption on a generated Column",
+ "Random Column Id",
+ grid.getColumn("randomColumnId").getCaption());
+ }
}
grid = new Grid<>();
grid.setItems(data);
- grid.addColumn(COLUMN_CAPTIONS[0],
- dataObj -> "(" + dataObj.getRowNumber() + ", 0)");
- grid.addColumn(COLUMN_CAPTIONS[1],
- dataObj -> "(" + dataObj.getRowNumber() + ", 1)");
- grid.addColumn(COLUMN_CAPTIONS[2],
- dataObj -> "(" + dataObj.getRowNumber() + ", 2)");
-
- grid.addColumn(COLUMN_CAPTIONS[3], DataObject::getRowNumber,
- new NumberRenderer());
- grid.addColumn(COLUMN_CAPTIONS[4], DataObject::getDate,
- new DateRenderer());
- grid.addColumn(COLUMN_CAPTIONS[5], DataObject::getHtmlString,
- new HtmlRenderer());
- grid.addColumn(COLUMN_CAPTIONS[6], DataObject::getBigRandom,
- new NumberRenderer());
- grid.addColumn(COLUMN_CAPTIONS[7], data -> data.getSmallRandom() / 5d,
- new ProgressBarRenderer());
+ grid.addColumn(dataObj -> "(" + dataObj.getRowNumber() + ", 0)")
+ .setCaption(COLUMN_CAPTIONS[0]);
+ grid.addColumn(dataObj -> "(" + dataObj.getRowNumber() + ", 1)")
+ .setCaption(COLUMN_CAPTIONS[1]);
+ grid.addColumn(dataObj -> "(" + dataObj.getRowNumber() + ", 2)")
+ .setCaption(COLUMN_CAPTIONS[2]);
+
+ grid.addColumn(DataObject::getRowNumber, new NumberRenderer())
+ .setCaption(COLUMN_CAPTIONS[3]);
+ grid.addColumn(DataObject::getDate, new DateRenderer())
+ .setCaption(COLUMN_CAPTIONS[4]);
+ grid.addColumn(DataObject::getHtmlString, new HtmlRenderer())
+ .setCaption(COLUMN_CAPTIONS[5]);
+ grid.addColumn(DataObject::getBigRandom, new NumberRenderer())
+ .setCaption(COLUMN_CAPTIONS[6]);
+ grid.addColumn(data -> data.getSmallRandom() / 5d,
+ new ProgressBarRenderer()).setCaption(COLUMN_CAPTIONS[7]);
grid.addSelectionListener(e -> log("Selected: " + e.getValue()));
@SuppressWarnings("unchecked")
private void createColumnsMenu(MenuItem columnsMenu) {
- for (int i = 0; i < grid.getColumns().size(); i++) {
- final int index = i;
- MenuItem columnMenu = columnsMenu.addItem("Column " + i, null);
+ for (Column<DataObject, ?> col : grid.getColumns()) {
+ MenuItem columnMenu = columnsMenu.addItem(col.getCaption(), null);
columnMenu.addItem("Move left", selectedItem -> {
+ int index = grid.getColumns().indexOf(col);
if (index > 0) {
List<Column<DataObject, ?>> columnOrder = new ArrayList<>(
grid.getColumns());
}
});
columnMenu.addItem("Move right", selectedItem -> {
+ int index = grid.getColumns().indexOf(col);
if (index < grid.getColumns().size() - 1) {
List<Column<DataObject, ?>> columnOrder = new ArrayList<>(
grid.getColumns());
});
columnMenu
.addItem("Sortable",
- selectedItem -> grid.getColumns().get(index)
+ selectedItem -> col
.setSortable(selectedItem.isChecked()))
.setCheckable(true);
columnMenu
.addItem("Hidable",
- selectedItem -> grid.getColumns().get(index)
+ selectedItem -> col
.setHidable(selectedItem.isChecked()))
.setCheckable(true);
columnMenu
.addItem("Hidden",
- selectedItem -> grid.getColumns().get(index)
+ selectedItem -> col
.setHidden(selectedItem.isChecked()))
.setCheckable(true);
}
public void testColumnReorder_onReorder_columnReorderEventTriggered() {
selectMenuPath("Component", "Header", "Prepend header row");
selectMenuPath("Component", "State", "Column reorder listener");
- selectMenuPath("Component", "Columns", "Column " + 3, "Move left");
+ selectMenuPath("Component", "Columns", GridBasics.COLUMN_CAPTIONS[3],
+ "Move left");
assertEquals("1. Registered a column reorder listener.", getLogRow(2));
assertEquals("2. Columns reordered, userOriginated: false",
assertColumnHeaderOrder(0, 1, 3, 2);
// trigger another event
- selectMenuPath("Component", "Columns", "Column " + 3, "Move left");
+ selectMenuPath("Component", "Columns", GridBasics.COLUMN_CAPTIONS[3],
+ "Move right");
assertColumnHeaderOrder(0, 1, 2, 3);
// test drag and drop is user originated