From 15321197878f7b863976393a1b2cf89ba0469083 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 26 May 2010 13:58:37 +0000 Subject: [PATCH] ConversionService is able to apply Converters to interface-based array elements (SPR-7150); a context ConversionService is able to override an ApplicationContext's resource editors (SPR-7079) --- .../beans/PropertyEditorRegistrySupport.java | 31 +++++++++++++++-- .../support/ResourceEditorRegistrar.java | 33 ++++++++++++++----- .../ConversionServiceFactoryBeanTests.java | 24 ++++++++++---- .../context/support/ResourceConverter.java | 32 ++++++++++++++++++ .../context/support/conversionService.xml | 8 ----- ...onversionServiceWithResourceOverriding.xml | 27 +++++++++++++++ .../support/GenericConversionService.java | 12 +++++-- .../GenericConversionServiceTests.java | 27 +++++++++++---- 8 files changed, 158 insertions(+), 36 deletions(-) create mode 100644 org.springframework.context/src/test/java/org/springframework/context/support/ResourceConverter.java create mode 100644 org.springframework.context/src/test/java/org/springframework/context/support/conversionServiceWithResourceOverriding.xml diff --git a/org.springframework.beans/src/main/java/org/springframework/beans/PropertyEditorRegistrySupport.java b/org.springframework.beans/src/main/java/org/springframework/beans/PropertyEditorRegistrySupport.java index 0735f2003c..b7d4db0e58 100644 --- a/org.springframework.beans/src/main/java/org/springframework/beans/PropertyEditorRegistrySupport.java +++ b/org.springframework.beans/src/main/java/org/springframework/beans/PropertyEditorRegistrySupport.java @@ -93,6 +93,8 @@ public class PropertyEditorRegistrySupport implements PropertyEditorRegistry { private Map defaultEditors; + private Map overriddenDefaultEditors; + private Map customEditors; private Map customEditorsForPath; @@ -141,6 +143,22 @@ public class PropertyEditorRegistrySupport implements PropertyEditorRegistry { this.configValueEditorsActive = true; } + /** + * Override the default editor for the specified type with the given property editor. + *

Note that this is different from registering a custom editor in that the editor + * semantically still is a default editor. A ConversionService will override such a + * default editor, whereas custom editors usually override the ConversionService. + * @param requiredType the type of the property + * @param propertyEditor the editor to register + * @see #registerCustomEditor(Class, PropertyEditor) + */ + public void overrideDefaultEditor(Class requiredType, PropertyEditor propertyEditor) { + if (this.overriddenDefaultEditors == null) { + this.overriddenDefaultEditors = new HashMap(); + } + this.overriddenDefaultEditors.put(requiredType, propertyEditor); + } + /** * Retrieve the default editor for the given property type, if any. *

Lazily registers the default editors, if they are active. @@ -152,8 +170,14 @@ public class PropertyEditorRegistrySupport implements PropertyEditorRegistry { if (!this.defaultEditorsActive) { return null; } + if (this.overriddenDefaultEditors != null) { + PropertyEditor editor = this.overriddenDefaultEditors.get(requiredType); + if (editor != null) { + return editor; + } + } if (this.defaultEditors == null) { - doRegisterDefaultEditors(); + createDefaultEditors(); } return this.defaultEditors.get(requiredType); } @@ -161,7 +185,7 @@ public class PropertyEditorRegistrySupport implements PropertyEditorRegistry { /** * Actually register the default editors for this registry instance. */ - private void doRegisterDefaultEditors() { + private void createDefaultEditors() { this.defaultEditors = new HashMap(64); // Simple editors, without parameterization capabilities. @@ -234,9 +258,10 @@ public class PropertyEditorRegistrySupport implements PropertyEditorRegistry { * @param target the target registry to copy to */ protected void copyDefaultEditorsTo(PropertyEditorRegistrySupport target) { - target.defaultEditors = this.defaultEditors; target.defaultEditorsActive = this.defaultEditorsActive; target.configValueEditorsActive = this.configValueEditorsActive; + target.defaultEditors = this.defaultEditors; + target.overriddenDefaultEditors = this.overriddenDefaultEditors; } diff --git a/org.springframework.beans/src/main/java/org/springframework/beans/support/ResourceEditorRegistrar.java b/org.springframework.beans/src/main/java/org/springframework/beans/support/ResourceEditorRegistrar.java index 7bed0ba4b9..6dee35cb29 100644 --- a/org.springframework.beans/src/main/java/org/springframework/beans/support/ResourceEditorRegistrar.java +++ b/org.springframework.beans/src/main/java/org/springframework/beans/support/ResourceEditorRegistrar.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2007 the original author or authors. + * Copyright 2002-2010 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. @@ -16,6 +16,7 @@ package org.springframework.beans.support; +import java.beans.PropertyEditor; import java.io.File; import java.io.InputStream; import java.net.URI; @@ -25,6 +26,7 @@ import org.xml.sax.InputSource; import org.springframework.beans.PropertyEditorRegistrar; import org.springframework.beans.PropertyEditorRegistry; +import org.springframework.beans.PropertyEditorRegistrySupport; import org.springframework.beans.propertyeditors.ClassEditor; import org.springframework.beans.propertyeditors.FileEditor; import org.springframework.beans.propertyeditors.InputSourceEditor; @@ -80,20 +82,33 @@ public class ResourceEditorRegistrar implements PropertyEditorRegistrar { */ public void registerCustomEditors(PropertyEditorRegistry registry) { ResourceEditor baseEditor = new ResourceEditor(this.resourceLoader); - registry.registerCustomEditor(Resource.class, baseEditor); - registry.registerCustomEditor(InputStream.class, new InputStreamEditor(baseEditor)); - registry.registerCustomEditor(InputSource.class, new InputSourceEditor(baseEditor)); - registry.registerCustomEditor(File.class, new FileEditor(baseEditor)); - registry.registerCustomEditor(URL.class, new URLEditor(baseEditor)); + doRegisterEditor(registry, Resource.class, baseEditor); + doRegisterEditor(registry, InputStream.class, new InputStreamEditor(baseEditor)); + doRegisterEditor(registry, InputSource.class, new InputSourceEditor(baseEditor)); + doRegisterEditor(registry, File.class, new FileEditor(baseEditor)); + doRegisterEditor(registry, URL.class, new URLEditor(baseEditor)); ClassLoader classLoader = this.resourceLoader.getClassLoader(); - registry.registerCustomEditor(Class.class, new ClassEditor(classLoader)); - registry.registerCustomEditor(URI.class, new URIEditor(classLoader)); + doRegisterEditor(registry, Class.class, new ClassEditor(classLoader)); + doRegisterEditor(registry, URI.class, new URIEditor(classLoader)); if (this.resourceLoader instanceof ResourcePatternResolver) { - registry.registerCustomEditor(Resource[].class, + doRegisterEditor(registry, Resource[].class, new ResourceArrayPropertyEditor((ResourcePatternResolver) this.resourceLoader)); } } + /** + * Override default editor, if possible (since that's what we really mean to do here); + * otherwise register as a custom editor. + */ + private void doRegisterEditor(PropertyEditorRegistry registry, Class requiredType, PropertyEditor editor) { + if (registry instanceof PropertyEditorRegistrySupport) { + ((PropertyEditorRegistrySupport) registry).overrideDefaultEditor(requiredType, editor); + } + else { + registry.registerCustomEditor(requiredType, editor); + } + } + } diff --git a/org.springframework.context/src/test/java/org/springframework/context/support/ConversionServiceFactoryBeanTests.java b/org.springframework.context/src/test/java/org/springframework/context/support/ConversionServiceFactoryBeanTests.java index ac45d1c7cb..b122ad09d8 100644 --- a/org.springframework.context/src/test/java/org/springframework/context/support/ConversionServiceFactoryBeanTests.java +++ b/org.springframework.context/src/test/java/org/springframework/context/support/ConversionServiceFactoryBeanTests.java @@ -16,13 +16,13 @@ package org.springframework.context.support; -import static org.junit.Assert.assertTrue; - import java.util.Collections; import java.util.HashSet; import java.util.Set; +import static org.junit.Assert.*; import org.junit.Test; + import org.springframework.beans.ResourceTestBean; import org.springframework.context.ApplicationContext; import org.springframework.core.convert.ConversionService; @@ -31,6 +31,7 @@ import org.springframework.core.convert.converter.Converter; import org.springframework.core.convert.converter.ConverterFactory; import org.springframework.core.convert.converter.GenericConverter; import org.springframework.core.io.ClassPathResource; +import org.springframework.core.io.FileSystemResource; /** * @author Keith Donald @@ -92,16 +93,25 @@ public class ConversionServiceFactoryBeanTests { @Test public void conversionServiceInApplicationContext() { - ApplicationContext ctx = new ClassPathXmlApplicationContext("conversionService.xml", getClass()); + doTestConversionServiceInApplicationContext("conversionService.xml", ClassPathResource.class); + } + + @Test + public void conversionServiceInApplicationContextWithResourceOverriding() { + doTestConversionServiceInApplicationContext("conversionServiceWithResourceOverriding.xml", FileSystemResource.class); + } + + private void doTestConversionServiceInApplicationContext(String fileName, Class resourceClass) { + ApplicationContext ctx = new ClassPathXmlApplicationContext(fileName, getClass()); ResourceTestBean tb = ctx.getBean("resourceTestBean", ResourceTestBean.class); - assertTrue(tb.getResource() instanceof ClassPathResource); + assertTrue(resourceClass.isInstance(tb.getResource())); assertTrue(tb.getResourceArray().length > 0); - assertTrue(tb.getResourceArray()[0] instanceof ClassPathResource); + assertTrue(resourceClass.isInstance(tb.getResourceArray()[0])); assertTrue(tb.getResourceMap().size() == 1); - assertTrue(tb.getResourceMap().get("key1") instanceof ClassPathResource); + assertTrue(resourceClass.isInstance(tb.getResourceMap().get("key1"))); assertTrue(tb.getResourceArrayMap().size() == 1); assertTrue(tb.getResourceArrayMap().get("key1").length > 0); - assertTrue(tb.getResourceArrayMap().get("key1")[0] instanceof ClassPathResource); + assertTrue(resourceClass.isInstance(tb.getResourceArrayMap().get("key1")[0])); } diff --git a/org.springframework.context/src/test/java/org/springframework/context/support/ResourceConverter.java b/org.springframework.context/src/test/java/org/springframework/context/support/ResourceConverter.java new file mode 100644 index 0000000000..84236dae1d --- /dev/null +++ b/org.springframework.context/src/test/java/org/springframework/context/support/ResourceConverter.java @@ -0,0 +1,32 @@ +/* + * Copyright 2002-2010 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.context.support; + +import org.springframework.core.convert.converter.Converter; +import org.springframework.core.io.FileSystemResource; +import org.springframework.core.io.Resource; + +/** + * @author Juergen Hoeller + */ +public class ResourceConverter implements Converter { + + public Resource convert(String source) { + return new FileSystemResource(source + ".xml"); + } + +} diff --git a/org.springframework.context/src/test/java/org/springframework/context/support/conversionService.xml b/org.springframework.context/src/test/java/org/springframework/context/support/conversionService.xml index 28972f0c5c..1675291138 100644 --- a/org.springframework.context/src/test/java/org/springframework/context/support/conversionService.xml +++ b/org.springframework.context/src/test/java/org/springframework/context/support/conversionService.xml @@ -3,14 +3,6 @@ - - - - - - - - diff --git a/org.springframework.context/src/test/java/org/springframework/context/support/conversionServiceWithResourceOverriding.xml b/org.springframework.context/src/test/java/org/springframework/context/support/conversionServiceWithResourceOverriding.xml new file mode 100644 index 0000000000..b554301fd6 --- /dev/null +++ b/org.springframework.context/src/test/java/org/springframework/context/support/conversionServiceWithResourceOverriding.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/org.springframework.core/src/main/java/org/springframework/core/convert/support/GenericConversionService.java b/org.springframework.core/src/main/java/org/springframework/core/convert/support/GenericConversionService.java index 8def693f59..8f6ba605b4 100644 --- a/org.springframework.core/src/main/java/org/springframework/core/convert/support/GenericConversionService.java +++ b/org.springframework.core/src/main/java/org/springframework/core/convert/support/GenericConversionService.java @@ -147,7 +147,8 @@ public class GenericConversionService implements ConversionService, ConverterReg logger.debug("Yes, I can convert"); } return true; - } else { + } + else { if (logger.isDebugEnabled()) { logger.debug("No, I cannot convert"); } @@ -252,7 +253,8 @@ public class GenericConversionService implements ConversionService, ConverterReg logger.debug("Matched cached converter " + converter); } return converter != NO_MATCH ? converter : null; - } else { + } + else { converter = findConverterForClassPair(sourceType, targetType); if (converter != null) { if (logger.isTraceEnabled()) { @@ -374,6 +376,9 @@ public class GenericConversionService implements ConversionService, ConverterReg if (componentType.getSuperclass() != null) { classQueue.addFirst(Array.newInstance(componentType.getSuperclass(), 0).getClass()); } + else if (componentType.isInterface()) { + classQueue.addFirst(Object[].class); + } } else { Class[] interfaces = currentClass.getInterfaces(); @@ -441,6 +446,9 @@ public class GenericConversionService implements ConversionService, ConverterReg if (componentType.getSuperclass() != null) { classQueue.addFirst(Array.newInstance(componentType.getSuperclass(), 0).getClass()); } + else if (componentType.isInterface()) { + classQueue.addFirst(Object[].class); + } } else { Class[] interfaces = currentClass.getInterfaces(); diff --git a/org.springframework.core/src/test/java/org/springframework/core/convert/support/GenericConversionServiceTests.java b/org.springframework.core/src/test/java/org/springframework/core/convert/support/GenericConversionServiceTests.java index ec1e3c8aba..b9b647ee1a 100644 --- a/org.springframework.core/src/test/java/org/springframework/core/convert/support/GenericConversionServiceTests.java +++ b/org.springframework.core/src/test/java/org/springframework/core/convert/support/GenericConversionServiceTests.java @@ -16,13 +16,6 @@ package org.springframework.core.convert.support; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedHashMap; @@ -30,7 +23,9 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; +import static org.junit.Assert.*; import org.junit.Test; + import org.springframework.core.convert.ConversionFailedException; import org.springframework.core.convert.ConverterNotFoundException; import org.springframework.core.convert.TypeDescriptor; @@ -190,6 +185,24 @@ public class GenericConversionServiceTests { assertEquals("RESULT", converted); } + @Test + public void testInterfaceArrayToStringArray() { + GenericConversionService conversionService = new GenericConversionService(); + conversionService.addConverter(new MyBaseInterfaceConverter()); + conversionService.addConverter(new ArrayToArrayConverter(conversionService)); + String[] converted = conversionService.convert(new MyInterface[] {new MyInterfaceImplementer()}, String[].class); + assertEquals("RESULT", converted[0]); + } + + @Test + public void testObjectArrayToStringArray() { + GenericConversionService conversionService = new GenericConversionService(); + conversionService.addConverter(new MyBaseInterfaceConverter()); + conversionService.addConverter(new ArrayToArrayConverter(conversionService)); + String[] converted = conversionService.convert(new MyInterfaceImplementer[] {new MyInterfaceImplementer()}, String[].class); + assertEquals("RESULT", converted[0]); + } + @Test public void testWildcardMap() throws Exception { GenericConversionService conversionService = ConversionServiceFactory.createDefaultConversionService(); -- GitLab