From 8f08c28e669671c1421b124e0369bf6984f9daed Mon Sep 17 00:00:00 2001
From: kaloyane <kaloyane>
Date: Tue, 13 Sep 2011 11:56:25 +0000
Subject: [PATCH] [LMS-2500] allow to access all well properties via the public
 screening API

SVN: 22911
---
 .../generic/shared/util/EntityHelper.java     |  15 +++
 .../api/v1/ScreeningOpenbisServiceFacade.java |   7 ++
 .../screening/server/ScreeningServer.java     |   9 +-
 .../server/ScreeningServerLogger.java         |   9 ++
 .../server/logic/PlateContentLoader.java      |  25 ++++
 .../server/logic/ScreeningApiImpl.java        | 105 ++++++++++++++++-
 .../shared/api/v1/IScreeningApiServer.java    |  16 +++
 .../screening/shared/api/v1/dto/Material.java |  50 ++++++++
 .../shared/api/v1/dto/PlateWithWells.java     |  90 +++++++++++++++
 .../screening/shared/api/v1/dto/Well.java     | 109 ++++++++++++++++++
 10 files changed, 432 insertions(+), 3 deletions(-)
 create mode 100644 screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/api/v1/dto/Material.java
 create mode 100644 screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/api/v1/dto/PlateWithWells.java
 create mode 100644 screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/api/v1/dto/Well.java

diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/util/EntityHelper.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/util/EntityHelper.java
index 5ed0db8bc64..c534b618bc4 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/util/EntityHelper.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/util/EntityHelper.java
@@ -18,6 +18,7 @@ package ch.systemsx.cisd.openbis.generic.shared.util;
 
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
@@ -199,6 +200,20 @@ public class EntityHelper
         return property;
     }
 
+    public static Map<String, String> convertToStringMap(List<IEntityProperty> properties)
+    {
+        Map<String, String> map = new HashMap<String, String>();
+        if (properties != null)
+        {
+            for (IEntityProperty prop : properties)
+            {
+                map.put(prop.getPropertyType().getCode(), prop.tryGetAsString());
+            }
+        }
+
+        return map;
+    }
+
     private static IEntityProperty createNewProperty(String propertyCode)
     {
         IEntityProperty property = new EntityProperty();
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 490ed0fcef0..6538d7a8f88 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
@@ -71,6 +71,7 @@ import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.PlateIdentifi
 import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.PlateImageReference;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.PlateWellMaterialMapping;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.PlateWellReferenceWithDatasets;
+import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.PlateWithWells;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.WellIdentifier;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.WellPosition;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.ScreeningConstants;
@@ -276,6 +277,12 @@ public class ScreeningOpenbisServiceFacade implements IScreeningOpenbisServiceFa
         return openbisScreeningServer.listPlates(sessionToken);
     }
 
+    public List<PlateWithWells> getPlates(List<? extends PlateIdentifier> plateIdentifiers)
+    {
+        checkASMinimalMinorVersion("getPlates", List.class);
+        return openbisScreeningServer.getPlates(sessionToken, plateIdentifiers);
+    }
+
     /**
      * Return the list of all plates for the given <var>experiment</var>.
      */
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 24596df1974..0c77afe61dd 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
@@ -86,6 +86,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.PlateWellMaterialMapping;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.PlateWellReferenceWithDatasets;
+import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.PlateWithWells;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.WellIdentifier;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.AnalysisProcedures;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.DatasetReference;
@@ -124,7 +125,7 @@ public final class ScreeningServer extends AbstractServer<IScreeningServer> impl
     /**
      * The minor version of this service.
      */
-    public static final int MINOR_VERSION = 7;
+    public static final int MINOR_VERSION = 8;
 
     @Resource(name = ResourceNames.SCREENING_BUSINESS_OBJECT_FACTORY)
     private IScreeningBusinessObjectFactory businessObjectFactory;
@@ -516,4 +517,10 @@ public final class ScreeningServer extends AbstractServer<IScreeningServer> impl
         return MINOR_VERSION;
     }
 
+    public List<PlateWithWells> getPlates(String sessionToken,
+            List<? extends PlateIdentifier> plateIdentifiers) throws IllegalArgumentException
+    {
+        return createScreeningApiImpl(sessionToken).getPlates(plateIdentifiers);
+    }
+
 }
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/ScreeningServerLogger.java b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/ScreeningServerLogger.java
index fccb762e2f4..489ef8d08fc 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/ScreeningServerLogger.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/ScreeningServerLogger.java
@@ -49,6 +49,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.PlateWellMaterialMapping;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.PlateWellReferenceWithDatasets;
+import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.PlateWithWells;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.WellIdentifier;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.AnalysisProcedures;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.DatasetReference;
@@ -388,4 +389,12 @@ final class ScreeningServerLogger extends AbstractServerLogger implements IScree
                 experimentSearchCriteria);
         return null;
     }
+
+    public List<PlateWithWells> getPlates(
+            String sessionToken, List<? extends PlateIdentifier> plates)
+            throws IllegalArgumentException
+    {
+        logAccess(sessionToken, "getPlates", "plates(%s)", plates);
+        return null;
+    }
 }
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/logic/PlateContentLoader.java b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/logic/PlateContentLoader.java
index 96c3067381e..e0794eeb092 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/logic/PlateContentLoader.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/logic/PlateContentLoader.java
@@ -115,6 +115,13 @@ public class PlateContentLoader
                 .getImageDatasetInfosForSample(sampleId, wellLocationOrNull);
     }
 
+    public static List<PlateMetadata> loadPlateMetadatas(Session session,
+            IScreeningBusinessObjectFactory businessObjectFactory, List<TechId> plateIds)
+    {
+        return new PlateContentLoader(session, businessObjectFactory).getPlateMetadatas(plateIds);
+
+    }
+
     private final Session session;
 
     private final IScreeningBusinessObjectFactory businessObjectFactory;
@@ -423,4 +430,22 @@ public class PlateContentLoader
         List<DatasetReference> unknownDatasetReferences = extractUnknownDatasets(datasets);
         return new ImageSampleContent(logicalImages, unknownDatasetReferences);
     }
+
+    private List<PlateMetadata> getPlateMetadatas(List<TechId> plateIds)
+    {
+        ArrayList<PlateMetadata> result = new ArrayList<PlateMetadata>();
+        for (TechId plateId : plateIds)
+        {
+            Sample plate = loadPlate(plateId);
+            List<WellMetadata> wells = loadWells(plateId);
+            Geometry plateGeometry = PlateDimensionParser.getPlateGeometry(plate.getProperties());
+            int rows = plateGeometry.getNumberOfRows();
+            int cols = plateGeometry.getNumberOfColumns();
+            PlateMetadata plateMetadata = new PlateMetadata(plate, wells, rows, cols);
+            result.add(plateMetadata);
+        }
+
+        return result;
+    }
+
 }
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 97a4f30db6a..1421dd59c7c 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
@@ -43,6 +43,7 @@ import ch.systemsx.cisd.openbis.generic.server.dataaccess.IDAOFactory;
 import ch.systemsx.cisd.openbis.generic.server.dataaccess.ISampleTypeDAO;
 import ch.systemsx.cisd.openbis.generic.shared.basic.TechId;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Experiment;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.IEntityProperty;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ListOrSearchSampleCriteria;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ListSampleCriteria;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Project;
@@ -63,6 +64,7 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.SampleOwnerIdentif
 import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.SpaceIdentifier;
 import ch.systemsx.cisd.openbis.generic.shared.translator.SampleTranslator;
 import ch.systemsx.cisd.openbis.generic.shared.translator.SampleTypeTranslator;
+import ch.systemsx.cisd.openbis.generic.shared.util.EntityHelper;
 import ch.systemsx.cisd.openbis.generic.shared.util.SpaceCodeHelper;
 import ch.systemsx.cisd.openbis.plugin.screening.server.IScreeningBusinessObjectFactory;
 import ch.systemsx.cisd.openbis.plugin.screening.server.dataaccess.IScreeningQuery;
@@ -74,17 +76,22 @@ import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.FeatureVector
 import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.Geometry;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.IDatasetIdentifier;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.ImageDatasetReference;
+import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.Material;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.MaterialIdentifier;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.MaterialTypeIdentifier;
 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.PlateWellMaterialMapping;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.PlateWellReferenceWithDatasets;
+import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.PlateWithWells;
+import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.Well;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.WellIdentifier;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.WellPosition;
+import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.PlateMetadata;
 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.WellMetadata;
 
 /**
  * Contains implementations of the screening public API calls.
@@ -524,8 +531,6 @@ public class ScreeningApiImpl
                 ScreeningUtils.tryCreateLocationFromMatrixCoordinate(sample.getSubCode());
         if (location == null)
         {
-            new IllegalArgumentException(String.format(
-                    "Failed to retrieve location of sample '%s'.", sample.getCode()));
             return null;
         }
         WellPosition position = new WellPosition(location.getRow(), location.getColumn());
@@ -863,4 +868,100 @@ public class ScreeningApiImpl
         return plateWellReferences;
     }
 
+    public List<PlateWithWells> getPlates(List<? extends PlateIdentifier> plateIdentifiers)
+    {
+        List<TechId> techIds = new ArrayList<TechId>();
+        for (PlateIdentifier identifier : plateIdentifiers)
+        {
+            TechId techId = getSampleTechId(identifier);
+            if (techId != null)
+            {
+                techIds.add(techId);
+            }
+        }
+
+        List<PlateMetadata> plateMetadatas =
+                PlateContentLoader.loadPlateMetadatas(session, businessObjectFactory, techIds);
+
+        List<PlateWithWells> result = new ArrayList<PlateWithWells>();
+        Map<Long, Material> materialsCache = new HashMap<Long, Material>();
+        for (PlateMetadata plateMetaData : plateMetadatas)
+        {
+            result.add(asPlateWithWells(plateMetaData, materialsCache));
+        }
+        return result;
+    }
+
+    private PlateWithWells asPlateWithWells(PlateMetadata plateMetadata,
+            Map<Long, Material> materialsCache)
+    {
+        Sample plate = plateMetadata.getPlate();
+        String spaceCodeOrNull = plate.getSpace() == null ? null : plate.getSpace().getCode();
+        PlateIdentifier plateIdentifier =
+                new PlateIdentifier(plate.getCode(), spaceCodeOrNull, plate.getPermId());
+        List<Well> wells = new ArrayList<Well>();
+        if (plateMetadata.getWells() != null)
+        {
+            for (WellMetadata wellMetadata : plateMetadata.getWells())
+            {
+                Well well = asWell(plateIdentifier, wellMetadata, materialsCache);
+                wells.add(well);
+            }
+        }
+        Geometry geometry =
+                Geometry.createFromRowColDimensions(plateMetadata.getRowsNum(),
+                        plateMetadata.getColsNum());
+        Map<String, String> properties = EntityHelper.convertToStringMap(plate.getProperties());
+
+        return new PlateWithWells(plateIdentifier, geometry, properties, wells);
+    }
+
+    private Well asWell(PlateIdentifier plateIdentifier, WellMetadata wellMetadata,
+            Map<Long, Material> materialsCache)
+    {
+        Sample well = wellMetadata.getWellSample();
+        WellLocation location = wellMetadata.tryGetLocation();
+        WellPosition wellPosition = new WellPosition(location.getRow(), location.getColumn());
+        Map<String, String> properties = EntityHelper.convertToStringMap(well.getProperties());
+        Map<String, Material> materialProperties =
+                convertMaterialProperties(well.getProperties(), materialsCache);
+        return new Well(plateIdentifier, well.getCode(), well.getPermId(), wellPosition,
+                properties, materialProperties);
+    }
+
+    private Map<String, Material> convertMaterialProperties(List<IEntityProperty> properties,
+            Map<Long, Material> materialsCache)
+    {
+        HashMap<String, Material> result = new HashMap<String, Material>();
+        if (properties != null)
+        {
+            for (IEntityProperty property : properties)
+            {
+                ch.systemsx.cisd.openbis.generic.shared.basic.dto.Material material =
+                        property.getMaterial();
+                if (material != null)
+                {
+                    Material apiMaterial = materialsCache.get(material.getId());
+                    if (apiMaterial == null)
+                    {
+                        apiMaterial = asApiMaterial(material);
+                        materialsCache.put(material.getId(), apiMaterial);
+                    }
+                    String propCode = property.getPropertyType().getCode();
+                    result.put(propCode, apiMaterial);
+                }
+            }
+        }
+        return result;
+    }
+
+    private Material asApiMaterial(
+            ch.systemsx.cisd.openbis.generic.shared.basic.dto.Material materialDto)
+    {
+        MaterialTypeIdentifier typeIdentifier = new MaterialTypeIdentifier(materialDto.getMaterialType().getCode());
+        Map<String, String> properties =
+                EntityHelper.convertToStringMap(materialDto.getProperties());
+        return new Material(typeIdentifier, materialDto.getCode(), properties);
+    }
+
 }
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/api/v1/IScreeningApiServer.java b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/api/v1/IScreeningApiServer.java
index 38f3ef90065..2b67aba777f 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/api/v1/IScreeningApiServer.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/api/v1/IScreeningApiServer.java
@@ -45,6 +45,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.PlateWellMaterialMapping;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.PlateWellReferenceWithDatasets;
+import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.PlateWithWells;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.WellIdentifier;
 
 /**
@@ -94,6 +95,7 @@ public interface IScreeningApiServer extends IRpcService
     @ReturnValueFilter(validatorClass = ScreeningPlateValidator.class)
     List<Plate> listPlates(String sessionToken) throws IllegalArgumentException;
 
+
     /**
      * Return the list of all plates assigned to the given experiment.
      * 
@@ -107,6 +109,20 @@ public interface IScreeningApiServer extends IRpcService
             @AuthorizationGuard(guardClass = ExperimentIdentifierPredicate.class) ExperimentIdentifier experiment)
             throws IllegalArgumentException;
 
+    /**
+     * Fetches the contents of a given list of plates. The result will contain well and material
+     * properties.
+     * 
+     * @since 1.8
+     */
+    @Transactional(readOnly = true)
+    @RolesAllowed(RoleWithHierarchy.SPACE_OBSERVER)
+    @MinimalMinorVersion(8)
+    List<PlateWithWells> getPlates(
+            String sessionToken,
+            @AuthorizationGuard(guardClass = ScreeningPlateListReadOnlyPredicate.class) List<? extends PlateIdentifier> plates)
+            throws IllegalArgumentException;
+
     /**
      * Return the list of all visible experiments, along with their hierarchical context (space,
      * project).
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/api/v1/dto/Material.java b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/api/v1/dto/Material.java
new file mode 100644
index 00000000000..6b03179f7f0
--- /dev/null
+++ b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/api/v1/dto/Material.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2011 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.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Material with properties.
+ * 
+ * @since 1.8
+ * @author Kaloyan Enimanev
+ */
+public class Material extends MaterialIdentifier
+{
+    private static final long serialVersionUID = 1L;
+
+    private final Map<String, String> properties;
+
+    public Material(MaterialTypeIdentifier materialTypeIdentifier, String materialCode,
+            Map<String, String> properties)
+    {
+        super(materialTypeIdentifier, materialCode);
+        this.properties = new HashMap<String, String>(properties);
+    }
+
+    /**
+     * @return the material properties
+     */
+    public Map<String, String> getProperties()
+    {
+        return Collections.unmodifiableMap(properties);
+    }
+
+}
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/api/v1/dto/PlateWithWells.java b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/api/v1/dto/PlateWithWells.java
new file mode 100644
index 00000000000..265a1ac521f
--- /dev/null
+++ b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/api/v1/dto/PlateWithWells.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2011 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.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Holds the complete metadata of a plate and its wells.
+ * 
+ * @since 1.8
+ * @author Kaloyan Enimanev
+ */
+public class PlateWithWells extends PlateIdentifier
+{
+
+    private static final long serialVersionUID = 1L;
+
+    private final Geometry plateGeometry;
+
+    private final Map<String, String> properties;
+    
+    private final List<Well> wells;
+
+    public PlateWithWells(PlateIdentifier identifier, Geometry plateGeometry, Map<String, String> properties,
+            List<Well> unsortedWells)
+    {
+        super(identifier.getPlateCode(), identifier.tryGetSpaceCode(), identifier.getPermId());
+        this.plateGeometry = plateGeometry;
+        this.properties = new HashMap<String, String>(properties);
+        this.wells = sortWells(unsortedWells);
+    }
+
+    public Geometry getPlateGeometry()
+    {
+        return plateGeometry;
+    }
+
+    public Map<String, String> getProperties()
+    {
+        return properties;
+    }
+
+    public List<Well> getWells()
+    {
+        return Collections.unmodifiableList(wells);
+    }
+
+    public Well getWell(int row, int col)
+    {
+        int idx = getWellIndexForRowAndCol(row, col);
+        return wells.get(idx);
+    }
+
+    private int getWellIndexForRowAndCol(int row, int col)
+    {
+        return (row - 1) * plateGeometry.getNumberOfColumns() + (col - 1);
+    }
+
+    private List<Well> sortWells(List<Well> unsortedWells)
+    {
+        Well[] wellsArray =
+                new Well[plateGeometry.getNumberOfRows() * plateGeometry.getNumberOfColumns()];
+        for (Well well : unsortedWells)
+        {
+            int row = well.getWellPosition().getWellRow();
+            int col = well.getWellPosition().getWellColumn();
+            wellsArray[getWellIndexForRowAndCol(row, col)] = well;
+        }
+        return Arrays.asList(wellsArray);
+    }
+
+}
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/api/v1/dto/Well.java b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/api/v1/dto/Well.java
new file mode 100644
index 00000000000..2e3a43fc0b0
--- /dev/null
+++ b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/api/v1/dto/Well.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2011 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.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * {@link Well} holds a complete set of metadata for an openBIS well. Material properties of wells 
+ * are given a special treatment - API users can retrieve {@link Material} property values via the method {@link #getMaterialProperties()}. All other property values are available via {@link #getProperties()}.
+ * @since 1.8
+ * @author Kaloyan Enimanev
+ */
+public class Well extends WellIdentifier
+{
+    private static final long serialVersionUID = 1L;
+
+    private final String code;
+
+    private final Map<String, String> properties;
+
+    private final Map<String, Material> materialProperties;
+
+    public Well(PlateIdentifier plateIdentifier, String code, String permId,
+            WellPosition wellPosition, Map<String, String> properties,
+            Map<String, Material> materialProperties)
+    {
+        super(plateIdentifier, wellPosition, permId);
+        this.code = code;
+        this.properties = new HashMap<String, String>(properties);
+        this.materialProperties = new HashMap<String, Material>(materialProperties);
+    }
+
+    public String getCode()
+    {
+        return code;
+    }
+
+    public Map<String, String> getProperties()
+    {
+        return Collections.unmodifiableMap(properties);
+    }
+
+    public Map<String, Material> getMaterialProperties()
+    {
+        return Collections.unmodifiableMap(materialProperties);
+    }
+
+    @Override
+    public int hashCode()
+    {
+        final int prime = 31;
+        int result = super.hashCode();
+        result = prime * result + ((code == null) ? 0 : code.hashCode());
+        return result;
+    }
+
+    @Override
+    public String toString()
+    {
+        return getPermId() + " " + getCode() + " " + getWellPosition() + ", plate: "
+                + getPlateIdentifier();
+    }
+
+    @Override
+    public boolean equals(Object obj)
+    {
+        if (this == obj)
+        {
+            return true;
+        }
+        if (!super.equals(obj))
+        {
+            return false;
+        }
+        if (!(obj instanceof Well))
+        {
+            return false;
+        }
+        Well other = (Well) obj;
+        if (code == null)
+        {
+            if (other.code != null)
+            {
+                return false;
+            }
+        } else if (false == code.equals(other.code))
+        {
+            return false;
+        }
+        return true;
+    }
+
+}
-- 
GitLab