blob: dcd4498bc0167199d2498dd921acfb41de0babe0 [file] [log] [blame]
package org.tepi.filtertable;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import org.tepi.filtertable.FilterFieldGenerator.IFilterTable;
import org.tepi.filtertable.datefilter.DateInterval;
import com.vaadin.data.Container;
import com.vaadin.data.util.converter.Converter.ConversionException;
import com.vaadin.server.LegacyPaint;
import com.vaadin.server.PaintException;
import com.vaadin.server.PaintTarget;
import com.vaadin.ui.AbstractField;
import com.vaadin.ui.Component;
import com.vaadin.ui.CustomTreeTable;
import com.vaadin.ui.HasComponents;
import com.vaadin.ui.TextField;
@SuppressWarnings("serial")
public class FilterTreeTable extends CustomTreeTable implements IFilterTable {
/* Maps property id's to column filter components */
private final Map<Object, Component> columnIdToFilterMap = new HashMap<>();
/* Internal list of currently collapsed column id:s */
private final Set<Object> collapsedColumnIds = new HashSet<>();
/* Set to true to show the filter components */
private boolean filtersVisible;
/* Filter Generator and Decorator */
private FilterGenerator filterGenerator;
private FilterDecorator decorator;
/* FilterFieldGenerator instance */
private final FilterFieldGenerator generator;
/* Is initialization done */
private final boolean initDone;
/* Force-render filter fields */
private boolean reRenderFilterFields;
/* Wrap filters with additional div for styling? */
private boolean wrapFilters = false;
/* Are filters run immediately, or only on demand? */
private boolean filtersRunOnDemand = false;
/**
* Creates a new empty FilterTable
*/
public FilterTreeTable() {
this(null);
}
/**
* Creates a new empty FilterTable with the given caption
*
* @param caption
* Caption to set for the FilterTable
*/
public FilterTreeTable(String caption) {
super(caption);
generator = new FilterFieldGenerator(this);
initDone = true;
}
@Override
public void paintContent(PaintTarget target) throws PaintException {
super.paintContent(target);
/* Add filter components to UIDL */
target.startTag("filters");
target.addAttribute("filtersvisible", filtersVisible);
target.addAttribute("forceRender", reRenderFilterFields);
reRenderFilterFields = false;
if (filtersVisible) {
for (Object key : getColumnIdToFilterMap().keySet()) {
/* Do not paint filters which are not children */
if (columnIdToFilterMap.get(key) != null
&& columnIdToFilterMap.get(key).getParent() == null) {
continue;
}
/* Paint the filter field */
target.startTag("filtercomponent-" + columnIdMap.key(key));
target.addAttribute("columnid", columnIdMap.key(key));
Component c = getColumnIdToFilterMap().get(key);
LegacyPaint.paint(c, target);
target.endTag("filtercomponent-" + columnIdMap.key(key));
}
}
target.endTag("filters");
}
@Override
public void setColumnCollapsed(Object propertyId, boolean collapsed)
throws IllegalStateException {
super.setColumnCollapsed(propertyId, collapsed);
Component c = getColumnIdToFilterMap().get(propertyId);
if (collapsed) {
collapsedColumnIds.add(propertyId);
if (c != null) {
c.setParent(null);
if (c instanceof TextField) {
((TextField) c).setValue("");
} else if (c instanceof AbstractField<?>) {
((AbstractField<?>) c).setValue(null);
}
}
} else {
if (c != null) {
c.setParent(this);
}
collapsedColumnIds.remove(propertyId);
}
reRenderFilterFields = true;
markAsDirty();
}
@Override
public void setContainerDataSource(Container newDataSource) {
super.setContainerDataSource(newDataSource);
resetFilters();
}
@Override
public void setContainerDataSource(Container newDataSource,
Collection<?> visibleIds) {
super.setContainerDataSource(newDataSource, visibleIds);
resetFilters();
}
/**
* Resets all filters.
*
* Note: Recreates the filter fields also!
*/
public void resetFilters() {
if (initDone) {
disableContentRefreshing();
for (Component c : columnIdToFilterMap.values()) {
c.setParent(null);
}
collapsedColumnIds.clear();
columnIdToFilterMap.clear();
generator.destroyFilterComponents();
generator.initializeFilterFields();
reRenderFilterFields = true;
enableContentRefreshing(true);
}
}
/**
* Clears all filters without recreating the filter fields.
*/
public void clearFilters() {
if (initDone) {
generator.clearFilterData();
}
}
/**
* Sets the FilterDecorator for this FilterTable. FilterDecorator may be
* used to provide proper translated display names and icons for the enum,
* boolean and date values used in the filters.
*
* Note: Recreates the filter fields also!
*
* @param decorator
* An implementation of FilterDecorator to use with this
* FilterTable. Remove by giving null as this parameter.
*/
public void setFilterDecorator(FilterDecorator decorator) {
this.decorator = decorator;
resetFilters();
}
/**
* Sets the FilterGenerator to use for providing custom Filters to the
* container for one or more properties.
*
* @param generator
* FilterGenerator to use with this FilterTable. Remove by giving
* null as this parameter.
*/
public void setFilterGenerator(FilterGenerator generator) {
filterGenerator = generator;
}
/**
* Sets the Filter bar visible or hidden.
*
* @param filtersVisible
* true to set the Filter bar visible.
*/
public void setFilterBarVisible(boolean filtersVisible) {
this.filtersVisible = filtersVisible;
reRenderFilterFields = true;
markAsDirty();
}
/**
* Returns the current visibility state of the filter bar.
*
* @return true if the filter bar is visible
*/
public boolean isFilterBarVisible() {
return filtersVisible;
}
/**
* Toggles the visibility of the filter field defined for the give column
* ID.
*
* @param columnId
* Column/Property ID of the filter to toggle
* @param visible
* true to set visible, false to set hidden
*/
public void setFilterFieldVisible(Object columnId, boolean visible) {
Component component = columnIdToFilterMap.get(columnId);
if (component != null) {
component.setVisible(visible);
reRenderFilterFields = true;
markAsDirty();
}
}
/**
* Returns visibility state of the filter field for the given column ID
*
* @param columnId
* Column/Property ID of the filter field to query
* @return true if filter is visible, false if it's hidden
*/
public boolean isFilterFieldVisible(Object columnId) {
Component component = columnIdToFilterMap.get(columnId);
if (component != null) {
return component.isVisible();
}
return false;
}
/**
* Set a value of a filter field. Note that for Date filters you need to
* provide a value of {@link DateInterval} type.
*
* @param propertyId
* Property id for which to set the value
* @param value
* New value
* @return true if setting succeeded, false if field was not found
* @throws ConversionException
* exception from the underlying field
*/
public boolean setFilterFieldValue(Object propertyId, Object value)
throws ConversionException {
Component field = getColumnIdToFilterMap().get(propertyId);
boolean retVal = field != null;
if (field != null) {
((AbstractField<?>) field).setConvertedValue(value);
}
return retVal;
}
/**
* Get the current value of a filter field
*
* @param propertyId
* Property id from which to get the value
* @return Current value
*/
public Object getFilterFieldValue(Object propertyId) {
Component field = getColumnIdToFilterMap().get(propertyId);
if (field != null) {
return ((AbstractField<?>) field).getValue();
}
return null;
}
/**
* Returns the filter component instance associated with the given property
* ID.
*
* @param propertyId
* Property id for which to find the filter component.
* @return Related component instance or null if not found.
*/
public Component getFilterField(Object propertyId) {
return getColumnIdToFilterMap().get(propertyId);
}
@Override
public Filterable getFilterable() {
return getContainerDataSource() instanceof Filterable ? (Filterable) getContainerDataSource()
: null;
}
@Override
public FilterGenerator getFilterGenerator() {
return filterGenerator;
}
@Override
public FilterDecorator getFilterDecorator() {
return decorator;
}
@Override
public Map<Object, Component> getColumnIdToFilterMap() {
return columnIdToFilterMap;
}
@Override
public HasComponents getAsComponent() {
return this;
}
@Override
public Iterator<Component> iterator() {
Set<Component> children = new HashSet<>();
if (visibleComponents != null) {
children.addAll(visibleComponents);
}
if (initDone) {
for (Object key : columnIdToFilterMap.keySet()) {
Component filter = columnIdToFilterMap.get(key);
if (equals(filter.getParent())) {
children.add(filter);
}
}
}
return children.iterator();
}
@Override
public void setVisibleColumns(Object... visibleColumns) {
reRenderFilterFields = true;
if (visibleColumns != null && columnIdToFilterMap != null) {
/* First clear all parent references */
for (Object key : columnIdToFilterMap.keySet()) {
columnIdToFilterMap.get(key).setParent(null);
}
/* Set this as parent to visible columns */
for (Object key : visibleColumns) {
Component filter = columnIdToFilterMap.get(key);
if (filter != null) {
filter.setParent(this);
}
}
}
super.setVisibleColumns(visibleColumns);
resetFilters();
}
@Override
public void setRefreshingEnabled(boolean enabled) {
if (enabled) {
enableContentRefreshing(true);
} else {
disableContentRefreshing();
}
}
public void setWrapFilters(boolean wrapFilters) {
if (this.wrapFilters == wrapFilters) {
return;
}
this.wrapFilters = wrapFilters;
reRenderFilterFields = true;
markAsDirty();
}
public boolean isWrapFilters() {
return wrapFilters;
}
public void setFilterOnDemand(boolean filterOnDemand) {
if (filtersRunOnDemand == filterOnDemand) {
return;
}
filtersRunOnDemand = filterOnDemand;
reRenderFilterFields = true;
generator.setFilterOnDemandMode(filtersRunOnDemand);
}
public boolean isFilterOnDemand() {
return filtersRunOnDemand;
}
public void runFilters() {
if (!filtersRunOnDemand) {
throw new IllegalStateException(
"Can't run filters on demand when filtersRunOnDemand is set to false");
}
generator.runFiltersNow();
}
}