basic conversion service impl; still need super converters, custom converters, and conversion of generic collection elements
This commit is contained in:
parent
c1f3ec0c5b
commit
3a7b4bf1b0
|
|
@ -0,0 +1,91 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2004-2009 the original author or authors.
|
||||||
|
*
|
||||||
|
* 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.springframework.core.convert.service;
|
||||||
|
|
||||||
|
import java.lang.reflect.Array;
|
||||||
|
|
||||||
|
import org.springframework.core.convert.ConversionExecutor;
|
||||||
|
import org.springframework.core.convert.ConversionService;
|
||||||
|
import org.springframework.core.convert.converter.SuperConverter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Special one-way converter that converts from a source array to a target array. Supports type conversion of the
|
||||||
|
* individual array elements; for example, the ability to convert a String[] to an Integer[]. Mainly used internally by
|
||||||
|
* {@link ConversionService} implementations.
|
||||||
|
*
|
||||||
|
* @author Keith Donald
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
class ArrayToArray implements SuperConverter {
|
||||||
|
|
||||||
|
private ConversionService conversionService;
|
||||||
|
|
||||||
|
private ConversionExecutor elementConverter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new array-to-array converter.
|
||||||
|
* @param conversionService the service to use to lookup conversion executors for individual array elements
|
||||||
|
* dynamically
|
||||||
|
*/
|
||||||
|
public ArrayToArray(ConversionService conversionService) {
|
||||||
|
this.conversionService = conversionService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new array-to-array converter.
|
||||||
|
* @param elementConverter a specific conversion executor to use to convert elements in the source array to elements
|
||||||
|
* in the target array.
|
||||||
|
*/
|
||||||
|
public ArrayToArray(ConversionExecutor elementConverter) {
|
||||||
|
this.elementConverter = elementConverter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Class getSourceClass() {
|
||||||
|
return Object[].class;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Class getSuperTargetClass() {
|
||||||
|
return Object[].class;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object convert(Object source, Class targetClass) throws Exception {
|
||||||
|
if (source == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Class sourceComponentType = source.getClass().getComponentType();
|
||||||
|
Class targetComponentType = targetClass.getComponentType();
|
||||||
|
int length = Array.getLength(source);
|
||||||
|
Object targetArray = Array.newInstance(targetComponentType, length);
|
||||||
|
ConversionExecutor converter = getElementConverter(sourceComponentType, targetComponentType);
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
Object value = Array.get(source, i);
|
||||||
|
Array.set(targetArray, i, converter.execute(value));
|
||||||
|
}
|
||||||
|
return targetArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object convertBack(Object target) throws Exception {
|
||||||
|
throw new UnsupportedOperationException("Not supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
private ConversionExecutor getElementConverter(Class sourceComponentType, Class targetComponentType) {
|
||||||
|
if (elementConverter != null) {
|
||||||
|
return elementConverter;
|
||||||
|
} else {
|
||||||
|
return conversionService.getConversionExecutor(sourceComponentType, targetComponentType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,147 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2004-2009 the original author or authors.
|
||||||
|
*
|
||||||
|
* 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.springframework.core.convert.service;
|
||||||
|
|
||||||
|
import java.lang.reflect.Array;
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.SortedSet;
|
||||||
|
import java.util.TreeSet;
|
||||||
|
|
||||||
|
import org.springframework.core.GenericCollectionTypeResolver;
|
||||||
|
import org.springframework.core.convert.ConversionExecutor;
|
||||||
|
import org.springframework.core.convert.ConversionService;
|
||||||
|
import org.springframework.core.convert.converter.SuperConverter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Special converter that converts from a source array to a target collection. Supports the selection of an
|
||||||
|
* "approximate" collection implementation when a target collection interface such as <code>List.class</code> is
|
||||||
|
* specified. Supports type conversion of array elements when a concrete parameterized collection class is provided,
|
||||||
|
* such as <code>IntegerList<Integer>.class</code>.
|
||||||
|
*
|
||||||
|
* Note that type erasure prevents arbitrary access to generic collection element type information at runtime,
|
||||||
|
* preventing the ability to convert elements for collections declared as properties.
|
||||||
|
*
|
||||||
|
* Mainly used internally by {@link ConversionService} implementations.
|
||||||
|
*
|
||||||
|
* @author Keith Donald
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
class ArrayToCollection implements SuperConverter {
|
||||||
|
|
||||||
|
private ConversionService conversionService;
|
||||||
|
|
||||||
|
private ConversionExecutor elementConverter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new array to collection converter.
|
||||||
|
* @param conversionService the conversion service to use to lookup the converter to apply to array elements added
|
||||||
|
* to the target collection
|
||||||
|
*/
|
||||||
|
public ArrayToCollection(ConversionService conversionService) {
|
||||||
|
this.conversionService = conversionService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new array to collection converter.
|
||||||
|
* @param elementConverter A specific converter to use on array elements when adding them to the target collection
|
||||||
|
*/
|
||||||
|
public ArrayToCollection(ConversionExecutor elementConverter) {
|
||||||
|
this.elementConverter = elementConverter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object convert(Object source, Class targetClass) throws Exception {
|
||||||
|
if (source == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Class collectionImplClass = getCollectionImplClass(targetClass);
|
||||||
|
Constructor constructor = collectionImplClass.getConstructor((Class[]) null);
|
||||||
|
Collection collection = (Collection) constructor.newInstance((Object[]) null);
|
||||||
|
ConversionExecutor converter = getArrayElementConverter(source, targetClass);
|
||||||
|
int length = Array.getLength(source);
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
Object value = Array.get(source, i);
|
||||||
|
if (converter != null) {
|
||||||
|
value = converter.execute(value);
|
||||||
|
}
|
||||||
|
collection.add(value);
|
||||||
|
}
|
||||||
|
return collection;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object convertBack(Object target) throws Exception {
|
||||||
|
throw new UnsupportedOperationException("Should never be called");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object convertBack(Object target, Class sourceClass) throws Exception {
|
||||||
|
if (target == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Collection collection = (Collection) target;
|
||||||
|
Object array = Array.newInstance(sourceClass.getComponentType(), collection.size());
|
||||||
|
int i = 0;
|
||||||
|
for (Iterator it = collection.iterator(); it.hasNext(); i++) {
|
||||||
|
Object value = it.next();
|
||||||
|
if (value != null) {
|
||||||
|
ConversionExecutor converter;
|
||||||
|
if (elementConverter != null) {
|
||||||
|
converter = elementConverter;
|
||||||
|
} else {
|
||||||
|
converter = conversionService.getConversionExecutor(value.getClass(), sourceClass
|
||||||
|
.getComponentType());
|
||||||
|
}
|
||||||
|
value = converter.execute(value);
|
||||||
|
}
|
||||||
|
Array.set(array, i, value);
|
||||||
|
}
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Class getCollectionImplClass(Class targetClass) {
|
||||||
|
if (targetClass.isInterface()) {
|
||||||
|
if (List.class.equals(targetClass)) {
|
||||||
|
return ArrayList.class;
|
||||||
|
} else if (Set.class.equals(targetClass)) {
|
||||||
|
return LinkedHashSet.class;
|
||||||
|
} else if (SortedSet.class.equals(targetClass)) {
|
||||||
|
return TreeSet.class;
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Unsupported collection interface [" + targetClass.getName() + "]");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return targetClass;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ConversionExecutor getArrayElementConverter(Object source, Class targetClass) {
|
||||||
|
if (elementConverter != null) {
|
||||||
|
return elementConverter;
|
||||||
|
} else {
|
||||||
|
Class elementType = GenericCollectionTypeResolver.getCollectionType(targetClass);
|
||||||
|
if (elementType != null) {
|
||||||
|
Class componentType = source.getClass().getComponentType();
|
||||||
|
return conversionService.getConversionExecutor(componentType, elementType);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,88 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2004-2009 the original author or authors.
|
||||||
|
*
|
||||||
|
* 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.springframework.core.convert.service;
|
||||||
|
|
||||||
|
import java.lang.reflect.Array;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
import org.springframework.core.convert.ConversionExecutor;
|
||||||
|
import org.springframework.core.convert.ConversionService;
|
||||||
|
import org.springframework.core.convert.converter.SuperConverter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Special converter that converts from a source array to a target collection. Supports the selection of an
|
||||||
|
* "approximate" collection implementation when a target collection interface such as <code>List.class</code> is
|
||||||
|
* specified. Supports type conversion of array elements when a concrete parameterized collection class is provided,
|
||||||
|
* such as <code>IntegerList<Integer>.class</code>.
|
||||||
|
*
|
||||||
|
* Note that type erasure prevents arbitrary access to generic collection element type information at runtime,
|
||||||
|
* preventing the ability to convert elements for collections declared as properties.
|
||||||
|
*
|
||||||
|
* Mainly used internally by {@link ConversionService} implementations.
|
||||||
|
*
|
||||||
|
* @author Keith Donald
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
class CollectionToArray implements SuperConverter {
|
||||||
|
|
||||||
|
private ConversionService conversionService;
|
||||||
|
|
||||||
|
private ConversionExecutor elementConverter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new array to collection converter.
|
||||||
|
* @param conversionService the conversion service to use to lookup the converter to apply to array elements added
|
||||||
|
* to the target collection
|
||||||
|
*/
|
||||||
|
public CollectionToArray(ConversionService conversionService) {
|
||||||
|
this.conversionService = conversionService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new array to collection converter.
|
||||||
|
* @param elementConverter A specific converter to use on array elements when adding them to the target collection
|
||||||
|
*/
|
||||||
|
public CollectionToArray(ConversionExecutor elementConverter) {
|
||||||
|
this.elementConverter = elementConverter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object convert(Object source, Class targetClass) throws Exception {
|
||||||
|
Collection collection = (Collection) source;
|
||||||
|
Object array = Array.newInstance(targetClass.getComponentType(), collection.size());
|
||||||
|
int i = 0;
|
||||||
|
for (Iterator it = collection.iterator(); it.hasNext(); i++) {
|
||||||
|
Object value = it.next();
|
||||||
|
if (value != null) {
|
||||||
|
ConversionExecutor converter;
|
||||||
|
if (elementConverter != null) {
|
||||||
|
converter = elementConverter;
|
||||||
|
} else {
|
||||||
|
converter = conversionService.getConversionExecutor(value.getClass(), targetClass
|
||||||
|
.getComponentType());
|
||||||
|
}
|
||||||
|
value = converter.execute(value);
|
||||||
|
}
|
||||||
|
Array.set(array, i, value);
|
||||||
|
}
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object convertBack(Object target) throws Exception {
|
||||||
|
throw new UnsupportedOperationException("Should never be called");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,123 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2004-2009 the original author or authors.
|
||||||
|
*
|
||||||
|
* 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.springframework.core.convert.service;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.SortedSet;
|
||||||
|
import java.util.TreeSet;
|
||||||
|
|
||||||
|
import org.springframework.core.GenericCollectionTypeResolver;
|
||||||
|
import org.springframework.core.convert.ConversionExecutor;
|
||||||
|
import org.springframework.core.convert.ConversionService;
|
||||||
|
import org.springframework.core.convert.converter.SuperConverter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A converter that can convert from one collection type to another.
|
||||||
|
*
|
||||||
|
* @author Keith Donald
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
class CollectionToCollection implements SuperConverter {
|
||||||
|
|
||||||
|
private ConversionService conversionService;
|
||||||
|
|
||||||
|
private ConversionExecutor elementConverter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new collection-to-collection converter
|
||||||
|
* @param conversionService the conversion service to use to convert collection elements to add to the target
|
||||||
|
* collection
|
||||||
|
*/
|
||||||
|
public CollectionToCollection(ConversionService conversionService) {
|
||||||
|
this.conversionService = conversionService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new collection-to-collection converter
|
||||||
|
* @param elementConverter a specific converter to use to convert collection elements added to the target collection
|
||||||
|
*/
|
||||||
|
public CollectionToCollection(ConversionExecutor elementConverter) {
|
||||||
|
this.elementConverter = elementConverter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Class getSourceClass() {
|
||||||
|
return Collection.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Class getSuperTargetClass() {
|
||||||
|
return Collection.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object convert(Object source, Class targetClass) throws Exception {
|
||||||
|
if (source == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Class targetCollectionImpl = getCollectionImplClass(targetClass);
|
||||||
|
Collection targetCollection = (Collection) targetCollectionImpl.getConstructor((Class[]) null).newInstance(
|
||||||
|
(Object[]) null);
|
||||||
|
ConversionExecutor elementConverter = getElementConverter(source, targetClass);
|
||||||
|
Collection sourceCollection = (Collection) source;
|
||||||
|
Iterator it = sourceCollection.iterator();
|
||||||
|
while (it.hasNext()) {
|
||||||
|
Object value = it.next();
|
||||||
|
if (elementConverter != null) {
|
||||||
|
value = elementConverter.execute(value);
|
||||||
|
}
|
||||||
|
targetCollection.add(value);
|
||||||
|
}
|
||||||
|
return targetCollection;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object convertBack(Object target) throws Exception {
|
||||||
|
throw new UnsupportedOperationException("Not supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
// this code is duplicated in ArrayToCollection.java and ObjectToCollection too
|
||||||
|
private Class getCollectionImplClass(Class targetClass) {
|
||||||
|
if (targetClass.isInterface()) {
|
||||||
|
if (List.class.equals(targetClass)) {
|
||||||
|
return ArrayList.class;
|
||||||
|
} else if (Set.class.equals(targetClass)) {
|
||||||
|
return LinkedHashSet.class;
|
||||||
|
} else if (SortedSet.class.equals(targetClass)) {
|
||||||
|
return TreeSet.class;
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Unsupported collection interface [" + targetClass.getName() + "]");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return targetClass;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ConversionExecutor getElementConverter(Object source, Class targetClass) {
|
||||||
|
if (elementConverter != null) {
|
||||||
|
return elementConverter;
|
||||||
|
} else {
|
||||||
|
Class elementType = GenericCollectionTypeResolver.getCollectionType(targetClass);
|
||||||
|
if (elementType != null) {
|
||||||
|
Class componentType = source.getClass().getComponentType();
|
||||||
|
return conversionService.getConversionExecutor(componentType, elementType);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,91 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2004-2009 the original author or authors.
|
||||||
|
*
|
||||||
|
* 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.springframework.core.convert.service;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import org.springframework.core.convert.converter.NumberToNumber;
|
||||||
|
import org.springframework.core.convert.converter.StringToBigDecimal;
|
||||||
|
import org.springframework.core.convert.converter.StringToBigInteger;
|
||||||
|
import org.springframework.core.convert.converter.StringToBoolean;
|
||||||
|
import org.springframework.core.convert.converter.StringToByte;
|
||||||
|
import org.springframework.core.convert.converter.StringToCharacter;
|
||||||
|
import org.springframework.core.convert.converter.StringToDouble;
|
||||||
|
import org.springframework.core.convert.converter.StringToEnum;
|
||||||
|
import org.springframework.core.convert.converter.StringToFloat;
|
||||||
|
import org.springframework.core.convert.converter.StringToInteger;
|
||||||
|
import org.springframework.core.convert.converter.StringToLocale;
|
||||||
|
import org.springframework.core.convert.converter.StringToLong;
|
||||||
|
import org.springframework.core.convert.converter.StringToShort;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default, local implementation of a conversion service. Will automatically register <i>from string</i> converters for
|
||||||
|
* a number of standard Java types like Class, Number, Boolean and so on.
|
||||||
|
*
|
||||||
|
* @author Keith Donald
|
||||||
|
*/
|
||||||
|
public class DefaultConversionService extends GenericConversionService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new default conversion service, installing the default converters.
|
||||||
|
*/
|
||||||
|
public DefaultConversionService() {
|
||||||
|
addDefaultConverters();
|
||||||
|
addDefaultAliases();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add all default converters to the conversion service.
|
||||||
|
*/
|
||||||
|
protected void addDefaultConverters() {
|
||||||
|
addConverter(new StringToByte());
|
||||||
|
addConverter(new StringToBoolean());
|
||||||
|
addConverter(new StringToCharacter());
|
||||||
|
addConverter(new StringToShort());
|
||||||
|
addConverter(new StringToInteger());
|
||||||
|
addConverter(new StringToLong());
|
||||||
|
addConverter(new StringToFloat());
|
||||||
|
addConverter(new StringToDouble());
|
||||||
|
addConverter(new StringToBigInteger());
|
||||||
|
addConverter(new StringToBigDecimal());
|
||||||
|
addConverter(new StringToLocale());
|
||||||
|
addConverter(new StringToEnum());
|
||||||
|
addConverter(new NumberToNumber());
|
||||||
|
addConverter(new ObjectToCollection(this));
|
||||||
|
addConverter(new CollectionToCollection(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void addDefaultAliases() {
|
||||||
|
addAlias("string", String.class);
|
||||||
|
addAlias("byte", Byte.class);
|
||||||
|
addAlias("boolean", Boolean.class);
|
||||||
|
addAlias("char", Character.class);
|
||||||
|
addAlias("short", Short.class);
|
||||||
|
addAlias("int", Integer.class);
|
||||||
|
addAlias("long", Long.class);
|
||||||
|
addAlias("float", Float.class);
|
||||||
|
addAlias("double", Double.class);
|
||||||
|
addAlias("bigInteger", BigInteger.class);
|
||||||
|
addAlias("bigDecimal", BigDecimal.class);
|
||||||
|
addAlias("locale", Locale.class);
|
||||||
|
addAlias("enum", Enum.class);
|
||||||
|
addAlias("date", Date.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,313 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2004-2009 the original author or authors.
|
||||||
|
*
|
||||||
|
* 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.springframework.core.convert.service;
|
||||||
|
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.lang.reflect.ParameterizedType;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.lang.reflect.TypeVariable;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.springframework.core.GenericTypeResolver;
|
||||||
|
import org.springframework.core.convert.ConversionException;
|
||||||
|
import org.springframework.core.convert.ConversionExecutor;
|
||||||
|
import org.springframework.core.convert.ConversionExecutorNotFoundException;
|
||||||
|
import org.springframework.core.convert.ConversionService;
|
||||||
|
import org.springframework.core.convert.converter.Converter;
|
||||||
|
import org.springframework.core.convert.converter.SuperConverter;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base implementation of a conversion service. Initially empty, e.g. no converters are registered by default.
|
||||||
|
*
|
||||||
|
* @author Keith Donald
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public class GenericConversionService implements ConversionService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An indexed map of Converters. Each Map.Entry key is a source class (S) that can be converted from. Each Map.Entry
|
||||||
|
* value is a Map that defines the targetClass-to-Converter mappings for that source.
|
||||||
|
*/
|
||||||
|
private final Map sourceClassConverters = new HashMap();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indexes classes by well-known aliases.
|
||||||
|
*/
|
||||||
|
private final Map aliasMap = new HashMap<String, Class<?>>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An optional parent conversion service.
|
||||||
|
*/
|
||||||
|
private ConversionService parent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the parent of this conversion service. Could be null.
|
||||||
|
*/
|
||||||
|
public ConversionService getParent() {
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the parent of this conversion service. This is optional.
|
||||||
|
*/
|
||||||
|
public void setParent(ConversionService parent) {
|
||||||
|
this.parent = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register the Converter with this conversion service.
|
||||||
|
* @param converter the converter to register
|
||||||
|
*/
|
||||||
|
public void addConverter(Converter converter) {
|
||||||
|
List typeInfo = getTypeInfo(converter);
|
||||||
|
Class sourceClass = (Class) typeInfo.get(0);
|
||||||
|
Class targetClass = (Class) typeInfo.get(1);
|
||||||
|
// index forward
|
||||||
|
Map sourceMap = getSourceMap(sourceClass);
|
||||||
|
sourceMap.put(targetClass, converter);
|
||||||
|
// index reverse
|
||||||
|
sourceMap = getSourceMap(targetClass);
|
||||||
|
sourceMap.put(sourceClass, new ReverseConverter(converter));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register the SuperConverter with this conversion service.
|
||||||
|
* @param converter the super converter to register
|
||||||
|
*/
|
||||||
|
public void addConverter(SuperConverter converter) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a convenient alias for the target type. {@link #getClassForAlias(String)} can then be used to lookup the type
|
||||||
|
* given the alias.
|
||||||
|
* @see #getClassForAlias(String)
|
||||||
|
*/
|
||||||
|
public void addAlias(String alias, Class targetType) {
|
||||||
|
aliasMap.put(alias, targetType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object executeConversion(Object source, Class targetClass) throws ConversionExecutorNotFoundException,
|
||||||
|
ConversionException {
|
||||||
|
if (source != null) {
|
||||||
|
ConversionExecutor conversionExecutor = getConversionExecutor(source.getClass(), targetClass);
|
||||||
|
return conversionExecutor.execute(source);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object executeConversion(String converterId, Object source, Class targetClass)
|
||||||
|
throws ConversionExecutorNotFoundException, ConversionException {
|
||||||
|
if (source != null) {
|
||||||
|
ConversionExecutor conversionExecutor = getConversionExecutor(converterId, source.getClass(), targetClass);
|
||||||
|
return conversionExecutor.execute(source);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConversionExecutor getConversionExecutor(Class sourceClass, Class targetClass)
|
||||||
|
throws ConversionExecutorNotFoundException {
|
||||||
|
Assert.notNull(sourceClass, "The source class to convert from is required");
|
||||||
|
Assert.notNull(targetClass, "The target class to convert to is required");
|
||||||
|
if (targetClass.isAssignableFrom(sourceClass)) {
|
||||||
|
return new StaticConversionExecutor(sourceClass, targetClass, new NoOpConverter());
|
||||||
|
}
|
||||||
|
sourceClass = convertToWrapperClassIfNecessary(sourceClass);
|
||||||
|
targetClass = convertToWrapperClassIfNecessary(targetClass);
|
||||||
|
// special handling for arrays since they are not indexable classes
|
||||||
|
if (sourceClass.isArray()) {
|
||||||
|
if (targetClass.isArray()) {
|
||||||
|
return new StaticSuperConversionExecutor(sourceClass, targetClass, new ArrayToArray(this));
|
||||||
|
} else if (Collection.class.isAssignableFrom(targetClass)) {
|
||||||
|
if (!targetClass.isInterface() && Modifier.isAbstract(targetClass.getModifiers())) {
|
||||||
|
throw new IllegalArgumentException("Conversion target class [" + targetClass.getName()
|
||||||
|
+ "] is invalid; cannot convert to abstract collection types--"
|
||||||
|
+ "request an interface or concrete implementation instead");
|
||||||
|
}
|
||||||
|
return new StaticSuperConversionExecutor(sourceClass, targetClass, new ArrayToCollection(this));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (targetClass.isArray()) {
|
||||||
|
if (Collection.class.isAssignableFrom(sourceClass)) {
|
||||||
|
return new StaticSuperConversionExecutor(sourceClass, targetClass, new CollectionToArray(this));
|
||||||
|
} else {
|
||||||
|
return new StaticSuperConversionExecutor(sourceClass, targetClass, new ObjectToArray(this));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Converter converter = findRegisteredConverter(sourceClass, targetClass);
|
||||||
|
if (converter != null) {
|
||||||
|
// we found a converter
|
||||||
|
return new StaticConversionExecutor(sourceClass, targetClass, converter);
|
||||||
|
} else {
|
||||||
|
if (parent != null) {
|
||||||
|
// try the parent
|
||||||
|
return parent.getConversionExecutor(sourceClass, targetClass);
|
||||||
|
} else {
|
||||||
|
throw new ConversionExecutorNotFoundException(sourceClass, targetClass,
|
||||||
|
"No ConversionExecutor found for converting from sourceClass [" + sourceClass.getName()
|
||||||
|
+ "] to target class [" + targetClass.getName() + "]");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConversionExecutor getConversionExecutor(String converterId, Class sourceClass, Class targetClass)
|
||||||
|
throws ConversionExecutorNotFoundException {
|
||||||
|
throw new UnsupportedOperationException("Not yet implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Class getClassForAlias(String name) throws IllegalArgumentException {
|
||||||
|
Class clazz = (Class) aliasMap.get(name);
|
||||||
|
if (clazz != null) {
|
||||||
|
return clazz;
|
||||||
|
} else {
|
||||||
|
if (parent != null) {
|
||||||
|
return parent.getClassForAlias(name);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// internal helpers
|
||||||
|
|
||||||
|
private List getTypeInfo(Converter converter) {
|
||||||
|
List typeInfo = new ArrayList(2);
|
||||||
|
Class classToIntrospect = converter.getClass();
|
||||||
|
while (classToIntrospect != null) {
|
||||||
|
Type[] genericInterfaces = converter.getClass().getGenericInterfaces();
|
||||||
|
for (Type genericInterface : genericInterfaces) {
|
||||||
|
if (genericInterface instanceof ParameterizedType) {
|
||||||
|
ParameterizedType parameterizedInterface = (ParameterizedType) genericInterface;
|
||||||
|
if (Converter.class.equals(parameterizedInterface.getRawType())) {
|
||||||
|
Type s = parameterizedInterface.getActualTypeArguments()[0];
|
||||||
|
Type t = parameterizedInterface.getActualTypeArguments()[1];
|
||||||
|
typeInfo.add(getParameterClass(s, converter.getClass()));
|
||||||
|
typeInfo.add(getParameterClass(t, converter.getClass()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
classToIntrospect = classToIntrospect.getSuperclass();
|
||||||
|
}
|
||||||
|
return typeInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Class<?> getParameterClass(Type parameterType, Class<?> converterClass) {
|
||||||
|
if (parameterType instanceof TypeVariable) {
|
||||||
|
parameterType = GenericTypeResolver.resolveTypeVariable((TypeVariable<?>) parameterType, converterClass);
|
||||||
|
}
|
||||||
|
if (parameterType instanceof Class) {
|
||||||
|
return (Class<?>) parameterType;
|
||||||
|
}
|
||||||
|
// when would this happen?
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map getSourceMap(Class sourceClass) {
|
||||||
|
Map sourceMap = (Map) sourceClassConverters.get(sourceClass);
|
||||||
|
if (sourceMap == null) {
|
||||||
|
sourceMap = new HashMap<Class<?>, Converter<?, ?>>();
|
||||||
|
sourceClassConverters.put(sourceClass, sourceMap);
|
||||||
|
}
|
||||||
|
return sourceMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Class convertToWrapperClassIfNecessary(Class targetType) {
|
||||||
|
if (targetType.isPrimitive()) {
|
||||||
|
if (targetType.equals(int.class)) {
|
||||||
|
return Integer.class;
|
||||||
|
} else if (targetType.equals(short.class)) {
|
||||||
|
return Short.class;
|
||||||
|
} else if (targetType.equals(long.class)) {
|
||||||
|
return Long.class;
|
||||||
|
} else if (targetType.equals(float.class)) {
|
||||||
|
return Float.class;
|
||||||
|
} else if (targetType.equals(double.class)) {
|
||||||
|
return Double.class;
|
||||||
|
} else if (targetType.equals(byte.class)) {
|
||||||
|
return Byte.class;
|
||||||
|
} else if (targetType.equals(boolean.class)) {
|
||||||
|
return Boolean.class;
|
||||||
|
} else if (targetType.equals(char.class)) {
|
||||||
|
return Character.class;
|
||||||
|
} else {
|
||||||
|
throw new IllegalStateException("Should never happen - primitive type is not a primitive?");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return targetType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Converter findRegisteredConverter(Class sourceClass, Class targetClass) {
|
||||||
|
if (sourceClass.isInterface()) {
|
||||||
|
LinkedList classQueue = new LinkedList();
|
||||||
|
classQueue.addFirst(sourceClass);
|
||||||
|
while (!classQueue.isEmpty()) {
|
||||||
|
Class currentClass = (Class) classQueue.removeLast();
|
||||||
|
Map<Class, Converter> sourceTargetConverters = findConvertersForSource(currentClass);
|
||||||
|
Converter converter = findTargetConverter(sourceTargetConverters, targetClass);
|
||||||
|
if (converter != null) {
|
||||||
|
return converter;
|
||||||
|
}
|
||||||
|
Class[] interfaces = currentClass.getInterfaces();
|
||||||
|
for (int i = 0; i < interfaces.length; i++) {
|
||||||
|
classQueue.addFirst(interfaces[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Map objectConverters = findConvertersForSource(Object.class);
|
||||||
|
return findTargetConverter(objectConverters, targetClass);
|
||||||
|
} else {
|
||||||
|
LinkedList classQueue = new LinkedList();
|
||||||
|
classQueue.addFirst(sourceClass);
|
||||||
|
while (!classQueue.isEmpty()) {
|
||||||
|
Class currentClass = (Class) classQueue.removeLast();
|
||||||
|
Map sourceTargetConverters = findConvertersForSource(currentClass);
|
||||||
|
Converter converter = findTargetConverter(sourceTargetConverters, targetClass);
|
||||||
|
if (converter != null) {
|
||||||
|
return converter;
|
||||||
|
}
|
||||||
|
if (currentClass.getSuperclass() != null) {
|
||||||
|
classQueue.addFirst(currentClass.getSuperclass());
|
||||||
|
}
|
||||||
|
Class[] interfaces = currentClass.getInterfaces();
|
||||||
|
for (int i = 0; i < interfaces.length; i++) {
|
||||||
|
classQueue.addFirst(interfaces[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map findConvertersForSource(Class sourceClass) {
|
||||||
|
Map sourceConverters = (Map) sourceClassConverters.get(sourceClass);
|
||||||
|
return sourceConverters != null ? sourceConverters : Collections.emptyMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Converter findTargetConverter(Map sourceTargetConverters, Class targetClass) {
|
||||||
|
if (sourceTargetConverters.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (Converter) sourceTargetConverters.get(targetClass);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2004-2009 the original author or authors.
|
||||||
|
*
|
||||||
|
* 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.springframework.core.convert.service;
|
||||||
|
|
||||||
|
import org.springframework.core.convert.converter.Converter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Package private converter that is a "no op".
|
||||||
|
*
|
||||||
|
* @author Keith Donald
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
class NoOpConverter implements Converter {
|
||||||
|
|
||||||
|
public Object convert(Object source) throws Exception {
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object convertBack(Object target) throws Exception {
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,79 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2004-2009 the original author or authors.
|
||||||
|
*
|
||||||
|
* 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.springframework.core.convert.service;
|
||||||
|
|
||||||
|
import java.lang.reflect.Array;
|
||||||
|
|
||||||
|
import org.springframework.core.convert.ConversionExecutor;
|
||||||
|
import org.springframework.core.convert.ConversionService;
|
||||||
|
import org.springframework.core.convert.converter.SuperConverter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Special two-way converter that converts an object to an single-element array. Mainly used internally by
|
||||||
|
* {@link ConversionService} implementations.
|
||||||
|
*
|
||||||
|
* @author Keith Donald
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
class ObjectToArray implements SuperConverter {
|
||||||
|
|
||||||
|
private ConversionService conversionService;
|
||||||
|
|
||||||
|
private ConversionExecutor elementConverter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new object to array converter.
|
||||||
|
* @param conversionService the conversion service to resolve the converter to use to convert the object added to
|
||||||
|
* the target array.
|
||||||
|
*/
|
||||||
|
public ObjectToArray(ConversionService conversionService) {
|
||||||
|
this.conversionService = conversionService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new object to array converter.
|
||||||
|
* @param elementConverter a specific converter to use to convert the object added to the target array.
|
||||||
|
*/
|
||||||
|
public ObjectToArray(ConversionExecutor elementConverter) {
|
||||||
|
this.elementConverter = elementConverter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Class getSourceClass() {
|
||||||
|
return Object.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Class getSuperTargetClass() {
|
||||||
|
return Object[].class;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object convert(Object source, Class targetClass) throws Exception {
|
||||||
|
Class componentType = targetClass.getComponentType();
|
||||||
|
Object array = Array.newInstance(componentType, 1);
|
||||||
|
ConversionExecutor converter;
|
||||||
|
if (elementConverter != null) {
|
||||||
|
converter = elementConverter;
|
||||||
|
} else {
|
||||||
|
converter = conversionService.getConversionExecutor(source.getClass(), componentType);
|
||||||
|
}
|
||||||
|
Array.set(array, 0, converter.execute(source));
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object convertBack(Object target) throws Exception {
|
||||||
|
throw new UnsupportedOperationException("Not supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,121 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2004-2009 the original author or authors.
|
||||||
|
*
|
||||||
|
* 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.springframework.core.convert.service;
|
||||||
|
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.SortedSet;
|
||||||
|
import java.util.TreeSet;
|
||||||
|
|
||||||
|
import org.springframework.core.GenericCollectionTypeResolver;
|
||||||
|
import org.springframework.core.convert.ConversionExecutor;
|
||||||
|
import org.springframework.core.convert.ConversionService;
|
||||||
|
import org.springframework.core.convert.converter.SuperConverter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Special two-way converter that converts an object to an single-element collection. Supports type conversion of the
|
||||||
|
* individual element with parameterized collection implementations.
|
||||||
|
*
|
||||||
|
* @author Keith Donald
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
class ObjectToCollection implements SuperConverter {
|
||||||
|
|
||||||
|
private ConversionService conversionService;
|
||||||
|
|
||||||
|
private ConversionExecutor elementConverter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new object to collection converter
|
||||||
|
* @param conversionService the conversion service to lookup the converter to use to convert an object when adding
|
||||||
|
* it to a target collection
|
||||||
|
*/
|
||||||
|
public ObjectToCollection(ConversionService conversionService) {
|
||||||
|
this.conversionService = conversionService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new object to collection converter
|
||||||
|
* @param elementConverter a specific converter to execute on an object when adding it to a target collection
|
||||||
|
*/
|
||||||
|
public ObjectToCollection(ConversionExecutor elementConverter) {
|
||||||
|
this.elementConverter = elementConverter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Class getSourceClass() {
|
||||||
|
return Object.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Class getSuperTargetClass() {
|
||||||
|
return Collection.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object convert(Object source, Class targetClass) throws Exception {
|
||||||
|
if (source == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Class collectionImplClass = getCollectionImplClass(targetClass);
|
||||||
|
Constructor constructor = collectionImplClass.getConstructor((Class[]) null);
|
||||||
|
Collection collection = (Collection) constructor.newInstance((Object[]) null);
|
||||||
|
ConversionExecutor converter = getElementConverter(source, targetClass);
|
||||||
|
Object value;
|
||||||
|
if (converter != null) {
|
||||||
|
value = converter.execute(source);
|
||||||
|
} else {
|
||||||
|
value = source;
|
||||||
|
}
|
||||||
|
collection.add(value);
|
||||||
|
return collection;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object convertBack(Object target) throws Exception {
|
||||||
|
throw new UnsupportedOperationException("Not supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
// this code is duplicated in ArrayToCollection and CollectionToCollection
|
||||||
|
private Class getCollectionImplClass(Class targetClass) {
|
||||||
|
if (targetClass.isInterface()) {
|
||||||
|
if (List.class.equals(targetClass)) {
|
||||||
|
return ArrayList.class;
|
||||||
|
} else if (Set.class.equals(targetClass)) {
|
||||||
|
return LinkedHashSet.class;
|
||||||
|
} else if (SortedSet.class.equals(targetClass)) {
|
||||||
|
return TreeSet.class;
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Unsupported collection interface [" + targetClass.getName() + "]");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return targetClass;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ConversionExecutor getElementConverter(Object source, Class targetClass) {
|
||||||
|
if (elementConverter != null) {
|
||||||
|
return elementConverter;
|
||||||
|
} else {
|
||||||
|
Class elementType = GenericCollectionTypeResolver.getCollectionType(targetClass);
|
||||||
|
if (elementType != null) {
|
||||||
|
Class componentType = source.getClass().getComponentType();
|
||||||
|
return conversionService.getConversionExecutor(componentType, elementType);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2004-2009 the original author or authors.
|
||||||
|
*
|
||||||
|
* 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.springframework.core.convert.service;
|
||||||
|
|
||||||
|
import org.springframework.core.convert.converter.Converter;
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
class ReverseConverter implements Converter {
|
||||||
|
|
||||||
|
private Converter converter;
|
||||||
|
|
||||||
|
public ReverseConverter(Converter converter) {
|
||||||
|
this.converter = converter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object convert(Object source) throws Exception {
|
||||||
|
return converter.convertBack(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object convertBack(Object target) throws Exception {
|
||||||
|
throw new IllegalStateException("Should not be called");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,81 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2004-2008 the original author or authors.
|
||||||
|
*
|
||||||
|
* 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.springframework.core.convert.service;
|
||||||
|
|
||||||
|
import org.springframework.core.convert.ConversionExecutionException;
|
||||||
|
import org.springframework.core.convert.ConversionExecutor;
|
||||||
|
import org.springframework.core.convert.converter.Converter;
|
||||||
|
import org.springframework.core.style.ToStringCreator;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
class StaticConversionExecutor implements ConversionExecutor {
|
||||||
|
|
||||||
|
private final Class sourceClass;
|
||||||
|
|
||||||
|
private final Class targetClass;
|
||||||
|
|
||||||
|
private final Converter converter;
|
||||||
|
|
||||||
|
public StaticConversionExecutor(Class sourceClass, Class targetClass, Converter converter) {
|
||||||
|
Assert.notNull(sourceClass, "The source class is required");
|
||||||
|
Assert.notNull(targetClass, "The target class is required");
|
||||||
|
Assert.notNull(converter, "The converter is required");
|
||||||
|
this.sourceClass = sourceClass;
|
||||||
|
this.targetClass = targetClass;
|
||||||
|
this.converter = converter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Class getSourceClass() {
|
||||||
|
return sourceClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Class getTargetClass() {
|
||||||
|
return targetClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object execute(Object source) throws ConversionExecutionException {
|
||||||
|
if (source == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (!sourceClass.isInstance(source)) {
|
||||||
|
throw new ConversionExecutionException(source, getSourceClass(), getTargetClass(), "Source object "
|
||||||
|
+ source + " to convert is expected to be an instance of [" + getSourceClass().getName() + "]");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return converter.convert(source);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new ConversionExecutionException(source, getSourceClass(), getTargetClass(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (!(o instanceof StaticConversionExecutor)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
StaticConversionExecutor other = (StaticConversionExecutor) o;
|
||||||
|
return sourceClass.equals(other.sourceClass) && targetClass.equals(other.targetClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int hashCode() {
|
||||||
|
return sourceClass.hashCode() + targetClass.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return new ToStringCreator(this).append("sourceClass", sourceClass).append("targetClass", targetClass)
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,81 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2004-2008 the original author or authors.
|
||||||
|
*
|
||||||
|
* 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.springframework.core.convert.service;
|
||||||
|
|
||||||
|
import org.springframework.core.convert.ConversionExecutionException;
|
||||||
|
import org.springframework.core.convert.ConversionExecutor;
|
||||||
|
import org.springframework.core.convert.converter.SuperConverter;
|
||||||
|
import org.springframework.core.style.ToStringCreator;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
class StaticSuperConversionExecutor implements ConversionExecutor {
|
||||||
|
|
||||||
|
private final Class sourceClass;
|
||||||
|
|
||||||
|
private final Class targetClass;
|
||||||
|
|
||||||
|
private final SuperConverter converter;
|
||||||
|
|
||||||
|
public StaticSuperConversionExecutor(Class sourceClass, Class targetClass, SuperConverter converter) {
|
||||||
|
Assert.notNull(sourceClass, "The source class is required");
|
||||||
|
Assert.notNull(targetClass, "The target class is required");
|
||||||
|
Assert.notNull(converter, "The super converter is required");
|
||||||
|
this.sourceClass = sourceClass;
|
||||||
|
this.targetClass = targetClass;
|
||||||
|
this.converter = converter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Class getSourceClass() {
|
||||||
|
return sourceClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Class getTargetClass() {
|
||||||
|
return targetClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object execute(Object source) throws ConversionExecutionException {
|
||||||
|
if (source == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (!sourceClass.isInstance(source)) {
|
||||||
|
throw new ConversionExecutionException(source, getSourceClass(), getTargetClass(), "Source object "
|
||||||
|
+ source + " to convert is expected to be an instance of [" + getSourceClass().getName() + "]");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return converter.convert(source, targetClass);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new ConversionExecutionException(source, getSourceClass(), getTargetClass(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (!(o instanceof StaticSuperConversionExecutor)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
StaticSuperConversionExecutor other = (StaticSuperConversionExecutor) o;
|
||||||
|
return sourceClass.equals(other.sourceClass) && targetClass.equals(other.targetClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int hashCode() {
|
||||||
|
return sourceClass.hashCode() + targetClass.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return new ToStringCreator(this).append("sourceClass", sourceClass).append("targetClass", targetClass)
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
<p>
|
||||||
|
ConversionService implementation.
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -0,0 +1,159 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2004-2009 the original author or authors.
|
||||||
|
*
|
||||||
|
* 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.springframework.core.convert.service;
|
||||||
|
|
||||||
|
import java.util.AbstractList;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
import org.springframework.core.convert.ConversionExecutor;
|
||||||
|
import org.springframework.core.convert.ConversionExecutorNotFoundException;
|
||||||
|
import org.springframework.core.convert.ConversionService;
|
||||||
|
import org.springframework.core.convert.converter.Converter;
|
||||||
|
import org.springframework.core.convert.converter.StringToBoolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test case for the default conversion service.
|
||||||
|
*
|
||||||
|
* @author Keith Donald
|
||||||
|
*/
|
||||||
|
public class DefaultConversionServiceTests extends TestCase {
|
||||||
|
|
||||||
|
ConversionService service = new DefaultConversionService();
|
||||||
|
|
||||||
|
public void testConversionForwardIndex() {
|
||||||
|
ConversionExecutor<String, Integer> executor = service.getConversionExecutor(String.class, Integer.class);
|
||||||
|
Integer three = executor.execute("3");
|
||||||
|
assertEquals(3, three.intValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testConversionReverseIndex() {
|
||||||
|
ConversionExecutor<Integer, String> executor = service.getConversionExecutor(Integer.class, String.class);
|
||||||
|
String threeString = executor.execute(new Integer(3));
|
||||||
|
assertEquals("3", threeString);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testConversionCompatibleTypes() {
|
||||||
|
ArrayList source = new ArrayList();
|
||||||
|
assertSame(source, service.getConversionExecutor(ArrayList.class, List.class).execute(source));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testConversionOverrideDefaultConverter() {
|
||||||
|
Converter<String, Boolean> customConverter = new StringToBoolean("ja", "nee");
|
||||||
|
((GenericConversionService) service).addConverter(customConverter);
|
||||||
|
ConversionExecutor<String, Boolean> executor = service.getConversionExecutor(String.class, Boolean.class);
|
||||||
|
assertTrue(executor.execute("ja").booleanValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testConverterLookupTargetClassNotSupported() {
|
||||||
|
try {
|
||||||
|
service.getConversionExecutor(String.class, HashMap.class);
|
||||||
|
fail("Should have thrown an exception");
|
||||||
|
} catch (ConversionExecutorNotFoundException e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testConversionToPrimitive() {
|
||||||
|
DefaultConversionService service = new DefaultConversionService();
|
||||||
|
ConversionExecutor executor = service.getConversionExecutor(String.class, int.class);
|
||||||
|
Integer three = (Integer) executor.execute("3");
|
||||||
|
assertEquals(3, three.intValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testConversionArrayToArray() {
|
||||||
|
ConversionExecutor executor = service.getConversionExecutor(String[].class, Integer[].class);
|
||||||
|
Integer[] result = (Integer[]) executor.execute(new String[] { "1", "2", "3" });
|
||||||
|
assertEquals(new Integer(1), result[0]);
|
||||||
|
assertEquals(new Integer(2), result[1]);
|
||||||
|
assertEquals(new Integer(3), result[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testConversionArrayToPrimitiveArray() {
|
||||||
|
ConversionExecutor executor = service.getConversionExecutor(String[].class, int[].class);
|
||||||
|
int[] result = (int[]) executor.execute(new String[] { "1", "2", "3" });
|
||||||
|
assertEquals(1, result[0]);
|
||||||
|
assertEquals(2, result[1]);
|
||||||
|
assertEquals(3, result[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testConversionArrayToList() {
|
||||||
|
ConversionExecutor executor = service.getConversionExecutor(String[].class, List.class);
|
||||||
|
List result = (List) executor.execute(new String[] { "1", "2", "3" });
|
||||||
|
assertEquals("1", result.get(0));
|
||||||
|
assertEquals("2", result.get(1));
|
||||||
|
assertEquals("3", result.get(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testConversionToArray() {
|
||||||
|
ConversionExecutor executor = service.getConversionExecutor(Collection.class, String[].class);
|
||||||
|
List list = new ArrayList();
|
||||||
|
list.add("1");
|
||||||
|
list.add("2");
|
||||||
|
list.add("3");
|
||||||
|
String[] result = (String[]) executor.execute(list);
|
||||||
|
assertEquals("1", result[0]);
|
||||||
|
assertEquals("2", result[1]);
|
||||||
|
assertEquals("3", result[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testConversionListToArrayWithComponentConversion() {
|
||||||
|
ConversionExecutor executor = service.getConversionExecutor(Collection.class, Integer[].class);
|
||||||
|
List list = new ArrayList();
|
||||||
|
list.add("1");
|
||||||
|
list.add("2");
|
||||||
|
list.add("3");
|
||||||
|
Integer[] result = (Integer[]) executor.execute(list);
|
||||||
|
assertEquals(new Integer(1), result[0]);
|
||||||
|
assertEquals(new Integer(2), result[1]);
|
||||||
|
assertEquals(new Integer(3), result[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testConversionArrayToConcreteList() {
|
||||||
|
ConversionExecutor executor = service.getConversionExecutor(String[].class, LinkedList.class);
|
||||||
|
LinkedList result = (LinkedList) executor.execute(new String[] { "1", "2", "3" });
|
||||||
|
assertEquals("1", result.get(0));
|
||||||
|
assertEquals("2", result.get(1));
|
||||||
|
assertEquals("3", result.get(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testConversionArrayToAbstractList() {
|
||||||
|
try {
|
||||||
|
service.getConversionExecutor(String[].class, AbstractList.class);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testConversionStringToArray() {
|
||||||
|
ConversionExecutor executor = service.getConversionExecutor(String.class, String[].class);
|
||||||
|
String[] result = (String[]) executor.execute("1,2,3");
|
||||||
|
assertEquals(1, result.length);
|
||||||
|
assertEquals("1,2,3", result[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testConversionStringToArrayWithElementConversion() {
|
||||||
|
ConversionExecutor executor = service.getConversionExecutor(String.class, Integer[].class);
|
||||||
|
Integer[] result = (Integer[]) executor.execute("123");
|
||||||
|
assertEquals(1, result.length);
|
||||||
|
assertEquals(new Integer(123), result[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue