package org.vaadin.viritin.v7.grid; import static org.vaadin.viritin.LazyList.DEFAULT_PAGE_SIZE; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import org.vaadin.viritin.LazyList; import org.vaadin.viritin.v7.ListContainer; import org.vaadin.viritin.MSize; import org.vaadin.viritin.v7.SortableLazyList; import org.vaadin.viritin.v7.grid.utils.GridUtils; import com.vaadin.v7.data.Container; import com.vaadin.v7.data.Item; import com.vaadin.v7.data.fieldgroup.FieldGroup; import com.vaadin.v7.data.util.PropertyValueGenerator; import com.vaadin.v7.event.SortEvent; import com.vaadin.v7.event.SortEvent.SortListener; import com.vaadin.server.Extension; import com.vaadin.v7.ui.Grid; /** * * @param <T> the entity type listed in the Grid */ public class MGrid<T> extends Grid { private static final long serialVersionUID = -7821775220281254054L; private Class<T> typeOfRows; public MGrid() { } /** * Creates a new instance of MGrid that contains certain types of rows. * * @param typeOfRows the type of objects that are listed in the grid */ public MGrid(Class<T> typeOfRows) { setRowType(typeOfRows); } /** * Sets the type of the objects used as rows and resets the listing. * * @param typeOfRows1 the type of objects used as rows * @return this */ public MGrid<T> setRowType(Class<T> typeOfRows1) { setContainerDataSource(new ListContainer<T>(typeOfRows1)); this.typeOfRows = typeOfRows1; return this; } /** * Creates a new instance of MGrid with given list of rows. Note that if * your list might be empty, it is better to use the constructor with the * type parameter and then initialize the content with setRows method. * * @param listOfEntities the (non-empty) list of entities to be displayed in * the grid */ public MGrid(List<T> listOfEntities) { setRows(listOfEntities); } /** * A shorthand to create MGrid using LazyList. By default page size of * LazyList.DEFAULT_PAGE_SIZE (30) is used. * * @param pageProvider the interface via entities are fetched * @param countProvider the interface via the count of items is detected */ public MGrid(LazyList.PagingProvider<T> pageProvider, LazyList.CountProvider countProvider) { this(new LazyList<T>(pageProvider, countProvider, DEFAULT_PAGE_SIZE)); } /** * A shorthand to create MGrid using a LazyList. * * @param pageProvider the interface via entities are fetched * @param countProvider the interface via the count of items is detected * @param pageSize the page size (aka maxResults) that is used in paging. */ public MGrid(LazyList.PagingProvider<T> pageProvider, LazyList.CountProvider countProvider, int pageSize) { this(new LazyList<T>(pageProvider, countProvider, pageSize)); } /** * A shorthand to create an MGrid using SortableLazyList. By default page * size of LazyList.DEFAULT_PAGE_SIZE (30) is used. * * @param pageProvider the interface via entities are fetched * @param countProvider the interface via the count of items is detected */ public MGrid(SortableLazyList.SortablePagingProvider<T> pageProvider, LazyList.CountProvider countProvider) { this(pageProvider, countProvider, DEFAULT_PAGE_SIZE); } /** * A shorthand to create an MGrid using SortableLazyList. By default page * size of LazyList.DEFAULT_PAGE_SIZE (30) is used. * * @param pageProvider the interface via entities are fetched * @param countProvider the interface via the count of items is detected */ public MGrid(SortableLazyList.MultiSortablePagingProvider<T> pageProvider, LazyList.CountProvider countProvider) { this(pageProvider, countProvider, DEFAULT_PAGE_SIZE); } /** * A shorthand to create MTable using SortableLazyList. * * @param pageProvider the interface via entities are fetched * @param countProvider the interface via the count of items is detected * @param pageSize the page size (aka maxResults) that is used in paging. */ public MGrid(SortableLazyList.SortablePagingProvider<T> pageProvider, LazyList.CountProvider countProvider, int pageSize) { this(new SortableLazyList<T>(pageProvider, countProvider, pageSize)); ensureSortListener(); } /** * A shorthand to create MTable using SortableLazyList. * * @param pageProvider the interface via entities are fetched * @param countProvider the interface via the count of items is detected * @param pageSize the page size (aka maxResults) that is used in paging. */ public MGrid(SortableLazyList.MultiSortablePagingProvider<T> pageProvider, LazyList.CountProvider countProvider, int pageSize) { this(new SortableLazyList(pageProvider, countProvider, pageSize)); ensureSortListener(); } private SortListener sortListener; private void ensureSortListener() { if (sortListener == null) { sortListener = new SortEvent.SortListener() { private static final long serialVersionUID = -8850456663417023533L; @Override public void sort(SortEvent event) { refreshVisibleRows(); } }; addSortListener(sortListener); } } /** * Enables saving/loading grid settings (visible columns, sort order, etc) * to cookies. * * @param settingsName cookie name where settings are saved should be * unique. */ public void attachSaveSettings(String settingsName) { GridUtils.attachToGrid(this, settingsName); } public MGrid<T> setRows(List<T> rows) { if (getContainerDataSource() instanceof ListContainer) { Collection<?> itemIds = getListContainer().getItemIds(); if (itemIds instanceof SortableLazyList) { SortableLazyList<T> old = (SortableLazyList<T>) itemIds; if(old.getSortProperty() != null && rows instanceof SortableLazyList ) { SortableLazyList<T> newList = (SortableLazyList<T>) rows; newList.setSortProperty(old.getSortProperty()); newList.setSortAscending(old.getSortAscending()); } } getListContainer().setCollection(rows); } else { setContainerDataSource(new ListContainer(rows)); } return this; } public List<T> getRows() { return (List<T>) getListContainer().getItemIds(); } protected ListContainer<T> getListContainer() { ListContainer<T> listContainer = (ListContainer<T>) getContainerDataSource(); return listContainer; } public MGrid<T> setRows(T... rows) { setRows(Arrays.asList(rows)); return this; } public <P> MGrid<T> withGeneratedColumn(String columnId, Class<P> presentationType, TypedPropertyValueGenerator.ValueGenerator<T, P> generator) { TypedPropertyValueGenerator<T, P> lambdaPropertyValueGenerator = new TypedPropertyValueGenerator<>(typeOfRows, presentationType, generator); addGeneratedColumn(columnId, lambdaPropertyValueGenerator); return this; } public MGrid<T> withGeneratedColumn(String columnId, StringPropertyValueGenerator.ValueGenerator<T> generator) { StringPropertyValueGenerator<T> lambdaPropertyValueGenerator = new StringPropertyValueGenerator<>(typeOfRows, generator); addGeneratedColumn(columnId, lambdaPropertyValueGenerator); return this; } public MGrid<T> withGeneratedColumn(String columnId, final PropertyValueGenerator<?> columnGenerator) { addGeneratedColumn(columnId, columnGenerator); return this; } private void addGeneratedColumn(String columnId, final PropertyValueGenerator<?> columnGenerator) { Container.Indexed container = getContainerDataSource(); GeneratedPropertyListContainer gplc; if (container instanceof GeneratedPropertyListContainer) { gplc = (GeneratedPropertyListContainer) container; } else { gplc = new GeneratedPropertyListContainer(typeOfRows); try { gplc.setCollection(getListContainer().getItemIds()); } catch (Exception e) {// NOP, not yet set } setContainerDataSource(gplc); } gplc.addGeneratedProperty(columnId, columnGenerator); addColumn(columnId); } public MGrid<T> withFullWidth() { setWidth(100, Unit.PERCENTAGE); return this; } @Override public T getSelectedRow() throws IllegalStateException { return (T) super.getSelectedRow(); } /** * * @param entity the entity (row) to be selected. * @return <code>true</code> if the selection state changed, * <code>false</code> if the itemId already was selected */ public boolean selectRow(T entity) { return select(entity); } /** * * @return true if something :-) See parent doc if you REALLY want to use * this. * @deprecated use the typed selectRow instead */ @Deprecated @Override public boolean select(Object itemId) throws IllegalArgumentException, IllegalStateException { return super.select(itemId); } public Collection<T> getSelectedRowsWithType() { // Maybe this is more complicated than it should be :-) return (Collection<T>) super.getSelectedRows(); } public MGrid<T> withProperties(String... propertyIds) { Container.Indexed containerDataSource = getContainerDataSource(); if (containerDataSource instanceof ListContainer) { ListContainer<T> lc = (ListContainer<T>) containerDataSource; lc.setContainerPropertyIds(propertyIds); } setColumns((Object[]) propertyIds); return this; } public MGrid<T> withId(String id) { setId(id); return this; } private FieldGroup.CommitHandler reloadDataEfficientlyAfterEditor; @Override public void setEditorEnabled(boolean isEnabled) throws IllegalStateException { super.setEditorEnabled(isEnabled); ensureRowRefreshListener(isEnabled); } protected void ensureRowRefreshListener(boolean isEnabled) { if (isEnabled && reloadDataEfficientlyAfterEditor == null) { reloadDataEfficientlyAfterEditor = new FieldGroup.CommitHandler() { private static final long serialVersionUID = -9107206992771475209L; @Override public void preCommit(FieldGroup.CommitEvent commitEvent) throws FieldGroup.CommitException { } @Override public void postCommit(FieldGroup.CommitEvent commitEvent) throws FieldGroup.CommitException { Item itemDataSource = commitEvent.getFieldBinder(). getItemDataSource(); if (itemDataSource instanceof ListContainer.DynaBeanItem) { ListContainer<T>.DynaBeanItem<T> dynaBeanItem = (ListContainer<T>.DynaBeanItem<T>) itemDataSource; T bean = dynaBeanItem.getBean(); refreshRow(bean); } } }; getEditorFieldGroup().addCommitHandler( reloadDataEfficientlyAfterEditor); } } /** * Manually forces refresh of the row that represents given entity. * ListContainer backing MGrid/MTable don't support property change * listeners (to save memory and CPU cycles). In some case with Grid, if you * know only certain row(s) are changed, you can make a smaller client side * change by refreshing rows with this method, instead of refreshing the * whole Grid (e.g. by re-assigning the bean list). * <p> * This method is automatically called if you use "editor row". * * @param bean the bean whose row should be refreshed. */ public void refreshRow(T bean) { Collection<Extension> extensions = getExtensions(); for (Extension extension : extensions) { // Calling with reflection for 7.6-7.5 compatibility if (extension.getClass().getName().contains( "RpcDataProviderExtension")) { try { Method method = extension.getClass().getMethod( "updateRowData", Object.class); method.invoke(extension, bean); break; } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) { Logger.getLogger(MGrid.class.getName()). log(Level.SEVERE, null, ex); } } } } /** * Manually forces refresh of the whole data. ListContainer backing * MGrid/MTable don't support property change listeners (to save memory and * CPU cycles). This method explicitly forces Grid's row cache invalidation. */ public void refreshRows() { if (getContainerDataSource() instanceof ListContainer) { ListContainer<T> listContainer = getListContainer(); if (listContainer.getItemIds() instanceof LazyList) { ((LazyList) listContainer.getItemIds()).reset(); } listContainer.fireItemSetChange(); } refreshVisibleRows(); } /** * Manually forces refresh of all visible rows. ListContainer backing * MGrid/MTable don't support property change listeners (to save memory and * CPU cycles). This method explicitly forces Grid's row cache invalidation. */ public void refreshVisibleRows() { Collection<Extension> extensions = getExtensions(); for (Extension extension : extensions) { // Calling with reflection for 7.6-7.5 compatibility if (extension.getClass().getName().contains( "RpcDataProviderExtension")) { try { Method method = extension.getClass().getMethod( "refreshCache"); method.invoke(extension); break; } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) { Logger.getLogger(MGrid.class.getName()). log(Level.SEVERE, null, ex); } } } } /** * Makes the table lazy load its content with given strategy. * * @param pageProvider the interface via entities are fetched * @param countProvider the interface via the count of items is detected * @return this MTable object */ public MGrid<T> lazyLoadFrom(LazyList.PagingProvider<T> pageProvider, LazyList.CountProvider countProvider) { setRows(new LazyList<T>(pageProvider, countProvider, DEFAULT_PAGE_SIZE)); return this; } /** * Makes the table lazy load its content with given strategy. * * @param pageProvider the interface via entities are fetched * @param countProvider the interface via the count of items is detected * @param pageSize the page size (aka maxResults) that is used in paging. * @return this MTable object */ public MGrid<T> lazyLoadFrom(LazyList.PagingProvider<T> pageProvider, LazyList.CountProvider countProvider, int pageSize) { setRows(new LazyList<T>(pageProvider, countProvider, pageSize)); return this; } /** * Makes the table lazy load its content with given strategy. * * @param pageProvider the interface via entities are fetched * @param countProvider the interface via the count of items is detected * @return this MTable object */ public MGrid<T> lazyLoadFrom( SortableLazyList.SortablePagingProvider<T> pageProvider, LazyList.CountProvider countProvider) { setRows(new SortableLazyList<T>(pageProvider, countProvider, DEFAULT_PAGE_SIZE)); ensureSortListener(); return this; } /** * Makes the table lazy load its content with given strategy. * * @param pageProvider the interface via entities are fetched * @param countProvider the interface via the count of items is detected * @return this MTable object */ public MGrid<T> lazyLoadFrom( SortableLazyList.MultiSortablePagingProvider<T> pageProvider, LazyList.CountProvider countProvider) { setRows(new SortableLazyList(pageProvider, countProvider, DEFAULT_PAGE_SIZE)); ensureSortListener(); return this; } /** * Makes the table lazy load its content with given strategy. * * @param pageProvider the interface via entities are fetched * @param countProvider the interface via the count of items is detected * @param pageSize the page size (aka maxResults) that is used in paging. * @return this MTable object */ public MGrid<T> lazyLoadFrom( SortableLazyList.SortablePagingProvider<T> pageProvider, LazyList.CountProvider countProvider, int pageSize) { setRows(new SortableLazyList<T>(pageProvider, countProvider, pageSize)); ensureSortListener(); return this; } /** * Makes the table lazy load its content with given strategy. * * @param pageProvider the interface via entities are fetched * @param countProvider the interface via the count of items is detected * @param pageSize the page size (aka maxResults) that is used in paging. * @return this MTable object */ public MGrid<T> lazyLoadFrom( SortableLazyList.MultiSortablePagingProvider<T> pageProvider, LazyList.CountProvider countProvider, int pageSize) { setRows(new SortableLazyList(pageProvider, countProvider, pageSize)); ensureSortListener(); return this; } public MGrid<T> withStyleName(String... styleNames) { for (String styleName : styleNames) { addStyleName(styleName); } return this; } public MGrid<T> withWidth(String width) { setWidth(width); return this; } public MGrid<T> withHeight(String height) { setHeight(height); return this; } public MGrid<T> withFullHeight() { return withHeight("100%"); } public MGrid<T> withSize(MSize mSize) { setWidth(mSize.getWidth(), mSize.getWidthUnit()); setHeight(mSize.getHeight(), mSize.getHeightUnit()); return this; } public MGrid<T> withColumnHeaders(String... header) { if (header.length != getColumns().size()) { throw new IllegalArgumentException("The length of the headers array must match the number of columns"); } for (int i = 0; i < getColumns().size(); i++) { Object propertyId = getColumns().get(i).getPropertyId(); if (header[i] != null) { getColumn(propertyId).setHeaderCaption(header[i]); } } return this; } }