From 432556fba6e015fe91d92692fc5e0ddb39bf1dc4 Mon Sep 17 00:00:00 2001
From: felmer <felmer>
Date: Tue, 7 Aug 2007 14:05:20 +0000
Subject: [PATCH] add general purpose create method (with test)

SVN: 1268
---
 .../cisd/common/utilities/ClassUtils.java     | 48 +++++++++++
 .../cisd/common/utilities/ClassUtilsTest.java | 85 ++++++++++++++++++-
 2 files changed, 131 insertions(+), 2 deletions(-)

diff --git a/common/source/java/ch/systemsx/cisd/common/utilities/ClassUtils.java b/common/source/java/ch/systemsx/cisd/common/utilities/ClassUtils.java
index 71670c9604b..625fde0bf8b 100644
--- a/common/source/java/ch/systemsx/cisd/common/utilities/ClassUtils.java
+++ b/common/source/java/ch/systemsx/cisd/common/utilities/ClassUtils.java
@@ -16,13 +16,17 @@
 
 package ch.systemsx.cisd.common.utilities;
 
+import java.lang.reflect.Constructor;
 import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Properties;
 
 import ch.systemsx.cisd.common.annotation.Mandatory;
 import ch.systemsx.cisd.common.exceptions.CheckedExceptionTunnel;
+import ch.systemsx.cisd.common.exceptions.ConfigurationFailureException;
 
 /**
  * Operations on classes using reflection.
@@ -125,4 +129,48 @@ public final class ClassUtils
         return null;
     }
 
+    /**
+     * Creates a new instance of a class specified by its fully-qualified name.
+     * 
+     * @param superClazz Super class <code>className</code> has to be implemented or extended.
+     * @param className Fully-qualified class name.
+     * @param properties Optional constructor argument. If not <code>null</code> an constructor with one single
+     *            <code>Properties</code> argument is expected. Otherwise the default constructor will used.
+     * @return an instance of type <code>interfaze</code>.
+     */
+    public static <T> T create(Class<T> superClazz, String className, Properties properties)
+    {
+        assert superClazz != null : "Missing super class";
+        assert className != null : "Missing class name";
+        
+        try
+        {
+            final Class<?> clazz = Class.forName(className);
+            assert clazz.isInterface() == false : clazz + " can not be instanciated";
+            assert superClazz.isAssignableFrom(clazz) : clazz + " does not implements/extends " + superClazz.getName();
+            if (properties == null)
+            {
+                return createInstance(clazz);
+            }
+            final Constructor<?> constructor = clazz.getConstructor(new Class[] {Properties.class});
+            return createInstance(constructor, properties);
+        } catch (Exception ex)
+        {
+            throw new ConfigurationFailureException("Cannot instanitate class " + className, ex);
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    private static <T> T createInstance(final Class<?> clazz) throws InstantiationException, IllegalAccessException
+    {
+        return (T) clazz.newInstance();
+    }
+    
+    @SuppressWarnings("unchecked")
+    private static <T> T createInstance(final Constructor<?> constructor, Properties properties)
+            throws InstantiationException, IllegalAccessException, InvocationTargetException
+    {
+        return (T) constructor.newInstance(new Object[] {properties});
+    }
+    
 }
diff --git a/common/sourceTest/java/ch/systemsx/cisd/common/utilities/ClassUtilsTest.java b/common/sourceTest/java/ch/systemsx/cisd/common/utilities/ClassUtilsTest.java
index 8cfb894e4c0..50ac8d2142f 100644
--- a/common/sourceTest/java/ch/systemsx/cisd/common/utilities/ClassUtilsTest.java
+++ b/common/sourceTest/java/ch/systemsx/cisd/common/utilities/ClassUtilsTest.java
@@ -16,7 +16,14 @@
 
 package ch.systemsx.cisd.common.utilities;
 
-import static org.testng.AssertJUnit.*;
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertNull;
+import static org.testng.AssertJUnit.assertSame;
+import static org.testng.AssertJUnit.assertTrue;
+import static org.testng.AssertJUnit.fail;
+
+import java.io.IOException;
+import java.util.Properties;
 
 import org.testng.annotations.Test;
 
@@ -71,4 +78,78 @@ public final class ClassUtilsTest
         // we will have 'ch.systemsx.cisd.common.utilities.ClassUtilsTest.privateMethodOnStack()' here.
         assertNull(ClassUtils.getMethodOnStack(1));
     }
-}
\ No newline at end of file
+    
+    @Test
+    public void testCreateWithDefaultConstructor()
+    {
+        CharSequence cs = ClassUtils.create(CharSequence.class, StringBuffer.class.getName(), null);
+        assertTrue(cs instanceof StringBuffer);
+        assertEquals(0, cs.length());
+    }
+    
+    @Test
+    public void testCreateWithPropertiesConstructor()
+    {
+        Properties properties = new Properties();
+        Appendable appendable = ClassUtils.create(Appendable.class, MyClass.class.getName(), properties);
+        assertTrue(appendable instanceof MyClass);
+        assertSame(properties, ((MyClass) appendable).getProperties());
+    }
+    
+    public static class MyClass implements Appendable
+    {
+        private final Properties properties;
+
+        public MyClass(Properties properties)
+        {
+            this.properties = properties;
+        }
+        
+        public final Properties getProperties()
+        {
+            return properties;
+        }
+
+        public Appendable append(char c) throws IOException
+        {
+            return null;
+        }
+
+        public Appendable append(CharSequence csq, int start, int end) throws IOException
+        {
+            return null;
+        }
+
+        public Appendable append(CharSequence csq) throws IOException
+        {
+            return null;
+        }
+        
+    }
+    
+    @Test
+    public void testCreateWithIncompatibleSuperclass()
+    {
+        try
+        {
+            ClassUtils.create(Float.class, Integer.class.getName(), null);
+            fail("AssertionError expected.");
+        } catch (AssertionError e)
+        {
+            assertEquals("class java.lang.Integer does not implements/extends java.lang.Float", e.getMessage());
+        }
+    }
+    
+    @Test
+    public void testCreateInstanceOfAnInterface()
+    {
+        try
+        {
+            ClassUtils.create(Float.class, CharSequence.class.getName(), null);
+            fail("AssertionError expected.");
+        } catch (AssertionError e)
+        {
+            assertEquals("interface java.lang.CharSequence can not be instanciated", e.getMessage());
+        }
+    }
+}
-- 
GitLab