From a54e98fd72c8c7980b022a0f202a249c7059c07a Mon Sep 17 00:00:00 2001
From: ribeaudc <ribeaudc>
Date: Fri, 26 Oct 2007 19:18:03 +0000
Subject: [PATCH] add: - 'IChecksum', 'IChecksumWriter' and 'ChecksumWriter'. -
 Unit test for 'IChecksumWriter'. change: - '.classpath' which used 'fast-md5'
 library.

SVN: 2266
---
 bds/.classpath                                |   1 +
 .../ch/systemsx/cisd/bds/ChecksumWriter.java  | 159 ++++++++++++++++++
 .../java/ch/systemsx/cisd/bds/IChecksum.java  |  37 ++++
 .../ch/systemsx/cisd/bds/IChecksumWriter.java |  42 +++++
 .../systemsx/cisd/bds/ChecksumWriterTest.java |  90 ++++++++++
 5 files changed, 329 insertions(+)
 create mode 100644 bds/source/java/ch/systemsx/cisd/bds/ChecksumWriter.java
 create mode 100644 bds/source/java/ch/systemsx/cisd/bds/IChecksum.java
 create mode 100644 bds/source/java/ch/systemsx/cisd/bds/IChecksumWriter.java
 create mode 100644 bds/sourceTest/java/ch/systemsx/cisd/bds/ChecksumWriterTest.java

diff --git a/bds/.classpath b/bds/.classpath
index f0cc47d3a9b..71f7dd90f65 100644
--- a/bds/.classpath
+++ b/bds/.classpath
@@ -13,5 +13,6 @@
 	<classpathentry kind="lib" path="/libraries/jmock/third-party-libs/hamcrest-library-1.0.jar"/>
 	<classpathentry kind="lib" path="/libraries/jmock/third-party-libs/objenesis-1.0.jar"/>
 	<classpathentry kind="lib" path="/libraries/log4j/log4j.jar" sourcepath="/libraries/log4j/src.zip"/>
+	<classpathentry kind="lib" path="/libraries/fast-md5/fast-md5.jar" sourcepath="/libraries/fast-md5/src.zip"/>
 	<classpathentry kind="output" path="targets/classes"/>
 </classpath>
diff --git a/bds/source/java/ch/systemsx/cisd/bds/ChecksumWriter.java b/bds/source/java/ch/systemsx/cisd/bds/ChecksumWriter.java
new file mode 100644
index 00000000000..8c8441b400a
--- /dev/null
+++ b/bds/source/java/ch/systemsx/cisd/bds/ChecksumWriter.java
@@ -0,0 +1,159 @@
+/*
+ * 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 java.io.IOException;
+import java.io.Writer;
+import java.util.Arrays;
+import java.util.Comparator;
+
+import com.twmacinta.util.MD5;
+
+import ch.systemsx.cisd.common.exceptions.EnvironmentFailureException;
+import ch.systemsx.cisd.common.utilities.CompositeComparator;
+import ch.systemsx.cisd.common.utilities.FileComparator;
+import ch.systemsx.cisd.common.utilities.FileUtilities;
+
+/**
+ * A default <code>IChecksumWriter</code> implementation.
+ * 
+ * @author Christian Ribeaud
+ */
+public final class ChecksumWriter implements IChecksumWriter
+{
+
+    private final static Comparator<File> BY_TYPE_AND_NAME = createFileComparator();
+
+    private static final String LINE_SEPARATOR = "\n";
+
+    private static final String COLUMN_SEPARATOR = "  ";
+
+    private final IChecksum checksumImpl;
+
+    /** We use only one instance of <code>StringBuilder</code> and recycle it. */
+    private final StringBuilder builder;
+
+    protected File root;
+
+    public ChecksumWriter()
+    {
+        this.checksumImpl = new MD5Checksum();
+        builder = new StringBuilder();
+    }
+
+    @SuppressWarnings("unchecked")
+    private final static Comparator<File> createFileComparator()
+    {
+        return new CompositeComparator<File>(FileComparator.BY_TYPE, FileComparator.BY_NAME);
+    }
+
+    private final static void checkFile(final File file)
+    {
+        assert file != null && file.exists();
+    }
+
+    private final void appendToWriter(final File file, final Writer writer)
+    {
+        try
+        {
+            writer.append(createChecksumLine(file, checksumImpl.getChecksum(file)));
+        } catch (IOException ex)
+        {
+            throw EnvironmentFailureException.fromTemplate(ex, "An I/O exception has been thrown regarding file '%s'.",
+                    file.getAbsolutePath());
+        }
+    }
+
+    private final void internalWriteChecksum(final File file, final Writer writer)
+    {
+        if (file.isFile())
+        {
+            appendToWriter(file, writer);
+        } else if (file.isDirectory())
+        {
+            final File[] files = FileUtilities.listFiles(file);
+            Arrays.sort(files, BY_TYPE_AND_NAME);
+            for (int i = 0; i < files.length; i++)
+            {
+                internalWriteChecksum(files[i], writer);
+            }
+        }
+    }
+
+    protected String getColumnSeparator()
+    {
+        return COLUMN_SEPARATOR;
+    }
+
+    protected String getLineSeparator()
+    {
+        return LINE_SEPARATOR;
+    }
+
+    protected String createChecksumLine(final File file, final String checksum)
+    {
+        builder.setLength(0);
+        builder.append(checksum);
+        builder.append(getColumnSeparator());
+        builder.append(FileUtilities.getRelativeFile(root.getAbsolutePath(), file.getAbsolutePath()));
+        builder.append(getLineSeparator());
+        return builder.toString();
+    }
+
+    //
+    // IChecksumHandler
+    //
+
+    public final void writeChecksum(final File file, final Writer writer)
+    {
+        checkFile(file);
+        root = file;
+        internalWriteChecksum(file, writer);
+    }
+
+    //
+    // Helper classes
+    //
+
+    /**
+     * A <code>IChecksum</code> implementation based on <i>MD5</i>.
+     * 
+     * @author Christian Ribeaud
+     */
+    private final static class MD5Checksum implements IChecksum
+    {
+
+        //
+        // IChecksum
+        //
+
+        public final String getChecksum(final File file) throws EnvironmentFailureException
+        {
+            checkFile(file);
+            try
+            {
+                return MD5.asHex(MD5.getHash(file));
+            } catch (IOException ex)
+            {
+                throw EnvironmentFailureException.fromTemplate(ex,
+                        "An I/O exception has been thrown regarding file '%s'.", file.getAbsolutePath());
+            }
+        }
+
+    }
+}
diff --git a/bds/source/java/ch/systemsx/cisd/bds/IChecksum.java b/bds/source/java/ch/systemsx/cisd/bds/IChecksum.java
new file mode 100644
index 00000000000..5655b5f63bd
--- /dev/null
+++ b/bds/source/java/ch/systemsx/cisd/bds/IChecksum.java
@@ -0,0 +1,37 @@
+/*
+ * 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 ch.systemsx.cisd.common.exceptions.EnvironmentFailureException;
+
+/**
+ * Implementations know how to compute a <a href="http://en.wikipedia.org/wiki/Checksum">checksum</a> from a file.
+ * 
+ * @author Christian Ribeaud
+ */
+public interface IChecksum
+{
+
+    /**
+     * Returns the checksum for given <var>file</var>.
+     * 
+     * @param file could not be <code>null</code> and must exist.
+     */
+    public String getChecksum(final File file) throws EnvironmentFailureException;
+}
diff --git a/bds/source/java/ch/systemsx/cisd/bds/IChecksumWriter.java b/bds/source/java/ch/systemsx/cisd/bds/IChecksumWriter.java
new file mode 100644
index 00000000000..0c959386d28
--- /dev/null
+++ b/bds/source/java/ch/systemsx/cisd/bds/IChecksumWriter.java
@@ -0,0 +1,42 @@
+/*
+ * 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 java.io.Writer;
+
+/**
+ * Implementations write computed checksums to some specified {@link Writer}.
+ * 
+ * @author Christian Ribeaud
+ */
+public interface IChecksumWriter
+{
+
+    /**
+     * Appends computed checksum for given <var>file</var> to given <var>writer</var>.
+     * <p>
+     * If given <var>file</var> is a directory, then recursively calls this method for each file composing the
+     * directory.
+     * </p>
+     * 
+     * @param file can not be <code>null</code> and must exist.
+     * @param writer can not be <code>null</code>.
+     */
+    public void writeChecksum(final File file, final Writer writer);
+
+}
\ No newline at end of file
diff --git a/bds/sourceTest/java/ch/systemsx/cisd/bds/ChecksumWriterTest.java b/bds/sourceTest/java/ch/systemsx/cisd/bds/ChecksumWriterTest.java
new file mode 100644
index 00000000000..86f90ce94d2
--- /dev/null
+++ b/bds/sourceTest/java/ch/systemsx/cisd/bds/ChecksumWriterTest.java
@@ -0,0 +1,90 @@
+/*
+ * 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 static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.fail;
+
+import java.io.File;
+import java.io.StringWriter;
+
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import ch.systemsx.cisd.common.logging.LogInitializer;
+import ch.systemsx.cisd.common.utilities.FileUtilities;
+
+/**
+ * Test cases for corresponding {@link ChecksumWriter} class.
+ * 
+ * @author Christian Ribeaud
+ */
+public class ChecksumWriterTest
+{
+    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, ChecksumWriterTest.class.getSimpleName());
+
+    private IChecksumWriter checksumWriter = new ChecksumWriter();
+
+    @BeforeMethod
+    public final void setUp()
+    {
+        LogInitializer.init();
+        WORKING_DIRECTORY.mkdirs();
+        assert WORKING_DIRECTORY.isDirectory();
+        WORKING_DIRECTORY.deleteOnExit();
+    }
+
+    @Test
+    public final void testWriteChecksum()
+    {
+        FileUtilities.writeToFile(new File(WORKING_DIRECTORY, "file1"), "This is my first file.");
+        FileUtilities.writeToFile(new File(WORKING_DIRECTORY, "abracadabra"), "This is my second file.");
+        FileUtilities.writeToFile(new File(WORKING_DIRECTORY, "zoro.txt"), "This is my third file.");
+        File dir = new File(WORKING_DIRECTORY, "dir");
+        dir.mkdir();
+        FileUtilities.writeToFile(new File(dir, "file2"), "This is my fourth file.");
+        FileUtilities.writeToFile(new File(dir, "escargot.png"), "This is my fifth file.");
+        FileUtilities.writeToFile(new File(dir, "barbapapa"), "This is my sixth file.");
+        try
+        {
+            checksumWriter.writeChecksum(null, null);
+            fail("Null value not allowed here.");
+        } catch (AssertionError e)
+        {
+            // Nothing to do here
+        }
+        final StringWriter writer = new StringWriter();
+        try
+        {
+            checksumWriter.writeChecksum(new File("choubidou"), writer);
+            fail("Given file must exist.");
+        } catch (AssertionError e)
+        {
+            // Nothing to do here
+        }
+        checksumWriter.writeChecksum(WORKING_DIRECTORY, writer);
+        assertEquals("31b98e3c4af8d09595dcef2ce8232fbd  abracadabra\n"
+                + "23b447be20a6ddfe875a8b59ceae83ff  file1\n"
+                + "0ba14d9315ba1c59449eb2fe1dbcbb61  zoro.txt\n"
+                + "23b541115335bbe719c447bb9c942b1a  dir/barbapapa\n"
+                + "b63f5226ef2d2fc6d38342d90869412c  dir/escargot.png\n"
+                + "0f782d8d89620f2a5600eba6565f6bfd  dir/file2\n", writer.toString());
+    }
+}
\ No newline at end of file
-- 
GitLab