Browse Source

Initial import of TypedGrid component

This patch introduces a generics version of Grid.

Change-Id: I0777a0468ccb5f38e266aa0f33ceb8d0324d72dd
feature/databinding
Teemu Suo-Anttila 8 years ago
parent
commit
888fec0669

+ 33
- 0
client/src/com/vaadin/client/connectors/proto/AbstractTypedGridExtensionConnector.java View File

@@ -0,0 +1,33 @@
/*
* Copyright 2000-2014 Vaadin Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.vaadin.client.connectors.proto;

import com.vaadin.client.extensions.AbstractExtensionConnector;

/**
* Base connector class for AbstractTypedGridExtensions. This class provides
* helpers to ease the use of TypedGrid client-side in extension connectors.
*
* @since
*/
public abstract class AbstractTypedGridExtensionConnector extends
AbstractExtensionConnector {

@Override
public TypedGridConnector getParent() {
return (TypedGridConnector) super.getParent();
}
}

+ 62
- 0
client/src/com/vaadin/client/connectors/proto/TypedGridColumnConnector.java View File

@@ -0,0 +1,62 @@
/*
* Copyright 2000-2014 Vaadin Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.vaadin.client.connectors.proto;

import com.vaadin.client.ServerConnector;
import com.vaadin.client.widgets.Grid.Column;
import com.vaadin.shared.ui.Connect;
import com.vaadin.shared.ui.grid.GridState;
import com.vaadin.shared.ui.proto.TypedGridColumnState;

import elemental.json.JsonObject;

/**
* Connector implementation for TypedGrid Columns.
*
* @since
*/
@Connect(com.vaadin.ui.proto.Column.class)
public class TypedGridColumnConnector extends
AbstractTypedGridExtensionConnector {

/* This is stored so it can be used in onUnregister */
private TypedGridConnector parent;

@Override
protected void extend(ServerConnector target) {
parent = getParent();
parent.addColumn(getState().id, new Column<String, JsonObject>() {
@Override
public String getValue(JsonObject row) {
return row.getObject(GridState.JSONKEY_DATA).getString(
getState().id);
}
});
}

@Override
public void onUnregister() {
super.onUnregister();

parent.removeColumn(getState().id);
}

@Override
public TypedGridColumnState getState() {
return (TypedGridColumnState) super.getState();
}

}

+ 125
- 0
client/src/com/vaadin/client/connectors/proto/TypedGridConnector.java View File

@@ -0,0 +1,125 @@
/*
* Copyright 2000-2014 Vaadin Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.vaadin.client.connectors.proto;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.vaadin.client.connectors.GridConnector;
import com.vaadin.client.data.DataSource;
import com.vaadin.client.data.HasDataSource;
import com.vaadin.client.ui.AbstractComponentConnector;
import com.vaadin.client.ui.SimpleManagedLayout;
import com.vaadin.client.widget.grid.selection.SelectionEvent;
import com.vaadin.client.widget.grid.selection.SelectionHandler;
import com.vaadin.client.widgets.Grid;
import com.vaadin.client.widgets.Grid.Column;
import com.vaadin.shared.data.DataProviderConstants;
import com.vaadin.shared.ui.Connect;
import com.vaadin.shared.ui.proto.TypedGridClientRpc;
import com.vaadin.shared.ui.proto.TypedGridServerRpc;
import com.vaadin.shared.ui.proto.TypedGridState;
import com.vaadin.shared.util.SharedUtil;

import elemental.json.JsonObject;

/**
* Connector implementation for TypedGrid. Uses {@link Grid} as its widget, just
* like {@link GridConnector}.
*
* @since
*/
@Connect(com.vaadin.ui.proto.TypedGrid.class)
public class TypedGridConnector extends AbstractComponentConnector implements
SimpleManagedLayout, HasDataSource {

private Map<String, Column<?, JsonObject>> columnMap = new HashMap<String, Column<?, JsonObject>>();

@Override
@SuppressWarnings("unchecked")
public Grid<JsonObject> getWidget() {
return (Grid<JsonObject>) super.getWidget();
}

@Override
public TypedGridState getState() {
return (TypedGridState) super.getState();
}

@Override
protected void init() {
super.init();

registerRpc(TypedGridClientRpc.class, new TypedGridClientRpc() {

@Override
public void setColumnOrder(String[] columnOrder) {
if (columnOrder.length == 0) {
return;
}

List<Column<?, JsonObject>> columns = new ArrayList<Column<?, JsonObject>>(
columnOrder.length);
for (String key : columnOrder) {
if (columnMap.containsKey(key)) {
columns.add(columnMap.get(key));
}
}
Column<?, JsonObject>[] c = new Column[] {};
getWidget().setColumnOrder(columns.toArray(c));
}
});

getWidget().addSelectionHandler(new SelectionHandler<JsonObject>() {

@Override
public void onSelect(SelectionEvent<JsonObject> event) {
String key = null;
if (!event.getSelected().isEmpty()) {
key = event.getSelected().iterator().next()
.getString(DataProviderConstants.KEY);
}
getRpcProxy(TypedGridServerRpc.class).setSelected(key);
}
});
}

public void addColumn(String key, Column<?, JsonObject> column) {
// TODO: Remove this when header support is implemented
getWidget().addColumn(column).setHeaderCaption(
SharedUtil.propertyIdToHumanFriendly(key));

columnMap.put(key, column);
}

public void removeColumn(String key) {
if (columnMap.containsKey(key)) {
getWidget().removeColumn(columnMap.remove(key));
}
}

@Override
public void setDataSource(DataSource<JsonObject> ds) {
getWidget().setDataSource(ds);
}

@Override
public void layout() {
getWidget().onResize();
}
}

+ 67
- 0
server/src/com/vaadin/ui/proto/Column.java View File

@@ -0,0 +1,67 @@
/*
* Copyright 2000-2014 Vaadin Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.vaadin.ui.proto;

import com.vaadin.server.communication.data.typed.TypedDataGenerator;
import com.vaadin.shared.ui.grid.GridState;
import com.vaadin.shared.ui.proto.TypedGridColumnState;
import com.vaadin.ui.proto.TypedGrid.AbstractTypedGridExtension;

import elemental.json.Json;
import elemental.json.JsonObject;

/**
* Grid Column.
*
* @param <T>
* grid data type
* @param <V>
* column value type
*/
public class Column<T, V> extends AbstractTypedGridExtension<T> implements
TypedDataGenerator<T> {

private ValueProvider<T, V> valueProvider;

Column(TypedGrid<T> grid, String id, Class<V> valueType,
ValueProvider<T, V> valueProvider) {
super(grid);
getState().id = id;
this.valueProvider = valueProvider;
// TODO: determine renderer from valueType
}

@Override
protected TypedGridColumnState getState() {
return (TypedGridColumnState) super.getState();
}

@Override
public void generateData(T data, JsonObject jsonObject) {
JsonObject dataObject;
if (!jsonObject.hasKey(GridState.JSONKEY_DATA)) {
jsonObject.put(GridState.JSONKEY_DATA, Json.createObject());
}
dataObject = jsonObject.getObject(GridState.JSONKEY_DATA);

V value = valueProvider.getValue(data);
dataObject.put(getState().id, value != null ? value.toString() : "");
}

@Override
public void destroyData(T data) {
}
}

+ 275
- 0
server/src/com/vaadin/ui/proto/TypedGrid.java View File

@@ -0,0 +1,275 @@
/*
* Copyright 2000-2014 Vaadin Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.vaadin.ui.proto;

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.EventObject;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.vaadin.data.util.BeanUtil;
import com.vaadin.server.AbstractExtension;
import com.vaadin.server.Extension;
import com.vaadin.server.communication.data.typed.CollectionDataSource;
import com.vaadin.server.communication.data.typed.DataProvider;
import com.vaadin.server.communication.data.typed.DataSource;
import com.vaadin.server.communication.data.typed.TypedDataGenerator;
import com.vaadin.shared.ui.proto.TypedGridClientRpc;
import com.vaadin.shared.ui.proto.TypedGridServerRpc;
import com.vaadin.shared.ui.proto.TypedGridState;
import com.vaadin.ui.AbstractComponent;

/**
* A typed version of the Grid component. This TypedGrid is using the new typed
* data system.
* <p>
* Note: This component does not yet provide all original Grid features.
*
* @since
* @param <T>
* type of data objects stored in this grid, e.g. Person bean
*/
public class TypedGrid<T> extends AbstractComponent {

// TODO: Refactor this to use some common "Select" API and extract it from
// TypedGrid
public static class SelectEvent<T> extends EventObject {

private final T selected;
private final TypedGrid<T> component;

SelectEvent(TypedGrid<T> source, T selected) {
super(source);
component = source;
this.selected = selected;
}

@Override
public TypedGrid<T> getSource() {
return component;
}

public T getSelected() {
return selected;
}

}

// TODO: Extract from TypedGrid when extracting the SelectEvent
public static interface SelectionListener<T> extends Serializable {
void select(SelectEvent<T> select);
}

public abstract static class AbstractTypedGridExtension<T> extends
AbstractExtension {
public AbstractTypedGridExtension(TypedGrid<T> grid) {
super(grid);
}
}

private T selected;

private final Map<String, Column<T, ?>> columns = new HashMap<String, Column<T, ?>>();
private final Set<SelectionListener<T>> listeners = new HashSet<SelectionListener<T>>();
private DataProvider<T> dp;

/**
* Constructs a new empty Grid.
*/
public TypedGrid() {
this(new ArrayList<T>());
}

/**
* Constructs a new Grid using the data from given collection.
*
* @param data
* collection of data
*/
public TypedGrid(Collection<T> data) {
this(new CollectionDataSource<T>(data));
}

/**
* Constructs a new Grid using the given data source.
*
* @param data
* data source to use
*/
public TypedGrid(DataSource<T> data) {
internalSetDataSource(data);
init();
setSizeFull();
}

/**
* Constructs a new Grid using the given data source.
* <p>
* This constructor uses the given class parameter to autodetect the columns
* needed to display the data.
*
* @param cls
* data type class
* @param data
* data source to use
*/
public TypedGrid(Class<T> cls, DataSource<T> data) {
this(data);
autodetectColumns(cls);
}

/**
* Constructs a new Grid using the data from given collection.
* <p>
* This constructor uses the given class parameter to autodetect the columns
* needed to display the data.
*
* @param cls
* data type class
* @param data
* collection of data
*/
public TypedGrid(Class<T> cls, Collection<T> data) {
this(cls, new CollectionDataSource<T>(data));
}

public void setDataSource(DataSource<T> data) {
internalSetDataSource(data);
}

public void setDataSource(Collection<T> data) {
internalSetDataSource(new CollectionDataSource<T>(data));
}

protected void init() {
registerRpc(new TypedGridServerRpc() {

@Override
public void setSelected(String rowKey) {
if (rowKey != null && !rowKey.isEmpty()) {
selected = dp.getKeyMapper().get(rowKey);
} else {
selected = null;
}
fireSelectionEvent(selected);
}
});
}

private void internalSetDataSource(DataSource<T> data) {
if (dp != null) {
dp.remove();
}

dp = DataProvider.create(data, this);
}

private void autodetectColumns(Class<T> cls) {
List<PropertyDescriptor> props;
try {
props = BeanUtil.getBeanPropertyDescriptor(cls);
for (final PropertyDescriptor p : props) {
if (p.getName().equals("class")) {
continue;
}

addColumn(p.getName(), (Class<Object>) p.getPropertyType(),
new ValueProvider<T, Object>() {

@Override
public Object getValue(T data) {
try {
return p.getReadMethod().invoke(data);
} catch (Exception e) {
// TODO: Improve exception handling
return null;
}
}
});
}
} catch (IntrospectionException e) {
throw new IllegalArgumentException(e);
}
}

public <V> Column<T, V> addColumn(String name, Class<V> valueType,
ValueProvider<T, V> valueProvider) {
Column<T, V> column = new Column<T, V>(this, name, valueType,
valueProvider);
columns.put(name, column);
return column;
}

public void removeColumn(String colName) {
if (columns.containsKey(colName)) {
columns.remove(colName).remove();
}
}

public void setColumnOrder(String... columns) {
getRpcProxy(TypedGridClientRpc.class).setColumnOrder(columns);
}

@Override
protected TypedGridState getState() {
return (TypedGridState) super.getState();
}

@Override
protected void addExtension(Extension extension) {
super.addExtension(extension);

if (extension instanceof TypedDataGenerator) {
dp.addDataGenerator((TypedDataGenerator<T>) extension);
}
}

@Override
public void removeExtension(Extension extension) {
super.removeExtension(extension);

if (extension instanceof TypedDataGenerator) {
dp.removeDataGenerator((TypedDataGenerator<T>) extension);
}
}

public void addSelectionListener(SelectionListener<T> listener) {
listeners.add(listener);
}

public void removeSelectionListener(SelectionListener<T> listener) {
listeners.remove(listener);
}

protected void fireSelectionEvent(T selected) {
List<SelectionListener<T>> set = new ArrayList<SelectionListener<T>>(
listeners);
for (SelectionListener<T> l : set) {
l.select(new SelectEvent<T>(this, selected));
}
}

public T getSelected() {
return selected;
}
}

+ 22
- 0
server/src/com/vaadin/ui/proto/ValueProvider.java View File

@@ -0,0 +1,22 @@
/*
* Copyright 2000-2014 Vaadin Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.vaadin.ui.proto;

import java.io.Serializable;

public interface ValueProvider<T, V> extends Serializable {
V getValue(T data);
}

+ 24
- 0
shared/src/com/vaadin/shared/ui/proto/TypedGridClientRpc.java View File

@@ -0,0 +1,24 @@
/*
* Copyright 2000-2014 Vaadin Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.vaadin.shared.ui.proto;

import com.vaadin.shared.communication.ClientRpc;

public interface TypedGridClientRpc extends ClientRpc {

void setColumnOrder(String[] columnOrder);

}

+ 24
- 0
shared/src/com/vaadin/shared/ui/proto/TypedGridColumnState.java View File

@@ -0,0 +1,24 @@
/*
* Copyright 2000-2014 Vaadin Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.vaadin.shared.ui.proto;

import com.vaadin.shared.communication.SharedState;

public class TypedGridColumnState extends SharedState {

public String id;

}

+ 23
- 0
shared/src/com/vaadin/shared/ui/proto/TypedGridServerRpc.java View File

@@ -0,0 +1,23 @@
/*
* Copyright 2000-2014 Vaadin Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.vaadin.shared.ui.proto;

import com.vaadin.shared.communication.ServerRpc;

public interface TypedGridServerRpc extends ServerRpc {

void setSelected(String rowKey);
}

+ 21
- 0
shared/src/com/vaadin/shared/ui/proto/TypedGridState.java View File

@@ -0,0 +1,21 @@
/*
* Copyright 2000-2014 Vaadin Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.vaadin.shared.ui.proto;

import com.vaadin.shared.AbstractComponentState;

public class TypedGridState extends AbstractComponentState {
}

+ 45
- 0
uitest/src/com/vaadin/tests/proto/TypedGridUI.java View File

@@ -0,0 +1,45 @@
/*
* Copyright 2000-2014 Vaadin Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.vaadin.tests.proto;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Random;

import com.vaadin.server.VaadinRequest;
import com.vaadin.tests.components.AbstractTestUI;
import com.vaadin.tests.fieldgroup.ComplexPerson;
import com.vaadin.ui.proto.TypedGrid;

public class TypedGridUI extends AbstractTestUI {

@Override
protected void setup(VaadinRequest request) {
addComponent(new TypedGrid<ComplexPerson>(ComplexPerson.class,
createPersons(100)));
}

private static Collection<ComplexPerson> createPersons(int count) {
Random r = new Random(1337);
List<ComplexPerson> list = new ArrayList<ComplexPerson>();
for (int i = 0; i < count; ++i) {
list.add(ComplexPerson.create(r));
}
return list;
}

}

Loading…
Cancel
Save