/* * Copyright (c) 2005-2016 Vincent Vandenschrick. All rights reserved. * * This file is part of the Jspresso framework. * * Jspresso is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Jspresso 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with Jspresso. If not, see <http://www.gnu.org/licenses/>. */ package org.jspresso.framework.view.descriptor.basic; import java.util.ArrayList; import java.util.List; import org.jspresso.framework.model.descriptor.ICollectionDescriptorProvider; import org.jspresso.framework.model.descriptor.ICollectionPropertyDescriptor; import org.jspresso.framework.model.descriptor.IComponentDescriptor; import org.jspresso.framework.model.descriptor.IModelDescriptor; import org.jspresso.framework.view.action.IDisplayableAction; import org.jspresso.framework.view.descriptor.IPropertyViewDescriptor; import org.jspresso.framework.view.descriptor.ITableViewDescriptor; /** * This descriptor is used to implement a table view. This is certainly the most * commonly used collection descriptor in Jspresso. A table view displays a * collection of components (one row per component in the collection) detailed * by a set of properties (one column per displayed component property). * <p> * The table view will automatically adapt its columns depending on the * underlying property descriptors, e.g. : * <ul> * <li>columns for read-only properties won't be editable</li> * <li>columns that are assigned writability gates will compute the editability * of their cells based on each cell's gates</li> * <li>columns will adapt their renderer/editor based on the underlying property * type, e.g. a calendar component will be used for dates</li> * <li>column titles will be filled with property names translations based on * the user locale</li> * <li>mandatory properties will be visually indicated</li> * <li>...</li> * </ul> * A table view provides sensible defaults regarding its configuration, but it * can be refined using either the simple {@code renderedProperties} or the * more advanced yet lot more powerful {@code columnViewDescriptors} * properties. * <p> * The description property is used to compute view tooltips and support the * following rules : * <ol> * <li>if the description is a property name of the underlying model, this * property will be used to compute the (dynamic) tooltip (depending on the * actual model).</li> * <li>if the description is not a property name of the underlying model, the * the tooltip is considered static and the translation will searched in the * application resource bundles.</li> * <li>if the description is the empty string (''), the tooltip is de-activated. * </li> * <li>if the description is not set, then the toHtml property (see toHtml * property on entities / components definition) is used as dynamic property. * And the toHtml falls back to the toString if not set, which falls back to the * 1st string rendered property if not set.</li> * </ol> * Note that on every case above, HTML is supported. This way, you can have * really useful tooltips (event multi-line), in order to detail some synthetic * data. Moreover, this rule is available for the table rows tooltip, but also * for each individual column (property view) in the table. * * @author Vincent Vandenschrick */ public class BasicTableViewDescriptor extends BasicCollectionViewDescriptor implements ITableViewDescriptor { private List<IPropertyViewDescriptor> columnViewDescriptors; private boolean horizontallyScrollable; private List<String> renderedProperties; private boolean sortable; private IDisplayableAction sortingAction; private boolean columnReorderingAllowed; /** * Constructs a new {@code BasicTableViewDescriptor} instance. */ public BasicTableViewDescriptor() { horizontallyScrollable = true; sortable = true; columnReorderingAllowed = true; } /** * {@inheritDoc} */ @Override public List<IPropertyViewDescriptor> getColumnViewDescriptors() { ICollectionDescriptorProvider<?> modelDescriptor = ((ICollectionDescriptorProvider<?>) getModelDescriptor()); IComponentDescriptor<?> rowModelDescriptor = modelDescriptor.getCollectionDescriptor().getElementDescriptor(); List<IPropertyViewDescriptor> declaredPropertyViewDescriptors = columnViewDescriptors; if (declaredPropertyViewDescriptors == null) { List<String> viewRenderedProperties = getRenderedProperties(); if (modelDescriptor instanceof ICollectionPropertyDescriptor<?> && ((ICollectionPropertyDescriptor<?>) modelDescriptor).getReverseRelationEnd() != null) { viewRenderedProperties.remove( ((ICollectionPropertyDescriptor<?>) modelDescriptor).getReverseRelationEnd().getName()); } declaredPropertyViewDescriptors = new ArrayList<>(); for (String renderedProperty : viewRenderedProperties) { BasicPropertyViewDescriptor columnDescriptor = new BasicPropertyViewDescriptor(); columnDescriptor.setName(renderedProperty); columnDescriptor.setModelDescriptor(rowModelDescriptor.getPropertyDescriptor(renderedProperty)); declaredPropertyViewDescriptors.add(columnDescriptor); } } List<IPropertyViewDescriptor> actualPropertyViewDescriptors = new ArrayList<>(); for (IPropertyViewDescriptor propertyViewDescriptor : declaredPropertyViewDescriptors) { IModelDescriptor columnModelDescriptor = propertyViewDescriptor.getModelDescriptor(); if (columnModelDescriptor == null) { if (propertyViewDescriptor.getName() != null) { columnModelDescriptor = rowModelDescriptor.getPropertyDescriptor(propertyViewDescriptor.getName()); } } // Collection properties are not supported as columns if (columnModelDescriptor != null && !(columnModelDescriptor instanceof ICollectionPropertyDescriptor<?>)) { actualPropertyViewDescriptors.addAll(PropertyViewDescriptorHelper.explodeComponentReferences( propertyViewDescriptor, rowModelDescriptor)); } } return actualPropertyViewDescriptors; } /** * Gets the sortingAction. * * @return the sortingAction. */ @Override public IDisplayableAction getSortingAction() { return sortingAction; } /** * Gets the horizontallyScrollable. * * @return the horizontallyScrollable. */ @Override public boolean isHorizontallyScrollable() { return horizontallyScrollable; } /** * Returns {@code true}. * <p> * {@inheritDoc} */ @Override public boolean isVerticallyScrollable() { return true; } /** * {@inheritDoc} */ @Override public boolean isScrollable() { return isVerticallyScrollable() || isHorizontallyScrollable(); } /** * Gets the sortable. * * @return the sortable. */ @Override public boolean isSortable() { return sortable; } /** * This property allows for configuring the columns of the table view in a * very customizable manner, thus overriding the model descriptor defaults. * Each property view descriptor contained in the list describes a table * column that will be rendered in the UI accordingly. * <p> * For instance, a writable property can be made specifically read-only on * this table view by specifying its column property view descriptor * read-only. In that case, the model remains untouched and only the view is * impacted. * <p> * Following the same scheme, you can assign a list of writability gates on a * column to introduce dynamic cell editability on the view without modifying * the model. * <p> * A last, yet important, example of column view descriptor usage is the * role-based column set configuration. Whenever you want a column to be * available only for certain user roles (profiles), you can configure a * column property view descriptor with a list of granted roles. If the user * doesn't have the column(s)required role, the forbidden columns simply won't * be displayed. This allows for high authorization-based versatility. * <p> * There are many other usages of defining column property view descriptors * all of them being linked to customizing the table columns without impacting * the model. * * @param columnViewDescriptors * the columnViewDescriptors to set. */ public void setColumnViewDescriptors( List<IPropertyViewDescriptor> columnViewDescriptors) { this.columnViewDescriptors = columnViewDescriptors; } /** * This property allows to define the table horizontal scrolling behaviour. * Whenever it is set to false, the corresponding table UI component will * adapt its columns to fit the available horizontal space. * <p> * Default value is {@code true}, i.e. table columns will have their * default size and tha table will scroll horizontally as needed. * * @param horizontallyScrollable * the horizontallyScrollable to set. */ public void setHorizontallyScrollable(boolean horizontallyScrollable) { this.horizontallyScrollable = horizontallyScrollable; } /** * This is somehow a shortcut to using the {@code columnViewDescriptors} * property. Instead of providing a full-blown list of property view * descriptors to configure the table columns, you just pass-in a list of * property names. Table columns are then created from this list, keeping * model defaults for all column characteristics. * <p> * Whenever the property value is {@code null} (default), the column list * is determined from the collection element component descriptor * {@code renderedProperties} property. * * @param renderedProperties * the renderedProperties to set. */ public void setRenderedProperties(List<String> renderedProperties) { this.renderedProperties = renderedProperties; } /** * This property allows to define the table horizontal sorting behaviour. * Whenever it is set to false, the corresponding table UI component will not * allow manual sorting of its rows. * <p> * Default value is {@code true}, i.e. table allows for its rows to be * sorted. * * @param sortable * the sortable to set. */ public void setSortable(boolean sortable) { this.sortable = sortable; } /** * Configures the action to be activated when a sort is triggered by the user. * It should be used with caution and rarely be overridden from the default. * * @param sortingAction * the sortingAction to set. */ public void setSortingAction(IDisplayableAction sortingAction) { this.sortingAction = sortingAction; } /** * Gets the renderedProperties. * * @return the renderedProperties. */ private List<String> getRenderedProperties() { if (renderedProperties == null) { renderedProperties = ((ICollectionDescriptorProvider<?>) getModelDescriptor()) .getCollectionDescriptor().getElementDescriptor() .getRenderedProperties(); } return renderedProperties; } /** * Is column reordering allowed. * * @return the boolean */ @Override public boolean isColumnReorderingAllowed() { return columnReorderingAllowed; } /** * Configures if the table view should allow for column reordering. * The default value is {@code true}. * * @param columnReorderingAllowed the column reordering allowed boolean. */ public void setColumnReorderingAllowed(boolean columnReorderingAllowed) { this.columnReorderingAllowed = columnReorderingAllowed; } }