/* * Copyright 2013, Unitils.org * * 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 org.unitils.core.config; import org.unitils.core.Factory; import org.unitils.core.UnitilsException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Properties; import static java.lang.Boolean.FALSE; import static java.lang.Boolean.TRUE; import static org.unitils.util.ReflectionUtils.createInstanceOfType; /** * todo javadoc * <p/> * If classifiers are used, it will first look for a property using all classifiers, then look for * a property without the last classifier etc. If still no property was found, it will look for the property * without discriminators. E.g. suppose the property name is 'key' and there are 2 classifiers 'a' and 'b'. First * it will look for a property with name 'key.a.b', if that doesn't exist it will look for 'key.a', and * finally it will try 'key'. * * @author Tim Ducheyne */ public class Configuration extends UnitilsConfiguration { /* All configuration properties, not null */ protected Properties properties; protected Properties overridingProperties; /** * Creates a configuration for the given properties. * * @param properties All configuration properties, not null */ public Configuration(Properties properties) { super(properties); this.properties = properties; } public Properties getOverridingProperties() { return overridingProperties; } public void setOverridingProperties(Properties overridingProperties) { this.overridingProperties = overridingProperties; } public Properties getAllProperties() { Properties result = new Properties(); result.putAll(properties); if (overridingProperties != null) { result.putAll(overridingProperties); } return result; } /** * Gets the string value for the property with the given name. If no such property is found or * the value is empty, an exception will be raised. * * @param propertyName The name, not null * @param classifiers An optional list of classifiers for the property name (see class javadoc for more info) * @return The trimmed string value, not null */ public String getString(String propertyName, String... classifiers) { String value = getOptionalString(propertyName, classifiers); if (value == null) { throw new UnitilsException("No value found for " + nameToString(propertyName, classifiers)); } return value; } /** * Gets the string value for the property with the given name. If no such property is found or * the value is empty, null is returned. * * @param propertyName The name, not null * @param classifiers An optional list of classifiers for the property name (see class javadoc for more info) * @return The trimmed string value, null if not found */ public String getOptionalString(String propertyName, String... classifiers) { String value = getProperty(propertyName); if (classifiers != null && classifiers.length > 0) { StringBuilder propertyNameWithClassifiers = new StringBuilder(propertyName); for (String classifier : classifiers) { if (classifier == null) { continue; } propertyNameWithClassifiers.append('.'); propertyNameWithClassifiers.append(classifier.trim()); String valueForClassifier = getProperty(propertyNameWithClassifiers.toString()); if (valueForClassifier != null) { value = valueForClassifier; } } } if (value == null) { return null; } value = value.trim(); if ("".equals(value)) { return null; } return value; } /** * Gets the list of comma separated string values for the property with the given name. If no such property is found or * the value is empty, an exception will be raised. Empty elements (",,") will not be added. * * @param propertyName The name, not null * @param classifiers An optional list of classifiers for the property name (see class javadoc for more info) * @return The trimmed string list, not null */ public List<String> getStringList(String propertyName, String... classifiers) { String value = getString(propertyName, classifiers); List<String> result = toStringList(value); if (result.isEmpty()) { throw new UnitilsException("No value found for " + nameToString(propertyName, classifiers)); } return result; } /** * Gets the list of comma separated string values for the property with the given name. If no such property is found or * the value is empty, an empty list is returned. Empty elements (",,") will not be added. * * @param propertyName The name, not null * @param classifiers An optional list of classifiers for the property name (see class javadoc for more info) * @return The trimmed string list, empty if not found */ public List<String> getOptionalStringList(String propertyName, String... classifiers) { String value = getOptionalString(propertyName, classifiers); return toStringList(value); } /** * Gets the boolean value for the property with the given name. If no such property is found, * the value is empty or not a boolean, an exception will be raised. * * @param propertyName The name, not null * @param classifiers An optional list of classifiers for the property name (see class javadoc for more info) * @return The boolean value, not null */ public Boolean getBoolean(String propertyName, String... classifiers) { String value = getString(propertyName, classifiers); return toBoolean(value, propertyName, classifiers); } /** * Gets the boolean value for the property with the given name. If no such property is found or * the value is empty, null is returned. An exception will be raised if the * value is not a boolean. * * @param propertyName The name, not null * @param classifiers An optional list of classifiers for the property name (see class javadoc for more info) * @return The boolean value, null if not found */ public Boolean getOptionalBoolean(String propertyName, String... classifiers) { String value = getOptionalString(propertyName, classifiers); return toBoolean(value, propertyName, classifiers); } /** * Gets the list of comma separated boolean values for the property with the given name. If no such property is found or * the value is empty, an exception will be raised. Empty elements (",,") will not be added. * * @param propertyName The name, not null * @param classifiers An optional list of classifiers for the property name (see class javadoc for more info) * @return The boolean list, not null */ public List<Boolean> getBooleanList(String propertyName, String... classifiers) { List<Boolean> result = getOptionalBooleanList(propertyName, classifiers); if (result.isEmpty()) { throw new UnitilsException("No value found for " + nameToString(propertyName, classifiers)); } return result; } /** * Gets the list of comma separated boolean values for the property with the given name. If no such property is found or * the value is empty, an empty list is returned. Empty elements (",,") will not be added. * * @param propertyName The name, not null * @param classifiers An optional list of classifiers for the property name (see class javadoc for more info) * @return The boolean list, empty if not found */ public List<Boolean> getOptionalBooleanList(String propertyName, String... classifiers) { List<String> values = getOptionalStringList(propertyName, classifiers); List<Boolean> result = new ArrayList<Boolean>(values.size()); for (String value : values) { Boolean bool = toBoolean(value, propertyName, classifiers); result.add(bool); } return result; } /** * Gets the int value for the property with the given name. If no such property is found, the value is empty * or cannot be converted to an int, an exception will be raised. * * @param propertyName The name, not null * @param classifiers An optional list of classifiers for the property name (see class javadoc for more info) * @return The int value */ public Integer getInteger(String propertyName, String... classifiers) { String value = getString(propertyName, classifiers); return toInteger(value, propertyName, classifiers); } /** * Gets the int value for the property with the given name. If no such property is found or * the value is empty, null is returned. An exception will be raised if the * value cannot be converted to an int. * * @param propertyName The name, not null * @param classifiers An optional list of classifiers for the property name (see class javadoc for more info) * @return The int value, null if not found */ public Integer getOptionalInteger(String propertyName, String... classifiers) { String value = getOptionalString(propertyName, classifiers); return toInteger(value, propertyName, classifiers); } /** * Gets the list of comma separated int values for the property with the given name. If no such property is found or * the value is empty, an exception will be raised. Empty elements (",,") will not be added. * * @param propertyName The name, not null * @param classifiers An optional list of classifiers for the property name (see class javadoc for more info) * @return The int list, not null */ public List<Integer> getIntegerList(String propertyName, String... classifiers) { List<Integer> result = getOptionalIntegerList(propertyName, classifiers); if (result.isEmpty()) { throw new UnitilsException("No value found for " + nameToString(propertyName, classifiers)); } return result; } /** * Gets the list of comma separated int values for the property with the given name. If no such property is found or * the value is empty, an empty list is returned. Empty elements (",,") will not be added. * * @param propertyName The name, not null * @param classifiers An optional list of classifiers for the property name (see class javadoc for more info) * @return The int list, empty if not found */ public List<Integer> getOptionalIntegerList(String propertyName, String... classifiers) { List<String> values = getOptionalStringList(propertyName, classifiers); List<Integer> result = new ArrayList<Integer>(values.size()); for (String value : values) { Integer integer = toInteger(value, propertyName, classifiers); result.add(integer); } return result; } /** * Gets the long value for the property with the given name. If no such property is found, the value is empty * or cannot be converted to a long, an exception will be raised. * * @param propertyName The name, not null * @param classifiers An optional list of classifiers for the property name (see class javadoc for more info) * @return The long value, not null */ public Long getLong(String propertyName, String... classifiers) { String value = getString(propertyName, classifiers); return toLong(value, propertyName, classifiers); } /** * Gets the long value for the property with the given name. If no such property is found or * the value is empty, null is returned. An exception will be raised if the * value cannot be converted to a long. * * @param propertyName The name, not null * @param classifiers An optional list of classifiers for the property name (see class javadoc for more info) * @return The long value, null if not found */ public Long getOptionalLong(String propertyName, String... classifiers) { String value = getOptionalString(propertyName, classifiers); return toLong(value, propertyName, classifiers); } /** * Gets the list of comma separated long values for the property with the given name. If no such property is found or * the value is empty, an exception will be raised. Empty elements (",,") will not be added. * * @param propertyName The name, not null * @param classifiers An optional list of classifiers for the property name (see class javadoc for more info) * @return The trimmed long list, not null */ public List<Long> getLongList(String propertyName, String... classifiers) { List<Long> result = getOptionalLongList(propertyName, classifiers); if (result.isEmpty()) { throw new UnitilsException("No value found for " + nameToString(propertyName, classifiers)); } return result; } /** * Gets the list of comma separated long values for the property with the given name. If no such property is found or * the value is empty, an empty list is returned. Empty elements (",,") will not be added. * * @param propertyName The name, not null * @param classifiers An optional list of classifiers for the property name (see class javadoc for more info) * @return The long list, empty if not found */ public List<Long> getOptionalLongList(String propertyName, String... classifiers) { List<String> values = getOptionalStringList(propertyName, classifiers); List<Long> result = new ArrayList<Long>(values.size()); for (String value : values) { Long longValue = toLong(value, propertyName, classifiers); result.add(longValue); } return result; } /** * Gets the class value for the property with the given name. If no such property is found, the value is empty * or cannot be converted to a class, an exception will be raised. * * @param propertyName The name, not null * @param classifiers An optional list of classifiers for the property name (see class javadoc for more info) * @return The class value, not null */ public Class<?> getClass(String propertyName, String... classifiers) { String value = getString(propertyName, classifiers); return toClass(value, propertyName, classifiers); } /** * Gets the class value for the property with the given name. If no such property is found or * the value is empty, null is returned. An exception will be raised if the value cannot be converted to a class. * * @param propertyName The name, not null * @param classifiers An optional list of classifiers for the property name (see class javadoc for more info) * @return The class value, null if not found */ public Class<?> getOptionalClass(String propertyName, String... classifiers) { String value = getOptionalString(propertyName, classifiers); return toClass(value, propertyName, classifiers); } /** * Gets the list of comma separated class values for the property with the given name. If no such property is found, * the value is empty or cannot be converted to a class, an exception will be raised. * Empty elements (",,") will not be added. * * @param propertyName The name, not null * @param classifiers An optional list of classifiers for the property name (see class javadoc for more info) * @return The class list, not null */ public List<Class<?>> getClassList(String propertyName, String... classifiers) { List<Class<?>> result = getOptionalClassList(propertyName, classifiers); if (result.isEmpty()) { throw new UnitilsException("No value found for " + nameToString(propertyName, classifiers)); } return result; } /** * Gets the list of comma separated class values for the property with the given name. If no such property is found or * the value is empty, an empty list is returned. Empty elements (",,") will not be added. * An exception will be raised if the value cannot be converted to a class. * * @param propertyName The name, not null * @param classifiers An optional list of classifiers for the property name (see class javadoc for more info) * @return The class list, empty if not found */ public List<Class<?>> getOptionalClassList(String propertyName, String... classifiers) { List<String> values = getOptionalStringList(propertyName, classifiers); List<Class<?>> result = new ArrayList<Class<?>>(values.size()); for (String value : values) { Class<?> clazz = toClass(value, propertyName, classifiers); result.add(clazz); } return result; } /** * Gets an instance of the given type (typically an interface). * It will look for a property using the classname and classifiers and create an instance of the classname * specified as value.<br/> * E.g. if you have following property:<br/> * <br/> * org.package.Reader=org.package.MyReaderImpl<br/> * <br/> * Calling getInstanceOf(Reader.class) will then return an instance of MyReaderImpl * * @param type The type of the instance * @param classifiers An optional list of classifiers for the property name (see class javadoc for more info) * @return The instance */ public <T> T getInstanceOf(Class<T> type, String... classifiers) { String propertyName = type.getName(); String value = getString(propertyName, classifiers); return toInstance(type, value, propertyName, classifiers); } /** * Gets an instance of the given type (typically an interface). * It will look for a property using the classname and classifiers and create an instance of the classname * specified as value.<br/> * E.g. if you have following property:<br/> * <br/> * org.package.Reader=org.package.MyReaderImpl<br/> * <br/> * Calling getInstanceOf(Reader.class) will then return an instance of MyReaderImpl * * @param type The type of the instance * @param classifiers An optional list of classifiers for the property name (see class javadoc for more info) * @return The instance */ public <T> T getOptionalInstanceOf(Class<T> type, String... classifiers) { String propertyName = type.getName(); String value = getOptionalString(type.getName(), classifiers); return toInstance(type, value, propertyName, classifiers); } /** * Gets the list of comma separated instances of the given type (typically an interface). * It will look for a property using the classname and classifiers and create an instance of the classname * specified as value.<br/> * E.g. if you have following property:<br/> * <br/> * org.package.Reader=org.package.MyReaderImpl, org.package.OtherReaderImpl<br/> * <br/> * Calling getInstanceOf(Reader.class) will then return an instance of MyReaderImpl and OtherReaderImpl<br/> * <br/> * If no such property is found or the value is empty, an exception will be raised. Empty elements (",,") will not be added. * * @param type The type of the instance * @param classifiers An optional list of classifiers for the property name (see class javadoc for more info) * @return The instance list, not null */ public <T> List<T> getInstanceOfList(Class<T> type, String... classifiers) { List<T> result = getOptionalInstanceOfList(type, classifiers); if (result.isEmpty()) { throw new UnitilsException("No value found for " + nameToString(type.getName(), classifiers)); } return result; } /** * Gets the list of comma separated instances of the given type (typically an interface). * It will look for a property using the classname and classifiers and create an instance of the classname * specified as value.<br/> * E.g. if you have following property:<br/> * <br/> * org.package.Reader=org.package.MyReaderImpl, org.package.OtherReaderImpl<br/> * <br/> * Calling getInstanceOf(Reader.class) will then return an instance of MyReaderImpl and OtherReaderImpl<br/> * <br/> * If no such property is found or the value is empty, an empty list is returned. Empty elements (",,") will not be added. * * @param type The type of the instance * @param classifiers An optional list of classifiers for the property name (see class javadoc for more info) * @return The trimmed string list, empty if not found */ public <T> List<T> getOptionalInstanceOfList(Class<T> type, String... classifiers) { String propertyName = type.getName(); List<String> values = getOptionalStringList(propertyName, classifiers); List<T> result = new ArrayList<T>(values.size()); for (String value : values) { T instance = toInstance(type, value, propertyName, classifiers); result.add(instance); } return result; } /** * Gets the enum value for the property with the given name. If no such property is found, the value is empty * or cannot be converted to the given enum type, an exception will be raised. * * @param type The enum type, not null * @param propertyName The name, not null * @param classifiers An optional list of classifiers for the property name (see class javadoc for more info) * @return The enum value, not null */ public <T extends Enum<T>> T getEnumValue(Class<T> type, String propertyName, String... classifiers) { String value = getString(propertyName, classifiers); return toEnum(type, value, propertyName, classifiers); } /** * Gets the enum value for the property with the given name. If no such property is found or * the value is empty, null is returned. An exception will be raised if the value cannot be converted to a the given enum type. * * @param type The enum type, not null * @param propertyName The name, not null * @param classifiers An optional list of classifiers for the property name (see class javadoc for more info) * @return The enum value, null if not found */ public <T extends Enum<T>> T getOptionalEnumValue(Class<T> type, String propertyName, String... classifiers) { String value = getOptionalString(propertyName, classifiers); return toEnum(type, value, propertyName, classifiers); } /** * Gets the list of comma separated enum values for the property with the given name. If no such property is found or * the value is empty, an exception will be raised. Empty elements (",,") will not be added. * * @param type The enum type, not null * @param propertyName The name, not null * @param classifiers An optional list of classifiers for the property name (see class javadoc for more info) * @return The enum list, not null */ public <T extends Enum<T>> List<T> getEnumList(Class<T> type, String propertyName, String... classifiers) { List<T> result = getOptionalEnumList(type, propertyName, classifiers); if (result.isEmpty()) { throw new UnitilsException("No value found for " + nameToString(propertyName, classifiers)); } return result; } /** * Gets the list of comma separated enum values for the property with the given name. If no such property is found or * the value is empty, an empty list is returned. Empty elements (",,") will not be added. * * @param type The enum type, not null * @param propertyName The name, not null * @param classifiers An optional list of classifiers for the property name (see class javadoc for more info) * @return The enum list, empty if not found */ public <T extends Enum<T>> List<T> getOptionalEnumList(Class<T> type, String propertyName, String... classifiers) { List<String> values = getOptionalStringList(propertyName, classifiers); List<T> result = new ArrayList<T>(values.size()); for (String value : values) { T enumValue = toEnum(type, value, propertyName, classifiers); result.add(enumValue); } return result; } // todo javadoc public <T> T getValueOfType(Class<T> type, String propertyName, String... classifiers) { String value = getString(propertyName, classifiers); return toValueOfType(type, value, propertyName, classifiers); } public <T> T getOptionalValueOfType(Class<T> type, String propertyName, String... classifiers) { String value = getOptionalString(propertyName, classifiers); return toValueOfType(type, value, propertyName, classifiers); } public <T> List<T> getValueListOfType(Class<T> type, String propertyName, String... classifiers) { List<T> result = getOptionalValueListOfType(type, propertyName, classifiers); if (result.isEmpty()) { throw new UnitilsException("No value found for " + nameToString(propertyName, classifiers)); } return result; } public <T> List<T> getOptionalValueListOfType(Class<T> type, String propertyName, String... classifiers) { List<String> values = getOptionalStringList(propertyName, classifiers); List<T> result = new ArrayList<T>(values.size()); for (String value : values) { T valueOfType = toValueOfType(type, value, propertyName, classifiers); result.add(valueOfType); } return result; } protected String nameToString(String propertyName, String... classifiers) { if (classifiers == null || classifiers.length == 0) { return "property " + propertyName; } return "property " + propertyName + " and classifiers " + Arrays.toString(classifiers); } protected String getProperty(String propertyName) { if (overridingProperties != null && overridingProperties.containsKey(propertyName)) { return overridingProperties.getProperty(propertyName); } return properties.getProperty(propertyName); } protected Boolean toBoolean(String value, String propertyName, String... classifiers) { if (value == null) { return null; } if ("true".equalsIgnoreCase(value)) { return TRUE; } if ("false".equalsIgnoreCase(value)) { return FALSE; } throw new UnitilsException("Value " + value + " of " + nameToString(propertyName, classifiers) + " is not a boolean."); } protected Integer toInteger(String value, String propertyName, String... classifiers) { try { if (value == null) { return null; } return Integer.valueOf(value); } catch (NumberFormatException e) { throw new UnitilsException("Value " + value + " of " + nameToString(propertyName, classifiers) + " is not an int."); } } protected Long toLong(String value, String propertyName, String... classifiers) { try { if (value == null) { return null; } return Long.valueOf(value); } catch (NumberFormatException e) { throw new UnitilsException("Value " + value + " of " + nameToString(propertyName, classifiers) + " is not a long."); } } protected List<String> toStringList(String value) { if (value == null) { return new ArrayList<String>(0); } String[] splitValues = value.split(","); List<String> result = new ArrayList<String>(splitValues.length); for (String splitValue : splitValues) { splitValue = splitValue.trim(); if ("".equals(splitValue)) { continue; } result.add(splitValue); } return result; } protected <T extends Enum<T>> T toEnum(Class<T> type, String value, String propertyName, String[] classifiers) { try { if (value == null) { return null; } return Enum.valueOf(type, value); } catch (Exception e) { throw new UnitilsException("Value " + value + " of " + nameToString(propertyName, classifiers) + " is not a valid enum value for type " + type.getName(), e); } } @SuppressWarnings({"unchecked"}) protected <T> T toInstance(String value, String propertyName, String... classifiers) { if (value == null) { return null; } try { T instance = (T) createInstanceOfType(value, true); if (instance instanceof Factory) { return (T) ((Factory<?>) instance).create(); } return instance; } catch (Exception e) { throw new UnitilsException("Value " + value + " of " + nameToString(propertyName, classifiers) + " is not a valid classname.", e); } } protected Class<?> toClass(String value, String propertyName, String[] classifiers) { if (value == null) { return null; } try { return Class.forName(value); } catch (Exception e) { throw new UnitilsException("Value " + value + " of " + nameToString(propertyName, classifiers) + " is not a valid class name.", e); } } @SuppressWarnings({"unchecked"}) protected <T> T toInstance(Class<T> type, String value, String propertyName, String... classifiers) { T instance = (T) toInstance(value, propertyName, classifiers); if (instance != null && !type.isAssignableFrom(instance.getClass())) { throw new UnitilsException("Value " + value + " of " + nameToString(propertyName, classifiers) + " is not of the expected type " + type.getName()); } return instance; } @SuppressWarnings({"unchecked"}) protected <T> T toValueOfType(Class<T> type, String value, String propertyName, String... classifiers) { if (type.isAssignableFrom(String.class)) { return (T) value; } if (Boolean.class.equals(type) || Boolean.TYPE.equals(type)) { return (T) toBoolean(value, propertyName, classifiers); } if (Integer.class.equals(type) || Integer.TYPE.equals(type)) { return (T) toInteger(value, propertyName, classifiers); } if (Long.class.equals(type) || Long.TYPE.equals(type)) { return (T) toLong(value, propertyName, classifiers); } if (type.isEnum()) { return (T) toEnum((Class<Enum>) type, value, propertyName, classifiers); } if (Class.class.equals(type)) { return (T) toClass(value, propertyName, classifiers); } return toInstance(type, value, propertyName, classifiers); } }