Clean-up and minor changes in core classes.

Adapted new code style.
No or few functional changes.
This commit is contained in:
Harald Kuhr 2011-02-16 22:29:23 +01:00
parent df0d3f90e8
commit 191643a36c
16 changed files with 298 additions and 127 deletions

View File

@ -276,19 +276,19 @@ public final class BeanUtil {
} }
try { try {
// If this does not throw an excption, it works // If this does not throw an exception, it works
method = pObject.getClass().getMethod(pName, pParams); method = pObject.getClass().getMethod(pName, pParams);
} }
catch (Throwable t) { catch (Throwable t) {
// Ignore // Ignore
} }
// 2: Try any supertypes of paramType, to see if we have a match // 2: Try any super-types of paramType, to see if we have a match
if (method == null) { if (method == null) {
while ((paramType = paramType.getSuperclass()) != null) { while ((paramType = paramType.getSuperclass()) != null) {
pParams[0] = paramType; pParams[0] = paramType;
try { try {
// If this does not throw an excption, it works // If this does not throw an exception, it works
method = pObject.getClass().getMethod(pName, pParams); method = pObject.getClass().getMethod(pName, pParams);
} }
catch (Throwable t) { catch (Throwable t) {
@ -365,6 +365,9 @@ public final class BeanUtil {
} }
} }
// TODO: Convert value to single-value array if needed
// TODO: Convert CSV String to string array (or potentially any type of array)
// TODO: Convert other types // TODO: Convert other types
if (pValue instanceof String) { if (pValue instanceof String) {
Converter converter = Converter.getInstance(); Converter converter = Converter.getInstance();
@ -596,8 +599,7 @@ public final class BeanUtil {
catch (NoSuchMethodException ignore) { catch (NoSuchMethodException ignore) {
// If invocation failed, convert lisp-style and try again // If invocation failed, convert lisp-style and try again
if (pLispToCamel && property.indexOf('-') > 0) { if (pLispToCamel && property.indexOf('-') > 0) {
setPropertyValue(pBean, StringUtil.lispToCamel(property, false), setPropertyValue(pBean, StringUtil.lispToCamel(property, false), entry.getValue());
entry.getValue());
} }
} }
} }

View File

@ -5,6 +5,8 @@ import java.io.IOException;
import java.lang.reflect.UndeclaredThrowableException; import java.lang.reflect.UndeclaredThrowableException;
import java.sql.SQLException; import java.sql.SQLException;
import static com.twelvemonkeys.lang.Validate.notNull;
/** /**
* ExceptionUtil * ExceptionUtil
* *
@ -14,6 +16,15 @@ import java.sql.SQLException;
*/ */
public final class ExceptionUtil { public final class ExceptionUtil {
/**
* Re-throws an exception, either as-is if the exception was already unchecked, otherwise wrapped in
* a {@link RuntimeException}.
* "Expected" exception types are wrapped in {@link RuntimeException}s, while
* "unexpected" exception types are wrapped in {@link java.lang.reflect.UndeclaredThrowableException}s.
*
* @param pThrowable the exception to launder
* @param pExpectedTypes the types of exception the code is expected to throw
*/
/*public*/ static void launder(final Throwable pThrowable, Class<? extends Throwable>... pExpectedTypes) { /*public*/ static void launder(final Throwable pThrowable, Class<? extends Throwable>... pExpectedTypes) {
if (pThrowable instanceof Error) { if (pThrowable instanceof Error) {
throw (Error) pThrowable; throw (Error) pThrowable;
@ -40,36 +51,38 @@ public final class ExceptionUtil {
throwAs(RuntimeException.class, pThrowable); throwAs(RuntimeException.class, pThrowable);
} }
/*public*/ static void handle(final Throwable pThrowable, final ThrowableHandler<? extends Throwable>... pHandler) { @SuppressWarnings({"unchecked"})
handleImpl(pThrowable, pHandler); /*public*/ static void handle(final Throwable pThrowable, final ThrowableHandler<? extends Throwable>... pHandlers) {
handleImpl(pThrowable, (ThrowableHandler<Throwable>[]) pHandlers);
} }
@SuppressWarnings({"unchecked"}) private static void handleImpl(final Throwable pThrowable, final ThrowableHandler<Throwable>... pHandlers) {
private static <T extends Throwable> void handleImpl(final Throwable pThrowable, final ThrowableHandler<T>... pHandler) {
// TODO: Sort more specific throwable handlers before less specific? // TODO: Sort more specific throwable handlers before less specific?
for (ThrowableHandler<T> handler : pHandler) { for (ThrowableHandler<Throwable> handler : pHandlers) {
if (handler.handles(pThrowable)) { if (handler.handles(pThrowable)) {
handler.handle((T) pThrowable); handler.handle(pThrowable);
return; return;
} }
} }
// Not handled, re-throw
throwUnchecked(pThrowable); throwUnchecked(pThrowable);
} }
public static abstract class ThrowableHandler<T extends Throwable> { public static abstract class ThrowableHandler<T extends Throwable> {
private Class<? extends T>[] mThrowables; private final Class<? extends T>[] throwables;
protected ThrowableHandler(final Class<? extends T>... pThrowables) { protected ThrowableHandler(final Class<? extends T>... pThrowables) {
// TODO: Assert not null throwables = notNull(pThrowables).clone();
mThrowables = pThrowables.clone();
} }
final public boolean handles(final Throwable pThrowable) { final public boolean handles(final Throwable pThrowable) {
for (Class<? extends T> throwable : mThrowables) { for (Class<? extends T> throwable : throwables) {
if (throwable.isAssignableFrom(pThrowable.getClass())) { if (throwable.isAssignableFrom(pThrowable.getClass())) {
return true; return true;
} }
} }
return false; return false;
} }

View File

@ -39,24 +39,24 @@ public final class Platform {
/** /**
* Normalized operating system constant * Normalized operating system constant
*/ */
final OperatingSystem mOS; final OperatingSystem os;
/** /**
* Unormalized operating system version constant (for completeness) * Unormalized operating system version constant (for completeness)
*/ */
final String mVersion; final String version;
/** /**
* Normalized system architecture constant * Normalized system architecture constant
*/ */
final Architecture mArchitecture; final Architecture architecture;
static final private Platform INSTANCE = new Platform(); static final private Platform INSTANCE = new Platform();
private Platform() { private Platform() {
mOS = normalizeOperatingSystem(); os = normalizeOperatingSystem();
mVersion = System.getProperty("os.version"); version = System.getProperty("os.version");
mArchitecture = normalizeArchitecture(mOS); architecture = normalizeArchitecture(os);
} }
private static OperatingSystem normalizeOperatingSystem() { private static OperatingSystem normalizeOperatingSystem() {
@ -138,21 +138,21 @@ public final class Platform {
* @return this platform's OS. * @return this platform's OS.
*/ */
public OperatingSystem getOS() { public OperatingSystem getOS() {
return mOS; return os;
} }
/** /**
* @return this platform's OS version. * @return this platform's OS version.
*/ */
public String getVersion() { public String getVersion() {
return mVersion; return version;
} }
/** /**
* @return this platform's architecture. * @return this platform's architecture.
*/ */
public Architecture getArchitecture() { public Architecture getArchitecture() {
return mArchitecture; return architecture;
} }
/** /**
@ -160,7 +160,7 @@ public final class Platform {
* @return the current {@code OperatingSystem}. * @return the current {@code OperatingSystem}.
*/ */
public static OperatingSystem os() { public static OperatingSystem os() {
return INSTANCE.mOS; return INSTANCE.os;
} }
/** /**
@ -168,7 +168,7 @@ public final class Platform {
* @return the current OS version. * @return the current OS version.
*/ */
public static String version() { public static String version() {
return INSTANCE.mVersion; return INSTANCE.version;
} }
/** /**
@ -176,7 +176,7 @@ public final class Platform {
* @return the current {@code Architecture}. * @return the current {@code Architecture}.
*/ */
public static Architecture arch() { public static Architecture arch() {
return INSTANCE.mArchitecture; return INSTANCE.architecture;
} }
/** /**
@ -197,14 +197,14 @@ public final class Platform {
Unknown(System.getProperty("os.arch")); Unknown(System.getProperty("os.arch"));
final String mName;// for debug only final String name;// for debug only
private Architecture(String pName) { private Architecture(String pName) {
mName = pName; name = pName;
} }
public String toString() { public String toString() {
return mName; return name;
} }
} }
@ -225,20 +225,20 @@ public final class Platform {
Unknown(System.getProperty("os.name"), ""); Unknown(System.getProperty("os.name"), "");
final String mId; final String id;
final String mName;// for debug only final String name;// for debug only
private OperatingSystem(String pName, String pId) { private OperatingSystem(String pName, String pId) {
mName = pName; name = pName;
mId = pId; id = pId;
} }
public String getName() { public String getName() {
return mName; return name;
} }
public String toString() { public String toString() {
return mId; return id;
} }
} }
} }

View File

@ -77,7 +77,7 @@ public final class StringUtil {
} }
/** /**
* Constructs a new {@link String} by decoding the specified subarray of bytes using the specified charset. * Constructs a new {@link String} by decoding the specified sub array of bytes using the specified charset.
* Replacement for {@link String#String(byte[], int, int, String) new String(byte[], int, int, String)}, that does * Replacement for {@link String#String(byte[], int, int, String) new String(byte[], int, int, String)}, that does
* not throw the checked {@link UnsupportedEncodingException}, * not throw the checked {@link UnsupportedEncodingException},
* but instead the unchecked {@link UnsupportedCharsetException} if the character set is not supported. * but instead the unchecked {@link UnsupportedCharsetException} if the character set is not supported.
@ -1580,7 +1580,7 @@ public final class StringUtil {
* Converts a string array to a string separated by the given delimiter. * Converts a string array to a string separated by the given delimiter.
* *
* @param pStringArray the string array * @param pStringArray the string array
* @param pDelimiterString the delimter string * @param pDelimiterString the delimiter string
* @return string of delimiter separated values * @return string of delimiter separated values
* @throws IllegalArgumentException if {@code pDelimiterString == null} * @throws IllegalArgumentException if {@code pDelimiterString == null}
*/ */

View File

@ -16,6 +16,8 @@ import java.util.Map;
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/lang/Validate.java#1 $ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/lang/Validate.java#1 $
*/ */
public final class Validate { public final class Validate {
// TODO: Make it possible to throw IllegalStateException instead of IllegalArgumentException?
private static final String UNSPECIFIED_PARAM_NAME = "method parameter"; private static final String UNSPECIFIED_PARAM_NAME = "method parameter";
private Validate() {} private Validate() {}
@ -121,4 +123,16 @@ public final class Validate {
return pParameter; return pParameter;
} }
public static boolean isTrue(final boolean pExpression, final String pMessage) {
return isTrue(pExpression, pExpression, pMessage);
}
public static <T> T isTrue(final boolean condition, final T value, final String message) {
if (!condition) {
throw new IllegalArgumentException(String.format(message, value));
}
return value;
}
} }

View File

@ -40,7 +40,7 @@ import java.io.Serializable;
/** /**
* A {@code Map} adapter for a Java Bean. * A {@code Map} adapter for a Java Bean.
* <p/> * <p/>
* Ruhtlessly stolen from * Ruthlessly stolen from
* <a href="http://binkley.blogspot.com/2006/08/mapping-java-bean.html>Binkley's Blog</a> * <a href="http://binkley.blogspot.com/2006/08/mapping-java-bean.html>Binkley's Blog</a>
*/ */
public final class BeanMap extends AbstractMap<String, Object> implements Serializable, Cloneable { public final class BeanMap extends AbstractMap<String, Object> implements Serializable, Cloneable {

View File

@ -37,60 +37,31 @@ package com.twelvemonkeys.util.convert;
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/util/convert/ConversionException.java#1 $ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/util/convert/ConversionException.java#1 $
*/ */
public class ConversionException extends IllegalArgumentException { public class ConversionException extends IllegalArgumentException {
protected Throwable mCause = this;
/** /**
* Creates a {@code ConversionException} with the given error message. * Creates a {@code ConversionException} with the given error message.
* *
* @param pMessage the error message * @param pMessage the error message
*/ */
public ConversionException(String pMessage) { public ConversionException(final String pMessage) {
super(pMessage); super(pMessage);
} }
/** /**
* Creates a {@code ConversionException} with the given cause. * Creates a {@code ConversionException} with the given cause.
* *
* @param pCause The Throwable that caused this exception * @param pCause The {@link Throwable} that caused this exception
*/ */
public ConversionException(Throwable pCause) { public ConversionException(final Throwable pCause) {
super(pCause == null ? null : pCause.getMessage()); super(pCause != null ? pCause.getMessage() : null, pCause);
initCause(pCause);
} }
/** /**
* Returns the cause of this {@code Throwable} or {@code null} if the * Creates a {@code ConversionException} with the given message and cause.
* cause is nonexistent or unknown.
* *
* @return the cause of this {@code Throwable} or {@code null} if the * @param pMessage the error message
* cause is nonexistent or unknown (the cause is the throwable that caused * @param pCause The {@link Throwable} that caused this exception
* this throwable to get thrown).
*/ */
public Throwable getCause() { public ConversionException(final String pMessage, final Throwable pCause) {
if (mCause == this) { super(pMessage, pCause);
return null;
}
return mCause;
}
/**
* Initializes this ConversionException with the given cause.
*
* @param pCause The Throwable that caused this exception
*
* @throws IllegalStateException if cause is allready set
* @throws IllegalArgumentException if {@code pCause == this}
*/
public Throwable initCause(Throwable pCause) {
if (mCause != this) {
throw new IllegalStateException("Can't overwrite cause");
}
if (pCause == this) {
throw new IllegalArgumentException("Can't be caused by self");
}
mCause = pCause;
return this;
} }
} }

View File

@ -56,13 +56,14 @@ import java.util.Map;
// Maybe have BeanUtil act as a "proxy", and hide this class alltogheter? // Maybe have BeanUtil act as a "proxy", and hide this class alltogheter?
// TODO: ServiceRegistry for registering 3rd party converters // TODO: ServiceRegistry for registering 3rd party converters
// TODO: URI scheme, for implicit typing? Is that a good idea? // TODO: URI scheme, for implicit typing? Is that a good idea?
// TODO: Array converters?
public abstract class Converter implements PropertyConverter { public abstract class Converter implements PropertyConverter {
/** Our singleton instance */ /** Our singleton instance */
protected static Converter sInstance = new ConverterImpl(); // Thread safe & EASY protected static Converter sInstance = new ConverterImpl(); // Thread safe & EASY
/** The conveters Map */ /** The conveters Map */
protected Map mConverters = new Hashtable(); protected Map converters = new Hashtable();
// Register our predefined converters // Register our predefined converters
static { static {
@ -115,7 +116,7 @@ public abstract class Converter implements PropertyConverter {
* @see #unregisterConverter(Class) * @see #unregisterConverter(Class)
*/ */
public static void registerConverter(Class pType, PropertyConverter pConverter) { public static void registerConverter(Class pType, PropertyConverter pConverter) {
getInstance().mConverters.put(pType, pConverter); getInstance().converters.put(pType, pConverter);
} }
/** /**
@ -128,7 +129,7 @@ public abstract class Converter implements PropertyConverter {
* @see #registerConverter(Class,PropertyConverter) * @see #registerConverter(Class,PropertyConverter)
*/ */
public static void unregisterConverter(Class pType) { public static void unregisterConverter(Class pType) {
getInstance().mConverters.remove(pType); getInstance().converters.remove(pType);
} }
/** /**

View File

@ -59,7 +59,7 @@ class ConverterImpl extends Converter {
// Loop until we find a suitable converter // Loop until we find a suitable converter
do { do {
// Have a match, return converter // Have a match, return converter
if ((converter = getInstance().mConverters.get(cl)) != null) { if ((converter = getInstance().converters.get(cl)) != null) {
return (PropertyConverter) converter; return (PropertyConverter) converter;
} }

View File

@ -28,9 +28,11 @@
package com.twelvemonkeys.util.convert; package com.twelvemonkeys.util.convert;
import com.twelvemonkeys.lang.*; import com.twelvemonkeys.lang.BeanUtil;
import com.twelvemonkeys.lang.StringUtil;
import java.lang.reflect.*; import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
/** /**
* Converts strings to objects and back. * Converts strings to objects and back.
@ -67,9 +69,7 @@ public final class DefaultConverter implements PropertyConverter {
* be converted into the given type, using a string constructor or static * be converted into the given type, using a string constructor or static
* {@code valueof} method. * {@code valueof} method.
*/ */
public Object toObject(String pString, final Class pType, String pFormat) public Object toObject(String pString, final Class pType, String pFormat) throws ConversionException {
throws ConversionException {
if (pString == null) { if (pString == null) {
return null; return null;
} }
@ -78,6 +78,14 @@ public final class DefaultConverter implements PropertyConverter {
throw new MissingTypeException(); throw new MissingTypeException();
} }
if (pType.isArray()) {
return toArray(pString, pType, pFormat);
}
// TODO: Separate CollectionConverter?
// should however, be simple to wrap array using Arrays.asList
// But what about generic type?! It's erased...
// Primitive -> wrapper // Primitive -> wrapper
Class type; Class type;
if (pType == Boolean.TYPE) { if (pType == Boolean.TYPE) {
@ -113,6 +121,31 @@ public final class DefaultConverter implements PropertyConverter {
} }
} }
private Object toArray(String pString, Class pType, String pFormat) {
String[] strings = StringUtil.toStringArray(pString, pFormat != null ? pFormat : StringUtil.DELIMITER_STRING);
Class type = pType.getComponentType();
if (type == String.class) {
return strings;
}
Object array = Array.newInstance(type, strings.length);
try {
for (int i = 0; i < strings.length; i++) {
Array.set(array, i, Converter.getInstance().toObject(strings[i], type));
}
}
catch (ConversionException e) {
if (pFormat != null) {
throw new ConversionException(String.format("%s for string \"%s\" with format \"%s\"", e.getMessage(), pString, pFormat), e);
}
else {
throw new ConversionException(String.format("%s for string \"%s\"", e.getMessage(), pString), e);
}
}
return array;
}
/** /**
* Converts the object to a string, using {@code pObject.toString()}. * Converts the object to a string, using {@code pObject.toString()}.
* *
@ -126,10 +159,77 @@ public final class DefaultConverter implements PropertyConverter {
throws ConversionException { throws ConversionException {
try { try {
return (pObject != null ? pObject.toString() : null); return pObject == null ? null : pObject.getClass().isArray() ? arrayToString(toObjectArray(pObject), pFormat) : pObject.toString();
} }
catch (RuntimeException rte) { catch (RuntimeException rte) {
throw new ConversionException(rte); throw new ConversionException(rte);
} }
} }
private String arrayToString(final Object[] pArray, final String pFormat) {
return pFormat == null ? StringUtil.toCSVString(pArray) : StringUtil.toCSVString(pArray, pFormat);
}
private Object[] toObjectArray(Object pObject) {
// TODO: Extract util method for wrapping/unwrapping native arrays?
Object[] array;
Class<?> componentType = pObject.getClass().getComponentType();
if (componentType.isPrimitive()) {
if (int.class == componentType) {
array = new Integer[Array.getLength(pObject)];
for (int i = 0; i < array.length; i++) {
Array.set(array, i, Array.get(pObject, i));
}
}
else if (short.class == componentType) {
array = new Short[Array.getLength(pObject)];
for (int i = 0; i < array.length; i++) {
Array.set(array, i, Array.get(pObject, i));
}
}
else if (long.class == componentType) {
array = new Long[Array.getLength(pObject)];
for (int i = 0; i < array.length; i++) {
Array.set(array, i, Array.get(pObject, i));
}
}
else if (float.class == componentType) {
array = new Float[Array.getLength(pObject)];
for (int i = 0; i < array.length; i++) {
Array.set(array, i, Array.get(pObject, i));
}
}
else if (double.class == componentType) {
array = new Double[Array.getLength(pObject)];
for (int i = 0; i < array.length; i++) {
Array.set(array, i, Array.get(pObject, i));
}
}
else if (boolean.class == componentType) {
array = new Boolean[Array.getLength(pObject)];
for (int i = 0; i < array.length; i++) {
Array.set(array, i, Array.get(pObject, i));
}
}
else if (byte.class == componentType) {
array = new Byte[Array.getLength(pObject)];
for (int i = 0; i < array.length; i++) {
Array.set(array, i, Array.get(pObject, i));
}
}
else if (char.class == componentType) {
array = new Character[Array.getLength(pObject)];
for (int i = 0; i < array.length; i++) {
Array.set(array, i, Array.get(pObject, i));
}
}
else {
throw new IllegalArgumentException("Unknown type " + componentType);
}
}
else {
array = (Object[]) pObject;
}
return array;
}
} }

View File

@ -39,14 +39,14 @@ import java.text.*;
* Converts strings to numbers and back. * Converts strings to numbers and back.
* <p/> * <p/>
* <small>This class has a static cache of {@code NumberFormats}, to avoid * <small>This class has a static cache of {@code NumberFormats}, to avoid
* creation and parsing of numberformats every time one is used.</small> * creation and parsing of number formats every time one is used.</small>
* *
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $ * @author last modified by $Author: haku $
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/util/convert/NumberConverter.java#2 $ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/util/convert/NumberConverter.java#2 $
*/ */
public class NumberConverter implements PropertyConverter { public class NumberConverter implements PropertyConverter {
// TODO: Need to either make this non-local aware, or document that it is... // TODO: Need to either make this non-locale aware, or document that it is...
private static final DecimalFormatSymbols SYMBOLS = new DecimalFormatSymbols(Locale.US); private static final DecimalFormatSymbols SYMBOLS = new DecimalFormatSymbols(Locale.US);
private static final NumberFormat sDefaultFormat = new DecimalFormat("#0.#", SYMBOLS); private static final NumberFormat sDefaultFormat = new DecimalFormat("#0.#", SYMBOLS);

View File

@ -45,8 +45,8 @@ import java.util.regex.PatternSyntaxException;
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/util/regex/RegExTokenIterator.java#1 $ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/util/regex/RegExTokenIterator.java#1 $
*/ */
public class RegExTokenIterator extends AbstractTokenIterator { public class RegExTokenIterator extends AbstractTokenIterator {
private final Matcher mMatcher; private final Matcher matcher;
private boolean mNext = false; private boolean next = false;
/** /**
* Creates a {@code RegExTokenIterator}. * Creates a {@code RegExTokenIterator}.
@ -80,7 +80,7 @@ public class RegExTokenIterator extends AbstractTokenIterator {
throw new IllegalArgumentException("pattern == null"); throw new IllegalArgumentException("pattern == null");
} }
mMatcher = Pattern.compile(pPattern).matcher(pString); matcher = Pattern.compile(pPattern).matcher(pString);
} }
/** /**
@ -88,18 +88,18 @@ public class RegExTokenIterator extends AbstractTokenIterator {
* *
*/ */
public void reset() { public void reset() {
mMatcher.reset(); matcher.reset();
} }
public boolean hasNext() { public boolean hasNext() {
return mNext || (mNext = mMatcher.find()); return next || (next = matcher.find());
} }
public String next() { public String next() {
if (!hasNext()) { if (!hasNext()) {
throw new NoSuchElementException(); throw new NoSuchElementException();
} }
mNext = false; next = false;
return mMatcher.group(); return matcher.group();
} }
} }

View File

@ -114,6 +114,7 @@ import java.io.PrintStream;
* @deprecated Will probably be removed in the near future * @deprecated Will probably be removed in the near future
*/ */
public class WildcardStringParser { public class WildcardStringParser {
// TODO: Get rid of this class
// Constants // Constants

View File

@ -65,7 +65,7 @@ import java.util.*;
*/ */
public class ServiceRegistry { public class ServiceRegistry {
// TODO: Security issues? // TODO: Security issues?
// TODO: Application contexts? // TODO: Application contexts? Probably use instance per thread group..
/** /**
* "META-INF/services/" * "META-INF/services/"
@ -73,7 +73,7 @@ public class ServiceRegistry {
public static final String SERVICES = "META-INF/services/"; public static final String SERVICES = "META-INF/services/";
// Class to CategoryRegistry mapping // Class to CategoryRegistry mapping
private final Map<Class<?>, CategoryRegistry> mCategoryMap; private final Map<Class<?>, CategoryRegistry> categoryMap;
/** /**
* Creates a {@code ServiceRegistry} instance with a set of categories * Creates a {@code ServiceRegistry} instance with a set of categories
@ -98,7 +98,7 @@ public class ServiceRegistry {
} }
// NOTE: Categories are constant for the lifetime of a registry // NOTE: Categories are constant for the lifetime of a registry
mCategoryMap = Collections.unmodifiableMap(map); categoryMap = Collections.unmodifiableMap(map);
} }
private <T> void putCategory(Map<Class<?>, CategoryRegistry> pMap, Class<T> pCategory) { private <T> void putCategory(Map<Class<?>, CategoryRegistry> pMap, Class<T> pCategory) {
@ -154,7 +154,7 @@ public class ServiceRegistry {
if (!classNames.isEmpty()) { if (!classNames.isEmpty()) {
@SuppressWarnings({"unchecked"}) @SuppressWarnings({"unchecked"})
CategoryRegistry<T> registry = mCategoryMap.get(pCategory); CategoryRegistry<T> registry = categoryMap.get(pCategory);
Set providerClassNames = classNames.keySet(); Set providerClassNames = classNames.keySet();
@ -213,7 +213,7 @@ public class ServiceRegistry {
* @return an {@code Iterator} containing all categories in this registry. * @return an {@code Iterator} containing all categories in this registry.
*/ */
protected Iterator<Class<?>> categories() { protected Iterator<Class<?>> categories() {
return mCategoryMap.keySet().iterator(); return categoryMap.keySet().iterator();
} }
/** /**
@ -260,18 +260,19 @@ public class ServiceRegistry {
return getRegistry(pElement).contatins(pProvider); return getRegistry(pElement).contatins(pProvider);
} }
}) { }) {
Class<?> mCurrent; Class<?> current;
public Class next() { public Class next() {
return (mCurrent = super.next()); return (current = super.next());
} }
public void remove() { public void remove() {
if (mCurrent == null) { if (current == null) {
throw new IllegalStateException("No current element"); throw new IllegalStateException("No current element");
} }
getRegistry(mCurrent).deregister(pProvider);
mCurrent = null; getRegistry(current).deregister(pProvider);
current = null;
} }
}; };
} }
@ -284,7 +285,7 @@ public class ServiceRegistry {
*/ */
private <T> CategoryRegistry<T> getRegistry(final Class<T> pCategory) { private <T> CategoryRegistry<T> getRegistry(final Class<T> pCategory) {
@SuppressWarnings({"unchecked"}) @SuppressWarnings({"unchecked"})
CategoryRegistry<T> registry = mCategoryMap.get(pCategory); CategoryRegistry<T> registry = categoryMap.get(pCategory);
if (registry == null) { if (registry == null) {
throw new IllegalArgumentException("No such category: " + pCategory.getName()); throw new IllegalArgumentException("No such category: " + pCategory.getName());
} }
@ -366,17 +367,17 @@ public class ServiceRegistry {
* Keeps track of each individual category. * Keeps track of each individual category.
*/ */
class CategoryRegistry<T> { class CategoryRegistry<T> {
private final Class<T> mCategory; private final Class<T> category;
private final Map<Class, T> mProviders = new LinkedHashMap<Class, T>(); private final Map<Class, T> providers = new LinkedHashMap<Class, T>();
CategoryRegistry(Class<T> pCategory) { CategoryRegistry(Class<T> pCategory) {
Validate.notNull(pCategory, "category"); Validate.notNull(pCategory, "category");
mCategory = pCategory; category = pCategory;
} }
private void checkCategory(final Object pProvider) { private void checkCategory(final Object pProvider) {
if (!mCategory.isInstance(pProvider)) { if (!category.isInstance(pProvider)) {
throw new IllegalArgumentException(pProvider + " not instance of category " + mCategory.getName()); throw new IllegalArgumentException(pProvider + " not instance of category " + category.getName());
} }
} }
@ -386,7 +387,7 @@ public class ServiceRegistry {
// NOTE: We only register the new instance, if we don't allready // NOTE: We only register the new instance, if we don't allready
// have an instance of pProvider's class. // have an instance of pProvider's class.
if (!contatins(pProvider)) { if (!contatins(pProvider)) {
mProviders.put(pProvider.getClass(), pProvider); providers.put(pProvider.getClass(), pProvider);
processRegistration(pProvider); processRegistration(pProvider);
return true; return true;
} }
@ -397,7 +398,7 @@ public class ServiceRegistry {
void processRegistration(final T pProvider) { void processRegistration(final T pProvider) {
if (pProvider instanceof RegisterableService) { if (pProvider instanceof RegisterableService) {
RegisterableService service = (RegisterableService) pProvider; RegisterableService service = (RegisterableService) pProvider;
service.onRegistration(ServiceRegistry.this, mCategory); service.onRegistration(ServiceRegistry.this, category);
} }
} }
@ -406,7 +407,7 @@ public class ServiceRegistry {
// NOTE: We remove any provider of the same class, this may or may // NOTE: We remove any provider of the same class, this may or may
// not be the same instance as pProvider. // not be the same instance as pProvider.
T oldProvider = mProviders.remove(pProvider.getClass()); T oldProvider = providers.remove(pProvider.getClass());
if (oldProvider != null) { if (oldProvider != null) {
processDeregistration(oldProvider); processDeregistration(oldProvider);
@ -419,12 +420,12 @@ public class ServiceRegistry {
void processDeregistration(final T pOldProvider) { void processDeregistration(final T pOldProvider) {
if (pOldProvider instanceof RegisterableService) { if (pOldProvider instanceof RegisterableService) {
RegisterableService service = (RegisterableService) pOldProvider; RegisterableService service = (RegisterableService) pOldProvider;
service.onDeregistration(ServiceRegistry.this, mCategory); service.onDeregistration(ServiceRegistry.this, category);
} }
} }
public boolean contatins(final Object pProvider) { public boolean contatins(final Object pProvider) {
return mProviders.containsKey(pProvider.getClass()); return providers.containsKey(pProvider.getClass());
} }
public Iterator<T> providers() { public Iterator<T> providers() {
@ -432,9 +433,9 @@ public class ServiceRegistry {
// using the deregister method will result in // using the deregister method will result in
// ConcurrentModificationException in the iterator.. // ConcurrentModificationException in the iterator..
// We wrap the iterator to track deregistration right. // We wrap the iterator to track deregistration right.
final Iterator<T> iterator = mProviders.values().iterator(); final Iterator<T> iterator = providers.values().iterator();
return new Iterator<T>() { return new Iterator<T>() {
T mCurrent; T current;
public boolean hasNext() { public boolean hasNext() {
return iterator.hasNext(); return iterator.hasNext();
@ -442,12 +443,12 @@ public class ServiceRegistry {
} }
public T next() { public T next() {
return (mCurrent = iterator.next()); return (current = iterator.next());
} }
public void remove() { public void remove() {
iterator.remove(); iterator.remove();
processDeregistration(mCurrent); processDeregistration(current);
} }
}; };
} }

View File

@ -1,5 +1,8 @@
package com.twelvemonkeys.util.convert; package com.twelvemonkeys.util.convert;
import java.io.File;
import java.net.URI;
/** /**
* DefaultConverterTestCase * DefaultConverterTestCase
* <p/> * <p/>
@ -20,7 +23,9 @@ public class DefaultConverterTestCase extends PropertyConverterAbstractTestCase
new Conversion("true", Boolean.TRUE), new Conversion("true", Boolean.TRUE),
new Conversion("TRUE", Boolean.TRUE, null, "true"), new Conversion("TRUE", Boolean.TRUE, null, "true"),
new Conversion("false", Boolean.FALSE), new Conversion("false", Boolean.FALSE),
new Conversion("FALSE", new Boolean(false), null, "false"), new Conversion("FALSE", false, null, "false"),
new Conversion("2", 2),
// Stupid but valid // Stupid but valid
new Conversion("fooBar", "fooBar"), new Conversion("fooBar", "fooBar"),
@ -29,6 +34,22 @@ public class DefaultConverterTestCase extends PropertyConverterAbstractTestCase
// Stupid test class that reveres chars // Stupid test class that reveres chars
new Conversion("fooBar", new FooBar("fooBar")), new Conversion("fooBar", new FooBar("fooBar")),
// String array tests
new Conversion("foo, bar, baz", new String[] {"foo", "bar", "baz"}),
new Conversion("foo", new String[] {"foo"}),
new Conversion("foo;bar; baz", new String[] {"foo", "bar", "baz"}, "; ", "foo; bar; baz"),
// Native array tests
new Conversion("1, 2, 3", new int[] {1, 2, 3}),
new Conversion("-1, 42, 0", new long[] {-1, 42, 0}),
new Conversion("true, true, false", new boolean[] {true, true, false}),
new Conversion(".3, 4E7, .97", new float[] {.3f, 4e7f, .97f}, ", ", "0.3, 4.0E7, 0.97"),
// Object array test
new Conversion("foo, bar", new FooBar[] {new FooBar("foo"), new FooBar("bar")}),
new Conversion("/temp, /usr/local/bin", new File[] {new File("/temp"), new File("/usr/local/bin")}),
new Conversion("file:/temp, http://java.net/", new URI[] {URI.create("file:/temp"), URI.create("http://java.net/")}),
// TODO: More tests // TODO: More tests
}; };
} }

View File

@ -2,6 +2,8 @@ package com.twelvemonkeys.util.convert;
import com.twelvemonkeys.lang.ObjectAbstractTestCase; import com.twelvemonkeys.lang.ObjectAbstractTestCase;
import java.util.Arrays;
/** /**
* PropertyConverterAbstractTestCase * PropertyConverterAbstractTestCase
* <p/> * <p/>
@ -30,16 +32,28 @@ public abstract class PropertyConverterAbstractTestCase extends ObjectAbstractTe
Object obj; Object obj;
try { try {
obj = converter.toObject(test.original(), test.type(), test.format()); obj = converter.toObject(test.original(), test.type(), test.format());
assertEquals("'" + test.original() + "' convtered to incorrect type", test.type(), obj.getClass());
assertEquals("'" + test.original() + "' converted to incorrect type", test.type(), obj.getClass());
if (test.type().isArray()) {
assertTrue("'" + test.original() + "' not converted", arrayEquals(test.value(), obj));
}
else {
assertEquals("'" + test.original() + "' not converted", test.value(), obj); assertEquals("'" + test.original() + "' not converted", test.value(), obj);
}
String result = converter.toString(test.value(), test.format()); String result = converter.toString(test.value(), test.format());
assertEquals("'" + test.converted() + "' does not macth", test.converted(), result); assertEquals("'" + test.converted() + "' does not match", test.converted(), result);
obj = converter.toObject(result, test.type(), test.format()); obj = converter.toObject(result, test.type(), test.format());
assertEquals("'" + test.original() + "' convtered to incorrect type", test.type(), obj.getClass()); assertEquals("'" + test.original() + "' converted to incorrect type", test.type(), obj.getClass());
assertEquals("'" + test.original() + "' did not survive roundrip conversion", test.value(), obj);
if (test.type().isArray()) {
assertTrue("'" + test.original() + "' did not survive round trip conversion", arrayEquals(test.value(), obj));
}
else {
assertEquals("'" + test.original() + "' did not survive round trip conversion", test.value(), obj);
}
} }
catch (ConversionException e) { catch (ConversionException e) {
e.printStackTrace(); e.printStackTrace();
@ -48,6 +62,39 @@ public abstract class PropertyConverterAbstractTestCase extends ObjectAbstractTe
} }
} }
// TODO: Util method?
private boolean arrayEquals(final Object left, final Object right) {
if (left.getClass().getComponentType().isPrimitive()) {
if (int.class == left.getClass().getComponentType()) {
return Arrays.equals((int[]) left, (int[]) right);
}
if (short.class == left.getClass().getComponentType()) {
return Arrays.equals((short[]) left, (short[]) right);
}
if (long.class == left.getClass().getComponentType()) {
return Arrays.equals((long[]) left, (long[]) right);
}
if (float.class == left.getClass().getComponentType()) {
return Arrays.equals((float[]) left, (float[]) right);
}
if (double.class == left.getClass().getComponentType()) {
return Arrays.equals((double[]) left, (double[]) right);
}
if (boolean.class == left.getClass().getComponentType()) {
return Arrays.equals((boolean[]) left, (boolean[]) right);
}
if (byte.class == left.getClass().getComponentType()) {
return Arrays.equals((byte[]) left, (byte[]) right);
}
if (char.class == left.getClass().getComponentType()) {
return Arrays.equals((char[]) left, (char[]) right);
}
// Else blow up below...
}
return Arrays.equals((Object[]) left, (Object[]) right);
}
public static final class Conversion { public static final class Conversion {
private final String mStrVal; private final String mStrVal;
private final Object mObjVal; private final Object mObjVal;