diff --git a/screening/etc/service.properties b/screening/etc/service.properties
index 014b948454598501ac86f21a464671cb256e285b..36f93e28c1d2255367867a7c87f4d5ce15cb5dc3 100644
--- a/screening/etc/service.properties
+++ b/screening/etc/service.properties
@@ -79,7 +79,7 @@ quiet-period = 3
 # ---------------------------------------------------------------------------
 
 # Comma separated names of reporting plugins. Each plugin should have configuration properties prefixed with its name.
-reporting-plugins = plate-image-reporter, plate-image-params-reporter, plate-image-analysis-merger, demo-reporter
+reporting-plugins = plate-image-reporter, plate-image-params-reporter, plate-image-analysis-merger, plate-image-analysis-graph, demo-reporter
 
 # Label of the plugin which will be shown for the users.
 demo-reporter.label = Show Dataset Size
@@ -113,6 +113,12 @@ plate-image-analysis-merger.dataset-types = HCS_IMAGE_ANALYSIS_DATA
 plate-image-analysis-merger.class = ch.systemsx.cisd.openbis.dss.generic.server.plugins.ImageAnalysisMergedRowsReportingPlugin
 plate-image-analysis-merger.properties-file =
 
+plate-image-analysis-graph.label = Show Image Analysis Graphs
+plate-image-analysis-graph.dataset-types = HCS_IMAGE_ANALYSIS_DATA
+plate-image-analysis-graph.class = ch.systemsx.cisd.openbis.dss.generic.server.plugins.ImageAnalysisGraphReportingPlugin
+plate-image-analysis-graph.servlet-path = datastore_server_graph/
+plate-image-analysis-graph.properties-file = etc/tabular-data-graph.properties
+
 # ---------------------------------------------------------------------------
 
 # Comma separated names of processing plugins. Each plugin should have configuration properties prefixed with its name.
@@ -134,13 +140,18 @@ hierarchical-storage-updater.hierarchy-root-dir = targets/hierarchical-store
 # ---------------------------------------------------------------------------
 
 # list of additional web servlets which will be exposed
-plugin-services = screening-image-download-servlet
+plugin-services = screening-image-download-servlet, tabular-data-graph-servlet
 # class of the web servlet
 #screening-image-download-servlet.class = ch.systemsx.cisd.openbis.dss.generic.server.MergingImagesDownloadServlet
 screening-image-download-servlet.class = ch.systemsx.cisd.openbis.dss.generic.server.SplittingImagesDownloadServlet
 # URL which will be mapped to this servlet
 screening-image-download-servlet.path = /datastore_server_screening/*
 
+tabular-data-graph-servlet.class = ch.systemsx.cisd.openbis.dss.generic.server.TabularDataGraphServlet
+# URL which will be mapped to this servlet
+tabular-data-graph-servlet.path = /datastore_server_graph/*
+tabular-data-graph-servlet.properties-file = etc/tabular-data-graph.properties
+
 # ---------------------------------------------------------------------------
 
 # Comma separated names of processing threads. Each thread should have configuration properties prefixed with its name.
diff --git a/screening/etc/tabular-data-graph.properties b/screening/etc/tabular-data-graph.properties
new file mode 100644
index 0000000000000000000000000000000000000000..9c3080b91851b38697b87b2c6134516d45d59781
--- /dev/null
+++ b/screening/etc/tabular-data-graph.properties
@@ -0,0 +1,3 @@
+# ------------------------------------------------------------------
+# Properties File For Generating Graphs From Tabular Data
+# ------------------------------------------------------------------
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/TabularDataGraphGeneratorServlet.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/TabularDataGraphGeneratorServlet.java
deleted file mode 100644
index 95c58d2db477225a563bd1f23cb04ea780350ffc..0000000000000000000000000000000000000000
--- a/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/TabularDataGraphGeneratorServlet.java
+++ /dev/null
@@ -1,206 +0,0 @@
-/*
- * Copyright 2010 ETH Zuerich, CISD
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package ch.systemsx.cisd.openbis.dss.generic.server;
-
-import java.io.IOException;
-import java.util.Enumeration;
-import java.util.Properties;
-
-import javax.servlet.ServletConfig;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import javax.servlet.http.HttpSession;
-
-import ch.systemsx.cisd.common.exceptions.UserFailureException;
-import ch.systemsx.cisd.openbis.dss.generic.server.graph.TabularDataGraphGenerator;
-import ch.systemsx.cisd.openbis.dss.generic.server.plugins.tasks.DatasetFileLines;
-
-/**
- * @author Chandrasekhar Ramakrishnan
- */
-public class TabularDataGraphGeneratorServlet extends AbstractDatasetDownloadServlet
-{
-
-    private static final long serialVersionUID = 1L;
-
-    // Required servlet parameters
-
-    public final static String DATASET_CODE_PARAM = "dataset";
-
-    public final static String GRAPH_TYPE_CODE = "type";
-
-    // Optional, but recommended servlet parameters
-
-    public final static String IMAGE_WIDTH_PARAM = "w";
-
-    public final static String IMAGE_HEIGHT_PARAM = "h";
-
-    // Default values for optional parameters
-    public final static int DEFAULT_WIDTH = 300;
-
-    public final static int DEFAULT_HEIGHT = 200;
-
-    /**
-     * A utility class for dealing with the parameters required to generate an image from a
-     * chromatogram. This class makes sure all the required parameters are in the request (it throws
-     * exceptions otherwise), and it defaults values for all optional parameters if they are not in
-     * the request.
-     * 
-     * @author Chandrasekhar Ramakrishnan
-     */
-    private static class RequestParams
-    {
-        private final String sessionId;
-
-        private final String datasetCode;
-
-        private final String graphTypeCode;
-
-        private final int width;
-
-        private final int height;
-
-        public RequestParams(HttpServletRequest request)
-        {
-            sessionId = getParam(request, SESSION_ID_PARAM);
-            datasetCode = getParam(request, DATASET_CODE_PARAM);
-            graphTypeCode = getParam(request, GRAPH_TYPE_CODE);
-            width = getIntParam(request, IMAGE_WIDTH_PARAM, DEFAULT_WIDTH);
-            height = getIntParam(request, IMAGE_HEIGHT_PARAM, DEFAULT_HEIGHT);
-        }
-
-        private static int getIntParam(HttpServletRequest request, String paramName,
-                int defaultValue)
-        {
-            String value = request.getParameter(paramName);
-            if (value == null)
-                return defaultValue;
-
-            try
-            {
-                return Integer.valueOf(value);
-            } catch (NumberFormatException e)
-            {
-                throw new UserFailureException("parameter " + paramName
-                        + " should be an integer, but is: " + value);
-            }
-        }
-
-        private static String getParam(final HttpServletRequest request, String paramName)
-        {
-            String value = request.getParameter(paramName);
-            if (value == null)
-            {
-                throw new UserFailureException("no value for the parameter " + paramName
-                        + " found in the URL");
-            }
-            return value;
-        }
-
-        public String getSessionId()
-        {
-            return sessionId;
-        }
-
-        public String getDatasetCode()
-        {
-            return datasetCode;
-        }
-
-        public String getGraphTypeCode()
-        {
-            return graphTypeCode;
-        }
-
-        public int getWidth()
-        {
-            return width;
-        }
-
-        public int getHeight()
-        {
-            return height;
-        }
-    }
-
-    // The properties needed for connecting to the database
-    private Properties dbProperties;
-
-    @Override
-    protected synchronized void doSpecificInitialization(Enumeration<String> parameterNames,
-            ServletConfig servletConfig)
-    {
-        // Only initialize the db properties once
-        if (dbProperties != null)
-            return;
-
-        dbProperties = new Properties();
-        String name;
-        while (parameterNames.hasMoreElements())
-        {
-            name = parameterNames.nextElement();
-            dbProperties.setProperty(name, servletConfig.getInitParameter(name));
-        }
-    }
-
-    @Override
-    protected final void doGet(final HttpServletRequest request, final HttpServletResponse response)
-            throws ServletException, IOException
-    {
-        try
-        {
-            // Get the parameters from the request
-            RequestParams params = new RequestParams(request);
-            String sessionId = params.getSessionId();
-            String datasetCode = params.getDatasetCode();
-            int height = params.getHeight();
-            int width = params.getWidth();
-
-            // Get the session and user from the request
-            HttpSession session = tryGetOrCreateSession(request, sessionId);
-            if (session == null)
-            {
-                printSessionExpired(response);
-                return;
-            }
-            // Check that the user has view access to the chromatogram data
-            // NOTE: This throws an exception -- it may be nicer to return an image for a
-            // non-accessible chromatogram...
-            ensureDatasetAccessible(datasetCode, session, sessionId);
-
-            // Get the chromatogram data
-            DatasetFileLines fileLines = tryDatasetLinesForParameters(params);
-
-            // Generate a chromatogram image into the stream
-            TabularDataGraphGenerator generator =
-                    new TabularDataGraphGenerator(fileLines, response.getOutputStream(), width,
-                            height);
-            generator.generateImage();
-
-        } catch (Exception e)
-        {
-            printErrorResponse(response, "Invalid Request");
-            e.printStackTrace();
-        }
-    }
-
-    private DatasetFileLines tryDatasetLinesForParameters(RequestParams params)
-    {
-        return null;
-    }
-}
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/TabularDataGraphServlet.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/TabularDataGraphServlet.java
new file mode 100644
index 0000000000000000000000000000000000000000..0c6b2da27aa827f0e3972be1a96423bf0f9646f4
--- /dev/null
+++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/TabularDataGraphServlet.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright 2010 ETH Zuerich, CISD
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package ch.systemsx.cisd.openbis.dss.generic.server;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+
+import com.csvreader.CsvReader;
+
+import ch.systemsx.cisd.common.exceptions.UserFailureException;
+import ch.systemsx.cisd.openbis.dss.generic.server.graph.ITabularDataGraph;
+import ch.systemsx.cisd.openbis.dss.generic.server.graph.TabularDataGraphCollectionConfiguration;
+import ch.systemsx.cisd.openbis.dss.generic.server.plugins.tasks.DatasetFileLines;
+
+/**
+ * @author Chandrasekhar Ramakrishnan
+ */
+public class TabularDataGraphServlet extends AbstractDatasetDownloadServlet
+{
+    // Required servlet parameters
+    public final static String DATASET_CODE_PARAM = "dataset";
+
+    public final static String FILE_PATH_PARAM = "file";
+
+    public final static String GRAPH_TYPE_CODE = "type";
+
+    private static final long serialVersionUID = 1L;
+
+    private TabularDataGraphCollectionConfiguration configuration;
+
+    private final static String PROPERTIES_FILE_KEY = "properties-file";
+
+    /**
+     * A utility class for dealing with the URL parameters required to generate an image. This class
+     * makes sure all the required parameters are in the request (it throws exceptions otherwise),
+     * and it defaults values for all optional parameters if they are not in the request.
+     * 
+     * @author Chandrasekhar Ramakrishnan
+     */
+    private static class RequestParams
+    {
+        private final String sessionId;
+
+        private final String datasetCode;
+
+        private final String filePath;
+
+        private final String graphTypeCode;
+
+        public RequestParams(HttpServletRequest request)
+        {
+            sessionId = getRequiredParameter(request, SESSION_ID_PARAM);
+            datasetCode = getRequiredParameter(request, DATASET_CODE_PARAM);
+            filePath = getRequiredParameter(request, FILE_PATH_PARAM);
+            graphTypeCode = getRequiredParameter(request, GRAPH_TYPE_CODE);
+        }
+
+        private static String getRequiredParameter(final HttpServletRequest request,
+                String paramName)
+        {
+            String value = request.getParameter(paramName);
+            if (value == null)
+            {
+                throw new UserFailureException("no value for the parameter " + paramName
+                        + " found in the URL");
+            }
+            return value;
+        }
+    }
+
+    @Override
+    protected synchronized void doSpecificInitialization(Enumeration<String> parameterNames,
+            ServletConfig servletConfig)
+    {
+        // Only initialize the db properties once
+        if (configuration != null)
+            return;
+
+        String propertiesFilePath = servletConfig.getInitParameter(PROPERTIES_FILE_KEY);
+        configuration =
+                TabularDataGraphCollectionConfiguration.getConfiguration(propertiesFilePath);
+    }
+
+    @Override
+    protected final void doGet(final HttpServletRequest request, final HttpServletResponse response)
+            throws ServletException, IOException
+    {
+        try
+        {
+            // Get the parameters from the request
+            RequestParams params = new RequestParams(request);
+            String sessionId = params.sessionId;
+            String datasetCode = params.datasetCode;
+            String filePath = params.filePath;
+
+            // Get the session and user from the request
+            HttpSession session = tryGetOrCreateSession(request, sessionId);
+            if (session == null)
+            {
+                printSessionExpired(response);
+                return;
+            }
+            // Check that the user has view access to the data
+            // NOTE: This throws an exception -- it may be nicer to return an image for a
+            // non-accessible dataset...
+            ensureDatasetAccessible(datasetCode, session, sessionId);
+
+            // Get the tabular data
+            DatasetFileLines fileLines = getDatasetFileLines(filePath);
+
+            // Generate an image image into the stream
+            ITabularDataGraph generator =
+                    configuration.getGraph(params.graphTypeCode, fileLines, response
+                            .getOutputStream());
+            generator.generateImage();
+
+        } catch (Exception e)
+        {
+            printErrorResponse(response, "Invalid Request");
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * Return the tabular data as a DatasetFileLines.
+     */
+    private DatasetFileLines getDatasetFileLines(String path) throws IOException
+    {
+        File file = new File(path);
+        CsvReader reader = getCsvReader(file);
+        List<String[]> lines = new ArrayList<String[]>();
+        while (reader.readRecord())
+        {
+            lines.add(reader.getValues());
+        }
+
+        return new DatasetFileLines(file, path, lines);
+    }
+
+    /**
+     * Get a CsvReader for parsing a tabular data file.
+     */
+    private CsvReader getCsvReader(File file) throws IOException
+    {
+        if (file.isFile() == false)
+        {
+            throw new UserFailureException(file + " does not exist or is not a file.");
+        }
+        FileInputStream fileInputStream = new FileInputStream(file);
+
+        CsvReader csvReader = new CsvReader(fileInputStream, Charset.defaultCharset());
+        csvReader.setDelimiter(configuration.getColumnDelimiter());
+        csvReader.setSkipEmptyRecords(true);
+        csvReader.setUseComments(configuration.isIgnoreComments());
+        csvReader.setComment(configuration.getCommentDelimiter());
+
+        return csvReader;
+    }
+}
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/graph/AbstractTabularDataGraph.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/graph/AbstractTabularDataGraph.java
index 926a5bbc575b9b0f998c0488c0692181c964c0f6..202adf9e9a20d6eaa82886eae519c00c5933d6fc 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/graph/AbstractTabularDataGraph.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/graph/AbstractTabularDataGraph.java
@@ -34,7 +34,8 @@ import ch.systemsx.cisd.openbis.dss.generic.server.plugins.tasks.DatasetFileLine
  * 
  * @author Chandrasekhar Ramakrishnan
  */
-public abstract class AbstractTabularDataGraph<T extends TabularDataGraphConfiguration>
+abstract class AbstractTabularDataGraph<T extends TabularDataGraphConfiguration> implements
+        ITabularDataGraph
 {
     protected final T configuration;
 
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/graph/ITabularDataGraph.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/graph/ITabularDataGraph.java
new file mode 100644
index 0000000000000000000000000000000000000000..59c01edd3775176cdc932e0a14facc6ab441875e
--- /dev/null
+++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/graph/ITabularDataGraph.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2010 ETH Zuerich, CISD
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package ch.systemsx.cisd.openbis.dss.generic.server.graph;
+
+import java.io.IOException;
+
+/**
+ * @author Chandrasekhar Ramakrishnan
+ */
+public interface ITabularDataGraph
+{
+
+    // Public API
+    /**
+     * Create an image from the file lines and write it to the output stream.
+     * 
+     * @throws IOException
+     */
+    public abstract void generateImage() throws IOException;
+
+}
\ No newline at end of file
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/graph/TabularDataGraphCollectionConfiguration.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/graph/TabularDataGraphCollectionConfiguration.java
new file mode 100644
index 0000000000000000000000000000000000000000..19bf44fa8b7628db2ec9ab3d207bf0559087b879
--- /dev/null
+++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/graph/TabularDataGraphCollectionConfiguration.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright 2010 ETH Zuerich, CISD
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package ch.systemsx.cisd.openbis.dss.generic.server.graph;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Properties;
+
+import ch.systemsx.cisd.common.exceptions.EnvironmentFailureException;
+import ch.systemsx.cisd.common.utilities.PropertyUtils;
+import ch.systemsx.cisd.openbis.dss.generic.server.plugins.tasks.DatasetFileLines;
+
+/**
+ * @author Chandrasekhar Ramakrishnan
+ */
+public class TabularDataGraphCollectionConfiguration
+{
+    private static final String SEPARATOR_PROPERTY_KEY = "separator";
+
+    private static final String IGNORE_COMMENTS_PROPERTY_KEY = "ignore-comments";
+
+    // the seperator in the file
+    private final char separator;
+
+    private final boolean ignoreComments;
+
+    // the comment marker in the file
+    private final char comment;
+
+    private final ArrayList<String> graphTypeCodes;
+
+    private final HashMap<String, TabularDataGraphConfiguration> graphTypeMap;
+
+    /**
+     * Create a configuration from the properties file located at path.
+     * 
+     * @param path Path to the properties file.
+     */
+    public static TabularDataGraphCollectionConfiguration getConfiguration(String path)
+            throws EnvironmentFailureException
+    {
+        Properties configurationProps = new Properties();
+        try
+        {
+            configurationProps.load(new FileInputStream(path));
+        } catch (FileNotFoundException ex)
+        {
+            throw new EnvironmentFailureException("Could not find the configuration file "
+                    + new File(path).getAbsolutePath());
+        } catch (IOException ex)
+        {
+            throw new EnvironmentFailureException("Could not read the configuration file " + path);
+        }
+
+        return new TabularDataGraphCollectionConfiguration(configurationProps);
+    }
+
+    /**
+     * Initialize the configuration based on the properties object.
+     */
+    private TabularDataGraphCollectionConfiguration(Properties properties)
+    {
+        comment = '#';
+
+        this.separator = PropertyUtils.getChar(properties, SEPARATOR_PROPERTY_KEY, ';');
+        this.ignoreComments =
+                PropertyUtils.getBoolean(properties, IGNORE_COMMENTS_PROPERTY_KEY, true);
+        graphTypeCodes = new ArrayList<String>();
+        graphTypeMap = new HashMap<String, TabularDataGraphConfiguration>();
+        initialzeGraphTypeMap(properties);
+
+    }
+
+    private void initialzeGraphTypeMap(Properties properties)
+    {
+        TabularDataGraphConfiguration config =
+                new TabularDataGraphConfiguration("Test", "TotalCells", "InfectedCells", 300, 200);
+        graphTypeMap.put("scatter", config);
+        graphTypeCodes.add("scatter");
+    }
+
+    /**
+     * Return the graph configuration associated with the graphTypeCode.
+     * 
+     * @param graphTypeCode The name of the graph type
+     */
+    public TabularDataGraphConfiguration getGraphConfiguration(String graphTypeCode)
+    {
+        TabularDataGraphConfiguration config = graphTypeMap.get(graphTypeCode);
+        if (null == config)
+        {
+            throw new IllegalArgumentException("No graph associated with code " + graphTypeCode);
+        }
+        return config;
+    }
+
+    /**
+     * Return the graph generator associated with the graphTypeCode, initialized by the fileLines
+     * and out.
+     * 
+     * @param graphTypeCode The name of the graph type
+     * @param fileLines The data to generate a graph from
+     * @param out The stream to write the graph to
+     */
+    public ITabularDataGraph getGraph(String graphTypeCode, DatasetFileLines fileLines,
+            OutputStream out)
+    {
+        TabularDataGraphConfiguration config = graphTypeMap.get(graphTypeCode);
+        if (null == config)
+        {
+            throw new IllegalArgumentException("No graph associated with code " + graphTypeCode);
+        }
+        return new TabularDataScatterplot(config, fileLines, out);
+    }
+
+    public char getColumnDelimiter()
+    {
+        return separator;
+    }
+
+    /**
+     * Should comments be ignored?
+     */
+    public boolean isIgnoreComments()
+    {
+        return ignoreComments;
+    }
+
+    public char getCommentDelimiter()
+    {
+        return comment;
+    }
+
+    public int getImageWidth()
+    {
+        return 800;
+    }
+
+    public int getImageHeight()
+    {
+        return 600;
+    }
+
+    public int getThumbnailWidth()
+    {
+        return 300;
+    }
+
+    public int getThumbnailHeight()
+    {
+        return 200;
+    }
+
+    public List<String> getGraphTypeCodes()
+    {
+        return graphTypeCodes;
+    }
+}
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/graph/TabularDataGraphConfiguration.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/graph/TabularDataGraphConfiguration.java
index b7cf659499c631bf04c36bcb441c2884d0593280..5a45d0bcde3111f58df452d3aaa777b3352a1465 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/graph/TabularDataGraphConfiguration.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/graph/TabularDataGraphConfiguration.java
@@ -44,7 +44,7 @@ public class TabularDataGraphConfiguration
      * @param imageWidth The desired width of the resulting image
      * @param imageHeight The desired height of the resulting image
      */
-    protected TabularDataGraphConfiguration(String title, String xAxisColumn,
+    public TabularDataGraphConfiguration(String title, String xAxisColumn,
             String yAxisColumn, int imageWidth, int imageHeight)
     {
         this.title = title;
@@ -57,7 +57,7 @@ public class TabularDataGraphConfiguration
     /**
      * The title for the resulting image.
      */
-    protected String getTitle()
+    public String getTitle()
     {
         return title;
     }
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/graph/TabularDataGraphGenerator.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/graph/TabularDataGraphGenerator.java
deleted file mode 100644
index 0dd108990b3cef36612cc92096eeacf40d0c5d1f..0000000000000000000000000000000000000000
--- a/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/graph/TabularDataGraphGenerator.java
+++ /dev/null
@@ -1,302 +0,0 @@
-/*
- * Copyright 2010 ETH Zuerich, CISD
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package ch.systemsx.cisd.openbis.dss.generic.server.graph;
-
-import java.awt.Color;
-import java.awt.Font;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.util.List;
-
-import org.jfree.chart.ChartFactory;
-import org.jfree.chart.ChartUtilities;
-import org.jfree.chart.JFreeChart;
-import org.jfree.chart.axis.Axis;
-import org.jfree.chart.axis.ValueAxis;
-import org.jfree.chart.plot.PlotOrientation;
-import org.jfree.chart.plot.XYPlot;
-import org.jfree.chart.renderer.xy.XYBarRenderer;
-import org.jfree.chart.renderer.xy.XYItemRenderer;
-import org.jfree.chart.renderer.xy.XYSplineRenderer;
-import org.jfree.data.xy.IntervalXYDataset;
-import org.jfree.data.xy.XYSeries;
-import org.jfree.data.xy.XYSeriesCollection;
-
-import ch.systemsx.cisd.openbis.dss.generic.server.plugins.tasks.DatasetFileLines;
-
-/**
- * Generates a graph from tabular data. Both the graph type and the columns used for the graph can
- * be configured.
- * 
- * @author Chandrasekhar Ramakrishnan
- */
-public class TabularDataGraphGenerator
-{
-    public static enum ChartType
-    {
-        AREA, BAR, LINE, SPLINE
-    }
-
-    public final static ChartType DEFAULT_CHART_TYPE = ChartType.LINE;
-
-    private final String title;
-
-    private final String xAxisHeader;
-
-    private final String yAxisHeader;
-
-    private final DatasetFileLines fileLines;
-
-    private final int imageWidth;
-
-    private final int imageHeight;
-
-    private final ChartType chartType;
-
-    private final OutputStream out;
-
-    /**
-     * Initialize a chromatogram generator. The chart type is defaulted to DEFAULT_CHART_TYPE.
-     * 
-     * @param fileLines The data to generate a graph from.
-     * @param out The stream to write the image to
-     * @param imageWidth The width in pixels of the generated image
-     * @param imageHeight The height in pixels of the generated image
-     */
-    public TabularDataGraphGenerator(DatasetFileLines fileLines, OutputStream out,
-            int imageWidth, int imageHeight)
-    {
-        this("Chart", "TotalCells", "InfectedCells", imageWidth, imageHeight, DEFAULT_CHART_TYPE,
-                fileLines, out);
-    }
-
-    /**
-     * Initialize a chromatogram generator.
-     * 
-     * @param title The title of the graph
-     * @param xAxisColumnHeader The header of the data column used to create the x-axis
-     * @param yAxisColumnHeader The header of the data column used to create the y-axis
-     * @param imageWidth The width in pixels of the generated image
-     * @param imageHeight The height in pixels of the generated image
-     * @param chartType One of AREA, BAR, LINE, or SPLINE
-     * @param fileLines The data to generate a graph from.
-     * @param out The stream to write the image to.
-     */
-    public TabularDataGraphGenerator(String title, String xAxisColumnHeader,
-            String yAxisColumnHeader, int imageWidth, int imageHeight, ChartType chartType,
-            DatasetFileLines fileLines, OutputStream out)
-    {
-        this.title = title;
-        this.xAxisHeader = xAxisColumnHeader;
-        this.yAxisHeader = yAxisColumnHeader;
-        this.imageWidth = imageWidth;
-        this.imageHeight = imageHeight;
-        this.chartType = chartType;
-        this.fileLines = fileLines;
-        this.out = out;
-    }
-
-    /**
-     * Create an image from the chromatogram and write it to the output stream.
-     * 
-     * @throws IOException
-     */
-    public void generateImage() throws IOException
-    {
-        JFreeChart chart = createChart(tryCreateDataset());
-        ChartUtilities.writeChartAsPNG(out, chart, imageWidth, imageHeight);
-    }
-
-    private JFreeChart createChart(IntervalXYDataset dataset)
-    {
-        JFreeChart chart = null;
-        switch (chartType)
-        {
-            case AREA:
-                chart = createAreaChart(dataset);
-                break;
-            case BAR:
-                chart = createBarChart(dataset);
-                break;
-            case LINE:
-                chart = createLineChart(dataset);
-                break;
-            case SPLINE:
-                chart = createSplineChart(dataset);
-                break;
-        }
-
-        return chart;
-    }
-
-    private JFreeChart createBarChart(IntervalXYDataset dataset)
-    {
-        JFreeChart chart = ChartFactory.createXYBarChart(title, // title
-                "Run Time", // x-axis label
-                false, // use date axis?
-                "Intensity", // y-axis label
-                dataset, // data
-                PlotOrientation.VERTICAL, // plot orientation
-                false, // create legend?
-                false, // generate tooltips?
-                false // generate URLs?
-                );
-
-        XYPlot plot = configureChart(chart);
-
-        XYItemRenderer r = plot.getRenderer();
-        if (r instanceof XYBarRenderer)
-        {
-            XYBarRenderer renderer = (XYBarRenderer) r;
-            renderer.setShadowVisible(false);
-        }
-
-        return chart;
-    }
-
-    private JFreeChart createLineChart(IntervalXYDataset dataset)
-    {
-        JFreeChart chart = ChartFactory.createXYLineChart(title, // title
-                "run time", // x-axis label
-                "intensity", // y-axis label
-                dataset, // data
-                PlotOrientation.VERTICAL, // plot orientation
-                false, // create legend?
-                false, // generate tooltips?
-                false // generate URLs?
-                );
-
-        configureChart(chart);
-
-        return chart;
-    }
-
-    private JFreeChart createSplineChart(IntervalXYDataset dataset)
-    {
-        JFreeChart chart = ChartFactory.createXYLineChart(title, // title
-                "run time", // x-axis label
-                "intensity", // y-axis label
-                dataset, // data
-                PlotOrientation.VERTICAL, // plot orientation
-                false, // create legend?
-                false, // generate tooltips?
-                false // generate URLs?
-                );
-
-        XYPlot plot = configureChart(chart);
-
-        XYSplineRenderer renderer = new XYSplineRenderer();
-        renderer.setSeriesShapesVisible(0, false);
-        plot.setRenderer(renderer);
-
-        return chart;
-    }
-
-    private JFreeChart createAreaChart(IntervalXYDataset dataset)
-    {
-        JFreeChart chart = ChartFactory.createXYAreaChart(title, // title
-                "run time", // x-axis label
-                "intensity", // y-axis label
-                dataset, // data
-                PlotOrientation.VERTICAL, // plot orientation
-                false, // create legend?
-                false, // generate tooltips?
-                false // generate URLs?
-                );
-
-        configureChart(chart);
-
-        return chart;
-    }
-
-    /**
-     * Create a JFreeChart dataset from the chromatogram data.
-     */
-    private IntervalXYDataset tryCreateDataset()
-    {
-        XYSeries s1 = new XYSeries(title);
-        String[] headers = fileLines.getHeaderTokens();
-        // figure out which indices are used for the x- and y-axes
-        int xIndex = -1, yIndex = -1, i = 0;
-        for (String header : headers)
-        {
-            if (xAxisHeader.equals(header))
-            {
-                xIndex = i;
-            } else if (yAxisHeader.equals(header))
-            {
-                yIndex = i;
-            }
-            ++i;
-        }
-
-        // The data set did not contain the expected headers.
-        if (xIndex < 0 || yIndex < 0)
-        {
-            return null;
-        }
-
-        List<String[]> lines = fileLines.getDataLines();
-        for (String[] line : lines)
-        {
-            float x = Float.valueOf(line[xIndex]);
-            float y = Float.valueOf(line[yIndex]);
-            s1.add(x, y);
-        }
-
-        XYSeriesCollection dataset = new XYSeriesCollection();
-        dataset.addSeries(s1);
-
-        return dataset;
-    }
-
-    /**
-     * Apply the visual preferences to the chart.
-     */
-    private XYPlot configureChart(JFreeChart chart)
-    {
-        Font font = JFreeChart.DEFAULT_TITLE_FONT;
-        chart.getTitle().setFont(cloneFontWithNewSize(font, Math.max(11, imageHeight / 30)));
-        chart.setBackgroundPaint(Color.WHITE);
-
-        XYPlot plot = (XYPlot) chart.getPlot();
-        setAxisLabelFontSize(plot.getDomainAxis());
-        setAxisLabelFontSize(plot.getRangeAxis());
-        plot.setBackgroundPaint(Color.WHITE);
-        plot.setDomainGridlinesVisible(false);
-        plot.setRangeGridlinesVisible(true);
-        plot.setRangeGridlinePaint(Color.LIGHT_GRAY);
-        plot.setDomainCrosshairVisible(false);
-        plot.setRangeCrosshairVisible(false);
-
-        ValueAxis axis = plot.getDomainAxis();
-        axis.setAutoRange(true);
-
-        return plot;
-    }
-
-    private void setAxisLabelFontSize(Axis axis)
-    {
-        Font labelFont = axis.getLabelFont();
-        axis.setLabelFont(cloneFontWithNewSize(labelFont, Math.max(10, imageHeight / 40)));
-    }
-
-    private Font cloneFontWithNewSize(Font font, int newSize)
-    {
-        return new Font(font.getName(), font.getStyle(), newSize);
-    }
-}
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/graph/TabularDataHeatmap.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/graph/TabularDataHeatmap.java
index 4ec497952ef1af961e98e78fcc0f96c330324e03..ed8e3c3e07fb4321f1abb5fd823db68127c137ab 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/graph/TabularDataHeatmap.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/graph/TabularDataHeatmap.java
@@ -25,6 +25,7 @@ import java.util.regex.Pattern;
 
 import org.jfree.chart.JFreeChart;
 import org.jfree.chart.axis.NumberAxis;
+import org.jfree.chart.axis.NumberTickUnit;
 import org.jfree.chart.plot.PlotOrientation;
 import org.jfree.chart.plot.XYPlot;
 import org.jfree.chart.renderer.GrayPaintScale;
@@ -46,6 +47,8 @@ import ch.systemsx.cisd.openbis.dss.generic.server.plugins.tasks.DatasetFileLine
 public class TabularDataHeatmap extends AbstractTabularDataGraph<TabularDataHeatmapConfiguration>
 {
 
+    private final Pattern xySplitterPattern;
+
     /**
      * @param configuration
      */
@@ -53,6 +56,7 @@ public class TabularDataHeatmap extends AbstractTabularDataGraph<TabularDataHeat
             DatasetFileLines fileLines, OutputStream out)
     {
         super(configuration, fileLines, out);
+        xySplitterPattern = Pattern.compile("([A-Z])([0-9]*)");
     }
 
     @Override
@@ -103,6 +107,7 @@ public class TabularDataHeatmap extends AbstractTabularDataGraph<TabularDataHeat
         }
         NumberAxis xAxis = new NumberAxis(xAxisLabel);
         xAxis.setAutoRangeIncludesZero(false);
+        xAxis.setTickUnit(new NumberTickUnit(1.));
         NumberAxis yAxis = new NumberAxis(yAxisLabel);
 
         XYBlockRenderer renderer = new XYBlockRenderer();
@@ -170,7 +175,7 @@ public class TabularDataHeatmap extends AbstractTabularDataGraph<TabularDataHeat
 
     private void splitColumnIntoXandY(String string, HeatmapElement element)
     {
-        Pattern p = Pattern.compile("([A-Z])([0-9]*)");
+        Pattern p = xySplitterPattern;
         Matcher m = p.matcher(string);
         if (m.matches())
         {
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/ImageAnalysisGraphReportingPlugin.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/ImageAnalysisGraphReportingPlugin.java
new file mode 100644
index 0000000000000000000000000000000000000000..6431cae5d3132ffb055035549042bc74962a896c
--- /dev/null
+++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/generic/server/plugins/ImageAnalysisGraphReportingPlugin.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright 2009 ETH Zuerich, CISD
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package ch.systemsx.cisd.openbis.dss.generic.server.plugins;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+
+import ch.systemsx.cisd.common.exceptions.EnvironmentFailureException;
+import ch.systemsx.cisd.common.exceptions.UserFailureException;
+import ch.systemsx.cisd.openbis.dss.generic.server.TabularDataGraphServlet;
+import ch.systemsx.cisd.openbis.dss.generic.server.graph.TabularDataGraphCollectionConfiguration;
+import ch.systemsx.cisd.openbis.dss.generic.server.graph.TabularDataGraphConfiguration;
+import ch.systemsx.cisd.openbis.dss.generic.server.plugins.standard.AbstractDataMergingReportingPlugin;
+import ch.systemsx.cisd.openbis.dss.generic.server.plugins.tasks.SimpleTableModelBuilder;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.GeneratedImageTableCell;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ISerializableComparable;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.TableModel;
+import ch.systemsx.cisd.openbis.generic.shared.dto.DatasetDescription;
+
+/**
+ * Reporting plugin that returns a table in which each column contains a graph. The number and
+ * format of the graphs can be configured in a properties file.
+ * 
+ * @author Chandrasekhar Ramakrishnan
+ */
+public class ImageAnalysisGraphReportingPlugin extends AbstractDataMergingReportingPlugin
+{
+    private static final long serialVersionUID = 1L;
+
+    private final String graphServletPath;
+
+    private final TabularDataGraphCollectionConfiguration configuration;
+
+    // Keys for the properties
+    private final String SERVLET_PATH_PROP = "servlet-path";
+
+    private final static String PROPERTIES_FILE_KEY = "properties-file";
+
+    public ImageAnalysisGraphReportingPlugin(Properties properties, File storeRoot)
+    {
+        super(properties, storeRoot, SEMICOLON_SEPARATOR);
+        graphServletPath = properties.getProperty(SERVLET_PATH_PROP, "datastore_server_graph/");
+        String propertiesFilePath = properties.getProperty(PROPERTIES_FILE_KEY);
+        if (propertiesFilePath == null)
+        {
+            throw new EnvironmentFailureException(
+                    "ImageAnalysisGraphReportingPlugin requires a properties file (specified with the "
+                            + PROPERTIES_FILE_KEY + "key).");
+        }
+
+        configuration =
+                TabularDataGraphCollectionConfiguration.getConfiguration(propertiesFilePath);
+    }
+
+    public TableModel createReport(List<DatasetDescription> datasets)
+    {
+        SimpleTableModelBuilder builder = new SimpleTableModelBuilder();
+        addHeaders(builder);
+        if (datasets.isEmpty())
+        {
+            return builder.getTableModel();
+        }
+        for (DatasetDescription dataset : datasets)
+        {
+            final File dir = getDataSubDir(dataset);
+            List<File> matchingFiles = findMatchingFiles(dataset, dir);
+            if (matchingFiles.size() > 1)
+            {
+                throw UserFailureException.fromTemplate(
+                        "Found multiple candidate files in the dataset %s ", dataset
+                                .getDatasetCode());
+            }
+            builder.addRow(createRow(dataset.getDatasetCode(), matchingFiles.get(0)));
+        }
+        return builder.getTableModel();
+    }
+
+    /**
+     * Add the headers to the table -- these depend on the configuration
+     */
+    private void addHeaders(SimpleTableModelBuilder builder)
+    {
+        builder.addHeader("Data Set Code");
+        int width = getThumbnailWidth();
+        for (String graphTypeCode : getGraphTypeCodes())
+        {
+            TabularDataGraphConfiguration graphConfig =
+                    configuration.getGraphConfiguration(graphTypeCode);
+            builder.addHeader(graphConfig.getTitle(), width);
+        }
+    }
+
+    private List<ISerializableComparable> createRow(String datasetCode, File file)
+    {
+        List<ISerializableComparable> row = new ArrayList<ISerializableComparable>();
+
+        // The data set code
+        row.add(SimpleTableModelBuilder.asText(datasetCode));
+
+        for (String graphTypeCode : getGraphTypeCodes())
+        {
+
+            GeneratedImageTableCell imageCell =
+                    new GeneratedImageTableCell(graphServletPath, getImageWidth(),
+                            getImageHeight(), getThumbnailWidth(), getThumbnailHeight());
+            imageCell.addParameter(TabularDataGraphServlet.DATASET_CODE_PARAM, datasetCode);
+            imageCell.addParameter(TabularDataGraphServlet.FILE_PATH_PARAM, file.getAbsolutePath());
+            imageCell.addParameter(TabularDataGraphServlet.GRAPH_TYPE_CODE, graphTypeCode);
+
+            row.add(imageCell);
+        }
+        
+        return row;
+    }
+
+    private List<String> getGraphTypeCodes()
+    {
+        return configuration.getGraphTypeCodes();
+    }
+
+    private int getImageWidth()
+    {
+        return configuration.getImageWidth();
+    }
+
+    private int getImageHeight()
+    {
+        return configuration.getImageHeight();
+    }
+
+    private int getThumbnailWidth()
+    {
+        return configuration.getThumbnailWidth();
+    }
+
+    private int getThumbnailHeight()
+    {
+        return configuration.getThumbnailHeight();
+    }
+}
diff --git a/screening/sourceTest/java/ch/systemsx/cisd/openbis/dss/generic/server/graph/TabularDataHeatmapTest.java b/screening/sourceTest/java/ch/systemsx/cisd/openbis/dss/generic/server/graph/TabularDataHeatmapTest.java
index 94f1bd136e5e48606f59a151b485a16b4eb9d06c..b8ca69d7222434c7c35339e52bca911b10585aa4 100644
--- a/screening/sourceTest/java/ch/systemsx/cisd/openbis/dss/generic/server/graph/TabularDataHeatmapTest.java
+++ b/screening/sourceTest/java/ch/systemsx/cisd/openbis/dss/generic/server/graph/TabularDataHeatmapTest.java
@@ -29,7 +29,7 @@ public class TabularDataHeatmapTest extends AbstractTabularDataGraphTest
     @Test
     public void testHeatmap() throws IOException
     {
-        File outputFile = getTestImageOutputFile();
+        File outputFile = getImageOutputFile();
 
         TabularDataHeatmapConfiguration config =
                 new TabularDataHeatmapConfiguration("Test", "WellName", "InfectionIndex", 300, 200);