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