/** * Copyright (C) 2011 BonitaSoft S.A. * BonitaSoft, 32 rue Gustave Eiffel - 38000 Grenoble * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2.0 of the License, or * (at your option) any later version. * This program 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 General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.bonitasoft.web.toolkit.client.ui.component.table; import static org.bonitasoft.web.toolkit.client.common.i18n.AbstractI18n._; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.IItem; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; import org.bonitasoft.web.toolkit.client.data.item.attribute.reader.AbstractAttributeReader; import org.bonitasoft.web.toolkit.client.data.item.attribute.reader.AttributeReader; import org.bonitasoft.web.toolkit.client.data.item.attribute.reader.DeployedJsId; import org.bonitasoft.web.toolkit.client.data.item.attribute.reader.HasCounters; import org.bonitasoft.web.toolkit.client.data.item.attribute.reader.HasDeploys; import org.bonitasoft.web.toolkit.client.data.item.attribute.reader.HasReaders; import org.bonitasoft.web.toolkit.client.ui.JsId; import org.bonitasoft.web.toolkit.client.ui.action.Action; import org.bonitasoft.web.toolkit.client.ui.action.popup.DeleteMultipleItemsPopupAction; import org.bonitasoft.web.toolkit.client.ui.action.popup.ItemDeletePopupAction; import org.bonitasoft.web.toolkit.client.ui.component.Link; import org.bonitasoft.web.toolkit.client.ui.component.Refreshable; import org.bonitasoft.web.toolkit.client.ui.component.containers.ContainerDummy; import org.bonitasoft.web.toolkit.client.ui.component.form.FormNode; import org.bonitasoft.web.toolkit.client.ui.component.table.Table.VIEW_TYPE; import org.bonitasoft.web.toolkit.client.ui.component.table.formatter.DefaultItemTableCellFormatter; import org.bonitasoft.web.toolkit.client.ui.component.table.formatter.ItemTableCellFormatter; import com.google.gwt.event.shared.HandlerRegistration; import com.google.gwt.user.client.Element; /** * @author Séverin Moussel */ public class ItemTable extends AbstractTable implements Refreshable, FormNode { protected Table table = null; protected ItemDefinition itemDefinition = null; private final ArrayList<AbstractAttributeReader> columns = new ArrayList<AbstractAttributeReader>(); protected List<ItemTableActionSet> actionSets = new LinkedList<ItemTableActionSet>(); protected Map<String, ItemTableCellFormatter> cellFormatters = new HashMap<String, ItemTableCellFormatter>(); private int actionColumnPosition = -1; private APIID defaultSelectedId = null; private Integer defaultSelectedLine = null; private HashMap<String, String> attributesForGroupedActions = new HashMap<String, String>(); private boolean itemIdOnRow = false; protected final HashMap<String, IItem> loadedItems = new HashMap<String, IItem>(); public ItemTable(final ItemDefinition itemDefinition) { this(null, itemDefinition); } public ItemTable(final ItemDefinition itemDefinition, boolean itemIdOnRow) { this(null, itemDefinition); this.itemIdOnRow = itemIdOnRow; } public ItemTable(final JsId jsid, final ItemDefinition itemDefinition) { super(jsid); assert itemDefinition != null; this.itemDefinition = itemDefinition; this.table = new Table(jsid); setFillOnRefresh(true); } public HandlerRegistration addItemCheckedHandler(ItemCheckedHandler handler) { return this.table.addItemCheckedHandler(handler); } public HandlerRegistration addItemUncheckedHandler(ItemUncheckedHandler handler) { return this.table.addItemUncheckedHandler(handler); } public HandlerRegistration addItemTableLoadedHandler(ItemTableLoadedHandler handler) { return addHandler(handler, ItemTableLoadedEvent.TYPE); } /** * Define the line to select by default. * * @param line * the zero based index of the line */ public ItemTable setDefaultSelectedLine(final Integer line) { this.defaultSelectedLine = line; return this; } public void setDefaultSelectedId(final APIID id) { this.defaultSelectedId = id; } public APIID getDefaultSelectedId() { return this.defaultSelectedId; } public final ItemDefinition<?> getItemDefinition() { return this.itemDefinition; } @Override public final ItemTable setFillOnLoad(final boolean fillOnLoad) { super.setFillOnLoad(fillOnLoad); this.table.setFillOnLoad(fillOnLoad); return this; } public ItemTable setItems(final List<IItem> items) { resetLines(); addItems(items); fireEvent(new ItemTableLoadedEvent(items)); return this; } public final ItemTable addItems(final List<IItem> items) { if (!this.table.isSaveCheckboxes()) { this.table.clearSelectedIds(); } for (final IItem item : items) { this.addItem(item); } this.table.updateHtml(); return this; } protected final ItemTable addItem(final IItem item) { return this.addItem(item, null); } protected ItemTable addItem(final IItem item, final String className) { loadedItems.put(item.getId().toString(), item); if (this.actionColumnPosition == -1 && !this.actionSets.isEmpty()) { addActionColumn(); } // Get default action (if there is one) Action defAction = null; final int lineNumber = this.table.getLinesNumber(); if (this.defaultAction != null) { defAction = new Action() { @Override public void execute() { ItemTable.this.defaultAction.addParameter("id", item.getId().toString()); ItemTable.this.defaultAction.addParameter("cell_index", String.valueOf(lineNumber + 1));// +1 because the line is build in the next // instruction ItemTable.this.defaultAction.execute(); } }; } else if (this.actionSets != null) { for (final ItemTableActionSet actionSet : this.actionSets) { defAction = actionSet.getDefaultAction(item); if (defAction != null) { break; } } } if (defAction != null) { defAction.addParameter("id", item.getId().toString()); defAction.addParameter("cell_index", String.valueOf(this.table.getLinesNumber() + 1));// +1 because the line is build in the next instruction } // Create the line component this.table.setItemIdOnRow(this.itemIdOnRow); this.table.addLine(item.getId().toString(), className, defAction, isGroupedActionAllowed(item)); // Fill it with data columns addItemCells(item); // Add the actions column // this.addItemActions(item); return this; } private Boolean isGroupedActionAllowed(IItem item) { if (attributesForGroupedActions.isEmpty()) { return true; } else { Iterator it = attributesForGroupedActions.entrySet().iterator(); while (it.hasNext()) { Map.Entry<String, String> pairs = (Map.Entry<String, String>) it.next(); if (item.getAttributeValue(pairs.getKey().toString()).equals(pairs.getValue())) { return false; } } return true; } } public ItemTable addAttributeToCheckForGroupedActions(String attributeName, String value) { attributesForGroupedActions.put(attributeName, value); return this; } protected final void addItemCells(final IItem item) { int index = 0; for (final AbstractAttributeReader column : this.columns) { if (index == this.actionColumnPosition) { this.addItemActions(item); } else { ItemTableCellFormatter cellFormatter = null; if (column instanceof HasDeploys) { /** * Get first cell formatter met for attributes deployed on our table */ for (final String deployedAttribute : ((HasDeploys) column).getDeploys()) { final String compoundAttribute = new DeployedJsId(deployedAttribute, column.getLeadAttribute()).toString(); if (this.cellFormatters.containsKey(compoundAttribute)) { cellFormatter = this.cellFormatters.get(compoundAttribute); break; } } } else { cellFormatter = this.cellFormatters.get(column.getLeadAttribute()); } if (cellFormatter == null) { cellFormatter = new DefaultItemTableCellFormatter(); } cellFormatter.setTable(this.table); cellFormatter.setColumn(this.table.getLastColumn()); cellFormatter.setLine(this.table.getLastLine()); cellFormatter.setAttribute(column); cellFormatter.setItem(item); cellFormatter.execute(); } index++; } } protected void addItemActions(final IItem item) { this.addItemActions(item, getActionsFor(item)); } protected void addItemActions(final IItem item, final List<ItemTableAction> actions) { final ContainerDummy<ItemTableAction> actionComponents = new ContainerDummy<ItemTableAction>(); for (final ItemTableAction itemTableAction : actions) { itemTableAction.addParameter("cell_index", String.valueOf(this.table.getLinesNumber())); itemTableAction.addParameter("id", item.getId().toString()); actionComponents.append(itemTableAction); } this.table.addCell(actionComponents); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // FILTERS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public final ItemTable addHiddenFilter(final String name, final String value) { this.table.addHiddenFilter(name, value); return this; } public final ItemTable addHiddenFilter(final String name, final APIID value) { if (value == null) { return addHiddenFilter(name, (String) null); } return addHiddenFilter(name, value.toString()); } public ItemTable resetHiddenFilters() { this.table.resetHiddenFilters(); return this; } public final ItemTable addHiddenFilters(final Map<String, String> filters) { this.table.addHiddenFilters(filters); return this; } public final ItemTable addHiddenFilter(final Map<String, String> filters) { this.table.addHiddenFilters(filters); return this; } public final Map<String, String> getFilters() { return this.table.getFilters(); } public final List<String> getDeploys() { final List<String> result = new ArrayList<String>(); for (final AbstractAttributeReader reader : this.columns) { getAttributeReaderDeploys(reader, result); } return result; } private List<String> getAttributeReaderDeploys(final AbstractAttributeReader reader, final List<String> deploys) { if (reader instanceof HasDeploys) { deploys.addAll(((HasDeploys) reader).getDeploys()); } if (reader instanceof HasReaders) { final Map<String, AbstractAttributeReader> readers = ((HasReaders) reader).getAttributeReaders(); if (readers != null) { for (final AbstractAttributeReader attributeReader : readers.values()) { getAttributeReaderDeploys(attributeReader, deploys); } } } return deploys; } public final List<String> getCounters() { final List<String> result = new ArrayList<String>(); // go through readers for (final AbstractAttributeReader reader : this.columns) { if (reader instanceof HasCounters) { result.addAll(((HasCounters) reader).getCounters()); } } // go through actions for (final ItemTableActionSet<?> actionSet : this.actionSets) { if (actionSet instanceof HasCounters) { result.addAll(((HasCounters) actionSet).getCounters()); } } return result; } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // COLUMNS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public ItemTable addActionColumn() { // this.table.addColumn(new JsId("actions"), _("Actions")); this.actionColumnPosition = this.columns.size(); this.columns.add(null); return this; } public final ItemTable addColumn(final AbstractAttributeReader attribute, final String label) { return this.addColumn(attribute, label, false); } public final ItemTable addColumn(final AbstractAttributeReader attribute, final String label, final boolean sorted, final boolean ascendant) { this.table.addColumn(new JsId(attribute.getLeadAttribute()), label, attribute.getLeadAttribute(), sorted, ascendant); this.columns.add(attribute); return this; } public final ItemTable addColumn(final AbstractAttributeReader attribute, final String label, final boolean sortable) { JsId columnJsid; // Get first attribute's reader first deploy to identify column. Used by formatters. if (attribute instanceof HasDeploys) { columnJsid = new DeployedJsId(((HasDeploys) attribute).getDeploys().get(0), attribute.getLeadAttribute()); } else { columnJsid = new JsId(attribute.getLeadAttribute()); } this.table.addColumn(columnJsid, label, sortable ? attribute.getLeadAttribute() : null); this.columns.add(attribute); return this; } public final ItemTable addColumn(final String attributeName, final String label) { return this.addColumn(attributeName, label, false); } public final ItemTable addColumn(final String attributeName, final String label, final boolean sorted, final boolean ascendant) { this.table.addColumn(new JsId(attributeName), label, attributeName, sorted, ascendant); this.columns.add(new AttributeReader(attributeName)); return this; } public final ItemTable addColumn(final String attributeName, final String label, final boolean sortable) { if (sortable) { this.table.addColumn(new JsId(attributeName), label, attributeName); } else { this.table.addColumn(new JsId(attributeName), label); } this.columns.add(new AttributeReader(attributeName)); return this; } /** * @see org.bonitasoft.web.toolkit.client.ui.component.table.Table#getColumns() */ public List<TableColumn> getColumns() { return this.table.getColumns(); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // CELL FORMATTERS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public final ItemTable addCellFormatter(final String attributeName, final ItemTableCellFormatter cellFormatter) { this.cellFormatters.put(attributeName, cellFormatter); return this; } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // ACTIONS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// private Action defaultAction = null; /** * @param defaultAction * the defaultAction to set */ public final ItemTable setDefaultAction(final Action defaultAction) { this.defaultAction = defaultAction; return this; } public final ItemTable resetActions() { this.actionSets.clear(); return this; } public final ItemTable setActions(final ItemTableActionSet itemTableActionSet) { resetActions(); return addActions(itemTableActionSet); } public final ItemTable addActions(final ItemTableActionSet itemTableActionSet) { itemTableActionSet.setItemTable(this); this.actionSets.add(itemTableActionSet); return this; } public final ItemTable addGroupedAction(final JsId id, final String label, final String tooltip, final Action action) { this.table.addGroupedAction(id, label, tooltip, action); return this; } /** * Add an action to the table - action not link to checkbox, always visible */ public final ItemTable addAction(Link link) { this.table.addAction(link); return this; } /** * * @param link * @param force * Force the visibility of the action (can't be disabled) * @return */ public final ItemTable addGroupedAction(Link link, final boolean force) { this.table.addGroupedAction(link, force); return this; } public final ItemTable addGroupedAction(Link link) { return addGroupedAction(link, false); } /** * * @param label * @param tooltip * @param action * @param force * Force the visibility of the action (can't be disabled) */ public final ItemTable addGroupedAction(final JsId id, final String label, final String tooltip, final Action action, final boolean force) { this.table.addGroupedAction(id, label, tooltip, action, force); return this; } public final ItemTable addGroupedMultipleDeleteAction(final String tooltip, final ItemDefinition definition, final String itemName, final String itemNamePlural) { this.table.addGroupedAction(new JsId("delete"), _("Delete"), tooltip, new DeleteMultipleItemsPopupAction(definition, itemName, itemNamePlural)); return this; } public List<ItemTableAction> getActionsFor(final IItem item) { if (this.actionSets == null) { return new LinkedList<ItemTableAction>(); } final List<ItemTableAction> actions = new LinkedList<ItemTableAction>(); for (final ItemTableActionSet set : this.actionSets) { actions.addAll(set.getActionsFor(item)); } return actions; } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // OPTIONS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public final ItemTable setNbLinesByPage(final int nbLinesByPage) { this.table.setPager(0, 0, nbLinesByPage); return this; } public final ItemTable setView(final VIEW_TYPE view) { this.table.setView(view); return this; } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // HTML GENERATION // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @Override protected Element makeElement() { if (this.actionSets != null && this.actionSets.size() > 0 && this.actionColumnPosition == -1) { final JsId actionJsId = new JsId("actions"); this.table.addColumn(actionJsId, _("Actions")); if (this.actionColumnPosition > -1) { this.table.setColumnPos(actionJsId, this.actionColumnPosition); } } return this.table.getElement(); } @Override protected void postProcessHtml() { super.postProcessHtml(); final ItemTableFiller filler = new ItemTableFiller(); this.table.setRefreshFiller(filler); filler.setTarget(this); setFiller(filler); } @Override public void refresh() { refresh(null); } public void refresh(final Action callback) { runFillers(callback); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // GENERIC DELEGATIONS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @Override public final ItemTable addClass(final String className) { if (this.table != null) { this.table.addClass(className); } return this; } public final int getNbLinesByPage() { return this.table.getNbLinesByPage(); } public final int getPage() { return this.table.getPage(); } public final List<String> getSelectedIds() { return this.table.getSelectedIds(); } public final String getSearch() { return this.table.getSearch(); } /** * @see org.bonitasoft.web.toolkit.client.ui.component.table.Table#setSearch(java.lang.String) */ public final ItemTable setSearch(final String query) { this.table.setSearch(query); return this; } public final ItemTable setSelectLineOnClick(boolean value) { this.table.setSelectLineOnClick(value); return this; } public final String getOrder() { return this.table.getOrder(); } public final ItemTable resetLines() { this.table.resetLines(); this.loadedItems.clear(); return this; } public final ItemTable setPager(final int currentPage, final int nbPages, final int nbLinesByPage) { this.table.setPager(currentPage, nbPages, nbLinesByPage); return this; } public final ItemTable setPage(final int page) { this.table.setPage(page); return this; } @Override public final String toString() { return this.table.toString(); } public final boolean hasGroupedActions() { return this.table.hasGroupedActions(); } public final ItemTable setOrder(final String sortName, final boolean sortAscending) { this.table.setOrder(sortName, sortAscending); return this; } public ItemTable setShowSearch(final boolean show) { this.table.setShowSearch(show); return this; } public void updateView() { this.table.updateView(); } public IItem getItem(String itemId) { return loadedItems.get(itemId); } }