diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/CommonServer.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/CommonServer.java
index bcc608330bbebe39e73caaa34b581a20bcf5662d..5f750f4ca1fcd476b279bf813027aa3555439f7d 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/CommonServer.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/CommonServer.java
@@ -118,6 +118,7 @@ import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Person;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Project;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.PropertyType;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.RoleAssignment;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.RoleWithHierarchy.RoleCode;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Sample;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.SampleType;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Space;
@@ -126,7 +127,6 @@ import ch.systemsx.cisd.openbis.generic.shared.basic.dto.UpdatedSample;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Vocabulary;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.VocabularyTerm;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.VocabularyTermReplacement;
-import ch.systemsx.cisd.openbis.generic.shared.basic.dto.RoleWithHierarchy.RoleCode;
 import ch.systemsx.cisd.openbis.generic.shared.dto.AttachmentHolderPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.AttachmentPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.AuthorizationGroupPE;
@@ -169,6 +169,7 @@ import ch.systemsx.cisd.openbis.generic.shared.translator.DataTypeTranslator;
 import ch.systemsx.cisd.openbis.generic.shared.translator.DtoConverters;
 import ch.systemsx.cisd.openbis.generic.shared.translator.ExperimentTranslator;
 import ch.systemsx.cisd.openbis.generic.shared.translator.ExternalDataTranslator;
+import ch.systemsx.cisd.openbis.generic.shared.translator.GridCustomExpressionTranslator.GridCustomFilterTranslator;
 import ch.systemsx.cisd.openbis.generic.shared.translator.GroupTranslator;
 import ch.systemsx.cisd.openbis.generic.shared.translator.MaterialTypeTranslator;
 import ch.systemsx.cisd.openbis.generic.shared.translator.PersonTranslator;
@@ -179,7 +180,6 @@ import ch.systemsx.cisd.openbis.generic.shared.translator.SampleTypeTranslator;
 import ch.systemsx.cisd.openbis.generic.shared.translator.TypeTranslator;
 import ch.systemsx.cisd.openbis.generic.shared.translator.VocabularyTermTranslator;
 import ch.systemsx.cisd.openbis.generic.shared.translator.VocabularyTranslator;
-import ch.systemsx.cisd.openbis.generic.shared.translator.GridCustomExpressionTranslator.GridCustomFilterTranslator;
 import ch.systemsx.cisd.openbis.generic.shared.util.EntityHelper;
 import ch.systemsx.cisd.openbis.generic.shared.util.HibernateUtils;
 
@@ -402,8 +402,8 @@ public final class CommonServer extends AbstractCommonServer<ICommonServer> impl
         {
             IHibernateSearchDAO searchDAO = getDAOFactory().getHibernateSearchDAO();
             final Collection<Long> sampleIds =
-                    searchDAO.searchForEntityIds(criteria, DtoConverters
-                            .convertEntityKind(EntityKind.SAMPLE));
+                    searchDAO.searchForEntityIds(criteria,
+                            DtoConverters.convertEntityKind(EntityKind.SAMPLE));
             final ISampleLister sampleLister = businessObjectFactory.createSampleLister(session);
             return sampleLister.list(new ListOrSearchSampleCriteria(sampleIds));
         } catch (final DataAccessException ex)
@@ -447,7 +447,7 @@ public final class CommonServer extends AbstractCommonServer<ICommonServer> impl
                 datasets = datasetLister.listByChildTechId(datasetId);
                 break;
             case PARENT:
-                datasets = datasetLister.listByParentTechId(datasetId);
+                datasets = datasetLister.listByParentTechIds(Arrays.asList(datasetId.getId()));
                 break;
         }
         Collections.sort(datasets);
@@ -617,8 +617,8 @@ public final class CommonServer extends AbstractCommonServer<ICommonServer> impl
         Session session = getSession(sessionToken);
 
         IEntityTypePropertyTypeBO etptBO =
-                businessObjectFactory.createEntityTypePropertyTypeBO(session, DtoConverters
-                        .convertEntityKind(entityKind));
+                businessObjectFactory.createEntityTypePropertyTypeBO(session,
+                        DtoConverters.convertEntityKind(entityKind));
         etptBO.loadAssignment(propertyTypeCode, entityTypeCode);
         etptBO.updateLoadedAssignment(isMandatory, defaultValue, section, previousETPTOrdinal);
     }
@@ -630,8 +630,8 @@ public final class CommonServer extends AbstractCommonServer<ICommonServer> impl
         Session session = getSession(sessionToken);
 
         IEntityTypePropertyTypeBO etptBO =
-                businessObjectFactory.createEntityTypePropertyTypeBO(session, DtoConverters
-                        .convertEntityKind(entityKind));
+                businessObjectFactory.createEntityTypePropertyTypeBO(session,
+                        DtoConverters.convertEntityKind(entityKind));
         etptBO.loadAssignment(propertyTypeCode, entityTypeCode);
         etptBO.deleteLoadedAssignment();
     }
@@ -643,8 +643,8 @@ public final class CommonServer extends AbstractCommonServer<ICommonServer> impl
         Session session = getSession(sessionToken);
 
         IEntityTypePropertyTypeBO etptBO =
-                businessObjectFactory.createEntityTypePropertyTypeBO(session, DtoConverters
-                        .convertEntityKind(entityKind));
+                businessObjectFactory.createEntityTypePropertyTypeBO(session,
+                        DtoConverters.convertEntityKind(entityKind));
         return etptBO.countAssignmentValues(propertyTypeCode, entityTypeCode);
     }
 
@@ -756,8 +756,8 @@ public final class CommonServer extends AbstractCommonServer<ICommonServer> impl
             IHibernateSearchDAO searchDAO = getDAOFactory().getHibernateSearchDAO();
 
             final Collection<Long> datasetIds =
-                    searchDAO.searchForEntityIds(criteria, DtoConverters
-                            .convertEntityKind(EntityKind.DATA_SET));
+                    searchDAO.searchForEntityIds(criteria,
+                            DtoConverters.convertEntityKind(EntityKind.DATA_SET));
             final IDatasetLister datasetLister = createDatasetLister(session);
             return datasetLister.listByDatasetIds(datasetIds);
         } catch (final DataAccessException ex)
@@ -903,8 +903,8 @@ public final class CommonServer extends AbstractCommonServer<ICommonServer> impl
             getDAOFactory().getFileFormatTypeDAO().createOrUpdate(fileFormatType);
         } catch (final DataAccessException ex)
         {
-            DataAccessExceptionTranslator.throwException(ex, String.format(
-                    "File format type '%s' ", fileFormatType.getCode()), null);
+            DataAccessExceptionTranslator.throwException(ex,
+                    String.format("File format type '%s' ", fileFormatType.getCode()), null);
         }
     }
 
@@ -1172,8 +1172,9 @@ public final class CommonServer extends AbstractCommonServer<ICommonServer> impl
         {
             IExperimentBO experimentBO = businessObjectFactory.createExperimentBO(session);
             experimentBO.loadDataByTechId(experimentId);
-            return AttachmentTranslator.translate(listHolderAttachments(session, experimentBO
-                    .getExperiment()), session.getBaseIndexURL());
+            return AttachmentTranslator.translate(
+                    listHolderAttachments(session, experimentBO.getExperiment()),
+                    session.getBaseIndexURL());
         } catch (final DataAccessException ex)
         {
             throw createUserFailureException(ex);
@@ -1187,8 +1188,9 @@ public final class CommonServer extends AbstractCommonServer<ICommonServer> impl
         {
             ISampleBO sampleBO = businessObjectFactory.createSampleBO(session);
             sampleBO.loadDataByTechId(sampleId);
-            return AttachmentTranslator.translate(listHolderAttachments(session, sampleBO
-                    .getSample()), session.getBaseIndexURL());
+            return AttachmentTranslator
+                    .translate(listHolderAttachments(session, sampleBO.getSample()),
+                            session.getBaseIndexURL());
         } catch (final DataAccessException ex)
         {
             throw createUserFailureException(ex);
@@ -1202,8 +1204,9 @@ public final class CommonServer extends AbstractCommonServer<ICommonServer> impl
         {
             IProjectBO projectBO = businessObjectFactory.createProjectBO(session);
             projectBO.loadDataByTechId(projectId);
-            return AttachmentTranslator.translate(listHolderAttachments(session, projectBO
-                    .getProject()), session.getBaseIndexURL());
+            return AttachmentTranslator.translate(
+                    listHolderAttachments(session, projectBO.getProject()),
+                    session.getBaseIndexURL());
         } catch (final DataAccessException ex)
         {
             throw createUserFailureException(ex);
@@ -1346,8 +1349,8 @@ public final class CommonServer extends AbstractCommonServer<ICommonServer> impl
     {
         if (entityOrNull == null)
         {
-            throw UserFailureException.fromTemplate("There is no %s with permId '%s'.", kind
-                    .getDescription(), permId);
+            throw UserFailureException.fromTemplate("There is no %s with permId '%s'.",
+                    kind.getDescription(), permId);
         }
         return createInformationHolder(kind, entityOrNull);
     }
@@ -1450,10 +1453,9 @@ public final class CommonServer extends AbstractCommonServer<ICommonServer> impl
                 } catch (DataIntegrityViolationException ex)
                 {
                     throw new UserFailureException(
-                            String
-                                    .format(
-                                            "File format type '%s' is being used. Use 'Data Set Search' to find all connected data sets.",
-                                            code));
+                            String.format(
+                                    "File format type '%s' is being used. Use 'Data Set Search' to find all connected data sets.",
+                                    code));
                 }
             }
         }
@@ -1482,12 +1484,11 @@ public final class CommonServer extends AbstractCommonServer<ICommonServer> impl
             if (types.size() != 1)
             {
                 section =
-                        String
-                                .format(
-                                        "[%s]\n%s%s\n",
-                                        entityType.getCode(),
-                                        firstSection ? "# Comments must be located after the type declaration ('[TYPE]').\n"
-                                                : "", section);
+                        String.format(
+                                "[%s]\n%s%s\n",
+                                entityType.getCode(),
+                                firstSection ? "# Comments must be located after the type declaration ('[TYPE]').\n"
+                                        : "", section);
             }
             sb.append(section);
             firstSection = false;
@@ -1674,8 +1675,8 @@ public final class CommonServer extends AbstractCommonServer<ICommonServer> impl
         IExternalDataTable externalDataTable =
                 businessObjectFactory.createExternalDataTable(session);
         Map<String, String> parameterBindings = new HashMap<String, String>();
-        externalDataTable.processDatasets(serviceDescription.getKey(), serviceDescription
-                .getDatastoreCode(), datasetCodes, parameterBindings);
+        externalDataTable.processDatasets(serviceDescription.getKey(),
+                serviceDescription.getDatastoreCode(), datasetCodes, parameterBindings);
     }
 
     public void registerAuthorizationGroup(String sessionToken,
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/datasetlister/DatasetLister.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/datasetlister/DatasetLister.java
index fef98587ae91649d944fff3cdd7fe0e941224a9f..437a7d4e4001009b543d3b81fa7fc24293df2797 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/datasetlister/DatasetLister.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/datasetlister/DatasetLister.java
@@ -30,8 +30,8 @@ import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
 import java.util.Map.Entry;
+import java.util.Set;
 
 import net.lemnik.eodsql.DataIterator;
 
@@ -210,9 +210,10 @@ public class DatasetLister implements IDatasetLister
         return enrichDatasets(query.getParentDatasetsForChild(childDatasetId.getId()));
     }
 
-    public List<ExternalData> listByParentTechId(TechId parentDatasetId)
+    public List<ExternalData> listByParentTechIds(Collection<Long> parentDatasetIds)
     {
-        return enrichDatasets(query.getChildDatasetsForParent(parentDatasetId.getId()));
+        return enrichDatasets(query
+                .getChildDatasetsForParents(new LongOpenHashSet(parentDatasetIds)));
     }
 
     public List<ExternalData> listByDatasetIds(Collection<Long> datasetIds)
@@ -225,8 +226,8 @@ public class DatasetLister implements IDatasetLister
         Long sampleTypeId =
                 referencedEntityDAO.getSampleTypeIdForSampleTypeCode(criteria
                         .getConnectedSampleTypeCode());
-        return enrichDatasets(query.getNewDataSetsForSampleType(sampleTypeId, criteria
-                .getLastSeenDataSetId()));
+        return enrichDatasets(query.getNewDataSetsForSampleType(sampleTypeId,
+                criteria.getLastSeenDataSetId()));
     }
 
     public List<ExternalData> listByArchiverCriteria(String dataStoreCode,
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/datasetlister/IDatasetLister.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/datasetlister/IDatasetLister.java
index 14a4b92c080db269f146d9c670019bcc12f6e01e..02de40f08c6ca7dc66ccb53e43a46047fd22d47f 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/datasetlister/IDatasetLister.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/datasetlister/IDatasetLister.java
@@ -46,8 +46,8 @@ public interface IDatasetLister
     /** @return datasets that are parents of a dataset with the specified id */
     List<ExternalData> listByChildTechId(TechId childDatasetId);
 
-    /** @return datasets that are children of a dataset with the specified id */
-    List<ExternalData> listByParentTechId(TechId parentDatasetId);
+    /** @return all datasets that are children of any specified dataset id */
+    List<ExternalData> listByParentTechIds(Collection<Long> parentDatasetIds);
 
     /**
      * Returns a map with all parent data set IDs of specified data set IDs. The keys of the map are
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/datasetlister/IDatasetListingQuery.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/datasetlister/IDatasetListingQuery.java
index 8b1baed17367118c994bc441ba358a16d69f6118..e2263f2d02321ce6d519855891a1f2e496ff0325 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/datasetlister/IDatasetListingQuery.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/datasetlister/IDatasetListingQuery.java
@@ -110,11 +110,12 @@ public interface IDatasetListingQuery extends TransactionQuery, IPropertyListing
     public DataIterator<DatasetRelationRecord> listParentDataSetIds(LongSet ids);
 
     /**
-     * Returns the datasets that are children of a dataset with given id.
+     * Returns all datasets that are children of any specified dataset id.
      */
     @Select(sql = "SELECT * FROM data JOIN external_data ON data.id = external_data.data_id"
-            + "    WHERE data.id IN (SELECT data_id_child FROM data_set_relationships r WHERE r.data_id_parent=?{1})", fetchSize = FETCH_SIZE)
-    public DataIterator<DatasetRecord> getChildDatasetsForParent(long parentDatasetId);
+            + "    WHERE data.id IN (SELECT data_id_child FROM data_set_relationships r WHERE r.data_id_parent = any(?{1}))", parameterBindings =
+        { LongSetMapper.class }, fetchSize = FETCH_SIZE)
+    public DataIterator<DatasetRecord> getChildDatasetsForParents(LongSet parentDatasetIds);
 
     /**
      * Returns the datasets that are parents of a dataset with given id.
diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/business/bo/datasetlister/DatasetListingQueryTest.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/business/bo/datasetlister/DatasetListingQueryTest.java
index 4d18229b6dd1b8458ca4e4c2e2a47c89604ba185..d296588591a828c802b29adecf5d7d568d2f9d1c 100644
--- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/business/bo/datasetlister/DatasetListingQueryTest.java
+++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/business/bo/datasetlister/DatasetListingQueryTest.java
@@ -22,6 +22,8 @@ import static org.testng.AssertJUnit.assertEquals;
 import static org.testng.AssertJUnit.assertNotNull;
 import static org.testng.AssertJUnit.assertTrue;
 import it.unimi.dsi.fastutil.longs.LongArraySet;
+import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
+import it.unimi.dsi.fastutil.longs.LongSet;
 
 import java.sql.SQLException;
 import java.util.Arrays;
@@ -132,7 +134,7 @@ public class DatasetListingQueryTest extends AbstractDAOTest
         ExperimentPE experiment1 =
                 getExperiment(dbInstance.getCode(), "CISD", "NEMO", "EXP-TEST-1", daoFactory);
         ExperimentPE experiment2 =
-            getExperiment(dbInstance.getCode(), "CISD", "NEMO", "EXP-TEST-2", daoFactory);
+                getExperiment(dbInstance.getCode(), "CISD", "NEMO", "EXP-TEST-2", daoFactory);
         experimentIds.add((long) experiment1.getId());
         experimentIds.add((long) experiment2.getId());
         List<DatasetRecord> datasets = asList(query.getDatasetsForExperiment(experimentIds));
@@ -144,7 +146,7 @@ public class DatasetListingQueryTest extends AbstractDAOTest
             assertEqualWithFetchedById(record);
             counters.count(record.expe_id);
         }
-        
+
         assertEquals(1, counters.getCountOf(experiment1.getId()));
         assertEquals(1, counters.getCountOf(experiment2.getId()));
         assertEquals(2, counters.getNumberOfDifferentObjectsCounted());
@@ -210,8 +212,9 @@ public class DatasetListingQueryTest extends AbstractDAOTest
     @Test
     public void testChildDatasetsForParent()
     {
-        long datasetId = 2;
-        List<DatasetRecord> children = asList(query.getChildDatasetsForParent(datasetId));
+        LongSet datasetIds = new LongOpenHashSet(new long[]
+            { 2 });
+        List<DatasetRecord> children = asList(query.getChildDatasetsForParents(datasetIds));
         assertEquals(2, children.size());
     }
 
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/FeatureTableBuilder.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/FeatureTableBuilder.java
index 469d37836cc7b16649188c9d82bc5242ea8fbf85..6b438b328347c02f149bf0dc24af817ea779664c 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/FeatureTableBuilder.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/FeatureTableBuilder.java
@@ -18,6 +18,7 @@ package ch.systemsx.cisd.openbis.dss.generic.server;
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedHashMap;
@@ -28,6 +29,9 @@ import java.util.Map.Entry;
 import java.util.Set;
 
 import ch.systemsx.cisd.common.exceptions.UserFailureException;
+import ch.systemsx.cisd.openbis.dss.generic.server.featurevectors.FeatureTableRow;
+import ch.systemsx.cisd.openbis.dss.generic.server.featurevectors.FeatureVectorValues;
+import ch.systemsx.cisd.openbis.dss.generic.server.featurevectors.WellFeatureVectorReference;
 import ch.systemsx.cisd.openbis.dss.generic.shared.IEncapsulatedOpenBISService;
 import ch.systemsx.cisd.openbis.dss.generic.shared.utils.CodeAndLabel;
 import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.SampleIdentifier;
@@ -70,7 +74,7 @@ public class FeatureTableBuilder
     private final boolean useAllFeatures;
 
     /** fetches all features of specified wells */
-    public static WellFeatureCollection fetchWellFeatures(
+    public static WellFeatureCollection<FeatureTableRow> fetchWellFeatures(
             List<FeatureVectorDatasetWellReference> references, IImagingReadonlyQueryDAO dao,
             IEncapsulatedOpenBISService service)
     {
@@ -82,18 +86,30 @@ public class FeatureTableBuilder
      * 
      * @param featureCodes empty list means no filtering.
      */
-    public static WellFeatureCollection fetchWellFeatures(
+    public static WellFeatureCollection<FeatureTableRow> fetchWellFeatures(
             List<FeatureVectorDatasetWellReference> references, List<String> featureCodes,
             IImagingReadonlyQueryDAO dao, IEncapsulatedOpenBISService service)
     {
         FeatureTableBuilder builder = new FeatureTableBuilder(featureCodes, dao, service);
         Set<String> datasetCodes = extractDatasetCodes(references);
-        for (String datasetCode : datasetCodes)
-        {
-            builder.addFeatureVectorsOfDataSet(datasetCode);
-        }
+        addFeatureVectorsOfDataSets(builder, datasetCodes);
         List<FeatureTableRow> features = builder.createFeatureTableRows(references);
-        return new WellFeatureCollection(features, builder.getCodesAndLabels());
+        return new WellFeatureCollection<FeatureTableRow>(features, builder.getCodesAndLabels());
+    }
+
+    /**
+     * fetches all features of specified wells, used basic data types
+     */
+    public static WellFeatureCollection<FeatureVectorValues> fetchWellFeatureValues(
+            List<WellFeatureVectorReference> references, IImagingReadonlyQueryDAO dao,
+            IEncapsulatedOpenBISService service)
+    {
+        FeatureTableBuilder builder =
+                new FeatureTableBuilder(new ArrayList<String>(), dao, service);
+        Set<String> datasetCodes = extractDatasetCodesFromSimpleReferences(references);
+        addFeatureVectorsOfDataSets(builder, datasetCodes);
+        List<FeatureVectorValues> features = builder.createFeatureVectorValues(references);
+        return new WellFeatureCollection<FeatureVectorValues>(features, builder.getCodesAndLabels());
     }
 
     /**
@@ -101,33 +117,30 @@ public class FeatureTableBuilder
      * 
      * @param featureCodes empty list means no filtering.
      */
-    public static WellFeatureCollection fetchDatasetFeatures(List<String> datasetCodes,
-            List<String> featureCodes, IImagingReadonlyQueryDAO dao,
+    public static WellFeatureCollection<FeatureTableRow> fetchDatasetFeatures(
+            List<String> datasetCodes, List<String> featureCodes, IImagingReadonlyQueryDAO dao,
             IEncapsulatedOpenBISService service)
     {
         FeatureTableBuilder builder = new FeatureTableBuilder(featureCodes, dao, service);
-        for (String datasetCode : datasetCodes)
-        {
-            builder.addFeatureVectorsOfDataSet(datasetCode);
-        }
+        addFeatureVectorsOfDataSets(builder, datasetCodes);
         List<FeatureTableRow> features = builder.createFeatureTableRows();
-        return new WellFeatureCollection(features, builder.getCodesAndLabels());
+        return new WellFeatureCollection<FeatureTableRow>(features, builder.getCodesAndLabels());
     }
 
     /** stores feature vectors for a set of wells */
-    public static class WellFeatureCollection
+    public static class WellFeatureCollection<T extends FeatureVectorValues>
     {
-        private final List<FeatureTableRow> features;
+        private final List<T> features;
 
         private final List<CodeAndLabel> featureNames;
 
-        public WellFeatureCollection(List<FeatureTableRow> features, List<CodeAndLabel> featureNames)
+        public WellFeatureCollection(List<T> features, List<CodeAndLabel> featureNames)
         {
             this.features = features;
             this.featureNames = featureNames;
         }
 
-        public List<FeatureTableRow> getFeatures()
+        public List<T> getFeatures()
         {
             return features;
         }
@@ -159,6 +172,26 @@ public class FeatureTableBuilder
         }
     }
 
+    private static void addFeatureVectorsOfDataSets(FeatureTableBuilder builder,
+            Collection<String> datasetCodes)
+    {
+        for (String datasetCode : datasetCodes)
+        {
+            builder.addFeatureVectorsOfDataSet(datasetCode);
+        }
+    }
+
+    private static Set<String> extractDatasetCodesFromSimpleReferences(
+            List<WellFeatureVectorReference> references)
+    {
+        Set<String> datasetCodes = new HashSet<String>();
+        for (WellFeatureVectorReference ref : references)
+        {
+            datasetCodes.add(ref.getDatasetCode());
+        }
+        return datasetCodes;
+    }
+
     private static Set<String> extractDatasetCodes(
             List<FeatureVectorDatasetWellReference> references)
     {
@@ -262,7 +295,6 @@ public class FeatureTableBuilder
         List<FeatureTableRow> rows = new ArrayList<FeatureTableRow>();
         for (DatasetFeaturesBundle bundle : bundles)
         {
-            String dataSetCode = bundle.dataSet.getPermId();
             ImgContainerDTO container = dao.getContainerById(bundle.dataSet.getContainerId());
             SampleIdentifier identifier = service.tryToGetSampleIdentifier(container.getPermId());
             for (int rowIndex = 1; rowIndex <= container.getNumberOfRows(); rowIndex++)
@@ -270,8 +302,8 @@ public class FeatureTableBuilder
                 for (int colIndex = 1; colIndex <= container.getNumberOfColumns(); colIndex++)
                 {
                     final FeatureTableRow row =
-                            createFeatureTableRow(bundle.featureDefToValuesMap, dataSetCode,
-                                    identifier, null, new WellPosition(rowIndex, colIndex));
+                            createFeatureTableRow(bundle, identifier, null, new WellPosition(
+                                    rowIndex, colIndex));
                     rows.add(row);
                 }
             }
@@ -279,6 +311,26 @@ public class FeatureTableBuilder
         return rows;
     }
 
+    /**
+     * Returns all features for the specified wells in previously loaded datasets. Operates on very
+     * basic data types.
+     */
+    private List<FeatureVectorValues> createFeatureVectorValues(
+            List<WellFeatureVectorReference> references)
+    {
+        Map<String/* dataset code */, DatasetFeaturesBundle> bundleMap = createBundleMap(bundles);
+        List<FeatureVectorValues> featureVectors = new ArrayList<FeatureVectorValues>();
+        for (WellFeatureVectorReference reference : references)
+        {
+            String dataSetCode = reference.getDatasetCode();
+            DatasetFeaturesBundle bundle = getDatasetFeaturesBundleOrDie(bundleMap, dataSetCode);
+            FeatureVectorValues featureVector =
+                    createFeatureVector(bundle, reference.getWellPosition());
+            featureVectors.add(featureVector);
+        }
+        return featureVectors;
+    }
+
     /**
      * Returns all features for the specified wells in previously loaded datasets.
      */
@@ -290,21 +342,28 @@ public class FeatureTableBuilder
         for (FeatureVectorDatasetWellReference reference : references)
         {
             String dataSetCode = reference.getDatasetCode();
-            DatasetFeaturesBundle bundle = bundleMap.get(dataSetCode);
-            if (bundle == null)
-            {
-                throw new IllegalStateException("Dataset has not been loaded: " + dataSetCode);
-            }
+            DatasetFeaturesBundle bundle = getDatasetFeaturesBundleOrDie(bundleMap, dataSetCode);
             ImgContainerDTO container = dao.getContainerById(bundle.dataSet.getContainerId());
             SampleIdentifier identifier = service.tryToGetSampleIdentifier(container.getPermId());
             final FeatureTableRow row =
-                    createFeatureTableRow(bundle.featureDefToValuesMap, dataSetCode, identifier,
-                            reference, reference.getWellPosition());
+                    createFeatureTableRow(bundle, identifier, reference,
+                            reference.getWellPosition());
             rows.add(row);
         }
         return rows;
     }
 
+    private DatasetFeaturesBundle getDatasetFeaturesBundleOrDie(
+            Map<String, DatasetFeaturesBundle> bundleMap, String dataSetCode)
+    {
+        DatasetFeaturesBundle bundle = bundleMap.get(dataSetCode);
+        if (bundle == null)
+        {
+            throw new IllegalStateException("Dataset has not been loaded: " + dataSetCode);
+        }
+        return bundle;
+    }
+
     private static HashMap<String, DatasetFeaturesBundle> createBundleMap(
             List<DatasetFeaturesBundle> bundles)
     {
@@ -316,16 +375,32 @@ public class FeatureTableBuilder
         return map;
     }
 
-    private FeatureTableRow createFeatureTableRow(
-            Map<ImgFeatureDefDTO, List<ImgFeatureValuesDTO>> featureDefToValuesMap,
-            String dataSetCode, SampleIdentifier identifier,
-            FeatureVectorDatasetWellReference reference, WellPosition wellPosition)
+    private FeatureTableRow createFeatureTableRow(DatasetFeaturesBundle bundle,
+            SampleIdentifier identifier, FeatureVectorDatasetWellReference reference,
+            WellPosition wellPosition)
     {
-        FeatureTableRow row = new FeatureTableRow();
-        row.setDataSetCode(dataSetCode);
+        FeatureVectorValues featureVector = createFeatureVector(bundle, wellPosition);
+        FeatureTableRow row = new FeatureTableRow(featureVector);
         row.setPlateIdentifier(identifier);
         row.setReference(reference);
-        row.setWellPosition(wellPosition);
+        return row;
+    }
+
+    private FeatureVectorValues createFeatureVector(DatasetFeaturesBundle bundle,
+            WellPosition wellPosition)
+    {
+        FeatureVectorValues fv = new FeatureVectorValues();
+        fv.setDataSetCode(bundle.dataSet.getPermId());
+        fv.setWellPosition(wellPosition);
+        float[] valueArray = createFeatureValueArray(bundle.featureDefToValuesMap, wellPosition);
+        fv.setFeatureValues(valueArray);
+        return fv;
+    }
+
+    private float[] createFeatureValueArray(
+            Map<ImgFeatureDefDTO, List<ImgFeatureValuesDTO>> featureDefToValuesMap,
+            WellPosition wellPosition)
+    {
         float[] valueArray = new float[featureCodeLabelToIndexMap.size()];
         Arrays.fill(valueArray, Float.NaN);
         for (Entry<ImgFeatureDefDTO, List<ImgFeatureValuesDTO>> entry : featureDefToValuesMap
@@ -348,8 +423,7 @@ public class FeatureTableBuilder
                     featureValues.getForWellLocation(wellPosition.getWellRow(),
                             wellPosition.getWellColumn());
         }
-        row.setFeatureValues(valueArray);
-        return row;
+        return valueArray;
     }
 
     private CodeAndLabel getCodeAndLabel(final ImgFeatureDefDTO featureDefinition)
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/TabularDataGraphServlet.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/TabularDataGraphServlet.java
index 2cb872ff7e233cd4d192a27b0af6a54e05205fb1..c8870edea08fa37d304c427d660f269705880986 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/TabularDataGraphServlet.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/TabularDataGraphServlet.java
@@ -22,6 +22,7 @@ import java.util.Arrays;
 import java.util.List;
 
 import ch.systemsx.cisd.openbis.dss.generic.server.FeatureTableBuilder.WellFeatureCollection;
+import ch.systemsx.cisd.openbis.dss.generic.server.featurevectors.FeatureTableRow;
 import ch.systemsx.cisd.openbis.dss.generic.server.plugins.tasks.ITabularData;
 import ch.systemsx.cisd.openbis.dss.generic.shared.IEncapsulatedOpenBISService;
 import ch.systemsx.cisd.openbis.dss.generic.shared.ServiceProvider;
@@ -85,7 +86,7 @@ public class TabularDataGraphServlet extends AbstractTabularDataGraphServlet
 
         private void initialize()
         {
-            WellFeatureCollection featureCollection =
+            WellFeatureCollection<FeatureTableRow> featureCollection =
                     FeatureTableBuilder.fetchDatasetFeatures(Arrays.asList(dataSetCode),
                             new ArrayList<String>(), dao, service);
 
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/FeatureTableRow.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/featurevectors/FeatureTableRow.java
similarity index 53%
rename from screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/FeatureTableRow.java
rename to screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/featurevectors/FeatureTableRow.java
index 010240425d8da34539f2427fdc8d1a7075eb3324..aed3481b0be4e5cd3fc49e42e9021a1fe15877bb 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/FeatureTableRow.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/featurevectors/FeatureTableRow.java
@@ -14,39 +14,28 @@
  * limitations under the License.
  */
 
-package ch.systemsx.cisd.openbis.dss.generic.server;
+package ch.systemsx.cisd.openbis.dss.generic.server.featurevectors;
 
 import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.SampleIdentifier;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.FeatureVectorDatasetWellReference;
-import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.WellPosition;
 
 /**
  * Bean for a row in a table of feature vectors. Each row is specified by data set code, plate
- * identifier, well position and and array of feature values. Double.NaN is used for unknown
- * feature value in this array.
+ * identifier, well position and and array of feature values.
  * 
  * @author Franz-Josef Elmer
  */
-public class FeatureTableRow
+public class FeatureTableRow extends FeatureVectorValues
 {
-    private String dataSetCode;
-    
     private FeatureVectorDatasetWellReference reference;
 
     private SampleIdentifier plateIdentifier;
 
-    private WellPosition wellPosition;
-    
-    private float[] featureValues;
-
-    public final String getDataSetCode()
+    public FeatureTableRow(FeatureVectorValues featureVector)
     {
-        return dataSetCode;
-    }
-
-    public final void setDataSetCode(String dataSetCode)
-    {
-        this.dataSetCode = dataSetCode;
+        setDataSetCode(featureVector.getDataSetCode());
+        setFeatureValues(featureVector.getFeatureValues());
+        setWellPosition(featureVector.getWellPosition());
     }
 
     public FeatureVectorDatasetWellReference getReference()
@@ -68,34 +57,4 @@ public class FeatureTableRow
     {
         this.plateIdentifier = plateIdentifier;
     }
-
-    public void setWellPosition(WellPosition wellPosition)
-    {
-        this.wellPosition = wellPosition;
-    }
-    
-    public final WellPosition getWellPosition()
-    {
-        return wellPosition;
-    }
-
-    public final float[] getFeatureValues()
-    {
-        return featureValues;
-    }
-
-    public final double[] getFeatureValuesAsDouble()
-    {
-        final double[] doubleValues = new double[featureValues.length];
-        for (int i = 0; i < featureValues.length; ++i)
-        {
-            doubleValues[i] = featureValues[i];
-        }
-        return doubleValues;
-    }
-
-    public final void setFeatureValues(float[] featureValues)
-    {
-        this.featureValues = featureValues;
-    }
 }
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/featurevectors/FeatureVectorValues.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/featurevectors/FeatureVectorValues.java
new file mode 100644
index 0000000000000000000000000000000000000000..d454894c077766d057dcb99bbb8bc00950aa988b
--- /dev/null
+++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/featurevectors/FeatureVectorValues.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2010 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.dss.generic.server.featurevectors;
+
+import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.WellPosition;
+
+/**
+ * Bean for one feature vector of the well. Contains data set code, well position and and array of
+ * feature values. Double.NaN is used for unknown feature value in this array.
+ * 
+ * @author Tomasz Pylak
+ */
+public class FeatureVectorValues
+{
+    private WellFeatureVectorReference featureVectorReference = new WellFeatureVectorReference();
+
+    private float[] featureValues;
+
+    public float[] getFeatureValues()
+    {
+        return featureValues;
+    }
+
+    public double[] getFeatureValuesAsDouble()
+    {
+        double[] doubleValues = new double[featureValues.length];
+        for (int i = 0; i < featureValues.length; ++i)
+        {
+            doubleValues[i] = featureValues[i];
+        }
+        return doubleValues;
+    }
+
+    public void setFeatureValues(float[] featureValues)
+    {
+        this.featureValues = featureValues;
+    }
+
+    public String getDataSetCode()
+    {
+        return featureVectorReference.getDatasetCode();
+    }
+
+    public void setDataSetCode(String dataSetCode)
+    {
+        featureVectorReference.setDatasetCode(dataSetCode);
+    }
+
+    public void setWellPosition(WellPosition wellPosition)
+    {
+        featureVectorReference.setWellPosition(wellPosition);
+    }
+
+    public WellPosition getWellPosition()
+    {
+        return featureVectorReference.getWellPosition();
+    }
+
+}
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/featurevectors/WellFeatureVectorReference.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/featurevectors/WellFeatureVectorReference.java
new file mode 100644
index 0000000000000000000000000000000000000000..0922c9eb36ca7d080e392c50f11861255924ee1e
--- /dev/null
+++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/featurevectors/WellFeatureVectorReference.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2010 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.dss.generic.server.featurevectors;
+
+import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.WellPosition;
+
+/**
+ * References a feature vector of one well
+ * 
+ * @author Tomasz Pylak
+ */
+public class WellFeatureVectorReference
+{
+    private String dataSetCode; // dataset with feature vectors
+
+    private WellPosition wellPosition;
+
+    public final String getDatasetCode()
+    {
+        return dataSetCode;
+    }
+
+    public final void setDatasetCode(String dataSetCode)
+    {
+        this.dataSetCode = dataSetCode;
+    }
+
+    public void setWellPosition(WellPosition wellPosition)
+    {
+        this.wellPosition = wellPosition;
+    }
+
+    public final WellPosition getWellPosition()
+    {
+        return wellPosition;
+    }
+}
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/ImageAnalysisMergedRowsReportingPlugin.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/ImageAnalysisMergedRowsReportingPlugin.java
index 93e70cbc3ca6f0e1ed10465ef92b9700136b6750..fa937cfa5099d03032e32b1f392fe148cf9722fd 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/ImageAnalysisMergedRowsReportingPlugin.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/ImageAnalysisMergedRowsReportingPlugin.java
@@ -23,7 +23,7 @@ import java.util.Properties;
 
 import ch.systemsx.cisd.openbis.dss.generic.server.FeatureTableBuilder;
 import ch.systemsx.cisd.openbis.dss.generic.server.FeatureTableBuilder.WellFeatureCollection;
-import ch.systemsx.cisd.openbis.dss.generic.server.FeatureTableRow;
+import ch.systemsx.cisd.openbis.dss.generic.server.featurevectors.FeatureTableRow;
 import ch.systemsx.cisd.openbis.dss.generic.server.plugins.standard.AbstractDatastorePlugin;
 import ch.systemsx.cisd.openbis.dss.generic.server.plugins.tasks.IReportingPluginTask;
 import ch.systemsx.cisd.openbis.dss.generic.server.plugins.tasks.SimpleTableModelBuilder;
@@ -82,7 +82,7 @@ public class ImageAnalysisMergedRowsReportingPlugin extends AbstractDatastorePlu
     {
         List<String> datasetCodes = extractDatasetCodes(datasets);
         ArrayList<String> featureCodes = new ArrayList<String>(); // fetch all
-        WellFeatureCollection featuresCollection =
+        WellFeatureCollection<FeatureTableRow> featuresCollection =
                 FeatureTableBuilder.fetchDatasetFeatures(datasetCodes, featureCodes, getDAO(),
                         getService());
 
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/screening/server/DssServiceRpcScreening.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/screening/server/DssServiceRpcScreening.java
index f0b29b0f271e046633ba7e96746ac2fec4629311..0ee7ce3f30d9a306f27d60c5e6f98d441ed92763 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/dss/screening/server/DssServiceRpcScreening.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/screening/server/DssServiceRpcScreening.java
@@ -42,7 +42,7 @@ import ch.systemsx.cisd.openbis.dss.generic.server.AbstractDatasetDownloadServle
 import ch.systemsx.cisd.openbis.dss.generic.server.AbstractDssServiceRpc;
 import ch.systemsx.cisd.openbis.dss.generic.server.FeatureTableBuilder;
 import ch.systemsx.cisd.openbis.dss.generic.server.FeatureTableBuilder.WellFeatureCollection;
-import ch.systemsx.cisd.openbis.dss.generic.server.FeatureTableRow;
+import ch.systemsx.cisd.openbis.dss.generic.server.featurevectors.FeatureTableRow;
 import ch.systemsx.cisd.openbis.dss.generic.server.images.ImageChannelStackReference;
 import ch.systemsx.cisd.openbis.dss.generic.server.images.ImageChannelsUtils;
 import ch.systemsx.cisd.openbis.dss.generic.shared.IEncapsulatedOpenBISService;
@@ -238,7 +238,7 @@ public class DssServiceRpcScreening extends AbstractDssServiceRpc implements
     private FeatureVectorDataset createFeatureVectorDataset(String sessionToken,
             FeatureVectorDatasetReference dataset, List<String> featureCodes)
     {
-        WellFeatureCollection datasetFeatures =
+        WellFeatureCollection<FeatureTableRow> datasetFeatures =
                 FeatureTableBuilder.fetchDatasetFeatures(Arrays.asList(dataset.getDatasetCode()),
                         featureCodes, getDAO(), getOpenBISService());
         List<FeatureVector> featureVectors = new ArrayList<FeatureVector>();
@@ -267,14 +267,14 @@ public class DssServiceRpcScreening extends AbstractDssServiceRpc implements
             List<String> featureNames)
     {
         checkDatasetsAuthorizationForIDatasetIdentifier(sessionToken, datasetWellReferences);
-        WellFeatureCollection features =
+        WellFeatureCollection<FeatureTableRow> features =
                 FeatureTableBuilder.fetchWellFeatures(datasetWellReferences, featureNames, dao,
                         getOpenBISService());
         return createFeatureVectorList(features);
     }
 
     private List<FeatureVectorWithDescription> createFeatureVectorList(
-            final WellFeatureCollection features)
+            final WellFeatureCollection<FeatureTableRow> features)
     {
         final List<String> featureCodes = features.getFeatureCodes();
         final List<FeatureTableRow> featureTableRows = features.getFeatures();
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/ScreeningServer.java b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/ScreeningServer.java
index 4990d245edbeaa1be13199b4ee2a68fac1a63df0..c5cf8db77c3bbc8dc3f0cb531b9d8f8e95b2b08b 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/ScreeningServer.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/ScreeningServer.java
@@ -156,7 +156,7 @@ public final class ScreeningServer extends AbstractServer<IScreeningServer> impl
     {
         Session session = getSession(sessionToken);
         return PlateMaterialLocationsLoader.load(session, businessObjectFactory, getDAOFactory(),
-                materialCriteria, true);
+                materialCriteria);
     }
 
     public List<WellImageChannelStack> listImageChannelStacks(String sessionToken,
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/logic/FeatureVectorDatasetLoader.java b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/logic/FeatureVectorDatasetLoader.java
index 169eafcace92cbee64086c3d76f74d4401d04e55..1e36639bc00c7f76aa89d5887c36e31f972164dd 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/logic/FeatureVectorDatasetLoader.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/logic/FeatureVectorDatasetLoader.java
@@ -16,16 +16,15 @@
 
 package ch.systemsx.cisd.openbis.plugin.screening.server.logic;
 
-import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap;
-import it.unimi.dsi.fastutil.longs.Long2ObjectSortedMap;
-
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
 
 import ch.systemsx.cisd.openbis.generic.server.business.bo.datasetlister.IDatasetLister;
-import ch.systemsx.cisd.openbis.generic.shared.basic.TechId;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DataStore;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ExternalData;
 import ch.systemsx.cisd.openbis.generic.shared.dto.Session;
@@ -45,7 +44,7 @@ class FeatureVectorDatasetLoader extends ImageDatasetLoader
     private final String featureVectorDatasetTypeCode;
 
     // Running state
-    private List<ExternalData> featureVectorDatasets;
+    private Collection<ExternalData> featureVectorDatasets;
 
     FeatureVectorDatasetLoader(Session session,
             IScreeningBusinessObjectFactory businessObjectFactory, String homeSpaceOrNull,
@@ -57,55 +56,143 @@ class FeatureVectorDatasetLoader extends ImageDatasetLoader
         featureVectorDatasetTypeCode = ScreeningConstants.IMAGE_ANALYSIS_DATASET_TYPE;
     }
 
-    public List<FeatureVectorDatasetReference> getFeatureVectorDatasets()
+    public static class FeatureVectorExternalData
+    {
+        private final ExternalData featureVectorDataset;
+
+        private final ExternalData imageDatasetOrNull;
+
+        public FeatureVectorExternalData(ExternalData featureVectorDataset,
+                ExternalData imageDatasetOrNull)
+        {
+            this.featureVectorDataset = featureVectorDataset;
+            this.imageDatasetOrNull = imageDatasetOrNull;
+        }
+
+        public ExternalData getFeatureVectorDataset()
+        {
+            return featureVectorDataset;
+        }
+
+        public ExternalData tryGetImageDataset()
+        {
+            return imageDatasetOrNull;
+        }
+    }
+
+    /** enriched with image dataset parents */
+    public Collection<ExternalData> getFeatureVectorDatasets()
+    {
+        loadAll();
+        return featureVectorDatasets;
+    }
+
+    public List<FeatureVectorDatasetReference> getFeatureVectorDatasetReferences()
+    {
+        loadAll();
+        return asFeatureVectorDatasetReferences();
+    }
+
+    private void loadAll()
     {
         // Load the image data sets
         load();
         loadFeatureVectorDatasets();
-        return asFeatureVectorDatasets();
     }
 
     private void loadFeatureVectorDatasets()
     {
-        final Long2ObjectSortedMap<ExternalData> featureVectorDatasetSet =
-                new Long2ObjectLinkedOpenHashMap<ExternalData>();
+        final Map<Long, ExternalData> featureVectorDatasetSet = new HashMap<Long, ExternalData>();
         IDatasetLister datasetLister = businessObjectFactory.createDatasetLister(session);
 
-        for (ExternalData data : getDatasets())
+        List<ExternalData> imageDatasets = new ArrayList<ExternalData>();
+        for (ExternalData dataset : getDatasets())
         {
-            if (ScreeningUtils.isTypeEqual(data, ScreeningConstants.IMAGE_ANALYSIS_DATASET_TYPE))
+            if (ScreeningUtils.isTypeEqual(dataset, ScreeningConstants.IMAGE_ANALYSIS_DATASET_TYPE))
+            {
+                featureVectorDatasetSet.put(dataset.getId(), dataset);
+            } else if (ScreeningUtils.isTypeEqual(dataset, ScreeningConstants.IMAGE_DATASET_TYPE))
             {
-                featureVectorDatasetSet.put(data.getId(), data);
+                imageDatasets.add(dataset);
             }
         }
 
+        List<ExternalData> connectedFeatureVectorDatasets =
+                listChildrenFeatureVectorDatasets(datasetLister, extractIds(imageDatasets));
+        Map<Long, Set<Long>> featureVectorToImageDatasetIdsMap =
+                datasetLister.listParentIds(extractIds(connectedFeatureVectorDatasets));
+        Map<Long, List<ExternalData>> featureVectorToImageDatasetsMap =
+                createFeatureVectorToImageDatasetsMap(featureVectorToImageDatasetIdsMap,
+                        imageDatasets);
         // Implementation note: some data sets in this loop may overwrite data from the first loop.
         // This is intended as we want to keep the parent relationship of the feature vector data
         // sets, if they exist.
-        for (ExternalData data : getDatasets())
+        for (ExternalData fv : connectedFeatureVectorDatasets)
         {
-            if (ScreeningUtils.isTypeEqual(data, ScreeningConstants.IMAGE_DATASET_TYPE))
+            List<ExternalData> parentImageDatasets =
+                    featureVectorToImageDatasetsMap.get(fv.getId());
+            if (parentImageDatasets != null)
             {
-                // TODO 2010-05-26, CR, : This way to access the database one by one is slow if
-                // there are a large number of image data sets
-                // Need to add a query to the dataset lister that returns, for a collection of tech
-                // ids, a child datasets and their parents.
-                final List<ExternalData> children =
-                        datasetLister.listByParentTechId(new TechId(data.getId()));
-                for (ExternalData child : children)
-                {
-                    child.setParents(Collections.singleton(data));
-                    featureVectorDatasetSet.put(child.getId(), child);
-                }
+                fv.setParents(parentImageDatasets);
             }
+            featureVectorDatasetSet.put(fv.getId(), fv);
         }
+        featureVectorDatasets = featureVectorDatasetSet.values();
+    }
 
-        featureVectorDatasets =
-                ScreeningUtils.filterExternalDataByType(featureVectorDatasetSet.values(),
-                        featureVectorDatasetTypeCode);
+    private List<ExternalData> listChildrenFeatureVectorDatasets(IDatasetLister datasetLister,
+            Collection<Long> imageDatasetIds)
+    {
+        List<ExternalData> datasets = datasetLister.listByParentTechIds(imageDatasetIds);
+        return ScreeningUtils.filterExternalDataByType(datasets, featureVectorDatasetTypeCode);
     }
 
-    private List<FeatureVectorDatasetReference> asFeatureVectorDatasets()
+    private static Map<Long/* feature vector dataset id */, List<ExternalData/* image dataset */>> createFeatureVectorToImageDatasetsMap(
+            Map<Long, Set<Long>> featureVectorToImageDatasetsMap, List<ExternalData> imageDatasets)
+    {
+        Map<Long, List<ExternalData>> featureVectorToImageDatasetMap =
+                new HashMap<Long, List<ExternalData>>();
+        for (Entry<Long, Set<Long>> entry : featureVectorToImageDatasetsMap.entrySet())
+        {
+            List<ExternalData> parentImageDatasets =
+                    findDatasetsWithIds(entry.getValue(), imageDatasets);
+            // NOTE: if a feature vector dataset has more than one image dataset parent, all the
+            // parents will be ignored.
+            if (parentImageDatasets.size() == 1)
+            {
+                Long featureVectorDatasetId = entry.getKey();
+                featureVectorToImageDatasetMap.put(featureVectorDatasetId, parentImageDatasets);
+            }
+        }
+        return featureVectorToImageDatasetMap;
+    }
+
+    // returns all dataset which have an id contained in the specified id set
+    private static List<ExternalData> findDatasetsWithIds(Set<Long> datasetIds,
+            List<ExternalData> datasets)
+    {
+        List<ExternalData> found = new ArrayList<ExternalData>();
+        for (ExternalData dataset : datasets)
+        {
+            if (datasetIds.contains(dataset.getId()))
+            {
+                found.add(dataset);
+            }
+        }
+        return found;
+    }
+
+    private static Collection<Long> extractIds(List<ExternalData> datasets)
+    {
+        List<Long> ids = new ArrayList<Long>();
+        for (ExternalData dataset : datasets)
+        {
+            ids.add(dataset.getId());
+        }
+        return ids;
+    }
+
+    private List<FeatureVectorDatasetReference> asFeatureVectorDatasetReferences()
     {
         List<FeatureVectorDatasetReference> result = new ArrayList<FeatureVectorDatasetReference>();
         for (ExternalData externalData : featureVectorDatasets)
@@ -115,10 +202,24 @@ class FeatureVectorDatasetLoader extends ImageDatasetLoader
         return result;
     }
 
+    private static ExternalData tryGetOneParent(ExternalData externalData)
+    {
+        Collection<ExternalData> parents = externalData.getParents();
+        if (parents != null && parents.size() == 1)
+        {
+            return parents.iterator().next();
+        } else
+        {
+            return null;
+        }
+    }
+
     protected FeatureVectorDatasetReference asFeatureVectorDataset(ExternalData externalData)
     {
         DataStore dataStore = externalData.getDataStore();
-        if (externalData.getParents() == null || externalData.getParents().isEmpty())
+        // there should be no more parents than one, we ensure about that earlier
+        ExternalData parentDataset = tryGetOneParent(externalData);
+        if (parentDataset == null)
         {
             return new FeatureVectorDatasetReference(externalData.getCode(),
                     getDataStoreUrlFromDataStore(dataStore), createPlateIdentifier(externalData),
@@ -129,7 +230,6 @@ class FeatureVectorDatasetLoader extends ImageDatasetLoader
             // Note: this only works reliably because this class sets the parents of the feature
             // vector data sets itself and sets it to a list with exactly one entry!
             // (see loadFeatureVectorDatasets() above)
-            final ExternalData parentDataset = externalData.getParents().iterator().next();
             return new FeatureVectorDatasetReference(externalData.getCode(),
                     getDataStoreUrlFromDataStore(dataStore), createPlateIdentifier(parentDataset),
                     createExperimentIdentifier(externalData), extractPlateGeometry(parentDataset),
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/logic/ImageDatasetLoader.java b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/logic/ImageDatasetLoader.java
index 374225ae738437608c76d697ec20c484dbf4b188..119d9ace0666bd0ea76fea538a1fff93b0d00454 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/logic/ImageDatasetLoader.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/logic/ImageDatasetLoader.java
@@ -49,25 +49,43 @@ class ImageDatasetLoader extends PlateDatasetLoader
     /**
      * Return the image datasets for the specified plates.
      */
-    public List<ImageDatasetReference> getImageDatasets()
+    public List<ExternalData> getImageDatasets()
     {
         load();
         return filterImageDatasets();
     }
 
-    private List<ImageDatasetReference> filterImageDatasets()
+    /**
+     * Return the image datasets references for the specified plates.
+     */
+    public List<ImageDatasetReference> getImageDatasetReferences()
+    {
+        return asImageDatasetReferences(getImageDatasets());
+    }
+
+    private List<ExternalData> filterImageDatasets()
     {
-        List<ImageDatasetReference> result = new ArrayList<ImageDatasetReference>();
+        List<ExternalData> result = new ArrayList<ExternalData>();
         for (ExternalData externalData : getDatasets())
         {
             if (ScreeningUtils.isTypeEqual(externalData, ScreeningConstants.IMAGE_DATASET_TYPE))
             {
-                result.add(asImageDataset(externalData));
+                result.add(externalData);
             }
         }
         return result;
     }
 
+    private List<ImageDatasetReference> asImageDatasetReferences(List<ExternalData> imageDatasets)
+    {
+        List<ImageDatasetReference> references = new ArrayList<ImageDatasetReference>();
+        for (ExternalData imageDataset : imageDatasets)
+        {
+            references.add(asImageDataset(imageDataset));
+        }
+        return references;
+    }
+
     protected ImageDatasetReference asImageDataset(ExternalData externalData)
     {
         DataStore dataStore = externalData.getDataStore();
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/logic/PlateDatasetLoader.java b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/logic/PlateDatasetLoader.java
index 31de9e17692dab1add7ab93341e3c9cdc803d778..776e7ad244bb3d8dc203264cb521612d53dcfb24 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/logic/PlateDatasetLoader.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/logic/PlateDatasetLoader.java
@@ -190,7 +190,7 @@ class PlateDatasetLoader
         return new PlateIdentifier(plateCode, spaceCodeOrNull, sample.getPermId());
     }
 
-    protected ExperimentIdentifier createExperimentIdentifier(ExternalData parentDataset)
+    protected static ExperimentIdentifier createExperimentIdentifier(ExternalData parentDataset)
     {
         return asExperimentIdentifier(parentDataset.getExperiment());
     }
@@ -282,7 +282,7 @@ class PlateDatasetLoader
         return sampleIds;
     }
 
-    protected String getDataStoreUrlFromDataStore(DataStore dataStore)
+    protected static String getDataStoreUrlFromDataStore(DataStore dataStore)
     {
         String datastoreUrl = dataStore.getDownloadUrl();
         // The url objained form a DataStore object is the *download* url. Convert this to the
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/logic/PlateMaterialLocationsLoader.java b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/logic/PlateMaterialLocationsLoader.java
index 9c8850e2c7ed364f646c109ad847405304f99904..cd88d1cb6c18a104ffa87e23218b41ae157c61cb 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/logic/PlateMaterialLocationsLoader.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/logic/PlateMaterialLocationsLoader.java
@@ -19,6 +19,7 @@ package ch.systemsx.cisd.openbis.plugin.screening.server.logic;
 import java.sql.Connection;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashMap;
@@ -31,11 +32,13 @@ import net.lemnik.eodsql.DataIterator;
 import net.lemnik.eodsql.QueryTool;
 
 import org.apache.commons.lang.ArrayUtils;
+import org.apache.log4j.Logger;
 
 import ch.rinn.restrictions.Friend;
 import ch.systemsx.cisd.common.exceptions.UserFailureException;
+import ch.systemsx.cisd.common.logging.LogCategory;
+import ch.systemsx.cisd.common.logging.LogFactory;
 import ch.systemsx.cisd.openbis.generic.server.business.bo.common.DatabaseContextUtils;
-import ch.systemsx.cisd.openbis.generic.server.business.bo.datasetlister.IDatasetLister;
 import ch.systemsx.cisd.openbis.generic.server.business.bo.materiallister.IMaterialLister;
 import ch.systemsx.cisd.openbis.generic.server.dataaccess.IDAOFactory;
 import ch.systemsx.cisd.openbis.generic.shared.basic.TechId;
@@ -56,18 +59,18 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.MaterialTypePropertyTypePE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.Session;
 import ch.systemsx.cisd.openbis.plugin.screening.server.IScreeningBusinessObjectFactory;
 import ch.systemsx.cisd.openbis.plugin.screening.server.dataaccess.IScreeningQuery;
+import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.PlateIdentifier;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.DatasetImagesReference;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.DatasetReference;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.ExperimentReference;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.PlateImageParameters;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.PlateMaterialsSearchCriteria;
-import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.ScreeningConstants;
-import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.WellContent;
-import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.WellLocation;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.PlateMaterialsSearchCriteria.ExperimentSearchCriteria;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.PlateMaterialsSearchCriteria.MaterialSearchCodesCriteria;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.PlateMaterialsSearchCriteria.MaterialSearchCriteria;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.PlateMaterialsSearchCriteria.SingleExperimentSearchCriteria;
+import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.WellContent;
+import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.WellLocation;
 
 /**
  * Loades selected wells content: metadata and (if available) image dataset and feature vectors.
@@ -77,6 +80,9 @@ import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.PlateMaterials
 @Friend(toClasses = IScreeningQuery.class)
 public class PlateMaterialLocationsLoader
 {
+    private final static Logger operationLog = LogFactory.getLogger(LogCategory.OPERATION,
+            PlateMaterialLocationsLoader.class);
+
     /**
      * Finds wells containing the specified material and belonging to the specified experiment.
      * Loads wells content: metadata and (if available) image dataset and feature vectors.
@@ -91,10 +97,10 @@ public class PlateMaterialLocationsLoader
 
     public static List<WellContent> load(Session session,
             IScreeningBusinessObjectFactory businessObjectFactory, IDAOFactory daoFactory,
-            PlateMaterialsSearchCriteria materialCriteria, boolean enrichWithImages)
+            PlateMaterialsSearchCriteria materialCriteria)
     {
         return new PlateMaterialLocationsLoader(session, businessObjectFactory, daoFactory)
-                .getPlateLocations(materialCriteria, enrichWithImages);
+                .getPlateLocations(materialCriteria, true);
     }
 
     /** loads wells metadata, but no information about image or image analysis datasets */
@@ -153,52 +159,186 @@ public class PlateMaterialLocationsLoader
 
     private List<WellContent> enrichWithDatasets(List<WellContent> locations)
     {
-        List<ExternalData> imageDatasets = loadImageDatasets(locations);
-        Map<Long/* plate id */, List<ExternalData>> plateToDatasetMap =
-                createPlateToDatasetMap(imageDatasets);
+        Collection<PlateIdentifier> plates = extractPlates(locations);
+        FeatureVectorDatasetLoader datasetsRetriever =
+                new FeatureVectorDatasetLoader(session, businessObjectFactory, null, plates);
+        Collection<ExternalData> imageDatasets = datasetsRetriever.getImageDatasets();
         Map<String, PlateImageParameters> imageParams = loadImagesReport(imageDatasets);
-        return enrichWithImages(locations, plateToDatasetMap, imageParams);
+
+        // TODO 2010-09-07, Tomasz Pylak: uncomment this when showing fetures in Reviewing Panel is
+        // implemented
+        // Collection<ExternalData> featureVectorDatasets =
+        // datasetsRetriever.getFeatureVectorDatasets();
+        // Collection<ExternalData> childlessImageDatasets =
+        // selectChildlessImageDatasets(imageDatasets, featureVectorDatasets);
+        //
+        // Map<Long/* plate id */, List<ExternalData>> plateToChildlessImageDatasetMap =
+        // createPlateToDatasetMap(childlessImageDatasets);
+        // Map<Long/* plate id */, List<ExternalData>> plateToFeatureVectoreDatasetMap =
+        // createPlateToDatasetMap(featureVectorDatasets);
+        //
+        // return enrichWithDatasets(locations, plateToChildlessImageDatasetMap,
+        // plateToFeatureVectoreDatasetMap, imageParams);
+
+        return enrichWithDatasets(locations, createPlateToDatasetMap(imageDatasets),
+                new HashMap<Long, List<ExternalData>>(), imageParams);
     }
 
-    private static List<WellContent> enrichWithImages(List<WellContent> wellContents,
-            Map<Long/* plate id */, List<ExternalData>> plateToDatasetMap,
-            Map<String, PlateImageParameters> imageParams)
+    @SuppressWarnings("unused")
+    private static Collection<ExternalData> selectChildlessImageDatasets(
+            Collection<ExternalData> imageDatasets, Collection<ExternalData> featureVectorDatasets)
     {
-        List<WellContent> wellsWithImages = new ArrayList<WellContent>();
-        for (WellContent wellContent : wellContents)
+        Collection<ExternalData> childlessImageDatasets = new ArrayList<ExternalData>();
+        Set<String> parentImageDatasetCodes = extractParentDatasetCodes(featureVectorDatasets);
+        for (ExternalData imageDataset : imageDatasets)
         {
-            List<ExternalData> datasets = plateToDatasetMap.get(wellContent.getPlate().getId());
-            boolean imagesExist = false;
-            // there can be more than one dataset with images for each well - in such a case we will
-            // have one well content duplicated for each dataset
-            if (datasets != null)
+            if (parentImageDatasetCodes.contains(imageDataset.getCode()) == false)
             {
-                for (ExternalData dataset : datasets)
+                childlessImageDatasets.add(imageDataset);
+            }
+        }
+        return childlessImageDatasets;
+    }
+
+    private static Set<String> extractParentDatasetCodes(Collection<ExternalData> datasets)
+    {
+        Set<String> codes = new HashSet<String>();
+        for (ExternalData dataset : datasets)
+        {
+            Collection<ExternalData> parents = dataset.getParents();
+            if (parents != null)
+            {
+                for (ExternalData parent : parents)
                 {
-                    PlateImageParameters imageParameters = imageParams.get(dataset.getCode());
-                    if (imageParameters != null)
-                    {
-                        DatasetReference datasetReference =
-                                ScreeningUtils.createDatasetReference(dataset);
-                        DatasetImagesReference wellImages =
-                                DatasetImagesReference.create(datasetReference, imageParameters);
-                        WellContent wellWithImages = wellContent.cloneWithImages(wellImages);
-                        wellsWithImages.add(wellWithImages);
-                        imagesExist = true;
-                    }
+                    codes.add(parent.getCode());
                 }
             }
+        }
+        return codes;
+    }
+
+    private static Collection<PlateIdentifier> extractPlates(List<WellContent> locations)
+    {
+        Collection<PlateIdentifier> plates = new ArrayList<PlateIdentifier>();
+        for (WellContent location : locations)
+        {
+            plates.add(PlateIdentifier.createFromPermId(location.getPlate().getPermId()));
+        }
+        return plates;
+    }
+
+    /**
+     * Connects wells with datasets.
+     */
+    private static List<WellContent> enrichWithDatasets(List<WellContent> wellContents,
+            Map<Long/* plate id */, List<ExternalData>> plateToChildlessImageDatasetMap,
+            Map<Long/* plate id */, List<ExternalData>> plateToFeatureVectoreDatasetMap,
+            Map<String, PlateImageParameters> imageParams)
+    {
+        List<WellContent> wellsWithDatasets = new ArrayList<WellContent>();
+        for (WellContent wellContent : wellContents)
+        {
+            List<WellContent> clonedWellContents =
+                    enrichWithDatasetReferences(wellContent, plateToChildlessImageDatasetMap,
+                            plateToFeatureVectoreDatasetMap, imageParams);
             // if there are no datasets for the well content, we add it without images
-            if (imagesExist == false)
+            if (clonedWellContents.isEmpty())
+            {
+                wellsWithDatasets.add(wellContent);
+            } else
             {
-                wellsWithImages.add(wellContent);
+                wellsWithDatasets.addAll(clonedWellContents);
             }
         }
-        return wellsWithImages;
+        wellsWithDatasets = enrichWithFeatureVectors(wellsWithDatasets);
+        return wellsWithDatasets;
+    }
+
+    private static List<WellContent> enrichWithFeatureVectors(List<WellContent> wellsWithDatasets)
+    {
+        // TODO 2010-09-07, Tomasz Pylak: Enrich each WellContent with feature values.
+        // WellFeatureCollection<FeatureVectorValues> featureVectors =
+        // FeatureTableBuilder.fetchWellFeatureValues(references, dao, service);
+        // return enrichWithFeatureVectors(wellsWithDatasets, featureVectors);
+        return wellsWithDatasets;
+    }
+
+    /**
+     * Connects one WellContent with dataset references.<br>
+     * We want to present all the data to the user, so if a well has several feature vector
+     * datasets, it will be cloned several times. By connecting to feature vector datasets we are
+     * possibly connecting to image datasets as well.<br>
+     * Additionally a join with childless image datasets has to be performed.
+     */
+    private static List<WellContent> enrichWithDatasetReferences(WellContent wellContent,
+            Map<Long, List<ExternalData>> plateToChildlessImageDatasetMap,
+            Map<Long, List<ExternalData>> plateToFeatureVectoreDatasetMap,
+            Map<String, PlateImageParameters> imageParams)
+    {
+        Long plateId = wellContent.getPlate().getId();
+        List<WellContent> clonedWellContents = new ArrayList<WellContent>();
+
+        List<ExternalData> featureVectoreDatasets = plateToFeatureVectoreDatasetMap.get(plateId);
+        if (featureVectoreDatasets != null)
+        {
+            for (ExternalData featureVectoreDataset : featureVectoreDatasets)
+            {
+                DatasetReference featureVectoreDatasetReference =
+                        ScreeningUtils.createDatasetReference(featureVectoreDataset);
+                DatasetImagesReference imagesDatasetReference =
+                        tryGetImageDatasetReference(featureVectoreDataset, imageParams);
+                clonedWellContents.add(wellContent.cloneWithDatasets(imagesDatasetReference,
+                        featureVectoreDatasetReference));
+            }
+        }
+
+        // there can be more than one dataset with images for each well - in such a case we will
+        // have one well content duplicated for each dataset
+        List<ExternalData> childlessImageDatasets = plateToChildlessImageDatasetMap.get(plateId);
+        if (childlessImageDatasets != null)
+        {
+            for (ExternalData childlessImageDataset : childlessImageDatasets)
+            {
+                DatasetImagesReference imagesDatasetReference =
+                        createDatasetImagesReference(childlessImageDataset, imageParams);
+                clonedWellContents.add(wellContent.cloneWithDatasets(imagesDatasetReference, null));
+            }
+        }
+        return clonedWellContents;
+    }
+
+    private static DatasetImagesReference tryGetImageDatasetReference(
+            ExternalData featureVectoreDataset, Map<String, PlateImageParameters> imageParams)
+    {
+        Collection<ExternalData> parents = featureVectoreDataset.getParents();
+        if (parents != null && parents.size() == 1)
+        {
+            ExternalData imageDataset = parents.iterator().next();
+            return createDatasetImagesReference(imageDataset, imageParams);
+        } else
+        {
+            return null;
+        }
+    }
+
+    private static DatasetImagesReference createDatasetImagesReference(ExternalData imageDataset,
+            Map<String, PlateImageParameters> imageParams)
+    {
+        PlateImageParameters imageParameters = imageParams.get(imageDataset.getCode());
+        if (imageParameters != null)
+        {
+            return DatasetImagesReference.create(
+                    ScreeningUtils.createDatasetReference(imageDataset), imageParameters);
+        } else
+        {
+            operationLog.error("Cannot find image parameters for dataset: "
+                    + imageDataset.getCode() + ". It will not be displayed");
+            return null;
+        }
     }
 
     private static Map<Long/* sample id */, List<ExternalData>> createPlateToDatasetMap(
-            List<ExternalData> datasets)
+            Collection<ExternalData> datasets)
     {
         Map<Long, List<ExternalData>> map = new HashMap<Long, List<ExternalData>>();
         for (ExternalData dataset : datasets)
@@ -221,7 +361,7 @@ public class PlateMaterialLocationsLoader
     }
 
     private Map<String/* dataset code */, PlateImageParameters> loadImagesReport(
-            List<ExternalData> imageDatasets)
+            Collection<ExternalData> imageDatasets)
     {
         List<PlateImageParameters> imageParameters = new ArrayList<PlateImageParameters>();
         for (ExternalData dataSet : imageDatasets)
@@ -242,28 +382,6 @@ public class PlateMaterialLocationsLoader
         return map;
     }
 
-    private List<ExternalData> loadImageDatasets(List<WellContent> locations)
-    {
-        Set<Long> plateIds = extractPlateIds(locations);
-
-        IDatasetLister datasetLister = businessObjectFactory.createDatasetLister(session);
-        List<ExternalData> imageDatasets = datasetLister.listBySampleIds(plateIds);
-        imageDatasets =
-                ScreeningUtils.filterExternalDataByType(imageDatasets,
-                        ScreeningConstants.IMAGE_DATASET_TYPE);
-        return imageDatasets;
-    }
-
-    private static Set<Long> extractPlateIds(List<WellContent> locations)
-    {
-        Set<Long> ids = new HashSet<Long>();
-        for (WellContent loc : locations)
-        {
-            ids.add(loc.getPlate().getId());
-        }
-        return ids;
-    }
-
     private List<WellContent> loadLocations(PlateMaterialsSearchCriteria materialCriteria)
     {
         DataIterator<ch.systemsx.cisd.openbis.plugin.screening.server.dataaccess.WellContent> locations;
@@ -403,7 +521,7 @@ public class PlateMaterialLocationsLoader
         return wellLocations;
     }
 
-    private List<Material> getMaterials(List<WellContent> wellLocations)
+    private static List<Material> getMaterials(List<WellContent> wellLocations)
     {
         List<Material> materials = new ArrayList<Material>();
         for (WellContent wc : wellLocations)
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/logic/ScreeningApiImpl.java b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/logic/ScreeningApiImpl.java
index 2d7102f23a45c158a52d8a72a9e39f6796f6a976..808cbd9d69fba9c9660b187d5623670822476af0 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/logic/ScreeningApiImpl.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/logic/ScreeningApiImpl.java
@@ -102,9 +102,9 @@ public class ScreeningApiImpl
             List<? extends PlateIdentifier> plates)
     {
         FeatureVectorDatasetLoader datasetRetriever =
-                new FeatureVectorDatasetLoader(session, businessObjectFactory, session
-                        .tryGetHomeGroupCode(), plates);
-        List<FeatureVectorDatasetReference> result = datasetRetriever.getFeatureVectorDatasets();
+                new FeatureVectorDatasetLoader(session, businessObjectFactory,
+                        session.tryGetHomeGroupCode(), plates);
+        List<FeatureVectorDatasetReference> result = datasetRetriever.getFeatureVectorDatasetReferences();
 
         return result;
     }
@@ -112,7 +112,7 @@ public class ScreeningApiImpl
     public List<ImageDatasetReference> listImageDatasets(List<? extends PlateIdentifier> plates)
     {
         return new ImageDatasetLoader(session, businessObjectFactory,
-                session.tryGetHomeGroupCode(), plates).getImageDatasets();
+                session.tryGetHomeGroupCode(), plates).getImageDatasetReferences();
     }
 
     public List<Plate> listPlates()
@@ -154,8 +154,8 @@ public class ScreeningApiImpl
         final String sampleSpaceCode = (sampleSpace != null) ? sampleSpace.getCode() : null;
         final Space experimentSpace = project.getSpace();
         final ExperimentIdentifier experimentId =
-                new ExperimentIdentifier(experiment.getCode(), project.getCode(), experimentSpace
-                        .getCode(), experiment.getPermId());
+                new ExperimentIdentifier(experiment.getCode(), project.getCode(),
+                        experimentSpace.getCode(), experiment.getPermId());
         return new Plate(sample.getCode(), sampleSpaceCode, sample.getPermId(), experimentId);
     }
 
@@ -250,34 +250,41 @@ public class ScreeningApiImpl
             throw UserFailureException.fromTemplate("Material '%s' does not exist",
                     materialIdentifier.getAugmentedCode());
         }
-        final List<WellContent> wellContent;
+        final List<WellContent> wellContents;
         final ExperimentIdentifier fullExperimentIdentifier =
                 getExperimentIdentifierFromDB(experimentIdentifier);
-        wellContent =
+        wellContents =
                 PlateMaterialLocationsLoader.load(session, businessObjectFactory, daoFactory,
                         new TechId(materialOrNull.getId()), fullExperimentIdentifier.getPermId(),
                         false);
         if (findDatasets)
         {
-            final Set<Plate> plates = new HashSet<Plate>(wellContent.size());
-            for (WellContent w : wellContent)
-            {
-                plates.add(asPlate(fullExperimentIdentifier, w));
-            }
+            final Set<Plate> plates = extractPlates(wellContents, fullExperimentIdentifier);
             final FeatureVectorDatasetLoader datasetRetriever =
-                    new FeatureVectorDatasetLoader(session, businessObjectFactory, session
-                            .tryGetHomeGroupCode(), plates);
-            final List<ImageDatasetReference> imageDatasets = datasetRetriever.getImageDatasets();
+                    new FeatureVectorDatasetLoader(session, businessObjectFactory,
+                            session.tryGetHomeGroupCode(), plates);
+            final List<ImageDatasetReference> imageDatasets = datasetRetriever.getImageDatasetReferences();
             final List<FeatureVectorDatasetReference> featureVectorDatasets =
-                    datasetRetriever.getFeatureVectorDatasets();
+                    datasetRetriever.getFeatureVectorDatasetReferences();
 
-            return asPlateWellReferences(fullExperimentIdentifier, wellContent,
+            return asPlateWellReferences(fullExperimentIdentifier, wellContents,
                     createPlateToDatasetsMap(imageDatasets, featureVectorDatasets));
         } else
         {
-            return asPlateWellReferences(fullExperimentIdentifier, wellContent, Collections
-                    .<String, DatasetReferenceHolder> emptyMap());
+            return asPlateWellReferences(fullExperimentIdentifier, wellContents,
+                    Collections.<String, DatasetReferenceHolder> emptyMap());
+        }
+    }
+
+    private static Set<Plate> extractPlates(final List<WellContent> wellContents,
+            final ExperimentIdentifier fullExperimentIdentifier)
+    {
+        final Set<Plate> plates = new HashSet<Plate>(wellContents.size());
+        for (WellContent wellContent : wellContents)
+        {
+            plates.add(asPlate(fullExperimentIdentifier, wellContent));
         }
+        return plates;
     }
 
     public List<PlateWellReferenceWithDatasets> listPlateWells(
@@ -294,8 +301,8 @@ public class ScreeningApiImpl
                     materialIdentifier.getAugmentedCode());
         }
         final List<WellContent> wellContent =
-                PlateMaterialLocationsLoader.loadOnlyMetadata(session, businessObjectFactory, daoFactory,
-                        new TechId(materialOrNull.getId()));
+                PlateMaterialLocationsLoader.loadOnlyMetadata(session, businessObjectFactory,
+                        daoFactory, new TechId(materialOrNull.getId()));
 
         if (findDatasets)
         {
@@ -305,18 +312,18 @@ public class ScreeningApiImpl
                 plates.add(asPlate(w));
             }
             final FeatureVectorDatasetLoader datasetRetriever =
-                    new FeatureVectorDatasetLoader(session, businessObjectFactory, session
-                            .tryGetHomeGroupCode(), plates);
-            final List<ImageDatasetReference> imageDatasets = datasetRetriever.getImageDatasets();
+                    new FeatureVectorDatasetLoader(session, businessObjectFactory,
+                            session.tryGetHomeGroupCode(), plates);
+            final List<ImageDatasetReference> imageDatasets = datasetRetriever.getImageDatasetReferences();
             final List<FeatureVectorDatasetReference> featureVectorDatasets =
-                    datasetRetriever.getFeatureVectorDatasets();
+                    datasetRetriever.getFeatureVectorDatasetReferences();
 
-            return asPlateWellReferences(wellContent, createPlateToDatasetsMap(imageDatasets,
-                    featureVectorDatasets));
+            return asPlateWellReferences(wellContent,
+                    createPlateToDatasetsMap(imageDatasets, featureVectorDatasets));
         } else
         {
-            return asPlateWellReferences(wellContent, Collections
-                    .<String, DatasetReferenceHolder> emptyMap());
+            return asPlateWellReferences(wellContent,
+                    Collections.<String, DatasetReferenceHolder> emptyMap());
         }
     }
 
@@ -330,8 +337,8 @@ public class ScreeningApiImpl
         for (PlateIdentifier plate : plates)
         {
             result.add(toPlateWellMaterialMapping(plate, materialTypeIdentifierOrNull,
-                    getPlateGeometry(query, plate), getPlateMapping(query, plate,
-                            materialTypeIdentifierOrNull)));
+                    getPlateGeometry(query, plate),
+                    getPlateMapping(query, plate, materialTypeIdentifierOrNull)));
         }
         return result;
     }
@@ -416,8 +423,8 @@ public class ScreeningApiImpl
         {
             if (plate.getPermId() == null)
             {
-                return query.getPlateMappingForMaterialType(plate.tryGetSpaceCode(), plate
-                        .getPlateCode(), materialTypeIdentifierOrNull.getMaterialTypeCode());
+                return query.getPlateMappingForMaterialType(plate.tryGetSpaceCode(),
+                        plate.getPlateCode(), materialTypeIdentifierOrNull.getMaterialTypeCode());
             } else
             {
                 return query.getPlateMappingForMaterialType(plate.getPermId(),
@@ -575,8 +582,7 @@ public class ScreeningApiImpl
                 {
                     return (o1.getExperimentPlateIdentifier().getAugmentedCode() + ":" + o1
                             .getWellPosition()).compareTo(o2.getExperimentPlateIdentifier()
-                            .getAugmentedCode()
-                            + ":" + o2.getWellPosition());
+                            .getAugmentedCode() + ":" + o2.getWellPosition());
                 }
             });
         return plateWellReferences;
@@ -599,8 +605,7 @@ public class ScreeningApiImpl
                 {
                     return (o1.getExperimentPlateIdentifier().getAugmentedCode() + ":" + o1
                             .getWellPosition()).compareTo(o2.getExperimentPlateIdentifier()
-                            .getAugmentedCode()
-                            + ":" + o2.getWellPosition());
+                            .getAugmentedCode() + ":" + o2.getWellPosition());
                 }
             });
         return plateWellReferences;
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/api/v1/dto/FeatureVectorDatasetWellReference.java b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/api/v1/dto/FeatureVectorDatasetWellReference.java
index 4237ba47b38de4bc6160c425df529a9c2abc3c58..123aff3186c66ad00c1991903c7f754b0fa14c25 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/api/v1/dto/FeatureVectorDatasetWellReference.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/api/v1/dto/FeatureVectorDatasetWellReference.java
@@ -39,6 +39,14 @@ public class FeatureVectorDatasetWellReference extends FeatureVectorDatasetRefer
         this.wellPosition = wellPosition;
     }
 
+    public FeatureVectorDatasetWellReference(FeatureVectorDatasetReference fvdr,
+            WellPosition wellPosition)
+    {
+        this(fvdr.getDatasetCode(), fvdr.getDatastoreServerUrl(), fvdr.getPlate(), fvdr
+                .getExperimentIdentifier(), fvdr.getPlateGeometry(), fvdr.getRegistrationDate(),
+                fvdr.getParentImageDataset(), wellPosition);
+    }
+
     public FeatureVectorDatasetWellReference(String datasetCode, String datastoreServerUrl,
             PlateIdentifier plate, ExperimentIdentifier experimentIdentifier,
             Geometry plateGeometry, Date registrationDate,
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/basic/dto/WellContent.java b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/basic/dto/WellContent.java
index d5ae969848daf776b3671f8adfb7843d84cdc2e2..adee923c87ab85ee8fe0b944f1cb36a37a83d9b1 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/basic/dto/WellContent.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/basic/dto/WellContent.java
@@ -37,11 +37,20 @@ public class WellContent implements IsSerializable
 
     private ExperimentReference experiment;
 
-    // a pointer to a material which was being searched for inside a well
+    // Material which was being searched for inside a well. Enriched with properties.
     private Material materialContent;
 
-    // contains only images for this well, null if no images have been acquired
-    private DatasetImagesReference imagesOrNull;
+    // dataset which contains images for this well, null if no images have been acquired
+    private DatasetImagesReference imagesDatasetOrNull;
+
+    // dataset which contains feature vectors for this well, null if images have not been analyzed
+    private DatasetReference featureVectorDatasetOrNull;
+
+    // Feature vector values, null if images have not been analyzed.
+    // Some features may not be available, then the value is Float.NaN.
+    // External data structure should be used to figure out which value corresponds to which
+    // feature.
+    private float[] featureVectorValuesOrNull;
 
     // GWT only
     @SuppressWarnings("unused")
@@ -81,7 +90,17 @@ public class WellContent implements IsSerializable
 
     public DatasetImagesReference tryGetImageDataset()
     {
-        return imagesOrNull;
+        return imagesDatasetOrNull;
+    }
+
+    public DatasetReference tryGetFeatureVectorDataset()
+    {
+        return featureVectorDatasetOrNull;
+    }
+
+    public float[] tryGetFeatureVectorValues()
+    {
+        return featureVectorValuesOrNull;
     }
 
     public ExperimentReference getExperiment()
@@ -89,11 +108,23 @@ public class WellContent implements IsSerializable
         return experiment;
     }
 
-    public WellContent cloneWithImages(DatasetImagesReference images)
+    public WellContent cloneWithDatasets(DatasetImagesReference imagesOrNull,
+            DatasetReference featureVectorOrNull)
+    {
+        WellContent clone =
+                new WellContent(locationOrNull, well, plate, experiment, materialContent);
+        clone.imagesDatasetOrNull = imagesOrNull;
+        clone.featureVectorDatasetOrNull = featureVectorOrNull;
+        return clone;
+    }
+
+    public WellContent cloneWithDatasets(float[] values)
     {
         WellContent clone =
                 new WellContent(locationOrNull, well, plate, experiment, materialContent);
-        clone.imagesOrNull = images;
+        clone.imagesDatasetOrNull = imagesDatasetOrNull;
+        clone.featureVectorDatasetOrNull = featureVectorDatasetOrNull;
+        clone.featureVectorValuesOrNull = values;
         return clone;
     }
 
diff --git a/screening/sourceTest/java/ch/systemsx/cisd/openbis/dss/generic/server/FeatureTableBuilderTest.java b/screening/sourceTest/java/ch/systemsx/cisd/openbis/dss/generic/server/FeatureTableBuilderTest.java
index 06aeb1158b05a5a2d69735be0aa9597ffce1a38e..0fa3a7a720dcbbc7996e665e5277ad73ff055c3b 100644
--- a/screening/sourceTest/java/ch/systemsx/cisd/openbis/dss/generic/server/FeatureTableBuilderTest.java
+++ b/screening/sourceTest/java/ch/systemsx/cisd/openbis/dss/generic/server/FeatureTableBuilderTest.java
@@ -27,6 +27,7 @@ import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
+import ch.systemsx.cisd.openbis.dss.generic.server.featurevectors.FeatureTableRow;
 import ch.systemsx.cisd.openbis.dss.generic.shared.IEncapsulatedOpenBISService;
 import ch.systemsx.cisd.openbis.dss.generic.shared.utils.CodeAndLabel;
 import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.SampleIdentifier;
diff --git a/screening/sourceTest/java/ch/systemsx/cisd/openbis/plugin/screening/server/logic/ScreeningApiImplTest.java b/screening/sourceTest/java/ch/systemsx/cisd/openbis/plugin/screening/server/logic/ScreeningApiImplTest.java
index c7d94e81902798a8405450fc5b5ede6c7a667b6d..c3d42709a24ce5489adbef95871c5ac66ba9ed13 100644
--- a/screening/sourceTest/java/ch/systemsx/cisd/openbis/plugin/screening/server/logic/ScreeningApiImplTest.java
+++ b/screening/sourceTest/java/ch/systemsx/cisd/openbis/plugin/screening/server/logic/ScreeningApiImplTest.java
@@ -19,7 +19,11 @@ package ch.systemsx.cisd.openbis.plugin.screening.server.logic;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
+import java.util.Set;
 
 import org.jmock.Expectations;
 import org.testng.annotations.BeforeMethod;
@@ -27,7 +31,6 @@ import org.testng.annotations.Test;
 
 import ch.systemsx.cisd.common.exceptions.UserFailureException;
 import ch.systemsx.cisd.openbis.generic.shared.AbstractServerTestCase;
-import ch.systemsx.cisd.openbis.generic.shared.basic.TechId;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DataSetType;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DataStore;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.EntityProperty;
@@ -81,8 +84,8 @@ public class ScreeningApiImplTest extends AbstractServerTestCase
                     one(screeningBOFactory).createDatasetLister(SESSION);
                     will(returnValue(datasetLister));
                     one(datasetLister).listBySampleIds(with(Arrays.asList((long) 1)));
-                    will(returnValue(Arrays.asList(imageDataSet(p1, "1", 1), imageAnalysisDataSet(
-                            p1, "2", 2))));
+                    will(returnValue(Arrays.asList(imageDataSet(p1, "1", 1),
+                            imageAnalysisDataSet(p1, "2", 2))));
                 }
             });
 
@@ -129,8 +132,8 @@ public class ScreeningApiImplTest extends AbstractServerTestCase
             fail("UserFailureException expected");
         } catch (UserFailureException ex)
         {
-            assertEquals("Sample '/p1' has no property " + ScreeningConstants.PLATE_GEOMETRY, ex
-                    .getMessage());
+            assertEquals("Sample '/p1' has no property " + ScreeningConstants.PLATE_GEOMETRY,
+                    ex.getMessage());
         }
         context.assertIsSatisfied();
     }
@@ -178,12 +181,22 @@ public class ScreeningApiImplTest extends AbstractServerTestCase
 
                     exactly(2).of(screeningBOFactory).createDatasetLister(SESSION);
                     will(returnValue(datasetLister));
-                    one(datasetLister).listBySampleIds(with(Arrays.asList((long) 1)));
-                    will(returnValue(Arrays.asList(imageDataSet(p1, "1", 1), imageAnalysisDataSet(
-                            p1, "2", 2))));
-
-                    one(datasetLister).listByParentTechId(new TechId(1));
-                    will(returnValue(Arrays.asList(imageAnalysisDataSet(null, "3", 3))));
+                    long imageDatasetId = 1;
+                    one(datasetLister).listBySampleIds(with(Arrays.asList(imageDatasetId)));
+                    will(returnValue(Arrays.asList(
+                            imageDataSet(p1, "" + imageDatasetId, imageDatasetId),
+                            imageAnalysisDataSet(p1, "2", 2))));
+
+                    one(datasetLister).listByParentTechIds(Arrays.asList(imageDatasetId));
+                    long analysisDatasetId = 3;
+                    will(returnValue(Arrays.asList(imageAnalysisDataSet(null, ""
+                            + analysisDatasetId, analysisDatasetId))));
+
+                    one(datasetLister).listParentIds(Arrays.asList(analysisDatasetId));
+                    Map<Long, Set<Long>> parentToChildrenMap = new HashMap<Long, Set<Long>>();
+                    parentToChildrenMap.put(analysisDatasetId,
+                            new HashSet<Long>(Arrays.asList(imageDatasetId)));
+                    will(returnValue(parentToChildrenMap));
                 }
             });