StringConvert.java   StringConvert.java 
/* /*
* Copyright 2010 Stephen Colebourne * Copyright 2010-2011 Stephen Colebourne
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied . * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied .
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.joda.convert; package org.joda.convert;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
/** /**
* Manager for conversion to and from a {@code String}, acting as the main client interface. * Manager for conversion to and from a {@code String}, acting as the main client interface.
* <p> * <p>
* Support is provided for conversions based on the {@link StringConverter} interface * Support is provided for conversions based on the {@link StringConverter} interface
* or the {@link ToString} and {@link FromString} annotations. * or the {@link ToString} and {@link FromString} annotations.
* <p> * <p>
* StringConvert is thread-safe with concurrent caches. * StringConvert is thread-safe with concurrent caches.
skipping to change at line 71 skipping to change at line 72
registered.put(conv.getType(), conv); registered.put(conv.getType(), conv);
} }
registered.put(Boolean.TYPE, JDKStringConverter.BOOLEAN); registered.put(Boolean.TYPE, JDKStringConverter.BOOLEAN);
registered.put(Byte.TYPE, JDKStringConverter.BYTE); registered.put(Byte.TYPE, JDKStringConverter.BYTE);
registered.put(Short.TYPE, JDKStringConverter.SHORT); registered.put(Short.TYPE, JDKStringConverter.SHORT);
registered.put(Integer.TYPE, JDKStringConverter.INTEGER); registered.put(Integer.TYPE, JDKStringConverter.INTEGER);
registered.put(Long.TYPE, JDKStringConverter.LONG); registered.put(Long.TYPE, JDKStringConverter.LONG);
registered.put(Float.TYPE, JDKStringConverter.FLOAT); registered.put(Float.TYPE, JDKStringConverter.FLOAT);
registered.put(Double.TYPE, JDKStringConverter.DOUBLE); registered.put(Double.TYPE, JDKStringConverter.DOUBLE);
registered.put(Character.TYPE, JDKStringConverter.CHARACTER); registered.put(Character.TYPE, JDKStringConverter.CHARACTER);
// JSR-310 classes
tryRegister("javax.time.Instant", "parse");
tryRegister("javax.time.Duration", "parse");
tryRegister("javax.time.calendar.LocalDate", "parse");
tryRegister("javax.time.calendar.LocalTime", "parse");
tryRegister("javax.time.calendar.LocalDateTime", "parse");
tryRegister("javax.time.calendar.OffsetDate", "parse");
tryRegister("javax.time.calendar.OffsetTime", "parse");
tryRegister("javax.time.calendar.OffsetDateTime", "parse");
tryRegister("javax.time.calendar.ZonedDateTime", "parse");
tryRegister("javax.time.calendar.Year", "parse");
tryRegister("javax.time.calendar.YearMonth", "parse");
tryRegister("javax.time.calendar.MonthDay", "parse");
tryRegister("javax.time.calendar.Period", "parse");
tryRegister("javax.time.calendar.ZoneOffset", "of");
tryRegister("javax.time.calendar.ZoneId", "of");
tryRegister("javax.time.calendar.TimeZone", "of");
}
}
/**
* Tries to register a class using the standard toString/parse pattern.
*
* @param className the class name, not null
*/
private void tryRegister(String className, String fromStringMethodName)
{
try {
Class<?> cls = getClass().getClassLoader().loadClass(className)
;
registerMethods(cls, "toString", fromStringMethodName);
} catch (Exception ex) {
// ignore
} }
} }
//--------------------------------------------------------------------- -- //--------------------------------------------------------------------- --
/** /**
* Converts the specified object to a {@code String}. * Converts the specified object to a {@code String}.
* <p> * <p>
* This uses {@link #findConverter} to provide the converter. * This uses {@link #findConverter} to provide the converter.
* *
* @param <T> the type to convert from * @param <T> the type to convert from
skipping to change at line 96 skipping to change at line 128
public <T> String convertToString(T object) { public <T> String convertToString(T object) {
if (object == null) { if (object == null) {
return null; return null;
} }
Class<T> cls = (Class<T>) object.getClass(); Class<T> cls = (Class<T>) object.getClass();
StringConverter<T> conv = findConverter(cls); StringConverter<T> conv = findConverter(cls);
return conv.convertToString(object); return conv.convertToString(object);
} }
/** /**
* Converts the specified object to a {@code String}.
* <p>
* This uses {@link #findConverter} to provide the converter.
* The class can be provided to select a more specific converter.
*
* @param <T> the type to convert from
* @param cls the class to convert from, not null
* @param object the object to convert, null returns null
* @return the converted string, may be null
* @throws RuntimeException (or subclass) if unable to convert
*/
public <T> String convertToString(Class<T> cls, T object) {
if (object == null) {
return null;
}
StringConverter<T> conv = findConverter(cls);
return conv.convertToString(object);
}
/**
* Converts the specified object from a {@code String}. * Converts the specified object from a {@code String}.
* <p> * <p>
* This uses {@link #findConverter} to provide the converter. * This uses {@link #findConverter} to provide the converter.
* *
* @param <T> the type to convert to * @param <T> the type to convert to
* @param cls the class to convert to, not null * @param cls the class to convert to, not null
* @param str the string to convert, null returns null * @param str the string to convert, null returns null
* @return the converted object, may be null * @return the converted object, may be null
* @throws RuntimeException (or subclass) if unable to convert * @throws RuntimeException (or subclass) if unable to convert
*/ */
skipping to change at line 122 skipping to change at line 174
} }
/** /**
* Finds a suitable converter for the type. * Finds a suitable converter for the type.
* <p> * <p>
* This returns an instance of {@code StringConverter} for the specifie d class. * This returns an instance of {@code StringConverter} for the specifie d class.
* This could be useful in other frameworks. * This could be useful in other frameworks.
* <p> * <p>
* The search algorithm first searches the registered converters. * The search algorithm first searches the registered converters.
* It then searches for {@code ToString} and {@code FromString} annotat ions on the specified class. * It then searches for {@code ToString} and {@code FromString} annotat ions on the specified class.
* Both searches consider superclasses. * Both searches consider superclasses, but not interfaces.
* *
* @param <T> the type of the converter * @param <T> the type of the converter
* @param cls the class to find a converter for, not null * @param cls the class to find a converter for, not null
* @return the converter, not null * @return the converter, not null
* @throws RuntimeException (or subclass) if no converter found * @throws RuntimeException (or subclass) if no converter found
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public <T> StringConverter<T> findConverter(final Class<T> cls) { public <T> StringConverter<T> findConverter(final Class<T> cls) {
if (cls == null) { if (cls == null) {
throw new IllegalArgumentException("Class must not be null"); throw new IllegalArgumentException("Class must not be null");
skipping to change at line 215 skipping to change at line 267
} }
/** /**
* Finds the conversion method. * Finds the conversion method.
* *
* @param <T> the type of the converter * @param <T> the type of the converter
* @param cls the class to find a method for, not null * @param cls the class to find a method for, not null
* @return the method to call, null means use {@code toString} * @return the method to call, null means use {@code toString}
*/ */
private <T> Constructor<T> findFromStringConstructor(Class<T> cls) { private <T> Constructor<T> findFromStringConstructor(Class<T> cls) {
Constructor<T> con;
try { try {
Constructor<T> con = cls.getDeclaredConstructor(String.class); con = cls.getDeclaredConstructor(String.class);
FromString fromString = con.getAnnotation(FromString.class);
return fromString != null ? con : null;
} catch (NoSuchMethodException ex) { } catch (NoSuchMethodException ex) {
return null; try {
con = cls.getDeclaredConstructor(CharSequence.class);
} catch (NoSuchMethodException ex2) {
return null;
}
} }
FromString fromString = con.getAnnotation(FromString.class);
return fromString != null ? con : null;
} }
/** /**
* Finds the conversion method. * Finds the conversion method.
* *
* @param cls the class to find a method for, not null * @param cls the class to find a method for, not null
* @return the method to call, null means use {@code toString} * @return the method to call, null means use {@code toString}
*/ */
private Method findFromStringMethod(Class<?> cls, boolean searchSupercl asses) { private Method findFromStringMethod(Class<?> cls, boolean searchSupercl asses) {
Method matched = null; Method matched = null;
skipping to change at line 263 skipping to change at line 320
/** /**
* Registers a converter for a specific type. * Registers a converter for a specific type.
* <p> * <p>
* The converter will be used for subclasses unless overidden. * The converter will be used for subclasses unless overidden.
* <p> * <p>
* No new converters may be registered for the global singleton. * No new converters may be registered for the global singleton.
* *
* @param <T> the type of the converter * @param <T> the type of the converter
* @param cls the class to register a converter for, not null * @param cls the class to register a converter for, not null
* @param converter the String converter, not null * @param converter the String converter, not null
* @throws IllegalArgumentException if unable to register
* @throws IllegalStateException if class already registered
*/ */
public <T> void register(final Class<T> cls, StringConverter<T> convert er) { public <T> void register(final Class<T> cls, StringConverter<T> convert er) {
if (cls == null ) { if (cls == null ) {
throw new IllegalArgumentException("Class must not be null"); throw new IllegalArgumentException("Class must not be null");
} }
if (converter == null) { if (converter == null) {
throw new IllegalArgumentException("StringConverter must not be null"); throw new IllegalArgumentException("StringConverter must not be null");
} }
if (this == INSTANCE) { if (this == INSTANCE) {
throw new IllegalStateException("Global singleton cannot be ext ended"); throw new IllegalStateException("Global singleton cannot be ext ended");
} }
StringConverter<?> old = registered.putIfAbsent(cls, converter); StringConverter<?> old = registered.putIfAbsent(cls, converter);
if (old != null) { if (old != null) {
throw new IllegalStateException("Converter already registered f or class: " + cls); throw new IllegalStateException("Converter already registered f or class: " + cls);
} }
} }
/**
* Registers a converter for a specific type by method names.
* <p>
* This method allows the converter to be used when the target class ca
nnot have annotations added.
* The two method names must obey the same rules as defined by the anno
tations
* {@link ToString} and {@link FromString}.
* The converter will be used for subclasses unless overidden.
* <p>
* No new converters may be registered for the global singleton.
* <p>
* For example, {@code convert.registerMethods(Distance.class, "toStrin
g", "parse");}
*
* @param <T> the type of the converter
* @param cls the class to register a converter for, not null
* @param toStringMethodName the name of the method converting to a st
ring, not null
* @param fromStringMethodName the name of the method converting from
a string, not null
* @throws IllegalArgumentException if unable to register
* @throws IllegalStateException if class already registered
*/
public <T> void registerMethods(final Class<T> cls, String toStringMeth
odName, String fromStringMethodName) {
if (cls == null ) {
throw new IllegalArgumentException("Class must not be null");
}
if (toStringMethodName == null || fromStringMethodName == null) {
throw new IllegalArgumentException("Method names must not be nu
ll");
}
if (this == INSTANCE) {
throw new IllegalStateException("Global singleton cannot be ext
ended");
}
Method toString = findToStringMethod(cls, toStringMethodName);
Method fromString = findFromStringMethod(cls, fromStringMethodName)
;
MethodsStringConverter<T> converter = new MethodsStringConverter<T>
(cls, toString, fromString);
StringConverter<?> old = registered.putIfAbsent(cls, converter);
if (old != null) {
throw new IllegalStateException("Converter already registered f
or class: " + cls);
}
}
/**
* Registers a converter for a specific type by method and constructor.
* <p>
* This method allows the converter to be used when the target class ca
nnot have annotations added.
* The two method name and constructor must obey the same rules as defi
ned by the annotations
* {@link ToString} and {@link FromString}.
* The converter will be used for subclasses unless overidden.
* <p>
* No new converters may be registered for the global singleton.
* <p>
* For example, {@code convert.registerMethodConstructor(Distance.class
, "toString");}
*
* @param <T> the type of the converter
* @param cls the class to register a converter for, not null
* @param toStringMethodName the name of the method converting to a st
ring, not null
* @throws IllegalArgumentException if unable to register
* @throws IllegalStateException if class already registered
*/
public <T> void registerMethodConstructor(final Class<T> cls, String to
StringMethodName) {
if (cls == null ) {
throw new IllegalArgumentException("Class must not be null");
}
if (toStringMethodName == null) {
throw new IllegalArgumentException("Method name must not be nul
l");
}
if (this == INSTANCE) {
throw new IllegalStateException("Global singleton cannot be ext
ended");
}
Method toString = findToStringMethod(cls, toStringMethodName);
Constructor<T> fromString = findFromStringConstructorByType(cls);
MethodConstructorStringConverter<T> converter = new MethodConstruct
orStringConverter<T>(cls, toString, fromString);
StringConverter<?> old = registered.putIfAbsent(cls, converter);
if (old != null) {
throw new IllegalStateException("Converter already registered f
or class: " + cls);
}
}
/**
* Finds the conversion method.
*
* @param cls the class to find a method for, not null
* @param methodName the name of the method to find, not null
* @return the method to call, null means use {@code toString}
*/
private Method findToStringMethod(Class<?> cls, String methodName) {
Method m;
try {
m = cls.getMethod(methodName);
} catch (NoSuchMethodException ex) {
throw new IllegalArgumentException(ex);
}
if (Modifier.isStatic(m.getModifiers())) {
throw new IllegalArgumentException("Method must not be static: "
+ methodName);
}
return m;
}
/**
* Finds the conversion method.
*
* @param cls the class to find a method for, not null
* @param methodName the name of the method to find, not null
* @return the method to call, null means use {@code toString}
*/
private Method findFromStringMethod(Class<?> cls, String methodName) {
Method m;
try {
m = cls.getMethod(methodName, String.class);
} catch (NoSuchMethodException ex) {
try {
m = cls.getMethod(methodName, CharSequence.class);
} catch (NoSuchMethodException ex2) {
throw new IllegalArgumentException("Method not found", ex2)
;
}
}
if (Modifier.isStatic(m.getModifiers()) == false) {
throw new IllegalArgumentException("Method must be static: " + me
thodName);
}
return m;
}
/**
* Finds the conversion method.
*
* @param <T> the type of the converter
* @param cls the class to find a method for, not null
* @return the method to call, null means use {@code toString}
*/
private <T> Constructor<T> findFromStringConstructorByType(Class<T> cls
) {
try {
return cls.getDeclaredConstructor(String.class);
} catch (NoSuchMethodException ex) {
try {
return cls.getDeclaredConstructor(CharSequence.class);
} catch (NoSuchMethodException ex2) {
throw new IllegalArgumentException("Constructor not found", e
x2);
}
}
}
//--------------------------------------------------------------------- -- //--------------------------------------------------------------------- --
/** /**
* Returns a simple string representation of the object. * Returns a simple string representation of the object.
* *
* @return the string representation, never null * @return the string representation, never null
*/ */
@Override @Override
public String toString() { public String toString() {
return getClass().getSimpleName(); return getClass().getSimpleName();
} }
 End of changes. 11 change blocks. 
6 lines changed or deleted 230 lines changed or added

This html diff was produced by rfcdiff 1.41. The latest version is available from http://tools.ietf.org/tools/rfcdiff/