From 273047625fa30b4de0cd5c00359e6883202b6101 Mon Sep 17 00:00:00 2001 From: Ben McCann Date: Sun, 17 Nov 2013 22:05:41 -0800 Subject: [PATCH] Add a config to java bean converter --- .../java/com/typesafe/config/BeanFactory.java | 80 +++++++++++++++++++ .../com/typesafe/config/BeanFactoryTest.scala | 19 +++++ 2 files changed, 99 insertions(+) create mode 100644 config/src/main/java/com/typesafe/config/BeanFactory.java create mode 100644 config/src/test/scala/com/typesafe/config/BeanFactoryTest.scala diff --git a/config/src/main/java/com/typesafe/config/BeanFactory.java b/config/src/main/java/com/typesafe/config/BeanFactory.java new file mode 100644 index 00000000..5da0bacc --- /dev/null +++ b/config/src/main/java/com/typesafe/config/BeanFactory.java @@ -0,0 +1,80 @@ +package com.typesafe.config; + +import java.beans.IntrospectionException; +import java.beans.Introspector; +import java.beans.PropertyDescriptor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; + +import com.typesafe.config.Config; +import com.typesafe.config.ConfigException; + +/** + * Sent as pull request to config project. + * https://github.com/typesafehub/config/pull/107 + */ +public class BeanFactory { + + public static T create(Config config, Class clazz) { + return create(config.root().unwrapped(), clazz); + } + + private static T create(Map config, Class clazz) { + Map configProps = new HashMap(); + for (Map.Entry configProp : config.entrySet()) { + configProps.put(toCamelCase(configProp.getKey()), configProp.getValue()); + } + + try { + T bean = clazz.newInstance(); + for (PropertyDescriptor beanProp : Introspector.getBeanInfo(clazz).getPropertyDescriptors()) { + if (beanProp.getReadMethod() == null || beanProp.getWriteMethod() == null) { + continue; + } + Method setter = beanProp.getWriteMethod(); + Object configValue = configProps.get(beanProp.getName()); + if (configValue == null) { + throw new ConfigException.Generic( + "Could not find " + beanProp.getName() + " from " + clazz.getName() + " in config."); + } + if (configValue instanceof Map) { + @SuppressWarnings("unchecked") + Map child = ((Map) configValue); + configValue = create(child, beanProp.getPropertyType()); + } + + setter.invoke(bean, configValue); + } + + return bean; + } catch (IntrospectionException e) { + throw new ConfigException.Generic("Could not resolve a string method name.", e); + } catch (InstantiationException e) { + throw new ConfigException.Generic(clazz + " needs a public no args constructor", e); + } catch (IllegalAccessException e) { + throw new ConfigException.Generic(clazz + " getters and setters are not accessible", e); + } catch (InvocationTargetException e) { + throw new ConfigException.Generic("Calling bean method caused exception", e); + } + } + + /** + * Converts from hyphenated name to camel case. + */ + protected static String toCamelCase(String originalName) { + String[] words = originalName.split("-+"); + StringBuilder nameBuilder = new StringBuilder(originalName.length()); + for (int i = 0; i < words.length; i++) { + if (nameBuilder.length() == 0) { + nameBuilder.append(words[i]); + } else { + nameBuilder.append(words[i].substring(0, 1).toUpperCase()); + nameBuilder.append(words[i].substring(1)); + } + } + return nameBuilder.toString(); + } + +} diff --git a/config/src/test/scala/com/typesafe/config/BeanFactoryTest.scala b/config/src/test/scala/com/typesafe/config/BeanFactoryTest.scala new file mode 100644 index 00000000..1a2cce07 --- /dev/null +++ b/config/src/test/scala/com/typesafe/config/BeanFactoryTest.scala @@ -0,0 +1,19 @@ +/** + * Copyright (C) 2013 Typesafe Inc. + */ +package com.typesafe.config + +import org.junit.Assert._ +import org.junit._ + +class BeanFactoryTest { + + @Test + def toCamelCase() { + assertEquals("configProp", BeanFactory.toCamelCase("config-prop")) + assertEquals("fooBar", BeanFactory.toCamelCase("foo-----bar")) + assertEquals("foo", BeanFactory.toCamelCase("-foo")) + assertEquals("bar", BeanFactory.toCamelCase("bar-")) + } + +}