From a1969d32a78b843ff91bd1dc4e68cd089cadb05e Mon Sep 17 00:00:00 2001
From: brinn <brinn>
Date: Wed, 28 Jul 2010 09:59:37 +0000
Subject: [PATCH] add: methods
 IScreeningOpenbisServiceFacade.loadFeaturesForFatasetWellReferences() and
 IScreeningOpenbisServiceFacade.loadFeaturesForPlateWells() to obtain the
 feature vectors for a defined set of dataset well references change: minor
 version of IDssServiceRpcScreening to 1

SVN: 17259
---
 .../generic/server/FeatureTableBuilder.java   | 106 ++++++++++++------
 .../dss/generic/server/FeatureTableRow.java   |  38 ++++---
 ...mageAnalysisMergedRowsReportingPlugin.java |  20 ++--
 .../server/DssServiceRpcScreening.java        |  77 ++++++++++---
 .../api/v1/IDssServiceRpcScreening.java       |  45 +++++++-
 .../v1/IScreeningOpenbisServiceFacade.java    |  98 +++++++++++++---
 .../api/v1/ScreeningOpenbisServiceFacade.java |  92 ++++++++++++++-
 .../FeatureVectorDatasetWellReference.java    |  54 +++++++++
 .../v1/dto/FeatureVectorWithDescription.java  |  55 +++++++++
 9 files changed, 488 insertions(+), 97 deletions(-)
 create mode 100644 screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/api/v1/dto/FeatureVectorDatasetWellReference.java
 create mode 100644 screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/api/v1/dto/FeatureVectorWithDescription.java

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 2a73c86937d..a72ecb55db5 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
@@ -30,6 +30,8 @@ import java.util.Map.Entry;
 import ch.systemsx.cisd.common.exceptions.UserFailureException;
 import ch.systemsx.cisd.openbis.dss.generic.shared.IEncapsulatedOpenBISService;
 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;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.dto.PlateFeatureValues;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.imaging.dataaccess.IImagingQueryDAO;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.imaging.dataaccess.ImgContainerDTO;
@@ -50,6 +52,8 @@ public class FeatureTableBuilder
     {
         private ImgDatasetDTO dataSet;
 
+        private FeatureVectorDatasetWellReference reference;
+
         private Map<ImgFeatureDefDTO, List<ImgFeatureValuesDTO>> featureDefToValuesMap;
     }
 
@@ -86,24 +90,35 @@ public class FeatureTableBuilder
         this.featureNames = new HashSet<String>(featureNames);
     }
 
+    /**
+     * Adds feature vectors for specified data set, marking the well position.
+     */
+    public Bundle addFeatureVectorsOfDataSet(FeatureVectorDatasetWellReference reference)
+    {
+        final Bundle bundle = addFeatureVectorsOfDataSet(reference.getDatasetCode());
+        bundle.reference = reference;
+        return bundle;
+    }
+
     /**
      * Adds feature vectors for specified data set.
      */
-    public void addFeatureVectorsOfDataSet(String dataSetCode)
+    public Bundle addFeatureVectorsOfDataSet(String dataSetCode)
     {
-        ImgDatasetDTO dataSet = dao.tryGetDatasetByPermId(dataSetCode);
+        final ImgDatasetDTO dataSet = dao.tryGetDatasetByPermId(dataSetCode);
         if (dataSet == null)
         {
             throw new UserFailureException("Unkown data set " + dataSetCode);
         }
-        Bundle bundle = new Bundle();
-        List<ImgFeatureDefDTO> featureDefinitions = dao.listFeatureDefsByDataSetId(dataSet.getId());
+        final Bundle bundle = new Bundle();
+        final List<ImgFeatureDefDTO> featureDefinitions =
+                dao.listFeatureDefsByDataSetId(dataSet.getId());
         bundle.dataSet = dataSet;
         bundle.featureDefToValuesMap = new HashMap<ImgFeatureDefDTO, List<ImgFeatureValuesDTO>>();
         bundles.add(bundle);
         for (ImgFeatureDefDTO featureDefinition : featureDefinitions)
         {
-            String featureName = featureDefinition.getName();
+            final String featureName = featureDefinition.getName();
             if (featureNames.isEmpty() || featureNames.contains(featureName))
             {
                 if (featureNameToIndexMap.containsKey(featureName) == false)
@@ -121,6 +136,7 @@ public class FeatureTableBuilder
                 bundle.featureDefToValuesMap.put(featureDefinition, featureValueSets);
             }
         }
+        return bundle;
     }
 
     /**
@@ -135,7 +151,7 @@ public class FeatureTableBuilder
     /**
      * Returns all features per well coordinates.
      */
-    public List<FeatureTableRow> getFeatureTableRows()
+    public List<FeatureTableRow> createFeatureTableRows()
     {
         List<FeatureTableRow> rows = new ArrayList<FeatureTableRow>();
         for (Bundle bundle : bundles)
@@ -143,40 +159,64 @@ public class FeatureTableBuilder
             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++)
+            if (bundle.reference == null)
             {
-                for (int colIndex = 1; colIndex <= container.getNumberOfColumns(); colIndex++)
+                for (int rowIndex = 1; rowIndex <= container.getNumberOfRows(); rowIndex++)
                 {
-                    FeatureTableRow row = new FeatureTableRow();
-                    rows.add(row);
-                    row.setDataSetCode(dataSetCode);
-                    row.setPlateIdentifier(identifier);
-                    row.setRowIndex(rowIndex);
-                    row.setColumnIndex(colIndex);
-                    float[] valueArray = new float[featureNameToIndexMap.size()];
-                    Arrays.fill(valueArray, Float.NaN);
-                    for (Entry<ImgFeatureDefDTO, List<ImgFeatureValuesDTO>> entry : bundle.featureDefToValuesMap
-                            .entrySet())
+                    for (int colIndex = 1; colIndex <= container.getNumberOfColumns(); colIndex++)
                     {
-                        ImgFeatureDefDTO featureDefinition = entry.getKey();
-                        List<ImgFeatureValuesDTO> featureValueSets = entry.getValue();
-                        // We take only the first set of feature value sets
-                        ImgFeatureValuesDTO featureValueDTO = featureValueSets.get(0);
-                        PlateFeatureValues featureValues = featureValueDTO.getValues();
-                        if (rowIndex > featureValues.getGeometry().getNumberOfRows()
-                                || colIndex > featureValues.getGeometry().getNumberOfColumns())
-                        {
-                            break;
-                        }
-                        Integer index = featureNameToIndexMap.get(featureDefinition.getName());
-                        assert index != null : "No index for feature "
-                                + featureDefinition.getName();
-                        valueArray[index] = featureValues.getForWellLocation(rowIndex, colIndex);
+                        final FeatureTableRow row =
+                                createFeatureTableRow(bundle.featureDefToValuesMap, dataSetCode,
+                                        identifier, null, new WellPosition(rowIndex,
+                                                colIndex));
+                        rows.add(row);
                     }
-                    row.setFeatureValues(valueArray);
                 }
+            } else
+            {
+                final FeatureTableRow row =
+                        createFeatureTableRow(bundle.featureDefToValuesMap, dataSetCode,
+                                identifier, bundle.reference, bundle.reference.getWellPosition());
+                rows.add(row);
+
             }
         }
         return rows;
     }
+
+    private FeatureTableRow createFeatureTableRow(
+            Map<ImgFeatureDefDTO, List<ImgFeatureValuesDTO>> featureDefToValuesMap,
+            String dataSetCode, SampleIdentifier identifier,
+            FeatureVectorDatasetWellReference reference, WellPosition wellPosition)
+    {
+        FeatureTableRow row = new FeatureTableRow();
+        row.setDataSetCode(dataSetCode);
+        row.setPlateIdentifier(identifier);
+        row.setReference(reference);
+        row.setWellPosition(wellPosition);
+        float[] valueArray = new float[featureNameToIndexMap.size()];
+        Arrays.fill(valueArray, Float.NaN);
+        for (Entry<ImgFeatureDefDTO, List<ImgFeatureValuesDTO>> entry : featureDefToValuesMap
+                .entrySet())
+        {
+            ImgFeatureDefDTO featureDefinition = entry.getKey();
+            List<ImgFeatureValuesDTO> featureValueSets = entry.getValue();
+            // We take only the first set of feature value sets
+            ImgFeatureValuesDTO featureValueDTO = featureValueSets.get(0);
+            PlateFeatureValues featureValues = featureValueDTO.getValues();
+            if (wellPosition.getWellRow() > featureValues.getGeometry().getNumberOfRows()
+                    || wellPosition.getWellColumn() > featureValues.getGeometry()
+                            .getNumberOfColumns())
+            {
+                break;
+            }
+            Integer index = featureNameToIndexMap.get(featureDefinition.getName());
+            assert index != null : "No index for feature " + featureDefinition.getName();
+            valueArray[index] =
+                    featureValues.getForWellLocation(wellPosition.getWellRow(), wellPosition
+                            .getWellColumn());
+        }
+        row.setFeatureValues(valueArray);
+        return row;
+    }
 }
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/FeatureTableRow.java
index dd1a3bfd84e..010240425d8 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/FeatureTableRow.java
@@ -17,10 +17,12 @@
 package ch.systemsx.cisd.openbis.dss.generic.server;
 
 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 coordinates and and array of feature values. Double.NaN is used for unknown
+ * identifier, well position and and array of feature values. Double.NaN is used for unknown
  * feature value in this array.
  * 
  * @author Franz-Josef Elmer
@@ -28,13 +30,13 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.SampleIdentifier;
 public class FeatureTableRow
 {
     private String dataSetCode;
+    
+    private FeatureVectorDatasetWellReference reference;
 
     private SampleIdentifier plateIdentifier;
 
-    private int rowIndex;
-
-    private int columnIndex;
-
+    private WellPosition wellPosition;
+    
     private float[] featureValues;
 
     public final String getDataSetCode()
@@ -47,34 +49,34 @@ public class FeatureTableRow
         this.dataSetCode = dataSetCode;
     }
 
-    public final SampleIdentifier getPlateIdentifier()
+    public FeatureVectorDatasetWellReference getReference()
     {
-        return plateIdentifier;
+        return reference;
     }
 
-    public final void setPlateIdentifier(SampleIdentifier plateIdentifier)
+    public void setReference(FeatureVectorDatasetWellReference reference)
     {
-        this.plateIdentifier = plateIdentifier;
+        this.reference = reference;
     }
 
-    public final int getRowIndex()
+    public final SampleIdentifier getPlateIdentifier()
     {
-        return rowIndex;
+        return plateIdentifier;
     }
 
-    public final void setRowIndex(int rowIndex)
+    public final void setPlateIdentifier(SampleIdentifier plateIdentifier)
     {
-        this.rowIndex = rowIndex;
+        this.plateIdentifier = plateIdentifier;
     }
 
-    public final int getColumnIndex()
+    public void setWellPosition(WellPosition wellPosition)
     {
-        return columnIndex;
+        this.wellPosition = wellPosition;
     }
-
-    public final void setColumnIndex(int columnIndex)
+    
+    public final WellPosition getWellPosition()
     {
-        this.columnIndex = columnIndex;
+        return wellPosition;
     }
 
     public final float[] getFeatureValues()
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 532a4194888..05407fbf021 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
@@ -50,14 +50,17 @@ public class ImageAnalysisMergedRowsReportingPlugin extends AbstractDatastorePlu
         IReportingPluginTask
 {
     private static final long serialVersionUID = 1L;
-    
+
     private static final String DATA_SET_CODE_TITLE = "Data Set Code";
+
     private static final String PLATE_IDENTIFIER_TITLE = "Plate Identifier";
+
     private static final String ROW_TITLE = "Row";
+
     private static final String COLUMN_TITLE = "Column";
-    
+
     private IEncapsulatedOpenBISService service;
-    
+
     private IImagingQueryDAO dao;
 
     public ImageAnalysisMergedRowsReportingPlugin(Properties properties, File storeRoot)
@@ -72,7 +75,7 @@ public class ImageAnalysisMergedRowsReportingPlugin extends AbstractDatastorePlu
         this.service = service;
         this.dao = dao;
     }
-    
+
     public TableModel createReport(List<DatasetDescription> datasets)
     {
         FeatureTableBuilder featureTableBuilder = new FeatureTableBuilder(getDAO(), getService());
@@ -82,7 +85,7 @@ public class ImageAnalysisMergedRowsReportingPlugin extends AbstractDatastorePlu
             featureTableBuilder.addFeatureVectorsOfDataSet(dataSetCode);
         }
         List<String> featureNames = featureTableBuilder.getFeatureNames();
-        List<FeatureTableRow> rows = featureTableBuilder.getFeatureTableRows();
+        List<FeatureTableRow> rows = featureTableBuilder.createFeatureTableRows();
         SimpleTableModelBuilder builder = new SimpleTableModelBuilder(true);
         builder.addHeader(DATA_SET_CODE_TITLE);
         builder.addHeader(PLATE_IDENTIFIER_TITLE);
@@ -97,8 +100,9 @@ public class ImageAnalysisMergedRowsReportingPlugin extends AbstractDatastorePlu
             List<ISerializableComparable> values = new ArrayList<ISerializableComparable>();
             values.add(new StringTableCell(row.getDataSetCode()));
             values.add(new StringTableCell(row.getPlateIdentifier().toString()));
-            values.add(new StringTableCell(PlateUtils.translateRowNumberIntoLetterCode(row.getRowIndex())));
-            values.add(new IntegerTableCell(row.getColumnIndex()));
+            values.add(new StringTableCell(PlateUtils.translateRowNumberIntoLetterCode(row
+                    .getWellPosition().getWellRow())));
+            values.add(new IntegerTableCell(row.getWellPosition().getWellColumn()));
             float[] featureValues = row.getFeatureValues();
             StringTableCell nullValue = new StringTableCell("");
             for (float value : featureValues)
@@ -124,7 +128,7 @@ public class ImageAnalysisMergedRowsReportingPlugin extends AbstractDatastorePlu
         }
         return dao;
     }
-    
+
     private IEncapsulatedOpenBISService getService()
     {
         if (service == null)
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 f80115e6129..f1970227f39 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
@@ -51,6 +51,8 @@ import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ExternalData;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.FeatureVector;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.FeatureVectorDataset;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.FeatureVectorDatasetReference;
+import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.FeatureVectorDatasetWellReference;
+import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.FeatureVectorWithDescription;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.IDatasetIdentifier;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.IFeatureVectorDatasetIdentifier;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.IImageDatasetIdentifier;
@@ -72,6 +74,11 @@ public class DssServiceRpcScreening extends AbstractDssServiceRpc implements
         IDssServiceRpcScreening
 {
 
+    /**
+     * The minor version of this service.
+     */
+    private static final int MINOR_VERSION = 1;
+
     private IImagingQueryDAO dao;
 
     public DssServiceRpcScreening(String storeRootDir)
@@ -203,17 +210,59 @@ public class DssServiceRpcScreening extends AbstractDssServiceRpc implements
                 new FeatureTableBuilder(featureNames, getDAO(), getOpenBISService());
         builder.addFeatureVectorsOfDataSet(dataset.getDatasetCode());
         List<String> existingFeatureNames = builder.getFeatureNames();
-        List<FeatureTableRow> featureTableRows = builder.getFeatureTableRows();
+        List<FeatureTableRow> featureTableRows = builder.createFeatureTableRows();
         List<FeatureVector> featureVectors = new ArrayList<FeatureVector>();
         for (FeatureTableRow featureTableRow : featureTableRows)
         {
-            featureVectors.add(new FeatureVector(new WellPosition(
-                    featureTableRow.getRowIndex(), featureTableRow.getColumnIndex()),
-                    featureTableRow.getFeatureValuesAsDouble()));
+            featureVectors.add(new FeatureVector(featureTableRow.getWellPosition(), featureTableRow
+                    .getFeatureValuesAsDouble()));
         }
         return new FeatureVectorDataset(dataset, existingFeatureNames, featureVectors);
     }
 
+    public List<FeatureVectorWithDescription> loadFeaturesForDatasetWellReferences(
+            String sessionToken, List<FeatureVectorDatasetWellReference> datasetWellReferences,
+            List<String> featureNames)
+    {
+        assertDataSetsAreAccessible(sessionToken, datasetWellReferences);
+        final FeatureTableBuilder builder =
+                createFeatureTableBuilder(datasetWellReferences, featureNames);
+        return createFeatureVectorList(builder);
+    }
+
+    private List<FeatureVectorWithDescription> createFeatureVectorList(
+            final FeatureTableBuilder builder)
+    {
+        final List<String> featureNames = builder.getFeatureNames();
+        final List<FeatureTableRow> featureTableRows = builder.createFeatureTableRows();
+        final List<FeatureVectorWithDescription> result =
+                new ArrayList<FeatureVectorWithDescription>(featureTableRows.size());
+        for (FeatureTableRow featureTableRow : featureTableRows)
+        {
+            result.add(createFeatureVector(featureTableRow, featureNames));
+        }
+        return result;
+    }
+
+    private FeatureVectorWithDescription createFeatureVector(FeatureTableRow featureTableRow,
+            final List<String> featureNames)
+    {
+        return new FeatureVectorWithDescription(featureTableRow.getReference(), featureNames,
+                featureTableRow.getFeatureValuesAsDouble());
+    }
+
+    private FeatureTableBuilder createFeatureTableBuilder(
+            List<FeatureVectorDatasetWellReference> plateWellReferences, List<String> featureNames)
+    {
+        final FeatureTableBuilder builder =
+                new FeatureTableBuilder(featureNames, getDAO(), getOpenBISService());
+        for (FeatureVectorDatasetWellReference datasetWellReference : plateWellReferences)
+        {
+            builder.addFeatureVectorsOfDataSet(datasetWellReference);
+        }
+        return builder;
+    }
+
     public InputStream loadImages(String sessionToken, List<PlateImageReference> imageReferences)
     {
         Map<String, IHCSImageDatasetLoader> imageLoadersMap =
@@ -353,16 +402,6 @@ public class DssServiceRpcScreening extends AbstractDssServiceRpc implements
         return dataset.getDataSetType().getCode().equals(ScreeningConstants.IMAGE_DATASET_TYPE);
     }
 
-    public int getMajorVersion()
-    {
-        return 1;
-    }
-
-    public int getMinorVersion()
-    {
-        return 0;
-    }
-
     private List<ImgFeatureDefDTO> getFeatureDefinitions(IDatasetIdentifier identifier)
     {
         ImgDatasetDTO dataSet = getDAO().tryGetDatasetByPermId(identifier.getDatasetCode());
@@ -393,4 +432,14 @@ public class DssServiceRpcScreening extends AbstractDssServiceRpc implements
         return dao;
     }
 
+    public int getMajorVersion()
+    {
+        return MAJOR_VERSION;
+    }
+
+    public int getMinorVersion()
+    {
+        return MINOR_VERSION;
+    }
+
 }
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/screening/shared/api/v1/IDssServiceRpcScreening.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/screening/shared/api/v1/IDssServiceRpcScreening.java
index 90367ede094..e2d61d81763 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/dss/screening/shared/api/v1/IDssServiceRpcScreening.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/screening/shared/api/v1/IDssServiceRpcScreening.java
@@ -22,6 +22,8 @@ import java.util.List;
 import ch.systemsx.cisd.common.api.IRpcService;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.FeatureVectorDataset;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.FeatureVectorDatasetReference;
+import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.FeatureVectorDatasetWellReference;
+import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.FeatureVectorWithDescription;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.IFeatureVectorDatasetIdentifier;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.IImageDatasetIdentifier;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.ImageDatasetMetadata;
@@ -34,21 +36,52 @@ import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.PlateImageRef
  */
 public interface IDssServiceRpcScreening extends IRpcService
 {
+    /**
+     * The major version of this service.
+     */
+    public static final int MAJOR_VERSION = 1;
+
     /**
      * For a given set of feature vector data sets provide the list of all available features. This
      * is just the name of the feature. If for different data sets different sets of features are
      * available, provide the union of the features of all data sets.
      */
-    List<String> listAvailableFeatureNames(String sessionToken,
+    public List<String> listAvailableFeatureNames(String sessionToken,
             List<? extends IFeatureVectorDatasetIdentifier> featureDatasets);
 
     /**
-     * For a given set of data sets and a set of features (given by their name), provide the feature
-     * matrix. Each column in that matrix is one feature, each row is one well in one data set.
+     * Conceptually, for a given list of data well references (i.e. specified wells on specified
+     * feature vector data sets) and a set of features (given by their name) provide the feature
+     * matrix. In this matrix, each column is one feature, each row is one well in one data set.
+     * <p>
+     * Physically, the result is delivered as a list of feature vector datasets. Each entry in this
+     * list corresponds to one well in one dataset.
+     * 
+     * @return The list of {@link FeatureVectorDataset}s, each element corresponds to one of the
+     *         <var>featureDatasets</var>.
      */
-    List<FeatureVectorDataset> loadFeatures(String sessionToken,
+    public List<FeatureVectorDataset> loadFeatures(String sessionToken,
             List<FeatureVectorDatasetReference> featureDatasets, List<String> featureNames);
 
+    /**
+     * Conceptually, for a given list of dataset well references (i.e. specified wells on specified
+     * feature vector data sets) and a set of features (given by their name) provide the feature
+     * matrix. In this matrix, each column is one feature, each row is one well in one data set.
+     * <p>
+     * Physically, the result is delivered as a list of feature vectors. Each entry in this list
+     * corresponds to one well in one dataset.
+     * 
+     * @return The list of {@link FeatureVectorWithDescription}s, each element corresponds to one of
+     *         the <var>datasetWellReferences</var>. <b>Note that the order of the returned ist is
+     *         <i>not</i> guaranteed to be the same as the order of the list
+     *         <var>datasetWellReferences</var>. Use
+     *         {@link FeatureVectorWithDescription#getDatasetWellReference()} to find the
+     *         corresponding dataset / well.</b>
+     */
+    public List<FeatureVectorWithDescription> loadFeaturesForDatasetWellReferences(
+            String sessionToken, List<FeatureVectorDatasetWellReference> datasetWellReferences,
+            List<String> featureNames);
+
     /**
      * Provide images for a given list of image references (given by data set code, well position,
      * channel and tile). The result is encoded into one stream, which consist of multiple blocks in
@@ -56,13 +89,13 @@ public interface IDssServiceRpcScreening extends IRpcService
      * encoded as one long number. The number of blocks is equal to the number of specified
      * references and the order of blocks corresponds to the order of image references.
      */
-    InputStream loadImages(String sessionToken, List<PlateImageReference> imageReferences);
+    public InputStream loadImages(String sessionToken, List<PlateImageReference> imageReferences);
 
     /**
      * For a given set of image data sets, provide all image channels that have been acquired and
      * the available (natural) image size(s).
      */
-    List<ImageDatasetMetadata> listImageMetadata(String sessionToken,
+    public List<ImageDatasetMetadata> listImageMetadata(String sessionToken,
             List<? extends IImageDatasetIdentifier> imageDatasets);
 
 }
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/api/v1/IScreeningOpenbisServiceFacade.java b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/api/v1/IScreeningOpenbisServiceFacade.java
index 5c9d28df3d3..40201e86a1b 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/api/v1/IScreeningOpenbisServiceFacade.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/api/v1/IScreeningOpenbisServiceFacade.java
@@ -23,6 +23,8 @@ import ch.systemsx.cisd.openbis.plugin.screening.client.api.v1.ScreeningOpenbisS
 import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.ExperimentIdentifier;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.FeatureVectorDataset;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.FeatureVectorDatasetReference;
+import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.FeatureVectorDatasetWellReference;
+import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.FeatureVectorWithDescription;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.IDatasetIdentifier;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.IFeatureVectorDatasetIdentifier;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.IImageDatasetIdentifier;
@@ -45,66 +47,128 @@ public interface IScreeningOpenbisServiceFacade
     /**
      * Return the session token for this authenticated user.
      */
-    public abstract String getSessionToken();
+    public String getSessionToken();
 
     /** Closes connection with the server. After calling this method this facade cannot be used. */
-    public abstract void logout();
+    public void logout();
 
     /**
      * Return the list of all visible plates assigned to any experiment, along with their
      * hierarchical context (space, project, experiment).
      */
-    public abstract List<Plate> listPlates();
+    public List<Plate> listPlates();
 
     /**
-     * Return the list of all visible experiments, along with their
-     * hierarchical context (space, project).
+     * Return the list of all visible experiments, along with their hierarchical context (space,
+     * project).
      */
-    public abstract List<ExperimentIdentifier> listExperiments();
+    public List<ExperimentIdentifier> listExperiments();
 
     /**
      * For a given set of plates provides the list of all connected data sets containing feature
      * vectors.
      */
-    public abstract List<FeatureVectorDatasetReference> listFeatureVectorDatasets(
+    public List<FeatureVectorDatasetReference> listFeatureVectorDatasets(
             List<? extends PlateIdentifier> plates);
 
     /**
      * For a given set of plates provides the list of all connected data sets containing images.
      */
-    public abstract List<ImageDatasetReference> listImageDatasets(
-            List<? extends PlateIdentifier> plates);
+    public List<ImageDatasetReference> listImageDatasets(List<? extends PlateIdentifier> plates);
 
     /**
      * For the given <var>experimentIdentifier</var> find all plate locations that are connected to
      * the specified <var>materialIdentifier</var>. If <code>findDatasets == true</code>, find also
      * the connected image and image analysis data sets for the relevant plates.
+     * <p>
+     * For how to get the feature vectors, see
+     * {@link #convertToFeatureVectorDatasetWellIdentifier(List)}.
      */
     public List<PlateWellReferenceWithDatasets> listPlateWells(
             ExperimentIdentifier experimentIdentifer, MaterialIdentifier materialIdentifier,
             boolean findDatasets);
-    
+
+    /**
+     * Converts the given list of {@link PlateWellReferenceWithDatasets} into a list of
+     * {@link FeatureVectorDatasetWellReference}.
+     * 
+     * @see #listPlateWells(ExperimentIdentifier, MaterialIdentifier, boolean)
+     * @see #loadFeaturesForDatasetWellReferences(List, List)
+     */
+    public List<FeatureVectorDatasetWellReference> convertToFeatureVectorDatasetWellIdentifier(
+            List<PlateWellReferenceWithDatasets> plateWellReferenceWithDataSets);
+
     /**
      * Converts a given list of dataset codes to dataset identifiers which can be used in other API
      * calls.
      */
-    public abstract List<IDatasetIdentifier> getDatasetIdentifiers(List<String> datasetCodes);
+    public List<IDatasetIdentifier> getDatasetIdentifiers(List<String> datasetCodes);
 
     /**
      * For a given set of feature vector data sets provides the list of all available features. This
      * is just the name of the feature. If for different data sets different sets of features are
      * available, provides the union of the feature names of all data sets.
      */
-    public abstract List<String> listAvailableFeatureNames(
+    public List<String> listAvailableFeatureNames(
             List<? extends IFeatureVectorDatasetIdentifier> featureDatasets);
 
     /**
-     * For a given set of data sets and a set of features (given by their name), provide all the
-     * feature vectors.
+     * Conceptually, for a given list of dataset well references (i.e. specified wells on specified
+     * feature vector data sets) and a set of features (given by their name) provide the feature
+     * matrix. In this matrix, each column is one feature, each row is one well in one data set.
+     * <p>
+     * Physically, the result is delivered as a list of feature vector datasets. Each entry in this
+     * list corresponds to one well in one dataset.
+     * 
+     * @return The list of {@link FeatureVectorDataset}s, each element corresponds to one of the
+     *         <var>featureDatasets</var>.
      */
-    public abstract List<FeatureVectorDataset> loadFeatures(
+    public List<FeatureVectorDataset> loadFeatures(
             List<FeatureVectorDatasetReference> featureDatasets, List<String> featureNames);
 
+    /**
+     * Conceptually, for a given list of dataset well references (i.e. specified wells on specified
+     * feature vector data sets) and a set of features (given by their name) provide the feature
+     * matrix. In this matrix, each column is one feature, each row is one well in one data set.
+     * <p>
+     * Physically, the result is delivered as a list of feature vectors. Each entry in this list
+     * corresponds to one well in one dataset.
+     * 
+     * @param datasetWellReferences The references for datasets / wells to get the feature vectors
+     *            for.
+     * @param featureNamesOrNull The names of the features to build the feature vectors from, or
+     *            <code>null</code>, if all available features should be included. Note that for an
+     *            empty list as well all features will be included.
+     * @return The list of {@link FeatureVectorWithDescription}s, each element corresponds to one of
+     *         the <var>datasetWellReferences</var>. <b>Note that the order of the returned list is
+     *         <i>not</i> guaranteed to be the same as the order of the list
+     *         <var>datasetWellReferences</var>. Use
+     *         {@link FeatureVectorWithDescription#getDatasetWellReference()} to find the
+     *         corresponding dataset / well.</b>
+     */
+    public List<FeatureVectorWithDescription> loadFeaturesForDatasetWellReferences(
+            List<FeatureVectorDatasetWellReference> datasetWellReferences,
+            List<String> featureNamesOrNull);
+
+    /**
+     * For the given <var>experimentIdentifier</var> find all plate locations that are connected to
+     * the specified <var>materialIdentifier</var> and load the feature vectors for the given
+     * <var>featureNamesOrNull</var> if not <code>null</code>, or all available features otherwise.
+     * 
+     * @param experimentIdentifer The identifier of the experiment to get the feature vectors for
+     * @param materialIdentifier The identifier of the material contained in the wells to get the
+     *            feature vectors for.
+     * @param featureNamesOrNull The names of the features to build the feature vectors from, or
+     *            <code>null</code>, if all available features should be included. Note that for an
+     *            empty list as well all features will be included.
+     * @return The list of {@link FeatureVectorWithDescription}s found in the given
+     *         <var>experimentIdentifer</var> and connected with the given
+     *         <var>materialIdentifier</var>.
+     */
+    public List<FeatureVectorWithDescription> loadFeaturesForPlateWells(
+            ExperimentIdentifier experimentIdentifer, MaterialIdentifier materialIdentifier,
+            List<String> featureNamesOrNull);
+
     /**
      * Saves images for a given list of image references (given by data set code, well position,
      * channel and tile) in the provided output streams. Output streams will not be closed
@@ -114,14 +178,14 @@ public interface IScreeningOpenbisServiceFacade
      * @throws IOException when reading images from the server or writing them to the output streams
      *             fails
      */
-    public abstract void loadImages(List<PlateImageReference> imageReferences,
+    public void loadImages(List<PlateImageReference> imageReferences,
             IImageOutputStreamProvider outputStreamProvider) throws IOException;
 
     /**
      * For a given set of image data sets, provide all image channels that have been acquired and
      * the available (natural) image size(s).
      */
-    public abstract List<ImageDatasetMetadata> listImageMetadata(
+    public List<ImageDatasetMetadata> listImageMetadata(
             List<? extends IImageDatasetIdentifier> imageDatasets);
 
 }
\ No newline at end of file
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/api/v1/ScreeningOpenbisServiceFacade.java b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/api/v1/ScreeningOpenbisServiceFacade.java
index 5d1f7593dd9..628bb60df76 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/api/v1/ScreeningOpenbisServiceFacade.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/api/v1/ScreeningOpenbisServiceFacade.java
@@ -19,6 +19,8 @@ import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.IScreeningApiServ
 import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.ExperimentIdentifier;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.FeatureVectorDataset;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.FeatureVectorDatasetReference;
+import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.FeatureVectorDatasetWellReference;
+import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.FeatureVectorWithDescription;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.IDatasetIdentifier;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.IFeatureVectorDatasetIdentifier;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.IImageDatasetIdentifier;
@@ -29,6 +31,7 @@ import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.Plate;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.PlateIdentifier;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.PlateImageReference;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.PlateWellReferenceWithDatasets;
+import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.WellPosition;
 
 /**
  * A client side facade of openBIS and Datastore Server API.
@@ -61,6 +64,8 @@ public class ScreeningOpenbisServiceFacade implements IScreeningOpenbisServiceFa
 
     private final DataStoreMultiplexer<FeatureVectorDatasetReference> featureVectorDataSetReferenceMultiplexer;
 
+    private final DataStoreMultiplexer<FeatureVectorDatasetWellReference> featureVectorDataSetWellReferenceMultiplexer;
+
     private final DataStoreMultiplexer<IImageDatasetIdentifier> metaDataMultiplexer;
 
     private final String sessionToken;
@@ -130,6 +135,8 @@ public class ScreeningOpenbisServiceFacade implements IScreeningOpenbisServiceFa
                 new DataStoreMultiplexer<IFeatureVectorDatasetIdentifier>(dssServiceCache);
         featureVectorDataSetReferenceMultiplexer =
                 new DataStoreMultiplexer<FeatureVectorDatasetReference>(dssServiceCache);
+        featureVectorDataSetWellReferenceMultiplexer =
+                new DataStoreMultiplexer<FeatureVectorDatasetWellReference>(dssServiceCache);
     }
 
     /**
@@ -250,6 +257,89 @@ public class ScreeningOpenbisServiceFacade implements IScreeningOpenbisServiceFa
         return result;
     }
 
+    public List<FeatureVectorDatasetWellReference> convertToFeatureVectorDatasetWellIdentifier(
+            List<PlateWellReferenceWithDatasets> plateWellReferenceWithDataSets)
+    {
+        final List<FeatureVectorDatasetWellReference> result =
+                new ArrayList<FeatureVectorDatasetWellReference>(plateWellReferenceWithDataSets
+                        .size());
+        for (PlateWellReferenceWithDatasets plateWellRef : plateWellReferenceWithDataSets)
+        {
+            for (FeatureVectorDatasetReference fvdr : plateWellRef
+                    .getFeatureVectorDatasetReferences())
+            {
+                result.add(createFVDatasetReference(fvdr, plateWellRef.getWellPosition()));
+            }
+        }
+        return result;
+    }
+
+    private FeatureVectorDatasetWellReference createFVDatasetReference(
+            FeatureVectorDatasetReference fvdr, WellPosition wellPosition)
+    {
+        return new FeatureVectorDatasetWellReference(fvdr.getDatasetCode(), fvdr
+                .getDatastoreServerUrl(), fvdr.getPlate(), fvdr.getPlateGeometry(), fvdr
+                .getRegistrationDate(), fvdr.getParentImageDataset(), wellPosition);
+    }
+
+    public List<FeatureVectorWithDescription> loadFeaturesForDatasetWellReferences(
+            final List<FeatureVectorDatasetWellReference> datasetWellReferences,
+            final List<String> featureNamesOrNull)
+    {
+        final List<String> featureNames =
+                (isEmpty(featureNamesOrNull)) ? listAvailableFeatureNames(datasetWellReferences)
+                        : featureNamesOrNull;
+
+        final List<FeatureVectorWithDescription> result =
+                new ArrayList<FeatureVectorWithDescription>();
+        featureVectorDataSetWellReferenceMultiplexer.process(datasetWellReferences,
+                new IReferenceHandler<FeatureVectorDatasetWellReference>()
+                    {
+                        public void handle(IDssServiceRpcScreening dssService,
+                                List<FeatureVectorDatasetWellReference> references)
+                        {
+                            result.addAll(dssService.loadFeaturesForDatasetWellReferences(
+                                    sessionToken, references, featureNames));
+                        }
+                    });
+        return result;
+    }
+
+    private boolean isEmpty(final List<String> featureNamesOrNull)
+    {
+        return featureNamesOrNull == null || featureNamesOrNull.isEmpty();
+    }
+
+    public List<FeatureVectorWithDescription> loadFeaturesForPlateWells(
+            ExperimentIdentifier experimentIdentifer, MaterialIdentifier materialIdentifier,
+            List<String> featureNamesOrNull)
+    {
+        final List<PlateWellReferenceWithDatasets> plateWellRefs =
+                listPlateWells(experimentIdentifer, materialIdentifier, true);
+        final List<String> featureNames =
+                (isEmpty(featureNamesOrNull)) ? listAvailableFeatureNamesForPlateWells(plateWellRefs)
+                        : featureNamesOrNull;
+        final List<FeatureVectorDatasetWellReference> datasetWellReferences =
+                convertToFeatureVectorDatasetWellIdentifier(plateWellRefs);
+        return loadFeaturesForDatasetWellReferences(datasetWellReferences, featureNames);
+    }
+
+    private List<String> listAvailableFeatureNamesForPlateWells(
+            final List<PlateWellReferenceWithDatasets> plateWellRefs)
+    {
+        final List<String> featureNames;
+        final List<FeatureVectorDatasetReference> featureVectorDatasetReferences =
+                new ArrayList<FeatureVectorDatasetReference>(plateWellRefs.size());
+        for (PlateWellReferenceWithDatasets plateWellRef : plateWellRefs)
+        {
+            featureVectorDatasetReferences.addAll(plateWellRef.getFeatureVectorDatasetReferences());
+        }
+        final List<String> availableFeatureNames =
+                listAvailableFeatureNames(featureVectorDatasetReferences);
+        featureNames = availableFeatureNames;
+        return featureNames;
+    }
+
     /**
      * An interface to provide mapping between image references and output streams where the images
      * should be saved.
@@ -362,7 +452,7 @@ public class ScreeningOpenbisServiceFacade implements IScreeningOpenbisServiceFa
 
     }
 
-    private static interface IReferenceHandler<R extends IDatasetIdentifier>
+    private interface IReferenceHandler<R extends IDatasetIdentifier>
     {
         public void handle(IDssServiceRpcScreening dssService, List<R> references);
     }
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
new file mode 100644
index 00000000000..c5673dc0570
--- /dev/null
+++ b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/api/v1/dto/FeatureVectorDatasetWellReference.java
@@ -0,0 +1,54 @@
+/*
+ * 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.plugin.screening.shared.api.v1.dto;
+
+import java.util.Date;
+
+/**
+ * Reference to one well in a feature vector dataset.
+ * 
+ * @author Bernd Rinn
+ */
+public class FeatureVectorDatasetWellReference extends FeatureVectorDatasetReference
+{
+    private static final long serialVersionUID = 1L;
+
+    private final WellPosition wellPosition;
+
+    public FeatureVectorDatasetWellReference(String datasetCode, String datastoreServerUrl,
+            PlateIdentifier plate, Geometry plateGeometry, Date registrationDate,
+            IImageDatasetIdentifier imageDatasetIdentifier, WellPosition wellPosition)
+    {
+        super(datasetCode, datastoreServerUrl, plate, plateGeometry, registrationDate,
+                imageDatasetIdentifier);
+        this.wellPosition = wellPosition;
+    }
+
+    /**
+     * Returns the well position of this reference.
+     */
+    public WellPosition getWellPosition()
+    {
+        return wellPosition;
+    }
+
+    @Override
+    public String toString()
+    {
+        return super.toString() + " " + wellPosition.toString();
+    }
+}
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/api/v1/dto/FeatureVectorWithDescription.java b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/api/v1/dto/FeatureVectorWithDescription.java
new file mode 100644
index 00000000000..74a099f456e
--- /dev/null
+++ b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/api/v1/dto/FeatureVectorWithDescription.java
@@ -0,0 +1,55 @@
+package ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * Feature vectors of one well in one feature vector dataset.
+ * 
+ * @author Bernd Rinn
+ */
+public class FeatureVectorWithDescription extends FeatureVector implements Serializable
+{
+    private static final long serialVersionUID = 1L;
+
+    private final FeatureVectorDatasetWellReference datasetWellReference;
+
+    private final List<String> featureNames;
+
+    public FeatureVectorWithDescription(FeatureVectorDatasetWellReference dataset,
+            List<String> featureNames, double[] values)
+    {
+        super(dataset.getWellPosition(), values);
+        this.datasetWellReference = dataset;
+        this.featureNames = featureNames;
+    }
+
+    /**
+     * Identifier of the dataset and well of this feature vector.
+     */
+    public FeatureVectorDatasetWellReference getDatasetWellReference()
+    {
+        return datasetWellReference;
+    }
+
+    /**
+     * Names (and implicitly order) of the features present in each feature vector.
+     */
+    public List<String> getFeatureNames()
+    {
+        return featureNames;
+    }
+
+    @Override
+    public String toString()
+    {
+        StringBuilder sb = new StringBuilder();
+        sb.append("datasetCode: " + datasetWellReference.getDatasetCode());
+        sb.append(", storeUrl: " + datasetWellReference.getDatastoreServerUrl());
+        sb.append("\n\tfeatures: " + featureNames);
+        sb.append("\n");
+        sb.append(super.toString());
+        return sb.toString();
+    }
+
+}
\ No newline at end of file
-- 
GitLab