
net.sf.mmm.util.value.ValueConverter Maven / Gradle / Ivy
The newest version!
/* $Id: ValueConverter.java 384 2007-12-20 10:18:29Z hohwille $
* Copyright (c) The m-m-m Team, Licensed under the Apache License, Version 2.0
* http://www.apache.org/licenses/LICENSE-2.0 */
package net.sf.mmm.util.value;
import java.util.Date;
import javax.annotation.Resource;
import net.sf.mmm.util.NumericUtil;
import net.sf.mmm.util.StringUtil;
import net.sf.mmm.util.component.AlreadyInitializedException;
import net.sf.mmm.util.date.Iso8601Util;
/**
* This is a utility class providing support for dealing with values (e.g. when
* reading configurations).
*
* @author Joerg Hohwiller (hohwille at users.sourceforge.net)
*/
public class ValueConverter {
/** @see #getInstance() */
private static ValueConverter instance;
/** @see #getIso8601Util() */
private Iso8601Util iso8601Util;
/**
* The constructor.
*/
public ValueConverter() {
super();
}
/**
* This method gets the singleton instance of this {@link ValueConverter}.
* This design is the best compromise between easy access (via this
* indirection you have direct, static access to all offered functionality)
* and IoC-style design which allows extension and customization.
* For IoC usage, simply ignore all static {@link #getInstance()} methods and
* construct new instances via the container-framework of your choice (like
* plexus, pico, springframework, etc.). To wire up the dependent components
* everything is properly annotated using common-annotations (JSR-250). If
* your container does NOT support this, you should consider using a better
* one.
*
* @return the singleton instance.
*/
public static ValueConverter getInstance() {
if (instance == null) {
synchronized (ValueConverter.class) {
if (instance == null) {
instance = new ValueConverter();
instance.setIso8601Util(Iso8601Util.getInstance());
}
}
}
return instance;
}
/**
* This method gets the util used to parse and format date and time according
* to the standard ISO-8601
.
*
* @return the iso8601Util
*/
protected Iso8601Util getIso8601Util() {
return this.iso8601Util;
}
/**
* This method sets the {@link #getIso8601Util() Iso8601Util}.
*
* @param iso8601Util the iso8601Util to set
*/
@Resource
public void setIso8601Util(Iso8601Util iso8601Util) {
if (this.iso8601Util != null) {
throw new AlreadyInitializedException();
}
this.iso8601Util = iso8601Util;
}
/**
* This method parses a numeric value.
*
* @param numberValue is the number value as string.
* @param valueSource describes the source of the value. This may be the
* filename where the value was read from, an XPath where the value was
* located in an XML document, etc. It is used in exceptions thrown if
* something goes wrong. This will help to find the problem easier.
* @return the value as number.
* @throws WrongValueTypeException if the given string is no number.
*/
private static Number parseNumber(String numberValue, Object valueSource)
throws WrongValueTypeException {
try {
Double d = Double.valueOf(numberValue);
return NumericUtil.getInstance().toSimplestNumber(d);
} catch (NumberFormatException e) {
// TODO: valueSource as first arg, booleanValue as additional arg!
throw new WrongValueTypeException(numberValue, valueSource, Number.class, e);
}
}
/**
* This method converts the given {@link String}-value
to the
* given type
.
*
* @param value is the value to convert. It may be null
.
* @param valueSource describes the source of the value. This may be the
* filename where the value was read from, an XPath where the value was
* located in an XML document, etc. It is used in exceptions thrown if
* something goes wrong. This will help to find the problem easier.
* @param type is the type the value
should be converted to.
* @param defaultValue is returned if the given value
is
* null
. It may also be null
.
* @param is the type the value
should be converted to.
* @return the value
converted to type
or the
* defaultValue
if value
was
* null
. It will only return null
if
* both value
and defaultValue
are
* null
.
* @throws WrongValueTypeException if the given value
is NOT
* null
but can NOT be converted to the given
* type
(e.g. if value
is "12x" and
* type
is Integer.class
).
*/
public final V convertValue(String value, Object valueSource, Class type, V defaultValue)
throws WrongValueTypeException {
if (value == null) {
return defaultValue;
} else {
return convertValue(value, valueSource, type);
}
}
/**
* This method converts the given value
to a numeric type and
* also validates that it is in the given range from minimum
to
* maximum
.
*
* @param value is the value to convert.
* @param valueSource describes the source of the value. This may be the
* filename where the value was read from, an XPath where the value was
* located in an XML document, etc. It is used in exceptions thrown if
* something goes wrong. This will help to find the problem easier.
* @param minimum is the minimum number allowed. Use MIN_VALUE (e.g.
* {@link Double#MIN_VALUE}) if unbound.
* @param maximum is the maximum number allowed. Use MAX_VALUE (e.g.
* {@link Long#MAX_VALUE}) if unbound.
*
* @param is the templated numeric value type.
* @return the requested value in the given range from minimum
* and maximum
.
* @throws ValueNotSetException if the given value
is
* null
.
* @throws WrongValueTypeException if the value is NO number.
* @throws ValueOutOfRangeException if the value is NOT in the given range
* from minimum
to maximum
.
*/
@SuppressWarnings("unchecked")
public T convertValue(String value, Object valueSource, T minimum, T maximum)
throws ValueNotSetException, WrongValueTypeException, ValueOutOfRangeException {
T result = (T) convertValue(value, valueSource, minimum.getClass());
checkRange(result, valueSource, minimum, maximum);
return result;
}
/**
* This method checks that the given value
is in the inclusive
* range from minimum
to maximum
.
*
* @param value is the value to check.
* @param valueSource describes the source of the value. This may be the
* filename where the value was read from, an XPath where the value was
* located in an XML document, etc. It is used in exceptions thrown if
* something goes wrong. This will help to find the problem easier.
* @param minimum is the minimum number allowed.
* @param maximum is the maximum number allowed.
*/
private void checkRange(Number value, Object valueSource, Number minimum, Number maximum) {
double d = value.doubleValue();
if ((d < minimum.doubleValue()) || (d > maximum.doubleValue())) {
throw new ValueOutOfRangeException(value, valueSource, minimum, maximum);
}
}
/**
* This method gets a numeric value and also validates that it is in the given
* range from minimum
to maximum
.
*
* @param is the templated numeric value type.
* @param value is the value to convert.
* @param valueSource describes the source of the value. This may be the
* filename where the value was read from, an XPath where the value was
* located in an XML document, etc. It is used in exceptions thrown if
* something goes wrong. This will help to find the problem easier.
* @param minimum is the minimum number allowed. Use MIN_VALUE (e.g.
* {@link Double#MIN_VALUE}) if unbound.
* @param maximum is the maximum number allowed. Use MAX_VALUE (e.g.
* {@link Long#MAX_VALUE}) if unbound.
* @param defaultValue is the default returned if value
is
* null
. It may be null
. Else it must
* be in the given range from minimum
to
* maximum
.
* @return the given value
converted to {@literal } in the
* range from minimum
to maximum
or the
* defaultValue
if value
is
* null
. Will only be null
if both
* value
and defaultValue
are
* null
.
* @throws WrongValueTypeException if the value is NO number.
* @throws ValueOutOfRangeException if the value is NOT in the given range
* from minimum
to maximum
.
*/
public T convertValue(String value, Object valueSource, T minimum, T maximum,
T defaultValue) throws WrongValueTypeException, ValueOutOfRangeException {
if (defaultValue != null) {
checkRange(defaultValue, valueSource, minimum, maximum);
}
if (value == null) {
return defaultValue;
} else {
return convertValue(value, valueSource, minimum, maximum);
}
}
/**
* This method converts the given {@link String}-value
to the
* given type
.
*
* @param value is the value to convert.
* @param valueSource describes the source of the value. This may be the
* filename where the value was read from, an XPath where the value was
* located in an XML document, etc. It is used in exceptions thrown if
* something goes wrong. This will help to find the problem easier.
* @param type is the type the value
should be converted to.
* @param is the type the value
should be converted to.
* @return the value
converted to type
.
* @throws ValueNotSetException if the given value
is
* null
.
* @throws WrongValueTypeException if the given value
is NOT
* null
but can NOT be converted to the given
* type
(e.g. if value
is "12x" and
* type
is Integer.class
).
*/
@SuppressWarnings("unchecked")
public final V convertValue(String value, Object valueSource, Class type)
throws ValueNotSetException, WrongValueTypeException {
if (value == null) {
throw new ValueNotSetException(valueSource);
}
Object result;
try {
if (type.isEnum()) {
result = Enum.valueOf((Class) type, value);
} else if (type.isAssignableFrom(String.class)) {
result = value;
} else if ((type == boolean.class) || (type == Boolean.class)) {
result = StringUtil.getInstance().parseBoolean(value);
if (result == null) {
throw new WrongValueTypeException(value, valueSource, type);
}
} else if ((type == int.class) || (type == Integer.class)) {
result = Integer.valueOf(value);
} else if ((type == long.class) || (type == Long.class)) {
result = Long.valueOf(value);
} else if ((type == double.class) || (type == Double.class)) {
result = Double.valueOf(value);
} else if (type == Class.class) {
result = Class.forName(value);
} else if ((type == float.class) || (type == Float.class)) {
result = Float.valueOf(value);
} else if ((type == short.class) || (type == Short.class)) {
result = Short.valueOf(value);
} else if ((type == byte.class) || (type == Byte.class)) {
result = Byte.valueOf(value);
} else if (type == Number.class) {
result = parseNumber(value, valueSource);
} else if (type == Date.class) {
result = this.iso8601Util.parseDate(value);
} else if ((type == Character.class) || ((type == char.class))) {
if (value.length() == 1) {
result = Character.valueOf(value.charAt(0));
} else {
throw new WrongValueTypeException(value, valueSource, type);
}
} else if (type == Class.class) {
result = Class.forName(value);
} else {
return convertUnknownValue(value, type, valueSource);
}
} catch (NumberFormatException e) {
throw new WrongValueTypeException(value, valueSource, type, e);
} catch (ClassNotFoundException e) {
throw new WrongValueTypeException(value, valueSource, type, e);
}
// ATTENTION: cast does NOT work if type is primitive
// return type.cast(result);
return (V) result;
}
/**
* This method converts the given {@link String}-value
to the
* given type
. It is called from
* {@link #convertValue(String, Object, Class)} if the given type
* is unknown. This default implementation simply throws a new
* {@link WrongValueTypeException}. You can extend this class and override
* this method in order to support the conversion for additional types. You
* should first handle the conversion for all value types you like. Then for
* all other types you should delegate to the super
method
* implementation.
*
* @param value is the value to convert.
* @param type is the type the value
should be converted to.
* @param valueSource describes the source of the value. This may be the
* filename where the value was read from, an XPath where the value was
* located in an XML document, etc. It is used in exceptions thrown if
* something goes wrong. This will help to find the problem easier.
*
* @param is the type the value
should be converted to.
* @return the value
converted to type
.
* @throws ValueNotSetException if the given value
is
* null
.
* @throws WrongValueTypeException if the given value
is NOT
* null
but can NOT be converted to the given
* type
(e.g. if value
is "12x" and
* type
is Integer.class
).
*/
protected V convertUnknownValue(String value, Class type, Object valueSource)
throws ValueNotSetException, WrongValueTypeException {
// throw new UnknownValueType();
throw new WrongValueTypeException(value, valueSource, type);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy