Second cut at ConfigBeanFactory

This version predates ConfigMemorySize and java.time.Duration
support.
This commit is contained in:
Andrey Zaytsev 2015-03-02 09:43:31 -05:00 committed by Havoc Pennington
parent d4a0255520
commit f1f198595a
15 changed files with 777 additions and 186 deletions

View File

@ -1,80 +0,0 @@
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> T create(Config config, Class<T> clazz) {
return create(config.root().unwrapped(), clazz);
}
private static <T> T create(Map<String,?> config, Class<T> clazz) {
Map<String, Object> configProps = new HashMap<String, Object>();
for (Map.Entry<String, ?> 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<String,?> child = ((Map<String,?>) 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();
}
}

View File

@ -0,0 +1,131 @@
package com.typesafe.config;
import com.typesafe.config.impl.DurationUnit;
import com.typesafe.config.impl.MemoryUnit;
import java.beans.BeanInfo;
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 java.util.concurrent.TimeUnit;
/**
* Factory for automatic creation of config classes populated with values from config.
*
* Example usage:
*
* Config configSource = ConfigFactory.parseReader(new InputStreamReader("converters.conf"));
* ConverterConfig config = ConfigBeanFactory.create(configSource,ConverterConfig.class);
*
* Supports nested configs.
* Supports automatic types conversion
* (https://github.com/typesafehub/config/blob/master/HOCON.md#automatic-type-conversions).
*/
public class ConfigBeanFactory {
/**
* Creates instance of class containing configuration info from config source
* @param config - source of config information
* @param clazz - class to be created
* @param <T>
* @return - instance of config class populated with data from config source
*/
public static <T> T create(Config config, Class<T> clazz) {
return createInternal(config, clazz);
}
private static <T> T createInternal(Config config, Class<T> clazz) {
Map<String, ?> configAsMap = config.root().unwrapped();
Map<String, Object> configProps = new HashMap<String, Object>();
Map<String,String> originalNames = new HashMap<String, String>();
for (Map.Entry<String, ?> configProp : configAsMap.entrySet()) {
configProps.put(toCamelCase(configProp.getKey()), configProp.getValue());
originalNames.put(toCamelCase(configProp.getKey()),configProp.getKey());
}
BeanInfo beanInfo = null;
try {
beanInfo = Introspector.getBeanInfo(clazz);
} catch(IntrospectionException e) {
throw new ConfigException.Generic("Could not get bean information for class " + clazz.getName(), e);
}
try {
T bean = clazz.newInstance();
for (PropertyDescriptor beanProp : beanInfo.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 property '" + beanProp.getName() + "' from class '" + clazz.getName() + "' in config.");
}
if (configValue instanceof Map) {
configValue = createInternal(config.getConfig(originalNames.get(beanProp.getDisplayName())), beanProp.getPropertyType());
} else {
Class parameterClass = setter.getParameterTypes()[0];
configValue = getValueWithAutoConversion(parameterClass, config, originalNames.get(beanProp.getDisplayName()));
}
setter.invoke(bean, configValue);
}
return bean;
} 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);
}
}
private static Object getValueWithAutoConversion(Class parameterClass, Config config, String configPropName) {
if (parameterClass == Boolean.class || parameterClass == boolean.class) {
return config.getBoolean(configPropName);
} else if (parameterClass == Byte.class || parameterClass == byte.class) {
return Integer.valueOf(config.getInt(configPropName)).byteValue();
} else if (parameterClass == Short.class || parameterClass == short.class) {
return Integer.valueOf(config.getInt(configPropName)).shortValue();
} else if (parameterClass == Integer.class || parameterClass == int.class) {
return config.getInt(configPropName);
} else if (parameterClass == Double.class || parameterClass == double.class) {
return config.getDouble(configPropName);
} else if (parameterClass == Long.class || parameterClass == long.class) {
String rawVal = config.getString(configPropName);
if (DurationUnit.containsDurationToken(rawVal)) {
return config.getDuration(configPropName, TimeUnit.NANOSECONDS);
} else if (MemoryUnit.containsMemoryToken(rawVal)) {
return config.getBytes(configPropName);
}
return config.getLong(configPropName);
} else if (parameterClass == String.class) {
return config.getString(configPropName);
}
return config.getAnyRef(configPropName);
}
/**
* Converts from hyphenated name to camel case.
*/
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();
}
}

View File

@ -0,0 +1,58 @@
package com.typesafe.config.impl;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* https://github.com/typesafehub/config/blob/master/HOCON.md#duration-format
*/
public enum DurationUnit {
NANOSECONDS(TimeUnit.NANOSECONDS, Arrays.asList("ns", "nano", "nanos", "nanosecond", "nanoseconds")),
MICROSECONDS(TimeUnit.MICROSECONDS, Arrays.asList("us", "micro", "micros", "microsecond", "microseconds")),
MILLISECONDS(TimeUnit.MILLISECONDS,Arrays.asList("ms", "milli", "millis", "millisecond", "milliseconds")),
SECONDS(TimeUnit.SECONDS,Arrays.asList("s", "second", "seconds")),
MINUTES(TimeUnit.MINUTES,Arrays.asList("m", "minute", "minutes")),
HOURS(TimeUnit.HOURS,Arrays.asList("h", "hour", "hours")),
DAYS(TimeUnit.DAYS,Arrays.asList("d", "day", "days"));
private List<String> aliases;
private TimeUnit timeUnit;
DurationUnit(TimeUnit timeUnit,List<String> aliases) {
this.timeUnit = timeUnit;
this.aliases = aliases;
}
/**
* Finds corresponding duration unit by its string representation.
* @param rawVal - string duration value ('s','ns' etc.)
* @return one of enum values or null if none applicable
*/
public static final DurationUnit fromString(String rawVal) {
for (DurationUnit durationUnit : DurationUnit.values()) {
// note that this is deliberately case-sensitive
if(durationUnit.aliases.contains(rawVal)) {
return durationUnit;
}
}
return null;
}
/**
* Checks whether given string contains duration token.
* @param rawVal - string like '10s','45nanos' etc.
* String is case sensitive, i.e. '10S' is not treated as valid duration string
* @return true - if string contains one of enum aliases, false otherwise
*/
public static boolean containsDurationToken(String rawVal) {
String unitStr = rawVal.replaceAll("[^A-Za-z]","");
return fromString(unitStr) != null;
}
public TimeUnit getTimeUnit() {
return timeUnit;
}
}

View File

@ -0,0 +1,95 @@
package com.typesafe.config.impl;
import java.math.BigInteger;
import java.util.HashMap;
import java.util.Map;
/**
* https://github.com/typesafehub/config/blob/master/HOCON.md#size-in-bytes-format
*/
public enum MemoryUnit {
BYTES("", 1024, 0),
KILOBYTES("kilo", 1000, 1),
MEGABYTES("mega", 1000, 2),
GIGABYTES("giga", 1000, 3),
TERABYTES("tera", 1000, 4),
PETABYTES("peta", 1000, 5),
EXABYTES("exa", 1000, 6),
ZETTABYTES("zetta", 1000, 7),
YOTTABYTES("yotta", 1000, 8),
KIBIBYTES("kibi", 1024, 1),
MEBIBYTES("mebi", 1024, 2),
GIBIBYTES("gibi", 1024, 3),
TEBIBYTES("tebi", 1024, 4),
PEBIBYTES("pebi", 1024, 5),
EXBIBYTES("exbi", 1024, 6),
ZEBIBYTES("zebi", 1024, 7),
YOBIBYTES("yobi", 1024, 8);
final String prefix;
final int powerOf;
final int power;
final BigInteger bytes;
MemoryUnit(String prefix, int powerOf, int power) {
this.prefix = prefix;
this.powerOf = powerOf;
this.power = power;
this.bytes = BigInteger.valueOf(powerOf).pow(power);
}
private static Map<String, MemoryUnit> makeUnitsMap() {
Map<String, MemoryUnit> map = new HashMap<String, MemoryUnit>();
for (MemoryUnit unit : MemoryUnit.values()) {
map.put(unit.prefix + "byte", unit);
map.put(unit.prefix + "bytes", unit);
if (unit.prefix.length() == 0) {
map.put("b", unit);
map.put("B", unit);
map.put("", unit); // no unit specified means bytes
} else {
String first = unit.prefix.substring(0, 1);
String firstUpper = first.toUpperCase();
if (unit.powerOf == 1024) {
map.put(first, unit); // 512m
map.put(firstUpper, unit); // 512M
map.put(firstUpper + "i", unit); // 512Mi
map.put(firstUpper + "iB", unit); // 512MiB
} else if (unit.powerOf == 1000) {
if (unit.power == 1) {
map.put(first + "B", unit); // 512kB
} else {
map.put(firstUpper + "B", unit); // 512MB
}
} else {
throw new RuntimeException("broken MemoryUnit enum");
}
}
}
return map;
}
private static Map<String, MemoryUnit> unitsMap = makeUnitsMap();
static MemoryUnit parseUnit(String unit) {
return unitsMap.get(unit);
}
/**
* Checks whether given string contains memory token.
* @param rawVal - string like '10B','45bytes' etc.
* String is case sensitive, i.e. '10KILOS' is not treated as valid memory size string
* @return true - if string contains memory data, false otherwise
*/
public static boolean containsMemoryToken(String rawVal) {
String unitStr = rawVal.replaceAll("[^A-Za-z]","");
for (String unit : unitsMap.keySet()) {
if(unit.equals(unitStr)) {
return true;
}
}
return false;
}
}

View File

@ -547,27 +547,17 @@ final class SimpleConfig implements Config, MergeableValue, Serializable {
if (unitString.length() > 2 && !unitString.endsWith("s"))
unitString = unitString + "s";
// note that this is deliberately case-sensitive
if (unitString.equals("") || unitString.equals("ms") || unitString.equals("millis")
|| unitString.equals("milliseconds")) {
if(unitString.equals("")) {
units = TimeUnit.MILLISECONDS;
} else if (unitString.equals("us") || unitString.equals("micros") || unitString.equals("microseconds")) {
units = TimeUnit.MICROSECONDS;
} else if (unitString.equals("ns") || unitString.equals("nanos") || unitString.equals("nanoseconds")) {
units = TimeUnit.NANOSECONDS;
} else if (unitString.equals("d") || unitString.equals("days")) {
units = TimeUnit.DAYS;
} else if (unitString.equals("h") || unitString.equals("hours")) {
units = TimeUnit.HOURS;
} else if (unitString.equals("s") || unitString.equals("seconds")) {
units = TimeUnit.SECONDS;
} else if (unitString.equals("m") || unitString.equals("minutes")) {
units = TimeUnit.MINUTES;
} else {
throw new ConfigException.BadValue(originForException,
DurationUnit durationUnit = DurationUnit.fromString(unitString);
units = durationUnit != null ? durationUnit.getTimeUnit() : null;
if(units == null) {
throw new ConfigException.BadValue(originForException,
pathForException, "Could not parse time unit '"
+ originalUnitString
+ "' (try ns, us, ms, s, m, h, d)");
}
}
try {
@ -587,77 +577,6 @@ final class SimpleConfig implements Config, MergeableValue, Serializable {
}
}
private static enum MemoryUnit {
BYTES("", 1024, 0),
KILOBYTES("kilo", 1000, 1),
MEGABYTES("mega", 1000, 2),
GIGABYTES("giga", 1000, 3),
TERABYTES("tera", 1000, 4),
PETABYTES("peta", 1000, 5),
EXABYTES("exa", 1000, 6),
ZETTABYTES("zetta", 1000, 7),
YOTTABYTES("yotta", 1000, 8),
KIBIBYTES("kibi", 1024, 1),
MEBIBYTES("mebi", 1024, 2),
GIBIBYTES("gibi", 1024, 3),
TEBIBYTES("tebi", 1024, 4),
PEBIBYTES("pebi", 1024, 5),
EXBIBYTES("exbi", 1024, 6),
ZEBIBYTES("zebi", 1024, 7),
YOBIBYTES("yobi", 1024, 8);
final String prefix;
final int powerOf;
final int power;
final BigInteger bytes;
MemoryUnit(String prefix, int powerOf, int power) {
this.prefix = prefix;
this.powerOf = powerOf;
this.power = power;
this.bytes = BigInteger.valueOf(powerOf).pow(power);
}
private static Map<String, MemoryUnit> makeUnitsMap() {
Map<String, MemoryUnit> map = new HashMap<String, MemoryUnit>();
for (MemoryUnit unit : MemoryUnit.values()) {
map.put(unit.prefix + "byte", unit);
map.put(unit.prefix + "bytes", unit);
if (unit.prefix.length() == 0) {
map.put("b", unit);
map.put("B", unit);
map.put("", unit); // no unit specified means bytes
} else {
String first = unit.prefix.substring(0, 1);
String firstUpper = first.toUpperCase();
if (unit.powerOf == 1024) {
map.put(first, unit); // 512m
map.put(firstUpper, unit); // 512M
map.put(firstUpper + "i", unit); // 512Mi
map.put(firstUpper + "iB", unit); // 512MiB
} else if (unit.powerOf == 1000) {
if (unit.power == 1) {
map.put(first + "B", unit); // 512kB
} else {
map.put(firstUpper + "B", unit); // 512MB
}
} else {
throw new RuntimeException("broken MemoryUnit enum");
}
}
}
return map;
}
private static Map<String, MemoryUnit> unitsMap = makeUnitsMap();
static MemoryUnit parseUnit(String unit) {
return unitsMap.get(unit);
}
}
/**
* Parses a size-in-bytes string. If no units are specified in the string,
* it is assumed to be in bytes. The returned value is in bytes. The purpose

View File

@ -0,0 +1,80 @@
package beanconfig;
import java.util.List;
public class ArraysConfig {
List<Integer> empty;
List<Integer> ofInt;
List<String> ofString;
List<Double> ofDouble;
List<Object> ofNull;
List<Boolean> ofBoolean;
List<List> ofArray;
List<Object> ofObject;
public List<Integer> getEmpty() {
return empty;
}
public void setEmpty(List<Integer> empty) {
this.empty = empty;
}
public List<Integer> getOfInt() {
return ofInt;
}
public void setOfInt(List<Integer> ofInt) {
this.ofInt = ofInt;
}
public List<String> getOfString() {
return ofString;
}
public void setOfString(List<String> ofString) {
this.ofString = ofString;
}
public List<Double> getOfDouble() {
return ofDouble;
}
public void setOfDouble(List<Double> ofDouble) {
this.ofDouble = ofDouble;
}
public List<Object> getOfNull() {
return ofNull;
}
public void setOfNull(List<Object> ofNull) {
this.ofNull = ofNull;
}
public List<Boolean> getOfBoolean() {
return ofBoolean;
}
public void setOfBoolean(List<Boolean> ofBoolean) {
this.ofBoolean = ofBoolean;
}
public List<List> getOfArray() {
return ofArray;
}
public void setOfArray(List<List> ofArray) {
this.ofArray = ofArray;
}
public List<Object> getOfObject() {
return ofObject;
}
public void setOfObject(List<Object> ofObject) {
this.ofObject = ofObject;
}
}

View File

@ -0,0 +1,59 @@
package beanconfig;
public class BooleansConfig {
Boolean trueVal;
Boolean trueValAgain;
Boolean falseVal;
Boolean falseValAgain;
Boolean on;
Boolean off;
public Boolean getTrueVal() {
return trueVal;
}
public void setTrueVal(Boolean trueVal) {
this.trueVal = trueVal;
}
public Boolean getTrueValAgain() {
return trueValAgain;
}
public void setTrueValAgain(Boolean trueValAgain) {
this.trueValAgain = trueValAgain;
}
public Boolean getFalseVal() {
return falseVal;
}
public void setFalseVal(Boolean falseVal) {
this.falseVal = falseVal;
}
public Boolean getFalseValAgain() {
return falseValAgain;
}
public void setFalseValAgain(Boolean falseValAgain) {
this.falseValAgain = falseValAgain;
}
public Boolean getOn() {
return on;
}
public void setOn(Boolean on) {
this.on = on;
}
public Boolean getOff() {
return off;
}
public void setOff(Boolean off) {
this.off = off;
}
}

View File

@ -0,0 +1,32 @@
package beanconfig;
public class BytesConfig {
private Long kilobyte;
private Long kibibyte;
private Long thousandBytes;
public Long getKilobyte() {
return kilobyte;
}
public void setKilobyte(Long kilobyte) {
this.kilobyte = kilobyte;
}
public Long getKibibyte() {
return kibibyte;
}
public void setKibibyte(Long kibibyte) {
this.kibibyte = kibibyte;
}
public Long getThousandBytes() {
return thousandBytes;
}
public void setThousandBytes(Long thousandBytes) {
this.thousandBytes = thousandBytes;
}
}

View File

@ -0,0 +1,33 @@
package beanconfig;
public class DurationsConfig {
Long second;
Long secondAsNumber;
Long halfSecond;
public Long getSecond() {
return second;
}
public void setSecond(Long second) {
this.second = second;
}
public Long getSecondAsNumber() {
return secondAsNumber;
}
public void setSecondAsNumber(Long secondAsNumber) {
this.secondAsNumber = secondAsNumber;
}
public Long getHalfSecond() {
return halfSecond;
}
public void setHalfSecond(Long halfSecond) {
this.halfSecond = halfSecond;
}
}

View File

@ -0,0 +1,14 @@
package beanconfig;
public class NoFoundPropBeanConfig extends TestBeanConfig{
private String propNotListedInConfig;
public String getPropNotListedInConfig() {
return propNotListedInConfig;
}
public void setPropNotListedInConfig(String propNotListedInConfig) {
this.propNotListedInConfig = propNotListedInConfig;
}
}

View File

@ -0,0 +1,98 @@
package beanconfig;
public class NumbersConfig {
private byte byteVal;
private Byte byteObj;
private short shortVal;
private Short shortObj;
private int intVal;
private Integer intObj;
private long longVal;
private Long longObj;
private double doubleVal;
private Double doubleObj;
public int getIntVal() {
return intVal;
}
public void setIntVal(int intVal) {
this.intVal = intVal;
}
public Integer getIntObj() {
return intObj;
}
public void setIntObj(Integer intObj) {
this.intObj = intObj;
}
public long getLongVal() {
return longVal;
}
public void setLongVal(long longVal) {
this.longVal = longVal;
}
public Long getLongObj() {
return longObj;
}
public void setLongObj(Long longObj) {
this.longObj = longObj;
}
public double getDoubleVal() {
return doubleVal;
}
public void setDoubleVal(double doubleVal) {
this.doubleVal = doubleVal;
}
public Double getDoubleObj() {
return doubleObj;
}
public void setDoubleObj(Double doubleObj) {
this.doubleObj = doubleObj;
}
public byte getByteVal() {
return byteVal;
}
public void setByteVal(byte byteVal) {
this.byteVal = byteVal;
}
public Byte getByteObj() {
return byteObj;
}
public void setByteObj(Byte byteObj) {
this.byteObj = byteObj;
}
public short getShortVal() {
return shortVal;
}
public void setShortVal(short shortVal) {
this.shortVal = shortVal;
}
public Short getShortObj() {
return shortObj;
}
public void setShortObj(Short shortObj) {
this.shortObj = shortObj;
}
}

View File

@ -0,0 +1,15 @@
package beanconfig;
public class TestBeanConfig {
private NumbersConfig numbers;
public NumbersConfig getNumbers() {
return numbers;
}
public void setNumbers(NumbersConfig numbers) {
this.numbers = numbers;
}
}

View File

@ -0,0 +1,62 @@
{
"booleans" : {
"trueVal" : true,
"trueValAgain" : ${booleans.trueVal},
"falseVal" : false,
"falseValAgain" : ${booleans.falseVal},
"on" : "on",
"off" : "off"
}
"numbers" : {
"byteVal" : "1",
"byteObj" : ${numbers.byteVal},
"shortVal" : "1",
"shortObj" : ${numbers.shortVal},
"intVal" : "1",
"intObj" : ${numbers.intVal},
"longVal" : "1",
"longObj" : ${numbers.longVal},
"doubleVal" : "1.0",
"doubleObj" : ${numbers.doubleVal}
}
//
"strings" : {
"abcd" : "abcd",
"abcdAgain" : ${strings.a}${strings.b}${strings.c}${strings.d},
"a" : "a",
"b" : "b",
"c" : "c",
"d" : "d",
"concatenated" : null bar 42 baz true 3.14 hi,
"double" : "3.14",
"number" : "57",
"null" : "null",
"true" : "true",
"yes" : "yes",
"false" : "false",
"no" : "no"
},
"arrays" : {
"empty" : [],
"ofInt" : [1, 2, 3],
"ofString" : [ ${strings.a}, ${strings.b}, ${strings.c} ],
"ofDouble" : [3.14, 4.14, 5.14],
"ofNull" : [null, null, null],
"ofBoolean" : [true, false],
"ofArray" : [${arrays.ofString}, ${arrays.ofString}, ${arrays.ofString}],
"ofObject" : [${numbers}, ${booleans}, ${strings}]
},
"bytes" : {
"kilobyte" : "1kB",
"kibibyte" : "1K",
"thousandBytes" : "1000B"
},
"durations" : {
"second" : 1s,
"secondAsNumber" : 1000000000,
"halfSecond" : 0.5s
},
}

View File

@ -1,19 +0,0 @@
/**
* Copyright (C) 2013 Typesafe Inc. <http://typesafe.com>
*/
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-"))
}
}

View File

@ -0,0 +1,94 @@
/**
* Copyright (C) 2013 Typesafe Inc. <http://typesafe.com>
*/
package com.typesafe.config
import java.io.{ InputStream, InputStreamReader }
import java.util.concurrent.TimeUnit
import beanconfig._
import org.junit.Assert._
import org.junit._
class ConfigBeanFactoryTest {
@Test
def toCamelCase() {
assertEquals("configProp", ConfigBeanFactory.toCamelCase("config-prop"))
assertEquals("fooBar", ConfigBeanFactory.toCamelCase("foo-----bar"))
assertEquals("foo", ConfigBeanFactory.toCamelCase("-foo"))
assertEquals("bar", ConfigBeanFactory.toCamelCase("bar-"))
}
@Test
def testCreate() {
val configIs: InputStream = this.getClass().getClassLoader().getResourceAsStream("beanconfig/beanconfig01.conf")
val config: Config = ConfigFactory.parseReader(new InputStreamReader(configIs),
ConfigParseOptions.defaults.setSyntax(ConfigSyntax.CONF)).resolve
val beanConfig: TestBeanConfig = ConfigBeanFactory.create(config, classOf[TestBeanConfig])
assertNotNull(beanConfig)
}
@Test
def testUnknownProp() {
val configIs: InputStream = this.getClass().getClassLoader().getResourceAsStream("beanconfig/beanconfig01.conf")
val config: Config = ConfigFactory.parseReader(new InputStreamReader(configIs),
ConfigParseOptions.defaults.setSyntax(ConfigSyntax.CONF)).resolve
var expected: ConfigException.Generic = null
var beanConfig: NoFoundPropBeanConfig = null
try {
beanConfig = ConfigBeanFactory.create(config, classOf[NoFoundPropBeanConfig])
} catch {
case cge: ConfigException.Generic => expected = cge
case e: Exception => expected = null
}
assertNotNull(expected)
assertEquals("Could not find property 'propNotListedInConfig' from class 'beanconfig.NoFoundPropBeanConfig' in config.",
expected.getMessage)
assertNull(beanConfig)
}
@Test
def testCreateBool() {
val beanConfig: BooleansConfig = ConfigBeanFactory.create(loadConfig().getConfig("booleans"), classOf[BooleansConfig])
assertNotNull(beanConfig)
}
@Test
def testCreateNumber() {
val beanConfig: NumbersConfig = ConfigBeanFactory.create(loadConfig().getConfig("numbers"), classOf[NumbersConfig])
assertNotNull(beanConfig)
}
@Test
def testCreateList() {
val beanConfig: ArraysConfig = ConfigBeanFactory.create(loadConfig().getConfig("arrays"), classOf[ArraysConfig])
assertNotNull(beanConfig)
}
@Test
def testCreateDuration() {
val beanConfig: DurationsConfig = ConfigBeanFactory.create(loadConfig().getConfig("durations"), classOf[DurationsConfig])
assertNotNull(beanConfig)
assertEquals(beanConfig.getHalfSecond, TimeUnit.MILLISECONDS.toNanos(500))
assertEquals(beanConfig.getSecond, TimeUnit.SECONDS.toNanos(1))
assertEquals(beanConfig.getSecondAsNumber, TimeUnit.SECONDS.toNanos(1))
}
@Test
def testCreateBytes() {
val beanConfig: BytesConfig = ConfigBeanFactory.create(loadConfig().getConfig("bytes"), classOf[BytesConfig])
assertNotNull(beanConfig)
assertEquals(beanConfig.getKibibyte, 1024L)
assertEquals(beanConfig.getKilobyte, 1000L)
assertEquals(beanConfig.getThousandBytes, 1000L)
}
private def loadConfig(): Config = {
val configIs: InputStream = this.getClass().getClassLoader().getResourceAsStream("beanconfig/beanconfig01.conf")
val config: Config = ConfigFactory.parseReader(new InputStreamReader(configIs),
ConfigParseOptions.defaults.setSyntax(ConfigSyntax.CONF)).resolve
config
}
}