diff --git a/bds/source/java/ch/systemsx/cisd/bds/Channel.java b/bds/source/java/ch/systemsx/cisd/bds/Channel.java
new file mode 100644
index 0000000000000000000000000000000000000000..72b6d170c5555e48eac990c13437e307c6da0108
--- /dev/null
+++ b/bds/source/java/ch/systemsx/cisd/bds/Channel.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2007 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.bds;
+
+import ch.systemsx.cisd.bds.storage.IDirectory;
+
+/**
+ * A channel is composed of only one child: <code>wavelength</code>.
+ * <p>
+ * Each channel has its <code>counter</code> which uniquely identifies it.
+ * </p>
+ * 
+ * @author Christian Ribeaud
+ */
+public final class Channel implements IStorable
+{
+
+    static final String CHANNEL = "channel";
+
+    static final String WAVELENGTH = "wavelength";
+
+    private final int counter;
+
+    private final int wavelength;
+
+    public Channel(final int counter, final int wavelength)
+    {
+        assert counter > 0 : "Given counter must be > 0.";
+        this.counter = counter;
+        this.wavelength = wavelength;
+    }
+
+    public final int getWavelength()
+    {
+        return wavelength;
+    }
+
+    final static Channel loadFrom(final IDirectory directory)
+    {
+        final String name = directory.getName();
+        assert name.startsWith(CHANNEL);
+        return new Channel(parseCounter(name), Utilities.getNumber(directory, WAVELENGTH));
+    }
+
+    private static int parseCounter(final String name)
+    {
+        try
+        {
+            return Integer.parseInt(name.substring(CHANNEL.length()));
+        } catch (NumberFormatException ex)
+        {
+            throw new DataStructureException(String.format("Could not parse the channel number in '%s'", name), ex);
+        }
+    }
+
+    //
+    // IStorable
+    //
+
+    public final void saveTo(final IDirectory directory)
+    {
+        final IDirectory channelDirectory = directory.makeDirectory(CHANNEL + counter);
+        channelDirectory.addKeyValuePair(WAVELENGTH, getWavelength() + "");
+    }
+
+    //
+    // Object
+    //
+
+    @Override
+    public final boolean equals(Object obj)
+    {
+        if (obj == this)
+        {
+            return true;
+        }
+        if (obj instanceof Channel == false)
+        {
+            return false;
+        }
+        final Channel channel = (Channel) obj;
+        return channel.counter == counter;
+    }
+
+    @Override
+    public final int hashCode()
+    {
+        return 17 * 37 + counter;
+    }
+
+    @Override
+    public final String toString()
+    {
+        return CHANNEL + counter + "[" + wavelength + "=" + getWavelength() + "]";
+    }
+}
diff --git a/bds/source/java/ch/systemsx/cisd/bds/ChannelList.java b/bds/source/java/ch/systemsx/cisd/bds/ChannelList.java
new file mode 100644
index 0000000000000000000000000000000000000000..e13741a1d9fed616630d3d973e2ee2f833424422
--- /dev/null
+++ b/bds/source/java/ch/systemsx/cisd/bds/ChannelList.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2007 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.bds;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import ch.systemsx.cisd.bds.storage.IDirectory;
+import ch.systemsx.cisd.bds.storage.INode;
+
+/**
+ * The list of encapsulated <code>Channels</code> available here.
+ * 
+ * @author Christian Ribeaud
+ */
+public final class ChannelList implements IStorable
+{
+    static final String NUMBER_OF_CHANNELS = "number_of_channels";
+
+    private final List<Channel> channels;
+
+    public ChannelList(final List<Channel> channels)
+    {
+        assert channels.size() > 0 : "At least one channel must be specified.";
+        this.channels = channels;
+    }
+
+    /**
+     * Loads all <code>Channels</code> from the specified directory.
+     * 
+     * @throws DataStructureException if the <code>Channels</code> could be loaded.
+     */
+    final static ChannelList loadFrom(final IDirectory directory)
+    {
+        final List<Channel> channels = new ArrayList<Channel>();
+        for (INode node : directory)
+        {
+            if (node.getName().startsWith(Channel.CHANNEL))
+            {
+                assert node instanceof IDirectory : "Must be an IDirectory";
+                channels.add(Channel.loadFrom((IDirectory) node));
+            }
+        }
+        return new ChannelList(channels);
+    }
+
+    /** Returns an unmodifiable list of <code>Channel</code>. */
+    public final List<Channel> getChannels()
+    {
+        return Collections.unmodifiableList(channels);
+    }
+
+    //
+    // IStorable
+    //
+
+    public final void saveTo(final IDirectory directory)
+    {
+        directory.addKeyValuePair(NUMBER_OF_CHANNELS, channels.size() + "");
+        for (Channel channel : channels)
+        {
+            channel.saveTo(directory);
+        }
+    }
+}
diff --git a/bds/source/java/ch/systemsx/cisd/bds/DataStructureV1_0.java b/bds/source/java/ch/systemsx/cisd/bds/DataStructureV1_0.java
index 8b55dc2b65020e23f722a869f995af609ef438ec..1447c1b2330cbf13ce52c0c5ffde3c56651afd04 100644
--- a/bds/source/java/ch/systemsx/cisd/bds/DataStructureV1_0.java
+++ b/bds/source/java/ch/systemsx/cisd/bds/DataStructureV1_0.java
@@ -42,19 +42,21 @@ public class DataStructureV1_0 extends AbstractDataStructure
     static final String CHECKSUM_DIRECTORY = "md5sum";
 
     static final String DIR_METADATA = "metadata";
-    
+
     static final String DIR_PARAMETERS = "parameters";
 
     static final String DIR_DATA = "data";
 
     static final String DIR_ORIGINAL = "original";
-    
+
     static final String MAPPING_FILE = "standard_original_mapping";
 
     private static final Version VERSION = new Version(1, 0);
-    
+
     private final ChecksumBuilder checksumBuilder = new ChecksumBuilder(new MD5ChecksumCalculator());
+
     private final Map<String, Reference> standardOriginalMapping = new LinkedHashMap<String, Reference>();
+
     private final FormatParameters formatParameters = new FormatParameters();
 
     private Format format;
@@ -121,7 +123,7 @@ public class DataStructureV1_0 extends AbstractDataStructure
         assert formatParameter != null : "Unspecified format parameter.";
         formatParameters.addParameter(formatParameter);
     }
-    
+
     /**
      * Returns the experiment identifier.
      * 
@@ -143,7 +145,7 @@ public class DataStructureV1_0 extends AbstractDataStructure
         assertOpenOrCreated();
         id.saveTo(getMetaDataDirectory());
     }
-    
+
     /**
      * Returns the date of registration of the experiment.
      * 
@@ -155,7 +157,7 @@ public class DataStructureV1_0 extends AbstractDataStructure
         assertOpenOrCreated();
         return ExperimentRegistratorDate.loadFrom(getMetaDataDirectory());
     }
-    
+
     /**
      * Sets the date of registration of the experiment.
      */
@@ -176,14 +178,14 @@ public class DataStructureV1_0 extends AbstractDataStructure
         assertOpenOrCreated();
         return ExperimentRegistrator.loadFrom(getMetaDataDirectory());
     }
-    
+
     public void setExperimentRegistrator(ExperimentRegistrator registrator)
     {
         assert registrator != null : "Unspecified experiment registrator.";
         assertOpenOrCreated();
         registrator.saveTo(getMetaDataDirectory());
     }
-    
+
     /**
      * Returns the measurement entity.
      * 
@@ -217,7 +219,7 @@ public class DataStructureV1_0 extends AbstractDataStructure
         assertOpenOrCreated();
         return ProcessingType.loadFrom(getMetaDataDirectory());
     }
-    
+
     /**
      * Sets the processing type. Overwrites an already set or loaded value.
      */
@@ -227,7 +229,7 @@ public class DataStructureV1_0 extends AbstractDataStructure
         assertOpenOrCreated();
         type.saveTo(getMetaDataDirectory());
     }
-    
+
     /**
      * Returns the standard-original mapping.
      * 
@@ -237,7 +239,7 @@ public class DataStructureV1_0 extends AbstractDataStructure
     {
         return Collections.unmodifiableMap(standardOriginalMapping);
     }
-    
+
     /**
      * Adds a reference to the standard-original mapping.
      * 
@@ -254,7 +256,7 @@ public class DataStructureV1_0 extends AbstractDataStructure
         }
         standardOriginalMapping.put(path, reference);
     }
-    
+
     @Override
     protected void assertValid()
     {
@@ -335,7 +337,7 @@ public class DataStructureV1_0 extends AbstractDataStructure
             standardOriginalMapping.put(path, new Reference(path, referenceDefinition.substring(i2 + 1), type));
         }
     }
-    
+
     @Override
     protected void performClosing()
     {
@@ -343,9 +345,9 @@ public class DataStructureV1_0 extends AbstractDataStructure
         IDirectory checksumDirectory = metaDataDirectory.makeDirectory(CHECKSUM_DIRECTORY);
         String checksumsOfOriginal = checksumBuilder.buildChecksumsForAllFilesIn(getOriginalData());
         checksumDirectory.addKeyValuePair(DIR_ORIGINAL, checksumsOfOriginal);
-        
+
         formatParameters.saveTo(getParametersDirectory());
-        
+
         StringWriter writer = new StringWriter();
         PrintWriter printWriter = new PrintWriter(writer, true);
         Collection<Reference> values = standardOriginalMapping.values();
@@ -358,7 +360,7 @@ public class DataStructureV1_0 extends AbstractDataStructure
         }
         printWriter.close();
         metaDataDirectory.addKeyValuePair(MAPPING_FILE, writer.toString());
-        
+
         if (metaDataDirectory.tryToGetNode(Format.FORMAT_DIR) == null && format != null)
         {
             format.saveTo(metaDataDirectory);
@@ -374,10 +376,10 @@ public class DataStructureV1_0 extends AbstractDataStructure
     {
         return Utilities.getOrCreateSubDirectory(root, DIR_METADATA);
     }
-    
+
     private IDirectory getParametersDirectory()
     {
         return Utilities.getOrCreateSubDirectory(getMetaDataDirectory(), DIR_PARAMETERS);
     }
-    
+
 }
diff --git a/bds/source/java/ch/systemsx/cisd/bds/ExperimentIdentifier.java b/bds/source/java/ch/systemsx/cisd/bds/ExperimentIdentifier.java
index 5a66b75bca27e01502d594867dbe0ffff4a46d11..3540ac25f61b14017addaf82947e470fb631eead 100644
--- a/bds/source/java/ch/systemsx/cisd/bds/ExperimentIdentifier.java
+++ b/bds/source/java/ch/systemsx/cisd/bds/ExperimentIdentifier.java
@@ -20,17 +20,20 @@ import ch.systemsx.cisd.bds.storage.IDirectory;
 
 /**
  * Identifier of the experiment which corresponds to the data. This is an immutable but extendable value object class.
- * An instance of this class allows unique identification in the database. 
- *
+ * An instance of this class allows unique identification in the database.
+ * 
  * @author Franz-Josef Elmer
  */
-public class ExperimentIdentifier
+public class ExperimentIdentifier implements IStorable
 {
     static final String FOLDER = "experiment_identifier";
+
     static final String GROUP_CODE = "group_code";
+
     static final String PROJECT_CODE = "project_code";
+
     static final String EXPERIMENT_CODE = "experiment_code";
-    
+
     /**
      * Loads the experiment identifier from the specified directory.
      * 
@@ -44,14 +47,16 @@ public class ExperimentIdentifier
         String experimentCode = Utilities.getTrimmedString(idFolder, EXPERIMENT_CODE);
         return new ExperimentIdentifier(groupCode, projectCode, experimentCode);
     }
-    
+
     private final String groupCode;
+
     private final String projectCode;
+
     private final String experimentCode;
 
     /**
      * Creates an instance for the specified codes of group, project, and experiment.
-     *
+     * 
      * @param groupCode A non-empty string of the group code.
      * @param projectCode A non-empty string of the project code.
      * @param experimentCode A non-empty string of the experiment code.
@@ -81,7 +86,7 @@ public class ExperimentIdentifier
     {
         return projectCode;
     }
-    
+
     /**
      * Returns the experiment code;
      */
@@ -89,11 +94,15 @@ public class ExperimentIdentifier
     {
         return experimentCode;
     }
-    
+
+    //
+    // IStorable
+    //
+
     /**
      * Saves this instance to the specified directory.
      */
-    void saveTo(IDirectory directory)
+    public final void saveTo(final IDirectory directory)
     {
         IDirectory folder = directory.makeDirectory(FOLDER);
         folder.addKeyValuePair(GROUP_CODE, groupCode);
@@ -128,6 +137,5 @@ public class ExperimentIdentifier
     {
         return "[group:" + groupCode + ",project:" + projectCode + ",experiment:" + experimentCode + "]";
     }
-    
-    
+
 }
diff --git a/bds/source/java/ch/systemsx/cisd/bds/ExperimentRegistrator.java b/bds/source/java/ch/systemsx/cisd/bds/ExperimentRegistrator.java
index 65d97e992be8fe9a15aacd627ac70f19a39af603..4d21cd66e391ca3db98b0d33fdaf50bafc84be47 100644
--- a/bds/source/java/ch/systemsx/cisd/bds/ExperimentRegistrator.java
+++ b/bds/source/java/ch/systemsx/cisd/bds/ExperimentRegistrator.java
@@ -20,18 +20,21 @@ import ch.systemsx.cisd.bds.storage.IDirectory;
 
 /**
  * Registrator of the experiment which corresponds to the data. This is an immutable value object class.
- *
+ * 
  * @author Franz-Josef Elmer
  */
-public final class ExperimentRegistrator
+public final class ExperimentRegistrator implements IStorable
 {
     static final String FOLDER = "experiment_registrator";
+
     static final String FIRST_NAME = "first_name";
+
     static final String SECOND_NAME = "last_name";
+
     static final String EMAIL = "email";
-    
+
     /**
-     * Loads the experiment registaror from the specified directory.
+     * Loads the experiment registrator from the specified directory.
      * 
      * @throws DataStructureException if file missing.
      */
@@ -43,14 +46,16 @@ public final class ExperimentRegistrator
         String email = Utilities.getTrimmedString(folder, EMAIL);
         return new ExperimentRegistrator(firstName, secondName, email);
     }
-    
+
     private final String firstName;
+
     private final String secondName;
+
     private final String email;
 
     /**
      * Creates an instance for the specified name and e-mail of the registrator.
-     *
+     * 
      * @param firstName A non-empty string of the first name.
      * @param secondName A non-empty string of the second name.
      * @param email A non-empty string of the email.
@@ -80,7 +85,7 @@ public final class ExperimentRegistrator
     {
         return secondName;
     }
-    
+
     /**
      * Returns the email.
      */
@@ -88,11 +93,15 @@ public final class ExperimentRegistrator
     {
         return email;
     }
-    
+
+    //
+    // IStorable
+    //
+
     /**
      * Saves this instance to the specified directory.
      */
-    void saveTo(IDirectory directory)
+    public final void saveTo(final IDirectory directory)
     {
         IDirectory folder = directory.makeDirectory(FOLDER);
         folder.addKeyValuePair(FIRST_NAME, firstName);
@@ -127,6 +136,5 @@ public final class ExperimentRegistrator
     {
         return firstName + " " + secondName + ", e-mail:" + email;
     }
-    
-    
+
 }
diff --git a/bds/source/java/ch/systemsx/cisd/bds/ExperimentRegistratorDate.java b/bds/source/java/ch/systemsx/cisd/bds/ExperimentRegistratorDate.java
index e2a842040e287f9c6b935fc519533d4b7616b66b..b7563dec65d968a151edf7a02078ff4070454c2a 100644
--- a/bds/source/java/ch/systemsx/cisd/bds/ExperimentRegistratorDate.java
+++ b/bds/source/java/ch/systemsx/cisd/bds/ExperimentRegistratorDate.java
@@ -23,15 +23,16 @@ import java.util.Date;
 import ch.systemsx.cisd.bds.storage.IDirectory;
 
 /**
- * Immutable class which holds the date of registration of an experiment. 
- *
+ * Immutable class which holds the date of registration of an experiment.
+ * 
  * @author Franz-Josef Elmer
  */
-public final class ExperimentRegistratorDate
+public final class ExperimentRegistratorDate implements IStorable
 {
     static final String FILE_NAME = "experiment_registration_date";
+
     private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z");
-    
+
     static ExperimentRegistratorDate loadFrom(IDirectory directory)
     {
         String dateAsString = Utilities.getTrimmedString(directory, FILE_NAME);
@@ -43,9 +44,9 @@ public final class ExperimentRegistratorDate
             throw new DataStructureException("Couldn't be parsed as a date: " + dateAsString);
         }
     }
-    
+
     private final Date date;
-    
+
     /**
      * Creates an instance for the specified date.
      */
@@ -61,11 +62,15 @@ public final class ExperimentRegistratorDate
     {
         return date;
     }
-    
+
+    //
+    // IStorable
+    //
+
     /**
      * Saves this instance to the specified directory.
      */
-    void saveTo(IDirectory directory)
+    public final void saveTo(IDirectory directory)
     {
         directory.addKeyValuePair(FILE_NAME, DATE_FORMAT.format(date));
     }
@@ -95,5 +100,5 @@ public final class ExperimentRegistratorDate
     {
         return DATE_FORMAT.format(date);
     }
-    
+
 }
diff --git a/bds/source/java/ch/systemsx/cisd/bds/Format.java b/bds/source/java/ch/systemsx/cisd/bds/Format.java
index 2c6b91ab4a9114902162cc549a1e95e439bd09ea..ed66c35b628860f156e32e9d2b0c60facfdf7cd3 100644
--- a/bds/source/java/ch/systemsx/cisd/bds/Format.java
+++ b/bds/source/java/ch/systemsx/cisd/bds/Format.java
@@ -22,13 +22,15 @@ import ch.systemsx.cisd.bds.storage.INode;
 
 /**
  * Inmutable value object of a versioned format.
- *
+ * 
  * @author Franz-Josef Elmer
  */
-public class Format
+public class Format implements IStorable
 {
     static final String FORMAT_CODE_FILE = "format_code";
+
     static final String FORMAT_DIR = "format";
+
     static final String FORMAT_VARIANT_FILE = "format_variant";
 
     /**
@@ -64,9 +66,11 @@ public class Format
         }
         return new Format(formatCode, formatVersion, variant);
     }
-    
+
     private final String code;
+
     private final Version version;
+
     private final String variant;
 
     /**
@@ -80,7 +84,7 @@ public class Format
         this.version = version;
         variant = variantOrNull;
     }
-    
+
     /**
      * Returns the format code.
      */
@@ -107,7 +111,11 @@ public class Format
         return variant;
     }
 
-    void saveTo(IDirectory directory)
+    //
+    // IStorable
+    //
+
+    public final void saveTo(final IDirectory directory)
     {
         IDirectory dir = directory.makeDirectory(FORMAT_DIR);
         dir.addKeyValuePair(FORMAT_CODE_FILE, code);
@@ -130,7 +138,7 @@ public class Format
             return false;
         }
         Format format = (Format) obj;
-        return format.code.equals(code) && format.version.equals(version) 
+        return format.code.equals(code) && format.version.equals(version)
                 && (format.variant == null ? null == variant : format.variant.equals(variant));
     }
 
diff --git a/bds/source/java/ch/systemsx/cisd/bds/FormatParameter.java b/bds/source/java/ch/systemsx/cisd/bds/FormatParameter.java
index 77a834c23a08faf20ef7c619537a4baf1b63a3be..1f6f40d729a10a346e369d81723163e4f4ec1926 100644
--- a/bds/source/java/ch/systemsx/cisd/bds/FormatParameter.java
+++ b/bds/source/java/ch/systemsx/cisd/bds/FormatParameter.java
@@ -17,18 +17,19 @@
 package ch.systemsx.cisd.bds;
 
 /**
- * A format parameter with a name and a string value. 
- *
+ * A format parameter with a name and a string value.
+ * 
  * @author Franz-Josef Elmer
  */
 public final class FormatParameter
 {
     private final String name;
+
     private final String value;
 
     /**
      * Creates an instance for the specified name and value.
-     *
+     * 
      * @param name A non-empty string as the name of the parameter.
      * @param value A non-<code>null</code> string as the value.
      */
diff --git a/bds/source/java/ch/systemsx/cisd/bds/FormatParameters.java b/bds/source/java/ch/systemsx/cisd/bds/FormatParameters.java
index 94f6e217afe81c131ecf0e1fba1fdc19b99e450c..317734aa8de707673cc41548a9b19609754f21c1 100644
--- a/bds/source/java/ch/systemsx/cisd/bds/FormatParameters.java
+++ b/bds/source/java/ch/systemsx/cisd/bds/FormatParameters.java
@@ -26,10 +26,10 @@ import ch.systemsx.cisd.bds.storage.INode;
 
 /**
  * Implementation of {@link IFormatParameters} which allows to add {@link FormatParameter} instances.
- *
+ * 
  * @author Franz-Josef Elmer
  */
-class FormatParameters implements IFormatParameters
+class FormatParameters implements IFormatParameters, IStorable
 {
     private final Map<String, FormatParameter> parameters = new LinkedHashMap<String, FormatParameter>();
 
@@ -45,15 +45,19 @@ class FormatParameters implements IFormatParameters
             }
         }
     }
-    
-    void saveTo(IDirectory directory)
+
+    //
+    // IStorable
+    //
+
+    public final void saveTo(final IDirectory directory)
     {
         for (FormatParameter parameter : parameters.values())
         {
             directory.addKeyValuePair(parameter.getName(), parameter.getValue());
         }
     }
-    
+
     /**
      * Adds the specified parameter.
      * 
@@ -68,7 +72,7 @@ class FormatParameters implements IFormatParameters
         }
         parameters.put(name, parameter);
     }
-    
+
     public String getValue(String parameterName)
     {
         FormatParameter formatParameter = parameters.get(parameterName);
diff --git a/bds/source/java/ch/systemsx/cisd/bds/FormattedDataContext.java b/bds/source/java/ch/systemsx/cisd/bds/FormattedDataContext.java
index 876bb3e091a51675f4601d17f96bc2da3aeb576d..c1c60e5360ac35a82c6fd7b1303fe08d7932ba0a 100644
--- a/bds/source/java/ch/systemsx/cisd/bds/FormattedDataContext.java
+++ b/bds/source/java/ch/systemsx/cisd/bds/FormattedDataContext.java
@@ -19,15 +19,17 @@ package ch.systemsx.cisd.bds;
 import ch.systemsx.cisd.bds.storage.IDirectory;
 
 /**
- * Context of {@link IFormattedData}. Argument of all constructores of concrete implementations of
- * {@link IFormattedData}. 
- *
+ * Context of {@link IFormattedData}. Argument of all constructors of concrete implementations of
+ * {@link IFormattedData}.
+ * 
  * @author Franz-Josef Elmer
  */
 class FormattedDataContext
 {
     private final IDirectory dataDirectory;
+
     private final Format format;
+
     private final IFormatParameters formatParameters;
 
     FormattedDataContext(IDirectory dataDirectory, Format format, IFormatParameters formatParameters)
diff --git a/bds/source/java/ch/systemsx/cisd/bds/Geometry.java b/bds/source/java/ch/systemsx/cisd/bds/Geometry.java
new file mode 100644
index 0000000000000000000000000000000000000000..abafed7aa277a901640006320d85029be66645b8
--- /dev/null
+++ b/bds/source/java/ch/systemsx/cisd/bds/Geometry.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2007 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.bds;
+
+import ch.systemsx.cisd.bds.storage.IDirectory;
+
+/**
+ * A <code>Geometry</code> is composed of 2 dimensions:
+ * <ul>
+ * <li>rows</li>
+ * <li>columns</li>
+ * </ul>
+ * <p>
+ * This class is not <code>abstract</code> but {@link #getGeometryDirectoryName()} must be overridden by subclasses in
+ * order to work properly.
+ * </p>
+ * 
+ * @author Christian Ribeaud
+ */
+public class Geometry implements IStorable
+{
+    static final String NOT_POSITIVE = "Given number '%d' must be > 0.";
+
+    final static String ROWS = "rows";
+
+    final static String COLUMNS = "columns";
+
+    private final int rows;
+
+    private final int columns;
+
+    protected Geometry(final int rows, final int columns)
+    {
+        assert columns > 0 : String.format(NOT_POSITIVE, columns);
+        this.columns = columns;
+        assert rows > 0 : String.format(NOT_POSITIVE, rows);
+        this.rows = rows;
+    }
+
+    /** Return the number of columns this <code>Geometry</code> is composed of. */
+    public final int getColumns()
+    {
+        return columns;
+    }
+
+    /** Return the number of rows this <code>Geometry</code> is composed of. */
+    public final int getRows()
+    {
+        return rows;
+    }
+
+    private final String toString(int number)
+    {
+        return Integer.toString(number);
+    }
+
+    /**
+     * Loads the geometry from the specified directory.
+     */
+    final static Geometry loadFrom(final IDirectory directory, final String geometryDirectoryName)
+    {
+        final IDirectory geometryFolder = Utilities.getSubDirectory(directory, geometryDirectoryName);
+        return new Geometry(Utilities.getNumber(geometryFolder, ROWS), Utilities.getNumber(geometryFolder, COLUMNS));
+    }
+
+    //
+    // IStorable
+    //
+
+    public final void saveTo(final IDirectory directory)
+    {
+        final IDirectory geometryDirectory = directory.makeDirectory(getGeometryDirectoryName());
+        geometryDirectory.addKeyValuePair(ROWS, toString(getRows()));
+        geometryDirectory.addKeyValuePair(COLUMNS, toString(getColumns()));
+    }
+
+    /**
+     * Returns the directory name where this <code>Geometry</code> is saved.
+     * <p>
+     * Currently this method is not supported and must be implemented.
+     * </p>
+     */
+    protected String getGeometryDirectoryName()
+    {
+        throw new UnsupportedOperationException();
+    }
+
+    //
+    // Object
+    //
+
+    @Override
+    public final boolean equals(Object obj)
+    {
+        if (obj == this)
+        {
+            return true;
+        }
+        if (obj instanceof Geometry == false)
+        {
+            return false;
+        }
+        final Geometry geometry = (Geometry) obj;
+        return geometry.rows == rows && geometry.columns == columns;
+    }
+
+    @Override
+    public final int hashCode()
+    {
+
+        return 17 * 37 + getRows() + getColumns();
+    }
+
+    @Override
+    public final String toString()
+    {
+        return getRows() + "x" + getColumns();
+    }
+}
\ No newline at end of file
diff --git a/bds/source/java/ch/systemsx/cisd/bds/IStorable.java b/bds/source/java/ch/systemsx/cisd/bds/IStorable.java
new file mode 100644
index 0000000000000000000000000000000000000000..954c5d2aa85ff4d354318b0bd8ab5a3b6e899b5f
--- /dev/null
+++ b/bds/source/java/ch/systemsx/cisd/bds/IStorable.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2007 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.bds;
+
+import ch.systemsx.cisd.bds.storage.IDirectory;
+
+/**
+ * Each implementation knows how to save itself in given <code>IDirectory</code>.
+ * <p>
+ * Note that each implementation should have a <code>static</code> method <code>loadFrom(IDirectory)</code> that
+ * knows how to construct itself from given <code>IDirectory</code>.
+ * </p>
+ * 
+ * @author Christian Ribeaud
+ */
+public interface IStorable
+{
+
+    /** Saves this implementation to given <var>directory</var>. */
+    public void saveTo(final IDirectory directory);
+}
\ No newline at end of file
diff --git a/bds/source/java/ch/systemsx/cisd/bds/MeasurementEntity.java b/bds/source/java/ch/systemsx/cisd/bds/MeasurementEntity.java
index 46ae7f3c066bbb75bcddb44df72b6a320c3400ae..a9861f378eba6d8e135624c768ff6208f15b3423 100644
--- a/bds/source/java/ch/systemsx/cisd/bds/MeasurementEntity.java
+++ b/bds/source/java/ch/systemsx/cisd/bds/MeasurementEntity.java
@@ -20,15 +20,17 @@ import ch.systemsx.cisd.bds.storage.IDirectory;
 
 /**
  * Enity of measurement or calculation covered by the data. This is an immutable value object class.
- *
+ * 
  * @author Franz-Josef Elmer
  */
-public final class MeasurementEntity
+public final class MeasurementEntity implements IStorable
 {
     static final String FOLDER = "measurement_entity";
+
     static final String ENTITY_TYPE_DESCRIPTION = "entity_type_description";
+
     static final String ENTITY_CODE = "entity_code";
-    
+
     /**
      * Loads the enity from the specified directory.
      * 
@@ -41,8 +43,9 @@ public final class MeasurementEntity
         String entityCode = Utilities.getTrimmedString(folder, ENTITY_CODE);
         return new MeasurementEntity(entityCode, entityTypeDescription);
     }
-    
+
     private final String entityTypeDescription;
+
     private final String entityCode;
 
     /**
@@ -74,11 +77,15 @@ public final class MeasurementEntity
     {
         return entityCode;
     }
-    
+
+    //
+    // IStorable
+    //
+
     /**
      * Saves this instance to the specified directory.
      */
-    void saveTo(IDirectory directory)
+    public final void saveTo(IDirectory directory)
     {
         IDirectory folder = directory.makeDirectory(FOLDER);
         folder.addKeyValuePair(ENTITY_TYPE_DESCRIPTION, entityTypeDescription);
@@ -111,6 +118,5 @@ public final class MeasurementEntity
     {
         return "[" + entityCode + ": " + entityTypeDescription + "]";
     }
-    
-    
+
 }
diff --git a/bds/source/java/ch/systemsx/cisd/bds/PlateGeometry.java b/bds/source/java/ch/systemsx/cisd/bds/PlateGeometry.java
new file mode 100644
index 0000000000000000000000000000000000000000..bbb35951e681c6b84c61638e49647a89b87f24ca
--- /dev/null
+++ b/bds/source/java/ch/systemsx/cisd/bds/PlateGeometry.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2007 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.bds;
+
+import ch.systemsx.cisd.bds.storage.IDirectory;
+
+/**
+ * An <code>AbstractGeometry</code> implementation suitable for <i>Plate</i>.
+ * 
+ * @author Christian Ribeaud
+ */
+public final class PlateGeometry extends Geometry
+{
+
+    static final String PLATE_GEOMETRY = "plate_geometry";
+
+    public PlateGeometry(final int rows, final int columns)
+    {
+        super(rows, columns);
+    }
+
+    /**
+     * Loads the geometry from the specified directory.
+     */
+    static Geometry loadFrom(final IDirectory directory)
+    {
+        return loadFrom(directory, PLATE_GEOMETRY);
+    }
+
+    //
+    // Geometry
+    //
+
+    @Override
+    protected final String getGeometryDirectoryName()
+    {
+        return PLATE_GEOMETRY;
+    }
+
+}
diff --git a/bds/source/java/ch/systemsx/cisd/bds/ProcessingType.java b/bds/source/java/ch/systemsx/cisd/bds/ProcessingType.java
index 86a0964bb387f8d558e3fbf336f3790166fd33f4..94d4c25419e817f350ebaa014eaef932d7633f38 100644
--- a/bds/source/java/ch/systemsx/cisd/bds/ProcessingType.java
+++ b/bds/source/java/ch/systemsx/cisd/bds/ProcessingType.java
@@ -18,18 +18,17 @@ package ch.systemsx.cisd.bds;
 
 import ch.systemsx.cisd.bds.storage.IDirectory;
 
-
 /**
  * Enumeration of processing types.
- *
+ * 
  * @author Franz-Josef Elmer
  */
-public enum ProcessingType
+public enum ProcessingType implements IStorable
 {
     OTHER, RAW_DATA, COMPUTED_DATA;
-    
+
     static final String PROCESSING_TYPE = "processing_type";
-    
+
     /**
      * Resolves the specified string representation of a processing type.
      * 
@@ -47,13 +46,17 @@ public enum ProcessingType
         }
         return OTHER;
     }
-    
+
     static ProcessingType loadFrom(IDirectory directory)
     {
         return resolve(Utilities.getTrimmedString(directory, PROCESSING_TYPE));
     }
-    
-    void saveTo(IDirectory directory)
+
+    //
+    // IStorable
+    //
+
+    public final void saveTo(IDirectory directory)
     {
         directory.addKeyValuePair(PROCESSING_TYPE, toString());
     }
diff --git a/bds/source/java/ch/systemsx/cisd/bds/Utilities.java b/bds/source/java/ch/systemsx/cisd/bds/Utilities.java
index bab9c8ae8ed5c340b5e4190c2301102ea5066644..a4943f6174fed67928c4147c8e83d9a04f63317f 100644
--- a/bds/source/java/ch/systemsx/cisd/bds/Utilities.java
+++ b/bds/source/java/ch/systemsx/cisd/bds/Utilities.java
@@ -16,7 +16,6 @@
 
 package ch.systemsx.cisd.bds;
 
-
 import ch.systemsx.cisd.bds.storage.IDirectory;
 import ch.systemsx.cisd.bds.storage.IFile;
 import ch.systemsx.cisd.bds.storage.INode;
@@ -83,12 +82,14 @@ public class Utilities
      * @param name Name of the file.
      * @throws DataStructureException if the requested file does not exist.
      */
-    public static String getString(IDirectory directory, String name)
+    public static String getString(final IDirectory directory, final String name)
     {
+        assert directory != null : String.format("Given directory can not be null.");
+        assert name != null : String.format("Given name can not be null.");
         INode node = directory.tryToGetNode(name);
         if (node == null)
         {
-            throw new DataStructureException("File '" + name + "' missing in " + directory);
+            throw new DataStructureException("File '" + name + "' missing in '" + directory + "'.");
         }
         if (node instanceof IFile == false)
         {
@@ -102,4 +103,17 @@ public class Utilities
     {
     }
 
+    /** For given <code>IDirectory</code> returns the number value corresponding to given <var>name</var>. */
+    public final static int getNumber(final IDirectory directory, final String name)
+    {
+        // No assertion here as 'getString(IDirectory, String)' already does it.
+        String value = getTrimmedString(directory, name);
+        try
+        {
+            return Integer.parseInt(value);
+        } catch (NumberFormatException ex)
+        {
+            throw new DataStructureException("Value of " + name + " version file is not a number: " + value);
+        }
+    }
 }
diff --git a/bds/source/java/ch/systemsx/cisd/bds/Version.java b/bds/source/java/ch/systemsx/cisd/bds/Version.java
index 58918ab300e16d844fc0cf7e3efd61a94d1d48c6..11721a4366375401ed19ad398a4c3016baa16d32 100644
--- a/bds/source/java/ch/systemsx/cisd/bds/Version.java
+++ b/bds/source/java/ch/systemsx/cisd/bds/Version.java
@@ -19,43 +19,34 @@ package ch.systemsx.cisd.bds;
 import ch.systemsx.cisd.bds.storage.IDirectory;
 
 /**
- * Immutable value object for the version of something.        
- *
+ * Immutable value object for the version of something.
+ * 
  * @author Franz-Josef Elmer
  */
 public final class Version
 {
     static final String VERSION = "version";
+
     static final String MAJOR = "major";
+
     static final String MINOR = "minor";
-    
+
     /**
      * Loads the version from the specified directory.
      */
     static Version loadFrom(IDirectory directory)
     {
         IDirectory versionFolder = Utilities.getSubDirectory(directory, VERSION);
-        return new Version(getNumber(versionFolder, MAJOR), getNumber(versionFolder, MINOR));
-    }
-    
-    private static int getNumber(IDirectory versionFolder, String name)
-    {
-        String value = Utilities.getTrimmedString(versionFolder, name);
-        try
-        {
-            return Integer.parseInt(value);
-        } catch (NumberFormatException ex)
-        {
-            throw new DataStructureException("Value of " + name + " version file is not a number: " + value);
-        }
+        return new Version(Utilities.getNumber(versionFolder, MAJOR), Utilities.getNumber(versionFolder, MINOR));
     }
-    
+
     private final int major;
+
     private final int minor;
 
     /**
      * Creates a new instance for the specified major and minor number.
-     *
+     * 
      * @param major A positive number.
      * @param minor A non-negative number.
      */
@@ -82,16 +73,16 @@ public final class Version
     {
         return minor;
     }
-    
+
     /**
-     * Returns <code>true</code> if this version is backwards compatible to the specified version. That is,
-     * if <code>version.getMajor() == this.getMajor()</code> and <code>version.getMinor() &lt;= this.getMinor()</code>.
+     * Returns <code>true</code> if this version is backwards compatible to the specified version. That is, if
+     * <code>version.getMajor() == this.getMajor()</code> and <code>version.getMinor() &lt;= this.getMinor()</code>.
      */
     public boolean isBackwardsCompatibleWith(Version version)
     {
         return version.major == major && version.minor <= minor;
     }
-    
+
     /**
      * Returns the previous minor version.
      * 
@@ -105,7 +96,7 @@ public final class Version
         }
         return new Version(major, minor - 1);
     }
-    
+
     void saveTo(IDirectory directory)
     {
         IDirectory versionFolder = directory.makeDirectory(VERSION);
@@ -139,7 +130,5 @@ public final class Version
     {
         return "V" + major + "." + minor;
     }
-    
-    
-    
+
 }
diff --git a/bds/source/java/ch/systemsx/cisd/bds/WellGeometry.java b/bds/source/java/ch/systemsx/cisd/bds/WellGeometry.java
new file mode 100644
index 0000000000000000000000000000000000000000..893e9b6ef06113c77b3761912d3dd8823388de39
--- /dev/null
+++ b/bds/source/java/ch/systemsx/cisd/bds/WellGeometry.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2007 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.bds;
+
+import ch.systemsx.cisd.bds.storage.IDirectory;
+
+/**
+ * An <code>AbstractGeometry</code> implementation suitable for <i>Well</i>.
+ * 
+ * @author Christian Ribeaud
+ */
+public final class WellGeometry extends Geometry
+{
+
+    static final String WELL_GEOMETRY = "well_geometry";
+
+    public WellGeometry(final int rows, final int columns)
+    {
+        super(rows, columns);
+    }
+
+    /**
+     * Loads the geometry from the specified directory.
+     */
+    static Geometry loadFrom(final IDirectory directory)
+    {
+        return loadFrom(directory, WELL_GEOMETRY);
+    }
+
+    //
+    // Geometry
+    //
+
+    @Override
+    protected final String getGeometryDirectoryName()
+    {
+        return WELL_GEOMETRY;
+    }
+
+}
diff --git a/bds/source/java/ch/systemsx/cisd/bds/storage/filesystem/Directory.java b/bds/source/java/ch/systemsx/cisd/bds/storage/filesystem/Directory.java
index 108c981c2bce993cc9565669f0207d3839ff6e9c..963379eea42293a396259a6f343754b45004c649 100644
--- a/bds/source/java/ch/systemsx/cisd/bds/storage/filesystem/Directory.java
+++ b/bds/source/java/ch/systemsx/cisd/bds/storage/filesystem/Directory.java
@@ -30,6 +30,8 @@ import ch.systemsx.cisd.common.exceptions.EnvironmentFailureException;
 import ch.systemsx.cisd.common.utilities.FileUtilities;
 
 /**
+ * An <code>IDirectory</code> implementation.
+ * 
  * @author Franz-Josef Elmer
  */
 class Directory extends AbstractNode implements IDirectory
diff --git a/bds/source/java/ch/systemsx/cisd/bds/storage/filesystem/NodeFactory.java b/bds/source/java/ch/systemsx/cisd/bds/storage/filesystem/NodeFactory.java
index 0ae75b1d5f9947a31d329faacfed2da6484cebff..f851a4c3d93f0367cce2f0ffc9d2397dcd3424f7 100644
--- a/bds/source/java/ch/systemsx/cisd/bds/storage/filesystem/NodeFactory.java
+++ b/bds/source/java/ch/systemsx/cisd/bds/storage/filesystem/NodeFactory.java
@@ -22,11 +22,21 @@ import ch.systemsx.cisd.bds.storage.INode;
 import ch.systemsx.cisd.common.exceptions.EnvironmentFailureException;
 
 /**
+ * A <code>INode</code> factory class.
+ * <p>
+ * You should prefer to use this class instead of directly instantiate the corresponding <code>INode</code>
+ * implementations.
+ * </p>
+ * 
  * @author Franz-Josef Elmer
  */
-class NodeFactory
+public final class NodeFactory
 {
-    static INode createNode(java.io.File file) throws EnvironmentFailureException
+
+    /**
+     * A <code>INode</code> factory method for given <var>file</var>.
+     */
+    public static INode createNode(final java.io.File file) throws EnvironmentFailureException
     {
         assert file != null : "Unspecified node";
         String absolutePath = file.getAbsolutePath();
@@ -50,5 +60,6 @@ class NodeFactory
 
     private NodeFactory()
     {
+        // Can not be instantiated.
     }
 }
diff --git a/bds/sourceTest/java/ch/systemsx/cisd/bds/UtilitiesTest.java b/bds/sourceTest/java/ch/systemsx/cisd/bds/UtilitiesTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..d16f1db54a468e7383c2c2d940cb0fb522fc660f
--- /dev/null
+++ b/bds/sourceTest/java/ch/systemsx/cisd/bds/UtilitiesTest.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2007 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.bds;
+
+import java.io.File;
+
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import ch.systemsx.cisd.bds.storage.IDirectory;
+import ch.systemsx.cisd.bds.storage.IFile;
+import ch.systemsx.cisd.bds.storage.filesystem.NodeFactory;
+import ch.systemsx.cisd.common.logging.LogInitializer;
+import ch.systemsx.cisd.common.utilities.FileUtilities;
+
+import static org.testng.AssertJUnit.*;
+
+/**
+ * Test cases for corresponding {@link Utilities} class.
+ * 
+ * @author Christian Ribeaud
+ */
+public class UtilitiesTest
+{
+    private static final File UNIT_TEST_ROOT_DIRECTORY = new File("targets" + File.separator + "unit-test-wd");
+
+    private static final File WORKING_DIRECTORY =
+            new File(UNIT_TEST_ROOT_DIRECTORY, UtilitiesTest.class.getSimpleName());
+
+    @BeforeMethod
+    public final void setUp()
+    {
+        LogInitializer.init();
+        FileUtilities.deleteRecursively(WORKING_DIRECTORY);
+        WORKING_DIRECTORY.mkdirs();
+        assert WORKING_DIRECTORY.isDirectory() && WORKING_DIRECTORY.listFiles().length == 0;
+        WORKING_DIRECTORY.deleteOnExit();
+    }
+
+    @Test
+    public final void testGetNumber()
+    {
+        final IDirectory directory = (IDirectory) NodeFactory.createNode(WORKING_DIRECTORY);
+        final String key = "age";
+        final String value = "35";
+        final IFile file = directory.addKeyValuePair(key, value);
+        final File[] listFiles = WORKING_DIRECTORY.listFiles();
+        assertEquals(1, listFiles.length);
+        assertEquals(key, listFiles[0].getName());
+        assertEquals(value, file.getStringContent().trim());
+        try
+        {
+            Utilities.getNumber(null, null);
+            fail("Directory and name can not be null.");
+        } catch (AssertionError ex)
+        {
+            // Nothing to do here
+        }
+        try
+        {
+            Utilities.getNumber(directory, "doesNotExist");
+            fail("File 'doesNotExist' missing");
+        } catch (DataStructureException ex)
+        {
+            // Nothing to do here
+        }
+        assertEquals(35, Utilities.getNumber(directory, key));
+    }
+}
\ No newline at end of file