From f59a1a2467eae3234c9fb1888b81a04cca4f1e18 Mon Sep 17 00:00:00 2001 From: tpylak <tpylak> Date: Thu, 22 Oct 2009 08:51:32 +0000 Subject: [PATCH] LMS-1073 Excel like Auto-Filter: server side SVN: 13037 --- .../experiment/ExperimentTypeColDefKind.java | 2 +- .../entity_type/AbstractEntityTypeGrid.java | 40 +----- .../ui/grid/AbstractBrowserGrid.java | 6 +- .../ui/grid/PagingColumnFilter.java | 12 +- .../column/GridCustomColumnGrid.java | 3 +- .../expressions/filter/FilterToolbar.java | 2 +- .../web/client/dto/ColumnDistinctValues.java | 58 +++++++++ .../client/web/client/dto/GridRowModels.java | 27 +++-- .../web/client/dto/IResultSetConfig.java | 11 +- .../calculator/GridExpressionUtils.java | 16 +-- .../resultset/CachedResultSetManager.java | 114 ++++++++++++++---- .../client/web/server/util/TSVRenderer.java | 5 +- .../generic/shared/basic/GridRowModel.java | 15 +-- .../shared/basic/dto/GridFilterInfo.java | 12 +- .../web/server/CommonClientServiceTest.java | 14 ++- .../server/calculator/RowCalculatorTest.java | 4 +- .../client/web/server/calculator/RowTest.java | 5 +- .../resultset/CachedResultSetManagerTest.java | 86 ++++++++++++- .../web/server/util/FilterUtilsTest.java | 5 +- .../web/server/util/TSVRendererTest.java | 10 +- 20 files changed, 309 insertions(+), 138 deletions(-) create mode 100644 openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/dto/ColumnDistinctValues.java diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/columns/specific/experiment/ExperimentTypeColDefKind.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/columns/specific/experiment/ExperimentTypeColDefKind.java index 1b5f293018c..97bde25215f 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/columns/specific/experiment/ExperimentTypeColDefKind.java +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/columns/specific/experiment/ExperimentTypeColDefKind.java @@ -37,7 +37,7 @@ public enum ExperimentTypeColDefKind implements IColumnDefinitionKind<Experiment DATABASE_INSTANCE(experimentTypeColDefKindFactory.createDatabaseInstanceColDefKind()); - // no specific Sample Type columns + // no specific Experiment Type columns private final AbstractColumnDefinitionKind<ExperimentType> columnDefinitionKind; diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/entity_type/AbstractEntityTypeGrid.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/entity_type/AbstractEntityTypeGrid.java index 28a41b5c3c1..06f7a663964 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/entity_type/AbstractEntityTypeGrid.java +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/entity_type/AbstractEntityTypeGrid.java @@ -35,7 +35,6 @@ import ch.systemsx.cisd.openbis.generic.client.web.client.application.Dict; import ch.systemsx.cisd.openbis.generic.client.web.client.application.IViewContext; import ch.systemsx.cisd.openbis.generic.client.web.client.application.framework.DisplayTypeIDGenerator; import ch.systemsx.cisd.openbis.generic.client.web.client.application.model.BaseEntityModel; -import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.columns.framework.AbstractColumnDefinitionKind; import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.columns.framework.IColumnDefinitionKind; import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.columns.specific.EntityTypeColDefKind; import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.field.DescriptionField; @@ -222,43 +221,6 @@ abstract public class AbstractEntityTypeGrid<T extends EntityType> extends } } - protected IColumnDefinitionKind<T> cast(final IColumnDefinitionKind<EntityType> colDefKind) - { - final AbstractColumnDefinitionKind<EntityType> descriptor = colDefKind.getDescriptor(); - return new IColumnDefinitionKind<T>() - { - - public AbstractColumnDefinitionKind<T> getDescriptor() - { - return new AbstractColumnDefinitionKind<T>(descriptor.getHeaderMsgKey(), - descriptor.getWidth(), descriptor.isHidden()) - { - @Override - public String tryGetValue(T entity) - { - return descriptor.tryGetValue(entity); - } - }; - } - - public String id() - { - return colDefKind.id(); - } - }; - } - - protected List<IColumnDefinitionKind<T>> asList(final IColumnDefinitionKind<EntityType>[] table) - { - final List<IColumnDefinitionKind<T>> list = - new ArrayList<IColumnDefinitionKind<T>>(table.length); - for (IColumnDefinitionKind<EntityType> colDefKind : table) - { - list.add(cast(colDefKind)); - } - return list; - } - @Override protected ColumnDefsAndConfigs<T> createColumnsDefinition() { @@ -273,7 +235,7 @@ abstract public class AbstractEntityTypeGrid<T extends EntityType> extends protected List<IColumnDefinition<T>> getInitialFilters() { return asColumnFilters((new IColumnDefinitionKind[] - { cast(EntityTypeColDefKind.CODE) })); + { EntityTypeColDefKind.CODE })); } public DatabaseModificationKind[] getRelevantModifications() diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/grid/AbstractBrowserGrid.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/grid/AbstractBrowserGrid.java index 93b7d1cce3f..d9974c3a992 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/grid/AbstractBrowserGrid.java +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/grid/AbstractBrowserGrid.java @@ -534,11 +534,7 @@ public abstract class AbstractBrowserGrid<T/* Entity */, M extends BaseEntityMod { for (PagingColumnFilter<T> filterWidget : filterWidgets) { - GridFilterInfo<T> filter = filterWidget.tryGetFilter(); - if (filter != null) - { - filters.add(filter); - } + filters.add(filterWidget.getFilter()); } } return filters; diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/grid/PagingColumnFilter.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/grid/PagingColumnFilter.java index 851019c27e4..11e6d8d8637 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/grid/PagingColumnFilter.java +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/grid/PagingColumnFilter.java @@ -50,17 +50,15 @@ public class PagingColumnFilter<T/* entity */> extends StoreFilterField<ModelDat return filteredField.getIdentifier(); } - /** @return filter with the pattern or null if pattern was not specified by the user */ - public GridFilterInfo<T> tryGetFilter() + /** @return filter with the pattern */ + public GridFilterInfo<T> getFilter() { String pattern = getRawValue(); - if (pattern != null && pattern.length() > 0) + if (pattern.length() == 0) { - return new GridFilterInfo<T>(filteredField, pattern); - } else - { - return null; + pattern = null; } + return new GridFilterInfo<T>(filteredField, pattern); } @Override diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/grid/expressions/column/GridCustomColumnGrid.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/grid/expressions/column/GridCustomColumnGrid.java index d6c9efc8640..4892019d197 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/grid/expressions/column/GridCustomColumnGrid.java +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/grid/expressions/column/GridCustomColumnGrid.java @@ -285,8 +285,7 @@ public class GridCustomColumnGrid extends AbstractSimpleBrowserGrid<GridCustomCo { pageResult.add(allModel.get(i)); } - result.setList(new GridRowModels<GridCustomColumn>(pageResult, allModel - .getCustomColumnsMetadata())); + result.setList(allModel.cloneWithData(pageResult)); } public void onFailure(Throwable caught) diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/grid/expressions/filter/FilterToolbar.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/grid/expressions/filter/FilterToolbar.java index c7f1d9832c5..37dc882cbfb 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/grid/expressions/filter/FilterToolbar.java +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/grid/expressions/filter/FilterToolbar.java @@ -214,7 +214,7 @@ public class FilterToolbar<T> extends ToolBar implements IDatabaseModificationOb Field f = (Field) field; // Simple 'f.reset()' causes automatic filter application, // but we want to reload data only once after all filters are cleared. - f.setRawValue(""); + f.setRawValue(f.getEmptyText()); } } } diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/dto/ColumnDistinctValues.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/dto/ColumnDistinctValues.java new file mode 100644 index 00000000000..ac92a793e45 --- /dev/null +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/dto/ColumnDistinctValues.java @@ -0,0 +1,58 @@ +/* + * Copyright 2009 ETH Zuerich, CISD + * + * 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 ch.systemsx.cisd.openbis.generic.client.web.client.dto; + +import java.util.Collections; +import java.util.List; + +import com.google.gwt.user.client.rpc.IsSerializable; + +/** + * Stores distinct values which could be found in one grid column. + * + * @author Tomasz Pylak + */ +public class ColumnDistinctValues implements IsSerializable +{ + private String columnIdentifier; + + // sorted alphabetically + private List<String> distinctValues; + + public ColumnDistinctValues(String columnIdentifier, List<String> distinctValues) + { + this.columnIdentifier = columnIdentifier; + this.distinctValues = distinctValues; + Collections.sort(distinctValues); + } + + public String getColumnIdentifier() + { + return columnIdentifier; + } + + public List<String> getDistinctValues() + { + return distinctValues; + } + + // GWT only + @SuppressWarnings("unused") + private ColumnDistinctValues() + { + } +} diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/dto/GridRowModels.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/dto/GridRowModels.java index fd39c198e11..21b9db16256 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/dto/GridRowModels.java +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/dto/GridRowModels.java @@ -37,15 +37,27 @@ public class GridRowModels<T> extends ArrayList<GridRowModel<T>> implements IsSe */ private List<GridCustomColumnInfo> customColumnsMetadata; - public GridRowModels(List<GridCustomColumnInfo> customColumnsMetadata) + /** + * Information about distinct values in the columns. Only those columns which user wanted to + * filter and which at the same time had values belonging to a small set are included.<br> + * Note that all rows are taken into account to compute distinct values, not only those from + * this grid row model.<br> + */ + private List<ColumnDistinctValues> columnDistinctValues; + + /** Creates a new instance with the specified list of data and previous values for other fields */ + public GridRowModels<T> cloneWithData(List<GridRowModel<T>> list) { - this.customColumnsMetadata = customColumnsMetadata; + return new GridRowModels<T>(list, this.getCustomColumnsMetadata(), this + .getColumnDistinctValues()); } - public GridRowModels(List<GridRowModel<T>> list, List<GridCustomColumnInfo> customColumnsMetadata) + public GridRowModels(List<GridRowModel<T>> list, + List<GridCustomColumnInfo> customColumnsMetadata, List<ColumnDistinctValues> arrayList) { super(list); this.customColumnsMetadata = customColumnsMetadata; + this.columnDistinctValues = arrayList; } /** Used when items are not displayed in a grid (usually we need the values for comboboxes */ @@ -68,17 +80,14 @@ public class GridRowModels<T> extends ArrayList<GridRowModel<T>> implements IsSe return customColumnsMetadata; } - // GWT only - @SuppressWarnings("unused") - private GridRowModels() + public List<ColumnDistinctValues> getColumnDistinctValues() { + return columnDistinctValues; } // GWT only @SuppressWarnings("unused") - private void setCustomColumnsMetadata(List<GridCustomColumnInfo> customColumnsMetadata) + private GridRowModels() { - this.customColumnsMetadata = customColumnsMetadata; } - } diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/dto/IResultSetConfig.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/dto/IResultSetConfig.java index b97b3c22503..fe39d42156a 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/dto/IResultSetConfig.java +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/dto/IResultSetConfig.java @@ -48,7 +48,10 @@ public interface IResultSetConfig<K, T> extends IResultSetKeyHolder<K> */ public int getLimit(); - /** Returns all non-custom columns available. Needed for customized filters and columns. */ + /** + * Returns all columns available. Needed for customized filters and columns. The result will + * include custom columns only if this information was already fetched. + */ public Set<IColumnDefinition<T>> getAvailableColumns(); /** @@ -56,7 +59,11 @@ public interface IResultSetConfig<K, T> extends IResultSetKeyHolder<K> */ public SortInfo<T> getSortInfo(); - /** The filters which should be applied for the result */ + /** + * The column filters which are visible for the user, they should be applied for the result if + * they are not empty. They will be also used to calculate distinct values in columns for more + * convenient filtering. + */ public List<GridFilterInfo<T>> getFilterInfos(); /** diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/server/calculator/GridExpressionUtils.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/server/calculator/GridExpressionUtils.java index 2b77ebc6638..ee1574cd3a6 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/server/calculator/GridExpressionUtils.java +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/server/calculator/GridExpressionUtils.java @@ -30,7 +30,6 @@ import ch.systemsx.cisd.common.logging.LogCategory; import ch.systemsx.cisd.common.logging.LogFactory; import ch.systemsx.cisd.openbis.generic.client.web.client.dto.CustomFilterInfo; import ch.systemsx.cisd.openbis.generic.client.web.client.dto.GridCustomColumnInfo; -import ch.systemsx.cisd.openbis.generic.client.web.client.dto.GridRowModels; import ch.systemsx.cisd.openbis.generic.client.web.client.dto.ParameterWithValue; import ch.systemsx.cisd.openbis.generic.client.web.client.exception.UserFailureException; import ch.systemsx.cisd.openbis.generic.shared.basic.GridRowModel; @@ -68,10 +67,10 @@ public class GridExpressionUtils * Applies the filter described by <code>customFilterInfo</code> to * <code>allRows<code> and returns the result. */ - public static <T> GridRowModels<T> applyCustomFilter(final GridRowModels<T> rows, + public static <T> List<GridRowModel<T>> applyCustomFilter(final List<GridRowModel<T>> rows, Set<IColumnDefinition<T>> availableColumns, CustomFilterInfo<T> customFilterInfo) { - GridRowModels<T> filtered = new GridRowModels<T>(rows.getCustomColumnsMetadata()); + List<GridRowModel<T>> filtered = new ArrayList<GridRowModel<T>>(); String expression = StringEscapeUtils.unescapeHtml(customFilterInfo.getExpression()); Set<ParameterWithValue> parameters = customFilterInfo.getParameters(); try @@ -109,10 +108,10 @@ public class GridExpressionUtils return new UserFailureException(msg, details); } - public static <T> GridRowModels<T> evalCustomColumns(final List<T> allRows, + public static <T> ArrayList<GridRowModel<T>> evalCustomColumns(final List<T> allRows, List<GridCustomColumn> customColumns, Set<IColumnDefinition<T>> availableColumns) { - GridRowModels<T> result = new GridRowModels<T>(extractColumnInfos(customColumns)); + ArrayList<GridRowModel<T>> result = new ArrayList<GridRowModel<T>>(); Map<String, RowCalculator<T>> calculators = new HashMap<String, RowCalculator<T>>(); for (GridCustomColumn customColumn : customColumns) { @@ -144,13 +143,14 @@ public class GridExpressionUtils return new RowCalculator<T>(availableColumns, expression); } catch (Exception ex) { + // if a column definition is faulty than we replace the original expression with the one + // which always evaluates to an error message String msg = createCustomColumnErrorMessage(customColumn, ex).replace("'", "\\'"); return new RowCalculator<T>(availableColumns, "'" + msg + "'"); } } - private static List<GridCustomColumnInfo> extractColumnInfos( - List<GridCustomColumn> customColumns) + public static List<GridCustomColumnInfo> extractColumnInfos(List<GridCustomColumn> customColumns) { List<GridCustomColumnInfo> result = new ArrayList<GridCustomColumnInfo>(); for (GridCustomColumn column : customColumns) @@ -172,7 +172,7 @@ public class GridExpressionUtils // dependencies create a DAG. Then the columns should be evaluated in a topological // order. GridRowModel<T> rowDataWithEmptyCustomColumns = - new GridRowModel<T>(rowData, new HashMap<String, PrimitiveValue>()); + GridRowModel.createWithoutCustomColumns(rowData); try { calculator.setRowData(rowDataWithEmptyCustomColumns); diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/server/resultset/CachedResultSetManager.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/server/resultset/CachedResultSetManager.java index ee718543efe..9ac0cc7b71c 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/server/resultset/CachedResultSetManager.java +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/server/resultset/CachedResultSetManager.java @@ -21,6 +21,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -33,7 +34,9 @@ import org.apache.log4j.Logger; import ch.rinn.restrictions.Private; import ch.systemsx.cisd.common.logging.LogCategory; import ch.systemsx.cisd.common.logging.LogFactory; +import ch.systemsx.cisd.openbis.generic.client.web.client.dto.ColumnDistinctValues; import ch.systemsx.cisd.openbis.generic.client.web.client.dto.CustomFilterInfo; +import ch.systemsx.cisd.openbis.generic.client.web.client.dto.GridCustomColumnInfo; import ch.systemsx.cisd.openbis.generic.client.web.client.dto.GridRowModels; import ch.systemsx.cisd.openbis.generic.client.web.client.dto.IResultSetConfig; import ch.systemsx.cisd.openbis.generic.client.web.server.calculator.GridExpressionUtils; @@ -57,6 +60,10 @@ public final class CachedResultSetManager<K> implements IResultSetManager<K>, Se private static final Logger operationLog = LogFactory.getLogger(LogCategory.OPERATION, CachedResultSetManager.class); + /** how many values can one column have to consider it reasonably distinct */ + @Private + static final int MAX_DISTINCT_COLUMN_VALUES_SIZE = 50; + private final IResultSetKeyGenerator<K> resultSetKeyProvider; private final ICustomColumnsProvider customColumnsProvider; @@ -92,7 +99,7 @@ public final class CachedResultSetManager<K> implements IResultSetManager<K>, Se private final String[] filterExpressionAlternatives; - FilterInfo(GridFilterInfo<T> gridFilterInfo) + private FilterInfo(GridFilterInfo<T> gridFilterInfo) { this.filteredField = gridFilterInfo.getFilteredField(); @@ -100,15 +107,20 @@ public final class CachedResultSetManager<K> implements IResultSetManager<K>, Se // - tokens are separated with whitespace // - quotes (both double and single quote) wrap data into tokens StrTokenizer tokenizer = - new StrTokenizer(gridFilterInfo.getFilterPattern().toLowerCase()); + new StrTokenizer(gridFilterInfo.tryGetFilterPattern().toLowerCase()); tokenizer.setQuoteMatcher(StrMatcher.quoteMatcher()); this.filterExpressionAlternatives = tokenizer.getTokenArray(); } - @SuppressWarnings("unchecked") - static <T> FilterInfo<T> create(GridFilterInfo<T> filterInfo) + static <T> FilterInfo<T> tryCreate(GridFilterInfo<T> filterInfo) { - return new FilterInfo(filterInfo); + if (filterInfo.tryGetFilterPattern() == null) + { + return null; + } else + { + return new FilterInfo<T>(filterInfo); + } } final IColumnDefinition<T> getFilteredField() @@ -126,23 +138,26 @@ public final class CachedResultSetManager<K> implements IResultSetManager<K>, Se Set<IColumnDefinition<T>> availableColumns, final List<GridFilterInfo<T>> filterInfos, CustomFilterInfo<T> customFilterInfo) { + List<GridRowModel<T>> filteredRows; if (customFilterInfo != null) { - return GridExpressionUtils.applyCustomFilter(rows, availableColumns, customFilterInfo); + filteredRows = + GridExpressionUtils.applyCustomFilter(rows, availableColumns, customFilterInfo); } else { - return applyStandardColumnFilter(rows, filterInfos); + filteredRows = applyStandardColumnFilter(rows, filterInfos); } + return rows.cloneWithData(filteredRows); } - private static <T> GridRowModels<T> applyStandardColumnFilter(final GridRowModels<T> rows, + private static <T> List<GridRowModel<T>> applyStandardColumnFilter(final GridRowModels<T> rows, final List<GridFilterInfo<T>> filterInfos) { - GridRowModels<T> filtered = new GridRowModels<T>(rows.getCustomColumnsMetadata()); - List<FilterInfo<T>> serverFilterInfos = processFilters(filterInfos); + List<GridRowModel<T>> filtered = new ArrayList<GridRowModel<T>>(); + List<FilterInfo<T>> appliedFilterInfos = processAppliedFilters(filterInfos); for (GridRowModel<T> row : rows) { - if (isMatching(row, serverFilterInfos)) + if (isMatching(row, appliedFilterInfos)) { filtered.add(row); } @@ -150,12 +165,17 @@ public final class CachedResultSetManager<K> implements IResultSetManager<K>, Se return filtered; } - private static <T> List<FilterInfo<T>> processFilters(final List<GridFilterInfo<T>> filterInfos) + private static <T> List<FilterInfo<T>> processAppliedFilters( + final List<GridFilterInfo<T>> filterInfos) { List<FilterInfo<T>> serverFilterInfos = new ArrayList<FilterInfo<T>>(filterInfos.size()); - for (GridFilterInfo<T> info : filterInfos) + for (GridFilterInfo<T> filterInfo : filterInfos) { - serverFilterInfos.add(FilterInfo.create(info)); + FilterInfo<T> info = FilterInfo.tryCreate(filterInfo); + if (info != null) + { + serverFilterInfos.add(info); + } } return serverFilterInfos; } @@ -271,7 +291,7 @@ public final class CachedResultSetManager<K> implements IResultSetManager<K>, Se final int offset, final int limit) { final int toIndex = offset + limit; - return new GridRowModels<T>(data.subList(offset, toIndex), data.getCustomColumnsMetadata()); + return data.cloneWithData(data.subList(offset, toIndex)); } // @@ -290,7 +310,7 @@ public final class CachedResultSetManager<K> implements IResultSetManager<K>, Se dataKey = resultSetKeyProvider.createKey(); debug("Unknown result set key: retrieving the data with a new key " + dataKey); List<T> rows = dataProvider.getOriginalData(); - data = calculateCustomColumns(sessionToken, rows, resultConfig); + data = calculateRowModels(sessionToken, rows, resultConfig); results.put(dataKey, data); } else { @@ -316,20 +336,70 @@ public final class CachedResultSetManager<K> implements IResultSetManager<K>, Se return new DefaultResultSet<K, T>(dataKey, list, size); } - private <T> GridRowModels<T> calculateCustomColumns(String sessionToken, List<T> rows, + private <T> GridRowModels<T> calculateRowModels(String sessionToken, List<T> rows, IResultSetConfig<?, T> resultConfig) + { + List<GridCustomColumn> customColumns = + fetchCustomColumnsMetadata(sessionToken, resultConfig); + List<GridRowModel<T>> rowModels = + GridExpressionUtils.evalCustomColumns(rows, customColumns, resultConfig + .getAvailableColumns()); + + List<ColumnDistinctValues> columnDistinctValues = + calculateColumnDistinctValues(rowModels, resultConfig.getFilterInfos()); + + List<GridCustomColumnInfo> customColumnInfos = + GridExpressionUtils.extractColumnInfos(customColumns); + + return new GridRowModels<T>(rowModels, customColumnInfos, columnDistinctValues); + } + + @Private + static <T> List<ColumnDistinctValues> calculateColumnDistinctValues( + List<GridRowModel<T>> rowModels, List<GridFilterInfo<T>> columns) + { + List<ColumnDistinctValues> result = new ArrayList<ColumnDistinctValues>(); + for (GridFilterInfo<T> column : columns) + { + ColumnDistinctValues distinctValues = + tryCalculateColumnDistinctValues(rowModels, column.getFilteredField()); + if (distinctValues != null) + { + result.add(distinctValues); + } + } + return result; + } + + /** @return null if values are not from a small set */ + private static <T> ColumnDistinctValues tryCalculateColumnDistinctValues( + List<GridRowModel<T>> rowModels, IColumnDefinition<T> column) + { + Set<String> distinctValues = new HashSet<String>(); + for (GridRowModel<T> rowModel : rowModels) + { + String value = column.getValue(rowModel); + distinctValues.add(value); + if (distinctValues.size() > MAX_DISTINCT_COLUMN_VALUES_SIZE) + { + return null; + } + } + ArrayList<String> distinctValuesList = new ArrayList<String>(distinctValues); + return new ColumnDistinctValues(column.getIdentifier(), distinctValuesList); + } + + private List<GridCustomColumn> fetchCustomColumnsMetadata(String sessionToken, + IResultSetConfig<?, ?> resultConfig) { String gridId = resultConfig.tryGetGridDisplayId(); - List<GridCustomColumn> customColumns; if (gridId == null) { - customColumns = new ArrayList<GridCustomColumn>(); + return new ArrayList<GridCustomColumn>(); } else { - customColumns = customColumnsProvider.getGridCustomColumn(sessionToken, gridId); + return customColumnsProvider.getGridCustomColumn(sessionToken, gridId); } - return GridExpressionUtils.evalCustomColumns(rows, customColumns, resultConfig - .getAvailableColumns()); } public final synchronized void removeResultSet(final K resultSetKey) diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/server/util/TSVRenderer.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/server/util/TSVRenderer.java index a87a87eed18..2ac99891561 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/server/util/TSVRenderer.java +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/server/util/TSVRenderer.java @@ -20,7 +20,6 @@ import java.util.List; import org.apache.commons.lang.StringUtils; -import ch.systemsx.cisd.openbis.generic.client.web.client.dto.GridRowModels; import ch.systemsx.cisd.openbis.generic.shared.basic.GridRowModel; import ch.systemsx.cisd.openbis.generic.shared.basic.IColumnDefinition; @@ -41,13 +40,13 @@ public class TSVRenderer * an appropriate value from the entity. * @param lineSeparator character used as a lineSeparator separator */ - public static <T> String createTable(GridRowModels<T> entities, + public static <T> String createTable(List<GridRowModel<T>> entities, List<IColumnDefinition<T>> list, String lineSeparator) { return new TSVRenderer(lineSeparator).createTable(entities, list); } - private <T> String createTable(GridRowModels<T> entities, List<IColumnDefinition<T>> list) + private <T> String createTable(List<GridRowModel<T>> entities, List<IColumnDefinition<T>> list) { StringBuffer sb = new StringBuffer(); appendHeader(list, sb); diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/GridRowModel.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/GridRowModel.java index 2c8f9637a64..6455a0250d9 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/GridRowModel.java +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/GridRowModel.java @@ -37,8 +37,12 @@ public class GridRowModel<T> implements IsSerializable // displaying serialization warnings in GWT 1.5. It was fixed in GWT 1.6 private List<GridCustomColumnValue> calculatedColumnValues; - public GridRowModel(T originalObject, - HashMap<String, PrimitiveValue> calculatedColumnMap) + public static <T> GridRowModel<T> createWithoutCustomColumns(T originalObject) + { + return new GridRowModel<T>(originalObject, new HashMap<String, PrimitiveValue>()); + } + + public GridRowModel(T originalObject, HashMap<String, PrimitiveValue> calculatedColumnMap) { this.originalObject = originalObject; this.calculatedColumnValues = asList(calculatedColumnMap); @@ -91,11 +95,4 @@ public class GridRowModel<T> implements IsSerializable private GridRowModel() { } - - // GWT only - @SuppressWarnings("unused") - private void setOriginalObject(T originalObject) - { - this.originalObject = originalObject; - } } diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/GridFilterInfo.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/GridFilterInfo.java index 83fdd54509e..e9200a2619c 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/GridFilterInfo.java +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/GridFilterInfo.java @@ -31,8 +31,8 @@ public class GridFilterInfo<T> implements IsSerializable // allows to fetch the value from the row model private IColumnDefinition<T> filteredField; - // the value has to match to this pattern - private String filterText; + // the value has to match to this pattern, If null filter should not be applied. + private String filterTextOrNull; // GWT only @SuppressWarnings("unused") @@ -40,10 +40,10 @@ public class GridFilterInfo<T> implements IsSerializable { } - public GridFilterInfo(IColumnDefinition<T> filteredField, String filterText) + public GridFilterInfo(IColumnDefinition<T> filteredField, String filterTextOrNull) { this.filteredField = filteredField; - this.filterText = filterText; + this.filterTextOrNull = filterTextOrNull; } public IColumnDefinition<T> getFilteredField() @@ -51,9 +51,9 @@ public class GridFilterInfo<T> implements IsSerializable return filteredField; } - public String getFilterPattern() + public String tryGetFilterPattern() { - return filterText; + return filterTextOrNull; } } diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/client/web/server/CommonClientServiceTest.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/client/web/server/CommonClientServiceTest.java index 6e8d275ea07..d81deeb4425 100644 --- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/client/web/server/CommonClientServiceTest.java +++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/client/web/server/CommonClientServiceTest.java @@ -37,13 +37,13 @@ import ch.systemsx.cisd.openbis.generic.client.web.client.dto.ResultSetWithEntit import ch.systemsx.cisd.openbis.generic.client.web.client.dto.TableExportCriteria; import ch.systemsx.cisd.openbis.generic.client.web.server.resultset.CacheManager; import ch.systemsx.cisd.openbis.generic.client.web.server.resultset.CachedResultSetManager; +import ch.systemsx.cisd.openbis.generic.client.web.server.resultset.CachedResultSetManagerTest; import ch.systemsx.cisd.openbis.generic.client.web.server.resultset.DefaultResultSet; import ch.systemsx.cisd.openbis.generic.client.web.server.resultset.IOriginalDataProvider; import ch.systemsx.cisd.openbis.generic.client.web.server.resultset.IResultSet; import ch.systemsx.cisd.openbis.generic.client.web.server.resultset.IResultSetKeyGenerator; import ch.systemsx.cisd.openbis.generic.client.web.server.resultset.CacheManager.TokenBasedResultSetKeyGenerator; import ch.systemsx.cisd.openbis.generic.client.web.server.resultset.CachedResultSetManager.ICustomColumnsProvider; -import ch.systemsx.cisd.openbis.generic.client.web.server.util.TSVRendererTest; import ch.systemsx.cisd.openbis.generic.server.SessionConstants; import ch.systemsx.cisd.openbis.generic.server.business.ManagerTestTool; import ch.systemsx.cisd.openbis.generic.shared.CommonTestUtils; @@ -164,8 +164,7 @@ public final class CommonClientServiceTest extends AbstractClientServiceTest IResultSet<String, String> entityTypeResultMock = context.mock(IResultSet.class); one(entityTypeResultMock).getList(); - will(returnValue(new GridRowModels<String>( - new ArrayList<GridCustomColumnInfo>()))); + will(returnValue(createGridRowModels(new ArrayList<GridCustomColumnInfo>()))); one(resultSetManager).getResultSet(with(SESSION_TOKEN), with(Expectations.any(IResultSetConfig.class)), @@ -212,9 +211,9 @@ public final class CommonClientServiceTest extends AbstractClientServiceTest final DefaultResultSetConfig<String, T> criteria) { final String resultSetKey = "131"; + GridRowModels<T> rowModels = createGridRowModels(entities); final DefaultResultSet<String, T> defaultResultSet = - new DefaultResultSet<String, T>(resultSetKey, TSVRendererTest.asRowModel(entities), - entities.size()); + new DefaultResultSet<String, T>(resultSetKey, rowModels, entities.size()); context.checking(new Expectations() { { @@ -228,6 +227,11 @@ public final class CommonClientServiceTest extends AbstractClientServiceTest }); } + private <T> GridRowModels<T> createGridRowModels(List<T> entities) + { + return CachedResultSetManagerTest.createGridRowModels(entities); + } + @SuppressWarnings("unchecked") private <T> void prepareGetResultSet(Expectations exp, IResultSetConfig<String, T> criteria) { diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/client/web/server/calculator/RowCalculatorTest.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/client/web/server/calculator/RowCalculatorTest.java index 62c737c7805..8d17595dcf8 100644 --- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/client/web/server/calculator/RowCalculatorTest.java +++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/client/web/server/calculator/RowCalculatorTest.java @@ -18,7 +18,6 @@ package ch.systemsx.cisd.openbis.generic.client.web.server.calculator; import java.math.BigInteger; import java.util.Arrays; -import java.util.HashMap; import java.util.LinkedHashSet; import java.util.Set; @@ -30,7 +29,6 @@ import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.columns import ch.systemsx.cisd.openbis.generic.client.web.client.dto.ParameterWithValue; import ch.systemsx.cisd.openbis.generic.shared.basic.GridRowModel; import ch.systemsx.cisd.openbis.generic.shared.basic.IColumnDefinition; -import ch.systemsx.cisd.openbis.generic.shared.basic.PrimitiveValue; /** * @author Franz-Josef Elmer @@ -236,6 +234,6 @@ public class RowCalculatorTest extends AssertJUnit { Data data = new Data(); data.setValue(value); - return new GridRowModel<Data>(data, new HashMap<String, PrimitiveValue>()); + return GridRowModel.createWithoutCustomColumns(data); } } diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/client/web/server/calculator/RowTest.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/client/web/server/calculator/RowTest.java index d73eb60951f..c707db2665a 100644 --- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/client/web/server/calculator/RowTest.java +++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/client/web/server/calculator/RowTest.java @@ -17,7 +17,6 @@ package ch.systemsx.cisd.openbis.generic.client.web.server.calculator; import java.util.Arrays; -import java.util.HashMap; import java.util.LinkedHashSet; import java.util.List; @@ -30,7 +29,6 @@ import org.testng.annotations.Test; import ch.systemsx.cisd.openbis.generic.shared.basic.GridRowModel; import ch.systemsx.cisd.openbis.generic.shared.basic.IColumnDefinition; -import ch.systemsx.cisd.openbis.generic.shared.basic.PrimitiveValue; /** * @author Franz-Josef Elmer @@ -231,8 +229,7 @@ public class RowTest extends AssertJUnit private GridRowModel<Data> createData(double value) { Data originalObject = new Data(value); - return new GridRowModel<Data>(originalObject, - new HashMap<String, PrimitiveValue>()); + return GridRowModel.createWithoutCustomColumns(originalObject); } @Test diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/client/web/server/resultset/CachedResultSetManagerTest.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/client/web/server/resultset/CachedResultSetManagerTest.java index fd512ad94ff..5e34424f950 100644 --- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/client/web/server/resultset/CachedResultSetManagerTest.java +++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/client/web/server/resultset/CachedResultSetManagerTest.java @@ -33,11 +33,15 @@ import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import ch.rinn.restrictions.Friend; +import ch.systemsx.cisd.openbis.generic.client.web.client.dto.ColumnDistinctValues; +import ch.systemsx.cisd.openbis.generic.client.web.client.dto.GridCustomColumnInfo; import ch.systemsx.cisd.openbis.generic.client.web.client.dto.GridRowModels; import ch.systemsx.cisd.openbis.generic.client.web.client.dto.IResultSetConfig; import ch.systemsx.cisd.openbis.generic.client.web.server.resultset.CacheManager.TokenBasedResultSetKeyGenerator; import ch.systemsx.cisd.openbis.generic.client.web.server.resultset.CachedResultSetManager.ICustomColumnsProvider; import ch.systemsx.cisd.openbis.generic.client.web.server.util.TSVRendererTest; +import ch.systemsx.cisd.openbis.generic.shared.basic.GridRowModel; +import ch.systemsx.cisd.openbis.generic.shared.basic.IColumnDefinition; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.GridCustomColumn; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.GridFilterInfo; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Sample; @@ -77,7 +81,7 @@ public final class CachedResultSetManagerTest expectations.one(resultSetConfig).getSortInfo(); expectations.will(Expectations.returnValue(sortInfo)); - expectations.one(resultSetConfig).getFilterInfos(); + expectations.allowing(resultSetConfig).getFilterInfos(); expectations.will(Expectations.returnValue(new ArrayList<GridFilterInfo<String>>())); expectations.one(resultSetConfig).tryGetCustomFilterInfo(); @@ -94,7 +98,19 @@ public final class CachedResultSetManagerTest sample.setCode("code" + i); list.add(sample); } - return TSVRendererTest.asRowModel(list); + return createGridRowModels(list); + } + + public static <T> GridRowModels<T> createGridRowModels(List<T> entities) + { + ArrayList<GridCustomColumnInfo> customColumnsMetadata = + new ArrayList<GridCustomColumnInfo>(); + ArrayList<ColumnDistinctValues> columnDistinctValues = + new ArrayList<ColumnDistinctValues>(); + GridRowModels<T> rowModels = + new GridRowModels<T>(TSVRendererTest.asRowModel(entities), customColumnsMetadata, + columnDistinctValues); + return rowModels; } @SuppressWarnings("unchecked") @@ -268,4 +284,70 @@ public final class CachedResultSetManagerTest assertFalse(fail); context.assertIsSatisfied(); } + + @Test + public void testCalculateColumnDistinctValues() + { + String separator = " "; + List<GridFilterInfo<String>> filterList = + createFilterList(createColDef("c1", separator, 0), createColDef("c2", separator, 1)); + List<GridRowModel<String>> rows = new ArrayList<GridRowModel<String>>(); + for (int i = 0; i < CachedResultSetManager.MAX_DISTINCT_COLUMN_VALUES_SIZE * 2; i++) + { + rows.add(GridRowModel.createWithoutCustomColumns(i + separator + (i % 2))); + } + List<ColumnDistinctValues> result = + CachedResultSetManager.calculateColumnDistinctValues(rows, filterList); + + assertEquals(1, result.size()); + ColumnDistinctValues distinctValues = result.get(0); + assertEquals("c2", distinctValues.getColumnIdentifier()); + List<String> values = distinctValues.getDistinctValues(); + assertEquals(2, values.size()); + assertEquals("0", values.get(0)); + assertEquals("1", values.get(1)); + } + + private static List<GridFilterInfo<String>> createFilterList(IColumnDefinition<String> c1, + IColumnDefinition<String> c2) + { + List<GridFilterInfo<String>> result = new ArrayList<GridFilterInfo<String>>(); + result.add(new GridFilterInfo<String>(c1, null)); + result.add(new GridFilterInfo<String>(c2, null)); + return result; + } + + private static IColumnDefinition<String> createColDef(final String identifier, + final String separator, final int tokenIndex) + { + return new IColumnDefinition<String>() + { + public String getValue(GridRowModel<String> rowModel) + { + return rowModel.getOriginalObject().split(separator)[tokenIndex]; + } + + public String getIdentifier() + { + return identifier; + } + + public Comparable<?> getComparableValue(GridRowModel<String> rowModel) + { + return getValue(rowModel); + } + + public String getHeader() + { + return null; // unused + } + + public String tryToGetProperty(String key) + { + return null; // unused + } + + }; + } + } diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/client/web/server/util/FilterUtilsTest.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/client/web/server/util/FilterUtilsTest.java index 2084d37bb6f..3f54c67daa1 100644 --- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/client/web/server/util/FilterUtilsTest.java +++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/client/web/server/util/FilterUtilsTest.java @@ -27,7 +27,6 @@ import org.testng.annotations.Test; import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.columns.framework.AbstractColumnDefinition; import ch.systemsx.cisd.openbis.generic.client.web.client.dto.CustomFilterInfo; -import ch.systemsx.cisd.openbis.generic.client.web.client.dto.GridRowModels; import ch.systemsx.cisd.openbis.generic.client.web.client.dto.ParameterWithValue; import ch.systemsx.cisd.openbis.generic.client.web.server.calculator.GridExpressionUtils; import ch.systemsx.cisd.openbis.generic.shared.basic.GridRowModel; @@ -85,7 +84,7 @@ public class FilterUtilsTest extends AssertJUnit } }); - GridRowModels<Data> filterdList = + List<GridRowModel<Data>> filterdList = GridExpressionUtils.applyCustomFilter(createData(57, 34), availableColumns, filterInfo); @@ -93,7 +92,7 @@ public class FilterUtilsTest extends AssertJUnit assertEquals(34.0, filterdList.get(0).getOriginalObject().getValue()); } - private GridRowModels<Data> createData(double... values) + private List<GridRowModel<Data>> createData(double... values) { List<Data> list = new ArrayList<Data>(); for (double value : values) diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/client/web/server/util/TSVRendererTest.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/client/web/server/util/TSVRendererTest.java index d819c809053..aff69868ed1 100644 --- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/client/web/server/util/TSVRendererTest.java +++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/client/web/server/util/TSVRendererTest.java @@ -17,18 +17,14 @@ package ch.systemsx.cisd.openbis.generic.client.web.server.util; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; import junit.framework.Assert; import org.testng.annotations.Test; -import ch.systemsx.cisd.openbis.generic.client.web.client.dto.GridCustomColumnInfo; -import ch.systemsx.cisd.openbis.generic.client.web.client.dto.GridRowModels; import ch.systemsx.cisd.openbis.generic.shared.basic.GridRowModel; import ch.systemsx.cisd.openbis.generic.shared.basic.IColumnDefinition; -import ch.systemsx.cisd.openbis.generic.shared.basic.PrimitiveValue; /** * Tests of {@link TSVRenderer} @@ -50,14 +46,14 @@ public class TSVRendererTest Assert.assertEquals("h0\th1#x\ty#a\tb#", content); } - public static <T> GridRowModels<T> asRowModel(List<T> entities) + public static <T> List<GridRowModel<T>> asRowModel(List<T> entities) { List<GridRowModel<T>> list = new ArrayList<GridRowModel<T>>(); for (T entity : entities) { - list.add(new GridRowModel<T>(entity, new HashMap<String, PrimitiveValue>())); + list.add(GridRowModel.createWithoutCustomColumns(entity)); } - return new GridRowModels<T>(list, new ArrayList<GridCustomColumnInfo>()); + return list; } private static List<IColumnDefinition<String[]>> createColumnDefs(int colNum) -- GitLab