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/ |