From 5bcf8975966a0beb52e0dd325e749fbed966d773 Mon Sep 17 00:00:00 2001
From: gpawel <gpawel>
Date: Wed, 5 Oct 2011 09:26:37 +0000
Subject: [PATCH] LMS-2537 InfectX SpotFire integration: allow to link to well
 images even if metadata for the well are missing

SVN: 23200
---
 .../locator/PermlinkLocatorResolver.java      |   2 +-
 .../application/ClientPluginFactory.java      |  61 ++++++++-
 .../application/DisplayTypeIDGenerator.java   |   2 +
 .../application/ScreeningViewContext.java     |   2 +
 .../detailviewers/ImageDataSetSection.java    | 109 +++++++++++++++
 .../detailviewers/ImageDataSetViewer.java     |  74 +++++++++++
 .../detailviewers/ImageSampleSection.java     |  65 ---------
 .../detailviewers/LogicalImageLayouter.java   |  72 ++++++++++
 .../detailviewers/WellContentDialog.java      |  71 +++++++---
 .../ImagingDataSetLocatorResolver.java        | 125 ++++++++++++++++++
 .../api/v1/dto/PlateImageReference.java       |   3 +-
 .../dto/ImageDatasetEnrichedReference.java    |  31 ++++-
 .../shared/basic/dto/WellLocation.java        |  10 ++
 13 files changed, 535 insertions(+), 92 deletions(-)
 create mode 100644 screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/web/client/application/detailviewers/ImageDataSetSection.java
 create mode 100644 screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/web/client/application/detailviewers/ImageDataSetViewer.java
 create mode 100644 screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/web/client/application/detailviewers/LogicalImageLayouter.java
 create mode 100644 screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/web/client/application/locator/ImagingDataSetLocatorResolver.java

diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/locator/PermlinkLocatorResolver.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/locator/PermlinkLocatorResolver.java
index 8354d5576f4..861c08b87df 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/locator/PermlinkLocatorResolver.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/locator/PermlinkLocatorResolver.java
@@ -43,7 +43,7 @@ public class PermlinkLocatorResolver extends AbstractViewLocatorResolver
     /**
      * Open the entity details tab for the specified entity kind and permId.
      */
-    private void openInitialEntityViewer(String entityKindValue, String permIdValue)
+    protected void openInitialEntityViewer(String entityKindValue, String permIdValue)
             throws UserFailureException
     {
         EntityKind entityKind = getEntityKind(entityKindValue);
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/web/client/application/ClientPluginFactory.java b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/web/client/application/ClientPluginFactory.java
index f1083106c93..d4d4a7cde29 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/web/client/application/ClientPluginFactory.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/web/client/application/ClientPluginFactory.java
@@ -54,9 +54,10 @@ import ch.systemsx.cisd.openbis.generic.shared.basic.dto.EntityKind;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.MaterialIdentifier;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.MaterialType;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.SampleType;
-import ch.systemsx.cisd.openbis.plugin.generic.client.web.client.application.sample.GenericSampleViewer;
+import ch.systemsx.cisd.openbis.plugin.generic.client.web.client.application.dataset.GenericDataSetViewer;
 import ch.systemsx.cisd.openbis.plugin.screening.client.web.client.IScreeningClientServiceAsync;
 import ch.systemsx.cisd.openbis.plugin.screening.client.web.client.application.detailviewers.ExperimentAnalysisSummaryViewer;
+import ch.systemsx.cisd.openbis.plugin.screening.client.web.client.application.detailviewers.ImageDataSetViewer;
 import ch.systemsx.cisd.openbis.plugin.screening.client.web.client.application.detailviewers.ImageSampleViewer;
 import ch.systemsx.cisd.openbis.plugin.screening.client.web.client.application.detailviewers.ImagingMaterialViewer;
 import ch.systemsx.cisd.openbis.plugin.screening.client.web.client.application.detailviewers.MaterialFeaturesFromAllExperimentsViewer;
@@ -67,6 +68,7 @@ import ch.systemsx.cisd.openbis.plugin.screening.client.web.client.application.d
 import ch.systemsx.cisd.openbis.plugin.screening.client.web.client.application.sample.LibrarySampleBatchRegistrationForm;
 import ch.systemsx.cisd.openbis.plugin.screening.client.web.client.application.ui.columns.specific.ScreeningLinkExtractor;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.ScreeningConstants;
+import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.WellLocation;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.WellSearchCriteria.AnalysisProcedureCriteria;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.WellSearchCriteria.ExperimentSearchByProjectCriteria;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.WellSearchCriteria.ExperimentSearchCriteria;
@@ -156,7 +158,7 @@ public final class ClientPluginFactory extends AbstractClientPluginFactory<Scree
         }
         if (EntityKind.DATA_SET.equals(entityKind))
         {
-            return (IClientPlugin<T, I>) new DatasetClientPlugin(viewContext);
+            return (IClientPlugin<T, I>) new createImageDataSetViewer(viewContext);
         }
         throw new UnsupportedOperationException("IClientPlugin for entity kind '" + entityKind
                 + "' not implemented yet.");
@@ -342,22 +344,67 @@ public final class ClientPluginFactory extends AbstractClientPluginFactory<Scree
             };
     }
 
-    private final class DatasetClientPlugin extends DelegatedClientPlugin<DataSetType>
+    private final class createImageDataSetViewer extends DelegatedClientPlugin<DataSetType>
     {
         private final ScreeningViewContext screeningViewContext;
 
-        private DatasetClientPlugin(ScreeningViewContext viewContext)
+        private createImageDataSetViewer(ScreeningViewContext viewContext)
         {
             super(viewContext, EntityKind.DATA_SET);
             this.screeningViewContext = viewContext;
         }
 
+        private AbstractTabItemFactory createImageDataSetViewer(
+                final IEntityInformationHolderWithPermId entity, final WellLocation wellLocation)
+        {
+            return new AbstractTabItemFactory()
+                {
+                    @Override
+                    public ITabItem create()
+                    {
+                        final DatabaseModificationAwareComponent viewer =
+                                ImageDataSetViewer.create(screeningViewContext, entity,
+                                        wellLocation);
+                        return createViewerTab(viewer, getTabTitle(), screeningViewContext);
+                    }
+
+                    @Override
+                    public String getId()
+                    {
+                        final TechId dataSetId = TechId.create(entity);
+                        return GenericDataSetViewer.createId(dataSetId);
+                    }
+
+                    @Override
+                    public HelpPageIdentifier getHelpPageIdentifier()
+                    {
+                        return HelpPageIdentifier.createSpecific("Plate Well Viewer");
+                    }
+
+                    @Override
+                    public String getTabTitle()
+                    {
+                        return getViewerTitle(Dict.WELL, entity, screeningViewContext);
+                    }
+
+                    @Override
+                    public String tryGetLink()
+                    {
+                        return LinkExtractor.tryExtract(entity);
+                    }
+                };
+        }
+
         @Override
         public final AbstractTabItemFactory createEntityViewer(
                 final IEntityInformationHolderWithPermId entity)
         {
             String datasetTypeCode = entity.getEntityType().getCode();
-            if (datasetTypeCode.matches(ScreeningConstants.ANY_HCS_IMAGE_DATASET_TYPE_PATTERN))
+            if (entity.getPermId().contains(":"))
+            {
+                return createImageDataSetViewer(entity, new WellLocation(1, 1));
+            } else if (datasetTypeCode
+                    .matches(ScreeningConstants.ANY_HCS_IMAGE_DATASET_TYPE_PATTERN))
             {
                 return createHCSImageDatasetTabItemFactory(entity);
             } else if (datasetTypeCode
@@ -502,7 +549,7 @@ public final class ClientPluginFactory extends AbstractClientPluginFactory<Scree
                     public String getId()
                     {
                         final TechId sampleId = TechId.create(entity);
-                        return GenericSampleViewer.createId(sampleId);
+                        return GenericDataSetViewer.createId(sampleId);
                     }
 
                     @Override
@@ -548,7 +595,7 @@ public final class ClientPluginFactory extends AbstractClientPluginFactory<Scree
                     public String getId()
                     {
                         final TechId sampleId = TechId.create(entity);
-                        return GenericSampleViewer.createId(sampleId);
+                        return GenericDataSetViewer.createId(sampleId);
                     }
 
                     @Override
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/web/client/application/DisplayTypeIDGenerator.java b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/web/client/application/DisplayTypeIDGenerator.java
index 5ffc9eb5447..b4363ba6d19 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/web/client/application/DisplayTypeIDGenerator.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/web/client/application/DisplayTypeIDGenerator.java
@@ -33,6 +33,8 @@ public enum DisplayTypeIDGenerator implements IDisplayTypeIDGenerator
 
     LOGICAL_IMAGE_DATASET_SECTION("logical-image-dataset-section"),
 
+    LOGICAL_IMAGE_WELL_DATASET_SECTION("logical-image-well-dataset-section"),
+
     PLATE_LOCATIONS_MATERIAL_SECTION("plate-locations-material-section"),
 
     EXPERIMENT_PLATE_LOCATIONS_SECTION("plate-locations-experiment-section"),
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/web/client/application/ScreeningViewContext.java b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/web/client/application/ScreeningViewContext.java
index 51c2651bf6c..383bc05e91a 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/web/client/application/ScreeningViewContext.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/web/client/application/ScreeningViewContext.java
@@ -10,6 +10,7 @@ import ch.systemsx.cisd.openbis.plugin.screening.client.web.client.IScreeningCli
 import ch.systemsx.cisd.openbis.plugin.screening.client.web.client.IScreeningClientServiceAsync;
 import ch.systemsx.cisd.openbis.plugin.screening.client.web.client.application.locator.ExperimentAnalysisSummaryResolver;
 import ch.systemsx.cisd.openbis.plugin.screening.client.web.client.application.locator.GlobalWellSearchLocatorResolver;
+import ch.systemsx.cisd.openbis.plugin.screening.client.web.client.application.locator.ImagingDataSetLocatorResolver;
 import ch.systemsx.cisd.openbis.plugin.screening.client.web.client.application.locator.ImagingMaterialLocatorResolver;
 import ch.systemsx.cisd.openbis.plugin.screening.client.web.client.application.locator.PlateMetadataBrowserLocatorResolver;
 import ch.systemsx.cisd.openbis.plugin.screening.client.web.client.application.locator.WellSearchLocatorResolver;
@@ -45,6 +46,7 @@ public final class ScreeningViewContext extends
     {
         super.initializeLocatorHandlerRegistry(handlerRegistry);
 
+        handlerRegistry.registerHandler(new ImagingDataSetLocatorResolver(this));
         handlerRegistry.registerHandler(new ImagingMaterialLocatorResolver(this));
         handlerRegistry.registerHandler(new PlateMetadataBrowserLocatorResolver(this));
         handlerRegistry.registerHandler(new WellSearchLocatorResolver(this));
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/web/client/application/detailviewers/ImageDataSetSection.java b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/web/client/application/detailviewers/ImageDataSetSection.java
new file mode 100644
index 00000000000..065444117b4
--- /dev/null
+++ b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/web/client/application/detailviewers/ImageDataSetSection.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.client.web.client.application.detailviewers;
+
+import java.util.Collections;
+
+import com.extjs.gxt.ui.client.Style.Scroll;
+import com.extjs.gxt.ui.client.widget.Text;
+import com.extjs.gxt.ui.client.widget.layout.RowData;
+import com.extjs.gxt.ui.client.widget.layout.RowLayout;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+
+import ch.systemsx.cisd.openbis.generic.client.web.client.application.AbstractAsyncCallback;
+import ch.systemsx.cisd.openbis.generic.client.web.client.application.TabContent;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ExternalData;
+import ch.systemsx.cisd.openbis.plugin.screening.client.web.client.application.Dict;
+import ch.systemsx.cisd.openbis.plugin.screening.client.web.client.application.DisplayTypeIDGenerator;
+import ch.systemsx.cisd.openbis.plugin.screening.client.web.client.application.ScreeningViewContext;
+import ch.systemsx.cisd.openbis.plugin.screening.client.web.client.application.detailviewers.heatmaps.LayoutUtils;
+import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.ImageDatasetEnrichedReference;
+import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.LogicalImageInfo;
+import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.WellLocation;
+
+/**
+ * @author Pawel Glyzewski
+ */
+public class ImageDataSetSection extends TabContent
+{
+    private final ExternalData dataSet;
+
+    private final WellLocation wellLocationOrNull;
+
+    private static final String WELL_IMAGE_SECTION_TITLE_PREFIX = "Well ";
+
+    private static final String WELL_IMAGE_SECTION_TITLE_SUFFIX = " Images";
+
+    public ImageDataSetSection(final ScreeningViewContext viewContext, final ExternalData dataSet,
+            WellLocation wellLocationOrNull)
+    {
+        super(WELL_IMAGE_SECTION_TITLE_PREFIX
+                + (wellLocationOrNull == null ? "" : wellLocationOrNull.toWellIdString())
+                + WELL_IMAGE_SECTION_TITLE_SUFFIX, viewContext, dataSet);
+
+        this.dataSet = dataSet;
+        this.wellLocationOrNull = wellLocationOrNull;
+        setIds(DisplayTypeIDGenerator.LOGICAL_IMAGE_WELL_DATASET_SECTION);
+    }
+
+    private ScreeningViewContext getViewContext()
+    {
+        return (ScreeningViewContext) viewContext;
+    }
+
+    @Override
+    protected void showContent()
+    {
+        final ScreeningViewContext context = getViewContext();
+        add(new Text(context.getMessage(Dict.LOAD_IN_PROGRESS)));
+        context.getService().getImageDatasetInfo(dataSet.getCode(),
+                dataSet.getDataStore().getCode(), wellLocationOrNull,
+                createDisplayImagesCallback(context));
+    }
+
+    private AsyncCallback<LogicalImageInfo> createDisplayImagesCallback(ScreeningViewContext context)
+    {
+        return new AbstractAsyncCallback<LogicalImageInfo>(context)
+            {
+                @Override
+                protected void process(LogicalImageInfo imageDataSet)
+                {
+                    removeAll();
+                    setLayout(new RowLayout());
+                    setScrollMode(Scroll.AUTO);
+
+                    addVisualisation(imageDataSet);
+                    layout();
+                }
+            };
+    }
+
+    private void addVisualisation(LogicalImageInfo imageDataSet)
+    {
+        RowData margins = LayoutUtils.createRowLayoutSurroundingData();
+
+        final ScreeningViewContext context = getViewContext();
+        final LogicalImageLayouter logicalImageLayouter =
+                new LogicalImageLayouter(context, wellLocationOrNull,
+                        Collections.singletonList(imageDataSet));
+
+        ImageDatasetEnrichedReference firstImageDataset = imageDataSet.getImageDataset();
+        logicalImageLayouter.changeDisplayedImageDataset(firstImageDataset);
+
+        add(logicalImageLayouter, margins);
+    }
+}
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/web/client/application/detailviewers/ImageDataSetViewer.java b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/web/client/application/detailviewers/ImageDataSetViewer.java
new file mode 100644
index 00000000000..f27ea4b7390
--- /dev/null
+++ b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/web/client/application/detailviewers/ImageDataSetViewer.java
@@ -0,0 +1,74 @@
+/*
+ * 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.client.web.client.application.detailviewers;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.google.gwt.user.client.rpc.AsyncCallback;
+
+import ch.systemsx.cisd.openbis.generic.client.web.client.application.TabContent;
+import ch.systemsx.cisd.openbis.generic.client.web.client.application.framework.DatabaseModificationAwareComponent;
+import ch.systemsx.cisd.openbis.generic.shared.basic.IIdAndCodeHolder;
+import ch.systemsx.cisd.openbis.generic.shared.basic.TechId;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ExternalData;
+import ch.systemsx.cisd.openbis.plugin.generic.client.web.client.application.dataset.GenericDataSetViewer;
+import ch.systemsx.cisd.openbis.plugin.screening.client.web.client.application.ScreeningViewContext;
+import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.WellLocation;
+
+/**
+ * @author Pawel Glyzewski
+ */
+public class ImageDataSetViewer extends GenericDataSetViewer
+{
+    public static DatabaseModificationAwareComponent create(final ScreeningViewContext viewContext,
+            final IIdAndCodeHolder identifiable, WellLocation wellLocationOrNull)
+    {
+        ImageDataSetViewer viewer =
+                new ImageDataSetViewer(viewContext, identifiable, wellLocationOrNull);
+        viewer.reloadAllData();
+        return new DatabaseModificationAwareComponent(viewer, viewer);
+    }
+
+    private final ScreeningViewContext screeningViewContext;
+
+    private final WellLocation wellLocationOrNull;
+
+    protected ImageDataSetViewer(ScreeningViewContext screeningViewContext,
+            IIdAndCodeHolder identifiable, WellLocation wellLocationOrNull)
+    {
+        super(screeningViewContext, identifiable);
+
+        this.screeningViewContext = screeningViewContext;
+        this.wellLocationOrNull = wellLocationOrNull;
+    }
+
+    @Override
+    protected List<TabContent> createAdditionalSectionPanels(ExternalData dataset)
+    {
+        List<TabContent> sections = new ArrayList<TabContent>();
+
+        sections.add(new ImageDataSetSection(screeningViewContext, dataset, wellLocationOrNull));
+        return sections;
+    }
+
+    @Override
+    protected void loadDatasetInfo(TechId datasetTechId, AsyncCallback<ExternalData> asyncCallback)
+    {
+        screeningViewContext.getService().getDataSetInfo(datasetTechId, asyncCallback);
+    }
+}
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/web/client/application/detailviewers/ImageSampleSection.java b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/web/client/application/detailviewers/ImageSampleSection.java
index fbf6adca39a..3d79891c6a4 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/web/client/application/detailviewers/ImageSampleSection.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/web/client/application/detailviewers/ImageSampleSection.java
@@ -16,13 +16,9 @@
 
 package ch.systemsx.cisd.openbis.plugin.screening.client.web.client.application.detailviewers;
 
-import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 
 import com.extjs.gxt.ui.client.Style.Scroll;
-import com.extjs.gxt.ui.client.widget.LayoutContainer;
 import com.extjs.gxt.ui.client.widget.Text;
 import com.extjs.gxt.ui.client.widget.layout.RowData;
 import com.extjs.gxt.ui.client.widget.layout.RowLayout;
@@ -35,8 +31,6 @@ import ch.systemsx.cisd.openbis.generic.shared.basic.TechId;
 import ch.systemsx.cisd.openbis.plugin.screening.client.web.client.application.Dict;
 import ch.systemsx.cisd.openbis.plugin.screening.client.web.client.application.DisplayTypeIDGenerator;
 import ch.systemsx.cisd.openbis.plugin.screening.client.web.client.application.ScreeningViewContext;
-import ch.systemsx.cisd.openbis.plugin.screening.client.web.client.application.detailviewers.ImagingDatasetGuiUtils.IDatasetImagesReferenceUpdater;
-import ch.systemsx.cisd.openbis.plugin.screening.client.web.client.application.detailviewers.dto.LogicalImageReference;
 import ch.systemsx.cisd.openbis.plugin.screening.client.web.client.application.detailviewers.heatmaps.LayoutUtils;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.DatasetReference;
 import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.ImageDatasetEnrichedReference;
@@ -142,63 +136,4 @@ public class ImageSampleSection extends TabContent
             add(logicalImageLayouter, margins);
         }
     }
-
-    private static class LogicalImageLayouter extends LayoutContainer implements
-            IDatasetImagesReferenceUpdater
-    {
-        private final ScreeningViewContext viewContext;
-
-        private final WellLocation wellLocationOrNull;
-
-        private final Map<ImageDatasetEnrichedReference, LogicalImageInfo> refsMap;
-
-        public LogicalImageLayouter(ScreeningViewContext viewContext,
-                WellLocation wellLocationOrNull, List<LogicalImageInfo> images)
-        {
-            this.viewContext = viewContext;
-            this.wellLocationOrNull = wellLocationOrNull;
-            this.refsMap = createRefsMap(images);
-        }
-
-        public void changeDisplayedImageDataset(ImageDatasetEnrichedReference dataset)
-        {
-            LogicalImageInfo imageInfo = refsMap.get(dataset);
-            assert imageInfo != null : "cannot find logical image for " + dataset;
-
-            removeAll();
-            Widget viewerWidget = createImageViewer(imageInfo);
-            add(viewerWidget);
-            layout();
-        }
-
-        private Widget createImageViewer(LogicalImageInfo imageInfo)
-        {
-            LogicalImageReference logicalImageReference =
-                    new LogicalImageReference(imageInfo.getImageDataset(), wellLocationOrNull);
-            LogicalImageViewer viewer =
-                    new LogicalImageViewer(logicalImageReference, viewContext,
-                            imageInfo.getExperimentIdentifier(), imageInfo.getExperimentPermId(),
-                            true);
-            return viewer.getViewerWidget(imageInfo.getChannelStacks());
-        }
-
-        public List<ImageDatasetEnrichedReference> getDatasetImagesReferences()
-        {
-            return new ArrayList<ImageDatasetEnrichedReference>(refsMap.keySet());
-        }
-
-        private static Map<ImageDatasetEnrichedReference, LogicalImageInfo> createRefsMap(
-                List<LogicalImageInfo> images)
-        {
-            Map<ImageDatasetEnrichedReference, LogicalImageInfo> map =
-                    new HashMap<ImageDatasetEnrichedReference, LogicalImageInfo>();
-            for (LogicalImageInfo imageInfo : images)
-            {
-                ImageDatasetEnrichedReference ref = imageInfo.getImageDataset();
-                map.put(ref, imageInfo);
-            }
-            return map;
-        }
-    }
-
 }
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/web/client/application/detailviewers/LogicalImageLayouter.java b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/web/client/application/detailviewers/LogicalImageLayouter.java
new file mode 100644
index 00000000000..9c8f3f46100
--- /dev/null
+++ b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/web/client/application/detailviewers/LogicalImageLayouter.java
@@ -0,0 +1,72 @@
+package ch.systemsx.cisd.openbis.plugin.screening.client.web.client.application.detailviewers;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.extjs.gxt.ui.client.widget.LayoutContainer;
+import com.google.gwt.user.client.ui.Widget;
+
+import ch.systemsx.cisd.openbis.plugin.screening.client.web.client.application.ScreeningViewContext;
+import ch.systemsx.cisd.openbis.plugin.screening.client.web.client.application.detailviewers.ImagingDatasetGuiUtils.IDatasetImagesReferenceUpdater;
+import ch.systemsx.cisd.openbis.plugin.screening.client.web.client.application.detailviewers.dto.LogicalImageReference;
+import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.ImageDatasetEnrichedReference;
+import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.LogicalImageInfo;
+import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.WellLocation;
+
+public class LogicalImageLayouter extends LayoutContainer implements IDatasetImagesReferenceUpdater
+{
+    private final ScreeningViewContext viewContext;
+
+    private final WellLocation wellLocationOrNull;
+
+    private final Map<ImageDatasetEnrichedReference, LogicalImageInfo> refsMap;
+
+    public LogicalImageLayouter(ScreeningViewContext viewContext, WellLocation wellLocationOrNull,
+            List<LogicalImageInfo> images)
+    {
+        this.viewContext = viewContext;
+        this.wellLocationOrNull = wellLocationOrNull;
+        this.refsMap = createRefsMap(images);
+    }
+
+    public void changeDisplayedImageDataset(ImageDatasetEnrichedReference dataset)
+    {
+        LogicalImageInfo imageInfo = refsMap.get(dataset);
+        assert imageInfo != null : "cannot find logical image for " + dataset;
+
+        removeAll();
+        Widget viewerWidget = createImageViewer(imageInfo);
+        add(viewerWidget);
+        layout();
+    }
+
+    private Widget createImageViewer(LogicalImageInfo imageInfo)
+    {
+        LogicalImageReference logicalImageReference =
+                new LogicalImageReference(imageInfo.getImageDataset(), wellLocationOrNull);
+        LogicalImageViewer viewer =
+                new LogicalImageViewer(logicalImageReference, viewContext,
+                        imageInfo.getExperimentIdentifier(), imageInfo.getExperimentPermId(), true);
+        return viewer.getViewerWidget(imageInfo.getChannelStacks());
+    }
+
+    public List<ImageDatasetEnrichedReference> getDatasetImagesReferences()
+    {
+        return new ArrayList<ImageDatasetEnrichedReference>(refsMap.keySet());
+    }
+
+    private static Map<ImageDatasetEnrichedReference, LogicalImageInfo> createRefsMap(
+            List<LogicalImageInfo> images)
+    {
+        Map<ImageDatasetEnrichedReference, LogicalImageInfo> map =
+                new HashMap<ImageDatasetEnrichedReference, LogicalImageInfo>();
+        for (LogicalImageInfo imageInfo : images)
+        {
+            ImageDatasetEnrichedReference ref = imageInfo.getImageDataset();
+            map.put(ref, imageInfo);
+        }
+        return map;
+    }
+}
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/web/client/application/detailviewers/WellContentDialog.java b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/web/client/application/detailviewers/WellContentDialog.java
index 6c19502c692..cffa9878d0c 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/web/client/application/detailviewers/WellContentDialog.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/web/client/application/detailviewers/WellContentDialog.java
@@ -48,6 +48,8 @@ import ch.systemsx.cisd.openbis.generic.client.web.client.application.IViewConte
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.renderer.LinkRenderer;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.listener.OpenEntityDetailsTabClickListener;
 import ch.systemsx.cisd.openbis.generic.shared.basic.IEntityInformationHolderWithPermId;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.BasicEntityType;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.EntityKind;
 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.Material;
@@ -77,8 +79,6 @@ import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.WellSearchCrit
  */
 public class WellContentDialog extends Dialog
 {
-    private static final String UNKNOWN_WELL_LABEL = "No well information available.";
-
     private static final String UNKNOWN_CHANNEL_LABEL = "No images available for this channel.";
 
     private static final String INCORRECT_WELL_CODE_LABEL = "Incorrect well code.";
@@ -96,17 +96,17 @@ public class WellContentDialog extends Dialog
             ImageDatasetEnrichedReference imageDatasetOrNull,
             final IViewContext<IScreeningClientServiceAsync> viewContext)
     {
-        final WellContentDialog contentDialog = createContentDialog(wellData, viewContext);
-        showContentDialog(contentDialog, imageDatasetOrNull, viewContext);
+        final WellContentDialog contentDialog =
+                createContentDialog(wellData, imageDatasetOrNull, viewContext);
+        showContentDialog(contentDialog, viewContext);
     }
 
     private static void showContentDialog(final WellContentDialog contentDialog,
-            final ImageDatasetEnrichedReference imagesOrNull,
             final IViewContext<IScreeningClientServiceAsync> viewContext)
     {
-        if (imagesOrNull != null)
+        if (contentDialog.tryGetImages() != null)
         {
-            LogicalImageViewer viewer = contentDialog.createImageViewer(imagesOrNull);
+            LogicalImageViewer viewer = contentDialog.createImageViewer();
             contentDialog.add(viewer.getViewerWidget());
             contentDialog.addImageEditorLaunchButton(viewer);
         }
@@ -114,6 +114,7 @@ public class WellContentDialog extends Dialog
     }
 
     private static WellContentDialog createContentDialog(final WellData wellData,
+            ImageDatasetEnrichedReference imageDatasetOrNull,
             final IViewContext<IScreeningClientServiceAsync> viewContext)
     {
         WellLocation wellLocation = wellData.getWellLocation();
@@ -126,7 +127,7 @@ public class WellContentDialog extends Dialog
             wellPropertiesOrNull = wellMetadata.getWellSample().getProperties();
         }
         return new WellContentDialog(wellOrNull, wellPropertiesOrNull, wellLocation,
-                getExperiment(wellData), viewContext);
+                getExperiment(wellData), imageDatasetOrNull, viewContext);
     }
 
     /**
@@ -200,12 +201,12 @@ public class WellContentDialog extends Dialog
     {
         WellContentDialog contentDialog =
                 new WellContentDialog(wellImage.getWell(), null, wellImage.tryGetLocation(),
-                        getExperiment(wellImage.getExperiment()), viewContext);
+                        getExperiment(wellImage.getExperiment()), imageDatasetOrNull, viewContext);
 
-        showContentDialog(contentDialog, imageDatasetOrNull, viewContext);
+        showContentDialog(contentDialog, viewContext);
     }
 
-    private LogicalImageViewer createImageViewer(ImageDatasetEnrichedReference imageDatasetOrNull)
+    private LogicalImageViewer createImageViewer()
     {
         final LogicalImageReference imagesOrNull =
                 new LogicalImageReference(imageDatasetOrNull, wellLocationOrNull);
@@ -245,9 +246,12 @@ public class WellContentDialog extends Dialog
 
     private final IViewContext<IScreeningClientServiceAsync> viewContext;
 
+    private ImageDatasetEnrichedReference imageDatasetOrNull;
+
     private WellContentDialog(IEntityInformationHolderWithPermId wellOrNull,
             List<IEntityProperty> wellPropertiesOrNull, final WellLocation wellLocationOrNull,
             final SingleExperimentSearchCriteria experimentCriteria,
+            ImageDatasetEnrichedReference imageDatasetOrNull,
             final IViewContext<IScreeningClientServiceAsync> viewContext)
     {
         this.wellOrNull = wellOrNull;
@@ -258,6 +262,7 @@ public class WellContentDialog extends Dialog
             Collections.sort(wellPropertiesOrNull);
         }
         this.experimentCriteria = experimentCriteria;
+        this.imageDatasetOrNull = imageDatasetOrNull;
         this.viewContext = viewContext;
         setScrollMode(Scroll.AUTO);
         setHideOnButtonClick(true);
@@ -287,6 +292,11 @@ public class WellContentDialog extends Dialog
             });
     }
 
+    private ImageDatasetEnrichedReference tryGetImages()
+    {
+        return imageDatasetOrNull;
+    }
+
     private void addImageEditorLaunchButton(final LogicalImageViewer viewer)
     {
         if (viewer.isImageEditorEnabled() == false)
@@ -327,10 +337,41 @@ public class WellContentDialog extends Dialog
         if (wellOrNull != null)
         {
             container.add(new Text(WELL_LABEL), cellLayout);
-            container.add(createEntityLink(wellOrNull));
+            container.add(createEntityLink(wellOrNull, wellOrNull.getCode()));
         } else
         {
-            container.add(new Text(UNKNOWN_WELL_LABEL));
+            container.add(new Text(WELL_LABEL), cellLayout);
+            final String suffix =
+                    wellLocationOrNull == null ? "" : ":" + wellLocationOrNull.toWellIdString();
+            container.add(createEntityLink(new IEntityInformationHolderWithPermId()
+                {
+                    private static final long serialVersionUID = 1L;
+
+                    public String getPermId()
+                    {
+                        return imageDatasetOrNull.getPermId() + suffix;
+                    }
+
+                    public String getCode()
+                    {
+                        return imageDatasetOrNull.getCode();
+                    }
+
+                    public Long getId()
+                    {
+                        return imageDatasetOrNull.getId();
+                    }
+
+                    public BasicEntityType getEntityType()
+                    {
+                        return imageDatasetOrNull.getEntityType();
+                    }
+
+                    public EntityKind getEntityKind()
+                    {
+                        return imageDatasetOrNull.getEntityKind();
+                    }
+                }, getWellDescription()));
         }
         if (wellPropertiesOrNull != null)
         {
@@ -459,10 +500,10 @@ public class WellContentDialog extends Dialog
         return ExperimentSearchCriteria.createExperiment(experimentCriteria, false);
     }
 
-    private Widget createEntityLink(IEntityInformationHolderWithPermId entity)
+    private Widget createEntityLink(IEntityInformationHolderWithPermId entity, String label)
     {
         final ClickHandler listener = new OpenEntityDetailsTabClickListener(entity, viewContext);
-        return LinkRenderer.getLinkWidget(entity.getCode(), listener);
+        return LinkRenderer.getLinkWidget(label, listener);
     }
 
 }
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/web/client/application/locator/ImagingDataSetLocatorResolver.java b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/web/client/application/locator/ImagingDataSetLocatorResolver.java
new file mode 100644
index 00000000000..cfe928b6510
--- /dev/null
+++ b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/web/client/application/locator/ImagingDataSetLocatorResolver.java
@@ -0,0 +1,125 @@
+/*
+ * 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.client.web.client.application.locator;
+
+import ch.systemsx.cisd.openbis.generic.client.web.client.application.AbstractAsyncCallback;
+import ch.systemsx.cisd.openbis.generic.client.web.client.application.IViewContext;
+import ch.systemsx.cisd.openbis.generic.client.web.client.application.locator.PermlinkLocatorResolver;
+import ch.systemsx.cisd.openbis.generic.client.web.client.application.locator.ViewLocator;
+import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.listener.OpenEntityDetailsTabAction;
+import ch.systemsx.cisd.openbis.generic.client.web.client.exception.UserFailureException;
+import ch.systemsx.cisd.openbis.generic.shared.basic.IEntityInformationHolderWithPermId;
+import ch.systemsx.cisd.openbis.generic.shared.basic.PermlinkUtilities;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.BasicEntityType;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.EntityKind;
+import ch.systemsx.cisd.openbis.plugin.screening.client.web.client.IScreeningClientServiceAsync;
+
+/**
+ * @author Pawel Glyzewski
+ */
+public class ImagingDataSetLocatorResolver extends PermlinkLocatorResolver
+{
+    private final IViewContext<IScreeningClientServiceAsync> viewContext;
+
+    public ImagingDataSetLocatorResolver(IViewContext<IScreeningClientServiceAsync> viewContext)
+    {
+        super(viewContext.getCommonViewContext());
+        this.viewContext = viewContext;
+    }
+
+    @Override
+    public boolean canHandleLocator(ViewLocator locator)
+    {
+        String entityKindValueOrNull = locator.tryGetEntity();
+        String permIdValueOrNull =
+                locator.getParameters().get(PermlinkUtilities.PERM_ID_PARAMETER_KEY);
+
+        return super.canHandleLocator(locator)
+                && EntityKind.DATA_SET.name().equals(entityKindValueOrNull)
+                && permIdValueOrNull != null && permIdValueOrNull.contains(":");
+    }
+
+    /**
+     * Open the entity details tab for the specified entity kind and permId.
+     */
+    @Override
+    protected void openInitialEntityViewer(String entityKindValue, String permIdValue)
+            throws UserFailureException
+    {
+        EntityKind entityKind = getEntityKind(entityKindValue);
+        int idx = permIdValue.indexOf(':');
+
+        viewContext.getCommonService().getEntityInformationHolder(entityKind,
+                permIdValue.substring(0, idx),
+                new OpenEntityDetailsTabCallback(viewContext, permIdValue.substring(idx + 1)));
+    }
+
+    private static class OpenEntityDetailsTabCallback extends
+            AbstractAsyncCallback<IEntityInformationHolderWithPermId>
+    {
+        private final String wellLocation;
+
+        private OpenEntityDetailsTabCallback(final IViewContext<?> viewContext,
+                final String wellLocation)
+        {
+            super(viewContext);
+            this.wellLocation = wellLocation;
+        }
+
+        //
+        // AbstractAsyncCallback
+        //
+
+        /**
+         * Opens the tab with <var>result</var> entity details.
+         */
+        @Override
+        protected final void process(final IEntityInformationHolderWithPermId result)
+        {
+            new OpenEntityDetailsTabAction(new IEntityInformationHolderWithPermId()
+                {
+                    private static final long serialVersionUID = 1L;
+
+                    public BasicEntityType getEntityType()
+                    {
+                        return result.getEntityType();
+                    }
+
+                    public EntityKind getEntityKind()
+                    {
+                        return result.getEntityKind();
+                    }
+
+                    public Long getId()
+                    {
+                        return result.getId();
+                    }
+
+                    public String getCode()
+                    {
+                        return result.getCode();
+                    }
+
+                    public String getPermId()
+                    {
+                        return result.getPermId() + ":" + wellLocation;
+                    }
+
+                }, viewContext, false).execute();
+        }
+    }
+}
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/api/v1/dto/PlateImageReference.java b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/api/v1/dto/PlateImageReference.java
index 03c5c3ec491..8ffbba35064 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/api/v1/dto/PlateImageReference.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/api/v1/dto/PlateImageReference.java
@@ -97,8 +97,7 @@ public class PlateImageReference extends DatasetIdentifier
         }
 
         final PlateImageReference other = (PlateImageReference) obj;
-        if ((channelOrNull == null && other.channelOrNull != null)
-                || channelOrNull.equals(other.channelOrNull) == false)
+        if ((channelOrNull == null && other.channelOrNull != null) ||  channelOrNull.equals(other.channelOrNull) == false)
         {
             return false;
         }
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/basic/dto/ImageDatasetEnrichedReference.java b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/basic/dto/ImageDatasetEnrichedReference.java
index e8c84861080..d1bd2a8a2f9 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/basic/dto/ImageDatasetEnrichedReference.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/basic/dto/ImageDatasetEnrichedReference.java
@@ -19,7 +19,9 @@ package ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto;
 import java.util.ArrayList;
 import java.util.List;
 
-import ch.systemsx.cisd.openbis.generic.shared.basic.ISerializable;
+import ch.systemsx.cisd.openbis.generic.shared.basic.IEntityInformationHolderWithPermId;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.BasicEntityType;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.EntityKind;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ServiceVersionHolder;
 
 /**
@@ -28,7 +30,7 @@ import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ServiceVersionHolder;
  * @author Tomasz Pylak
  */
 // NOTE: is supposed to be extended with derived analysis datasets and optionally raw image datasets
-public class ImageDatasetEnrichedReference implements ISerializable
+public class ImageDatasetEnrichedReference implements IEntityInformationHolderWithPermId
 {
     private static final long serialVersionUID = ServiceVersionHolder.VERSION;
 
@@ -69,4 +71,29 @@ public class ImageDatasetEnrichedReference implements ISerializable
     {
         return overlayDatasets;
     }
+
+    public BasicEntityType getEntityType()
+    {
+        return imageDataset.getDatasetReference().getEntityType();
+    }
+
+    public EntityKind getEntityKind()
+    {
+        return imageDataset.getDatasetReference().getEntityKind();
+    }
+
+    public Long getId()
+    {
+        return imageDataset.getDatasetReference().getId();
+    }
+
+    public String getCode()
+    {
+        return imageDataset.getDatasetReference().getCode();
+    }
+
+    public String getPermId()
+    {
+        return imageDataset.getDatasetReference().getPermId();
+    }
 }
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/basic/dto/WellLocation.java b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/basic/dto/WellLocation.java
index 4f6a1470137..4b6dc6f56dc 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/basic/dto/WellLocation.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/basic/dto/WellLocation.java
@@ -241,6 +241,16 @@ public class WellLocation implements ISerializable
         return "(" + row + "," + column + ")";
     }
 
+    public String toWellIdString()
+    {
+        return getNumberLetter(row) + column;
+    }
+
+    private String getNumberLetter(@SuppressWarnings("hiding") int row)
+    {
+        return Character.toString((char) ((char) row + 'A' - (char) 1));
+    }
+
     @Override
     public int hashCode()
     {
-- 
GitLab