diff --git a/spring-binding/src/main/java/org/springframework/binding/convert/ConversionService.java b/spring-binding/src/main/java/org/springframework/binding/convert/ConversionService.java index d625da0c..8ac56cfa 100644 --- a/spring-binding/src/main/java/org/springframework/binding/convert/ConversionService.java +++ b/spring-binding/src/main/java/org/springframework/binding/convert/ConversionService.java @@ -38,32 +38,4 @@ public interface ConversionService { */ public ConversionExecutor getConversionExecutor(Class sourceClass, Class targetClass) throws ConversionException; - /** - * Return a conversion executor command object capable of converting source objects of the specified - * sourceClass to target objects of the type associated with the specified alias. - * @param sourceClass the sourceClass - * @param targetAlias the target alias - * @return the conversion executor, or null if the alias cannot be found - * @throws ConversionException an exception occured retrieving a converter for the source-to-target pair - */ - public ConversionExecutor getConversionExecutorByTargetAlias(Class sourceClass, String targetAlias) - throws ConversionException; - - /** - * Return all conversion executors capable of converting source objects of the the specified - * sourceClass. - * @param sourceClass the source class to convert from - * @return the matching conversion executors - * @throws ConversionException an exception occured retrieving the converters - */ - public ConversionExecutor[] getConversionExecutorsForSource(Class sourceClass) throws ConversionException; - - /** - * Return the class with the specified alias. - * @param alias the class alias - * @return the class, or null if not aliased - * @throws ConversionException when an error occurs looking up the class by alias - */ - public Class getClassByAlias(String alias) throws ConversionException; - } \ No newline at end of file diff --git a/spring-binding/src/main/java/org/springframework/binding/convert/support/CompositeConversionService.java b/spring-binding/src/main/java/org/springframework/binding/convert/support/CompositeConversionService.java deleted file mode 100644 index d44e5023..00000000 --- a/spring-binding/src/main/java/org/springframework/binding/convert/support/CompositeConversionService.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright 2004-2007 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.binding.convert.support; - -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; - -import org.springframework.binding.convert.ConversionException; -import org.springframework.binding.convert.ConversionExecutor; -import org.springframework.binding.convert.ConversionService; -import org.springframework.util.Assert; - -/** - * A conversion service that delegates to an ordered chain of other conversion services. The first correct reply - * received from a conversion service in the chain is returned to the caller. - * - * @author Erwin Vervaet - */ -public class CompositeConversionService implements ConversionService { - - private ConversionService[] chain; - - /** - * Create a new composite conversion service. - * @param conversionServices the conversion services in the chain - */ - public CompositeConversionService(ConversionService[] conversionServices) { - Assert.notNull(conversionServices, "The conversion services chain is required"); - this.chain = conversionServices; - } - - /** - * Returns the conversion services in the chain managed by this composite conversion service. - */ - public ConversionService[] getConversionServices() { - return chain; - } - - public ConversionExecutor getConversionExecutor(Class sourceClass, Class targetClass) throws ConversionException { - for (int i = 0; i < chain.length; i++) { - try { - return chain[i].getConversionExecutor(sourceClass, targetClass); - } catch (ConversionException e) { - // ignore and try the next conversion service in the chain - } - } - throw new ConversionException(sourceClass, targetClass, "No converter registered to convert from sourceClass '" - + sourceClass + "' to target class '" + targetClass + "'"); - } - - public ConversionExecutor getConversionExecutorByTargetAlias(Class sourceClass, String targetAlias) - throws ConversionException { - boolean exceptionThrown = false; - for (int i = 0; i < chain.length; i++) { - try { - ConversionExecutor res = chain[i].getConversionExecutorByTargetAlias(sourceClass, targetAlias); - if (res != null) { - return res; - } - } catch (ConversionException e) { - exceptionThrown = true; - } - } - if (exceptionThrown) { - throw new ConversionException(sourceClass, "No converter registered to convert from sourceClass '" - + sourceClass + "' to aliased target type '" + targetAlias + "'"); - } else { - // alias was not recognized by any conversion service in the chain - return null; - } - } - - public ConversionExecutor[] getConversionExecutorsForSource(Class sourceClass) throws ConversionException { - Set executors = new HashSet(); - for (int i = 0; i < chain.length; i++) { - executors.addAll(Arrays.asList(chain[i].getConversionExecutorsForSource(sourceClass))); - } - return (ConversionExecutor[]) executors.toArray(new ConversionExecutor[executors.size()]); - } - - public Class getClassByAlias(String alias) throws ConversionException { - for (int i = 0; i < chain.length; i++) { - try { - Class res = chain[i].getClassByAlias(alias); - if (res != null) { - return res; - } - } catch (ConversionException e) { - // ignore and try the next conversion service in the chain - } - } - return null; - } -} \ No newline at end of file diff --git a/spring-binding/src/main/java/org/springframework/binding/convert/support/ConversionServiceAware.java b/spring-binding/src/main/java/org/springframework/binding/convert/support/ConversionServiceAware.java deleted file mode 100644 index 5c5f1686..00000000 --- a/spring-binding/src/main/java/org/springframework/binding/convert/support/ConversionServiceAware.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2004-2007 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.binding.convert.support; - -import org.springframework.binding.convert.ConversionService; - -/** - * Marker interface that denotes an object has a dependency on a conversion service that is expected to be fulfilled. - * - * @author Keith Donald - */ -public interface ConversionServiceAware { - - /** - * Set the conversion service this object should be made aware of (as it presumably depends on it). - * - * @param conversionService the conversion service - */ - public void setConversionService(ConversionService conversionService); -} \ No newline at end of file diff --git a/spring-binding/src/main/java/org/springframework/binding/convert/support/ConversionServiceAwareConverter.java b/spring-binding/src/main/java/org/springframework/binding/convert/support/ConversionServiceAwareConverter.java deleted file mode 100644 index 4295a727..00000000 --- a/spring-binding/src/main/java/org/springframework/binding/convert/support/ConversionServiceAwareConverter.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright 2004-2007 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.binding.convert.support; - -import org.springframework.binding.convert.ConversionService; -import org.springframework.binding.convert.ConversionExecutor; -import org.springframework.binding.expression.Expression; - -/** - * Base class for converters that use other converters to convert things, thus they are conversion-service aware. - * - * @author Keith Donald - */ -public abstract class ConversionServiceAwareConverter extends AbstractConverter implements ConversionServiceAware { - - /** - * The conversion service this converter is aware of. - */ - private ConversionService conversionService; - - /** - * Default constructor, expects to conversion service to be injected using - * {@link #setConversionService(ConversionService)}. - */ - protected ConversionServiceAwareConverter() { - } - - /** - * Create a converter using given conversion service. - */ - protected ConversionServiceAwareConverter(ConversionService conversionService) { - setConversionService(conversionService); - } - - /** - * Returns the conversion service used. - */ - public ConversionService getConversionService() { - if (conversionService == null) { - throw new IllegalStateException("Conversion service not yet set: set it first before calling this method"); - } - return conversionService; - } - - public void setConversionService(ConversionService conversionService) { - this.conversionService = conversionService; - } - - /** - * Returns a conversion executor capable of converting string objects to the specified target class. - * @param targetClass the target class - * @return the conversion executor, never null - */ - protected ConversionExecutor fromStringTo(Class targetClass) { - return getConversionService().getConversionExecutor(String.class, targetClass); - } - - /** - * Returns a conversion executor capable of converting string objects to the target class aliased by the provided - * alias. - * @param targetAlias the target class alias, e.g "long" or "float" - * @return the conversion executor, or null if no suitable converter exists for alias - */ - protected ConversionExecutor fromStringToAliased(String targetAlias) { - return getConversionService().getConversionExecutorByTargetAlias(String.class, targetAlias); - } - - /** - * Returns a conversion executor capable of converting objects from one class to another. - * @param sourceClass the source class to convert from - * @param targetClass the target class to convert to - * @return the conversion executor, never null - */ - protected ConversionExecutor converterFor(Class sourceClass, Class targetClass) { - return getConversionService().getConversionExecutor(sourceClass, targetClass); - } - - /** - * Helper that parsers the given expression string into an expression, using the installed String->Expression - * converter. - * @param expressionString the expression string to parse - * @return the parsed, evaluatable expression - */ - protected Expression parseExpression(String expressionString) { - return (Expression) fromStringTo(Expression.class).execute(expressionString); - } -} \ No newline at end of file diff --git a/spring-binding/src/main/java/org/springframework/binding/convert/support/CustomConverterConfigurer.java b/spring-binding/src/main/java/org/springframework/binding/convert/support/CustomConverterConfigurer.java deleted file mode 100644 index 5c05ff28..00000000 --- a/spring-binding/src/main/java/org/springframework/binding/convert/support/CustomConverterConfigurer.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2004-2005 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.binding.convert.support; - -import org.springframework.beans.BeansException; -import org.springframework.beans.PropertyEditorRegistrar; -import org.springframework.beans.PropertyEditorRegistry; -import org.springframework.beans.factory.InitializingBean; -import org.springframework.beans.factory.config.BeanFactoryPostProcessor; -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.binding.convert.ConversionService; -import org.springframework.binding.convert.ConversionExecutor; -import org.springframework.util.Assert; - -/** - * Registers all 'from string' converters known to a conversion service with a Spring bean factory. - *

- * Acts as bean factory post processor, registering property editor adapters for each supported conversion with a - * java.lang.String sourceClass. This makes for very convenient use with the Spring container. - * - * @author Keith Donald - */ -public class CustomConverterConfigurer implements BeanFactoryPostProcessor, InitializingBean { - - private ConversionService conversionService; - - /** - * Set the conversion service. - * @param conversionService the conversion service to take converters from - */ - public void setConversionService(ConversionService conversionService) { - this.conversionService = conversionService; - } - - public void afterPropertiesSet() throws Exception { - Assert.notNull(conversionService, "The conversion service is required"); - } - - public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { - final ConversionExecutor[] executors = conversionService.getConversionExecutorsForSource(String.class); - PropertyEditorRegistrar registrar = new PropertyEditorRegistrar() { - public void registerCustomEditors(PropertyEditorRegistry registry) { - for (int i = 0; i < executors.length; i++) { - ConverterPropertyEditorAdapter editor = new ConverterPropertyEditorAdapter(executors[i]); - registry.registerCustomEditor(editor.getTargetClass(), editor); - } - } - }; - beanFactory.addPropertyEditorRegistrar(registrar); - } -} \ No newline at end of file diff --git a/spring-binding/src/main/java/org/springframework/binding/convert/support/DefaultConversionService.java b/spring-binding/src/main/java/org/springframework/binding/convert/support/DefaultConversionService.java index 82c24efe..e89cd296 100644 --- a/spring-binding/src/main/java/org/springframework/binding/convert/support/DefaultConversionService.java +++ b/spring-binding/src/main/java/org/springframework/binding/convert/support/DefaultConversionService.java @@ -15,14 +15,10 @@ */ package org.springframework.binding.convert.support; -import java.math.BigDecimal; -import java.math.BigInteger; - import org.springframework.binding.format.FormatterRegistry; import org.springframework.binding.format.factories.DateFormatterFactory; import org.springframework.binding.format.factories.NumberFormatterFactory; import org.springframework.binding.format.impl.FormatterRegistryImpl; -import org.springframework.core.enums.LabeledEnum; /** * Default, local implementation of a conversion service. Will automatically register from string converters for @@ -54,19 +50,6 @@ public class DefaultConversionService extends GenericConversionService { addConverter(new TextToLabeledEnum()); addConverter(new TextToNumber(formatterRegistry)); addConverter(new TextToDate(formatterRegistry)); - addAlias("string", String.class); - addAlias("short", Short.class); - addAlias("integer", Integer.class); - addAlias("int", Integer.class); - addAlias("byte", Byte.class); - addAlias("long", Long.class); - addAlias("float", Float.class); - addAlias("double", Double.class); - addAlias("bigInteger", BigInteger.class); - addAlias("bigDecimal", BigDecimal.class); - addAlias("boolean", Boolean.class); - addAlias("class", Class.class); - addAlias("labeledEnum", LabeledEnum.class); } protected FormatterRegistry createDefaultFormatterRegistry() { diff --git a/spring-binding/src/main/java/org/springframework/binding/convert/support/GenericConversionService.java b/spring-binding/src/main/java/org/springframework/binding/convert/support/GenericConversionService.java index 32321cef..4879132a 100644 --- a/spring-binding/src/main/java/org/springframework/binding/convert/support/GenericConversionService.java +++ b/spring-binding/src/main/java/org/springframework/binding/convert/support/GenericConversionService.java @@ -15,22 +15,16 @@ */ package org.springframework.binding.convert.support; -import java.util.Arrays; import java.util.Collections; import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; import java.util.LinkedList; import java.util.Map; -import java.util.Set; import org.springframework.binding.convert.ConversionException; import org.springframework.binding.convert.ConversionExecutor; import org.springframework.binding.convert.ConversionService; import org.springframework.binding.convert.Converter; import org.springframework.util.Assert; -import org.springframework.util.ClassUtils; -import org.springframework.util.StringUtils; /** * Base implementation of a conversion service. Initially empty, e.g. no converters are registered by default. @@ -46,11 +40,6 @@ public class GenericConversionService implements ConversionService { */ private Map sourceClassConverters = new HashMap(); - /** - * A map of string aliases to convertible classes. Allows lookup of converters by alias. - */ - private Map aliasMap = new HashMap(); - /** * An optional parent conversion service. */ @@ -71,8 +60,8 @@ public class GenericConversionService implements ConversionService { } /** - * Add given converter to this conversion service. If the converter is {@link ConversionServiceAware}, it will get - * the conversion service injected. + * Add given converter to this conversion service. + * @param converter the converter */ public void addConverter(Converter converter) { Class[] sourceClasses = converter.getSourceClasses(); @@ -89,44 +78,6 @@ public class GenericConversionService implements ConversionService { sourceMap.put(targetClass, converter); } } - if (converter instanceof ConversionServiceAware) { - ((ConversionServiceAware) converter).setConversionService(this); - } - } - - /** - * Add all given converters. If the converters are {@link ConversionServiceAware}, they will get the conversion - * service injected. - */ - public void addConverters(Converter[] converters) { - for (int i = 0; i < converters.length; i++) { - addConverter(converters[i]); - } - } - - /** - * Add given converter with an alias to the conversion service. If the converter is {@link ConversionServiceAware}, - * it will get the conversion service injected. - */ - public void addConverter(Converter converter, String alias) { - aliasMap.put(alias, converter); - addConverter(converter); - } - - /** - * Add an alias for given target type. - */ - public void addAlias(String alias, Class targetType) { - Assert.isTrue(!targetType.isPrimitive(), "Primitive types cannot be registered"); - aliasMap.put(alias, targetType); - } - - /** - * Generate a conventions based alias for given target type. For instance, "java.lang.Boolean" will get the - * "boolean" alias. - */ - public void addDefaultAlias(Class targetType) { - addAlias(StringUtils.uncapitalize(ClassUtils.getShortName(targetType)), targetType); } public ConversionExecutor getConversionExecutor(Class sourceClass, Class targetClass) throws ConversionException { @@ -157,72 +108,6 @@ public class GenericConversionService implements ConversionService { } } - public ConversionExecutor getConversionExecutorByTargetAlias(Class sourceClass, String alias) - throws IllegalArgumentException { - Assert.notNull(sourceClass, "The source class to convert from is required"); - Assert.hasText(alias, "The target alias is required and must either be a type alias (e.g 'boolean') " - + "or a generic converter alias (e.g. 'bean') "); - Object targetType = aliasMap.get(alias); - if (targetType == null) { - if (parent != null) { - // try the parent - return parent.getConversionExecutorByTargetAlias(sourceClass, alias); - } else { - // not aliased - return null; - } - } else if (targetType instanceof Class) { - return getConversionExecutor(sourceClass, (Class) targetType); - } else { - Assert.isInstanceOf(Converter.class, targetType, "Not a converter: "); - Converter converter = (Converter) targetType; - return new ConversionExecutorImpl(sourceClass, Object.class, converter); - } - } - - public ConversionExecutor[] getConversionExecutorsForSource(Class sourceClass) { - Assert.notNull(sourceClass, "The source class to convert from is required"); - Map sourceTargetConverters = findConvertersForSource(sourceClass); - if (sourceTargetConverters.isEmpty()) { - if (parent != null) { - // use the parent - return parent.getConversionExecutorsForSource(sourceClass); - } else { - // no converters for source class - return new ConversionExecutor[0]; - } - } else { - Set executors = new HashSet(); - if (parent != null) { - executors.addAll(Arrays.asList(parent.getConversionExecutorsForSource(sourceClass))); - } - Iterator it = sourceTargetConverters.entrySet().iterator(); - while (it.hasNext()) { - Map.Entry entry = (Map.Entry) it.next(); - executors.add(new ConversionExecutorImpl(sourceClass, (Class) entry.getKey(), (Converter) entry - .getValue())); - } - return (ConversionExecutor[]) executors.toArray(new ConversionExecutor[executors.size()]); - } - } - - public Class getClassByAlias(String alias) { - Assert.hasText(alias, "The alias is required and must be a type alias (e.g 'boolean')"); - Object clazz = aliasMap.get(alias); - if (clazz != null) { - Assert.isInstanceOf(Class.class, clazz, "Not a Class alias '" + alias + "': "); - return (Class) clazz; - } else { - if (parent != null) { - // try parent service - return parent.getClassByAlias(alias); - } else { - // alias does not index a class, return null - return null; - } - } - } - // subclassing support /** @@ -235,11 +120,13 @@ public class GenericConversionService implements ConversionService { } /** - * Returns a map of known aliases. Each entry key is a String alias and the associated value is either a target - * class or a converter. + * Returns a registered converter object + * @param sourceClass the source class + * @param targetClass the target class */ - protected Map getAliasMap() { - return aliasMap; + protected Converter getConverter(Class sourceClass, Class targetClass) { + Map sourceTargetConverters = findConvertersForSource(sourceClass); + return findTargetConverter(sourceTargetConverters, targetClass); } // internal helpers diff --git a/spring-binding/src/main/java/org/springframework/binding/convert/support/TextToClass.java b/spring-binding/src/main/java/org/springframework/binding/convert/support/TextToClass.java index 6576c2e8..5ca3641b 100644 --- a/spring-binding/src/main/java/org/springframework/binding/convert/support/TextToClass.java +++ b/spring-binding/src/main/java/org/springframework/binding/convert/support/TextToClass.java @@ -15,8 +15,13 @@ */ package org.springframework.binding.convert.support; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.HashMap; +import java.util.Map; + import org.springframework.binding.convert.ConversionContext; -import org.springframework.util.Assert; +import org.springframework.core.enums.LabeledEnum; import org.springframework.util.ClassUtils; import org.springframework.util.StringUtils; @@ -25,11 +30,20 @@ import org.springframework.util.StringUtils; * * @author Keith Donald */ -public class TextToClass extends ConversionServiceAwareConverter { +public class TextToClass extends AbstractConverter { - private static final String ALIAS_PREFIX = "type:"; + private Map aliasMap = new HashMap(); - private static final String CLASS_PREFIX = "class:"; + public TextToClass() { + addDefaultAliases(); + } + + /** + * Add an alias for given target type. + */ + public void addAlias(String alias, Class targetType) { + aliasMap.put(alias, targetType); + } public Class[] getSourceClasses() { return new Class[] { String.class }; @@ -42,27 +56,30 @@ public class TextToClass extends ConversionServiceAwareConverter { protected Object doConvert(Object source, Class targetClass, ConversionContext context) throws Exception { String text = (String) source; if (StringUtils.hasText(text)) { - String classNameOrAlias = text.trim(); - if (classNameOrAlias.startsWith(CLASS_PREFIX)) { - return ClassUtils.forName(text.substring(CLASS_PREFIX.length())); - } else if (classNameOrAlias.startsWith(ALIAS_PREFIX)) { - String alias = text.substring(ALIAS_PREFIX.length()); - Class clazz = getConversionService().getClassByAlias(alias); - Assert.notNull(clazz, "No class found associated with type alias '" + alias + "'"); - return clazz; + text = text.trim(); + if (aliasMap.containsKey(text)) { + return aliasMap.get(text); } else { - // try first an aliased based lookup - if (getConversionService() != null) { - Class aliasedClass = getConversionService().getClassByAlias(classNameOrAlias); - if (aliasedClass != null) { - return aliasedClass; - } - } - // treat as a class name - return ClassUtils.forName(classNameOrAlias); + return ClassUtils.forName(text); } } else { return null; } } + + protected void addDefaultAliases() { + addAlias("string", String.class); + addAlias("short", Short.class); + addAlias("integer", Integer.class); + addAlias("int", Integer.class); + addAlias("byte", Byte.class); + addAlias("long", Long.class); + addAlias("float", Float.class); + addAlias("double", Double.class); + addAlias("bigInteger", BigInteger.class); + addAlias("bigDecimal", BigDecimal.class); + addAlias("boolean", Boolean.class); + addAlias("class", Class.class); + addAlias("labeledEnum", LabeledEnum.class); + } } \ No newline at end of file diff --git a/spring-binding/src/test/java/org/springframework/binding/convert/support/CompositeConversionServiceTests.java b/spring-binding/src/test/java/org/springframework/binding/convert/support/CompositeConversionServiceTests.java deleted file mode 100644 index 6136f21e..00000000 --- a/spring-binding/src/test/java/org/springframework/binding/convert/support/CompositeConversionServiceTests.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright 2004-2007 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.binding.convert.support; - -import java.util.Date; - -import junit.framework.TestCase; - -import org.springframework.binding.convert.ConversionException; -import org.springframework.binding.convert.ConversionExecutor; -import org.springframework.binding.convert.ConversionService; - -/** - * Test case for the {@link CompositeConversionService}. - * - * @author Erwin Vervaet - */ -public class CompositeConversionServiceTests extends TestCase { - - private CompositeConversionService service; - - protected void setUp() throws Exception { - GenericConversionService first = new GenericConversionService(); - first.addConverter(new TextToClass()); - first.addConverter(new TextToBoolean("ja", "nee")); - first.addDefaultAlias(Boolean.class); - GenericConversionService second = new GenericConversionService(); - second.addConverter(new TextToBoolean()); - second.addDefaultAlias(Integer.class); - service = new CompositeConversionService(new ConversionService[] { first, second }); - } - - public void testGetConversionExecutor() { - assertNotNull(service.getConversionExecutor(String.class, Class.class)); - assertNotNull(service.getConversionExecutor(String.class, Boolean.class)); - assertEquals(Boolean.TRUE, service.getConversionExecutor(String.class, Boolean.class).execute("ja")); - try { - service.getConversionExecutor(String.class, Date.class); - fail(); - } catch (ConversionException e) { - // expected - } - } - - public void testGetConversionExecutorByTargetAlias() { - assertNotNull(service.getConversionExecutorByTargetAlias(String.class, "boolean")); - assertEquals(Boolean.TRUE, service.getConversionExecutorByTargetAlias(String.class, "boolean").execute("ja")); - assertNull(service.getConversionExecutorByTargetAlias(String.class, "class")); - } - - public void testGetConversionExecutorsForSource() { - assertEquals(new TextToClass().getTargetClasses().length + new TextToBoolean().getTargetClasses().length, - service.getConversionExecutorsForSource(String.class).length); - assertEquals(0, service.getConversionExecutorsForSource(Date.class).length); - ConversionExecutor[] fromStringConversionExecutors = service.getConversionExecutorsForSource(String.class); - ConversionExecutorImpl booleanConversionExecutor = null; - for (int i = 0; i < fromStringConversionExecutors.length; i++) { - if (((ConversionExecutorImpl) fromStringConversionExecutors[i]).getConverter() instanceof TextToBoolean) { - booleanConversionExecutor = (ConversionExecutorImpl) fromStringConversionExecutors[i]; - } - } - assertEquals(Boolean.TRUE, booleanConversionExecutor.execute("ja")); - } - - public void testGetClassByAlias() { - assertEquals(Boolean.class, service.getClassByAlias("boolean")); - assertNull(service.getClassByAlias("class")); - } -} diff --git a/spring-binding/src/test/java/org/springframework/binding/convert/support/DefaultConversionServiceTests.java b/spring-binding/src/test/java/org/springframework/binding/convert/support/DefaultConversionServiceTests.java index c96a2d0d..5482bb18 100644 --- a/spring-binding/src/test/java/org/springframework/binding/convert/support/DefaultConversionServiceTests.java +++ b/spring-binding/src/test/java/org/springframework/binding/convert/support/DefaultConversionServiceTests.java @@ -34,11 +34,9 @@ public class DefaultConversionServiceTests extends TestCase { public void testConvertCompatibleTypes() { DefaultConversionService service = new DefaultConversionService(); - service.addAlias("list", List.class); List lst = new ArrayList(); assertSame(lst, service.getConversionExecutor(ArrayList.class, List.class).execute(lst)); - assertSame(lst, service.getConversionExecutorByTargetAlias(ArrayList.class, "list").execute(lst)); try { service.getConversionExecutor(List.class, ArrayList.class); diff --git a/spring-faces/.classpath b/spring-faces/.classpath index e279d13c..384d9d51 100644 --- a/spring-faces/.classpath +++ b/spring-faces/.classpath @@ -1,7 +1,9 @@ + + diff --git a/spring-faces/src/main/java/org/springframework/faces/model/converter/FacesConversionService.java b/spring-faces/src/main/java/org/springframework/faces/model/converter/FacesConversionService.java index 66867703..66578e86 100644 --- a/spring-faces/src/main/java/org/springframework/faces/model/converter/FacesConversionService.java +++ b/spring-faces/src/main/java/org/springframework/faces/model/converter/FacesConversionService.java @@ -14,6 +14,7 @@ package org.springframework.faces.model.converter; import org.springframework.binding.convert.ConversionService; import org.springframework.binding.convert.support.DefaultConversionService; +import org.springframework.binding.convert.support.TextToClass; import org.springframework.faces.model.OneSelectionTrackingListDataModel; /** @@ -25,13 +26,12 @@ import org.springframework.faces.model.OneSelectionTrackingListDataModel; public class FacesConversionService extends DefaultConversionService { public FacesConversionService() { - super(); addFacesConverters(); } protected void addFacesConverters() { addConverter(new DataModelConverter()); - - addAlias("dataModel", OneSelectionTrackingListDataModel.class); + TextToClass classConverter = (TextToClass) getConverter(String.class, Class.class); + classConverter.addAlias("dataModel", OneSelectionTrackingListDataModel.class); } } diff --git a/spring-webflow/.classpath b/spring-webflow/.classpath index d4f8148d..82423b83 100644 --- a/spring-webflow/.classpath +++ b/spring-webflow/.classpath @@ -1,7 +1,9 @@ + + diff --git a/spring-webflow/src/main/java/org/springframework/webflow/config/FlowExecutorFactoryBean.java b/spring-webflow/src/main/java/org/springframework/webflow/config/FlowExecutorFactoryBean.java index 97cbb530..be080750 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/config/FlowExecutorFactoryBean.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/config/FlowExecutorFactoryBean.java @@ -177,7 +177,8 @@ class FlowExecutorFactoryBean implements FactoryBean, InitializingBean { } /** - * Sets the conversion service for converting string-encoded flow execution attributes to typed values. + * TODO never called. Sets the conversion service for converting string-encoded flow execution attributes to typed + * values. * @param conversionService the conversion service */ public void setConversionService(ConversionService conversionService) { @@ -351,11 +352,15 @@ class FlowExecutorFactoryBean implements FactoryBean, InitializingBean { private Object getConvertedValue(FlowElementAttribute attribute) { if (attribute.needsTypeConversion()) { - ConversionExecutor converter = conversionService.getConversionExecutorByTargetAlias(String.class, attribute - .getType()); + Class targetType = fromStringToClass(attribute.getType()); + ConversionExecutor converter = conversionService.getConversionExecutor(String.class, targetType); return converter.execute(attribute.getValue()); } else { return attribute.getValue(); } } + + private Class fromStringToClass(String type) { + return (Class) conversionService.getConversionExecutor(String.class, Class.class).execute(type); + } } \ No newline at end of file diff --git a/spring-webflow/src/main/java/org/springframework/webflow/config/FlowRegistryFactoryBean.java b/spring-webflow/src/main/java/org/springframework/webflow/config/FlowRegistryFactoryBean.java index b62f1e21..dcfb1b9a 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/config/FlowRegistryFactoryBean.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/config/FlowRegistryFactoryBean.java @@ -151,14 +151,20 @@ class FlowRegistryFactoryBean implements FactoryBean, InitializingBean { private Object getConvertedValue(FlowElementAttribute attribute) { if (attribute.needsTypeConversion()) { - ConversionExecutor converter = flowBuilderServices.getConversionService() - .getConversionExecutorByTargetAlias(String.class, attribute.getType()); + Class targetType = fromStringToClass(attribute.getType()); + ConversionExecutor converter = flowBuilderServices.getConversionService().getConversionExecutor( + String.class, targetType); return converter.execute(attribute.getValue()); } else { return attribute.getValue(); } } + private Class fromStringToClass(String type) { + return (Class) flowBuilderServices.getConversionService().getConversionExecutor(String.class, Class.class) + .execute(type); + } + private FlowDefinition buildFlowDefinition(FlowBuilderInfo builderInfo) { try { Class flowBuilderClass = ClassUtils.forName(builderInfo.getClassName()); diff --git a/spring-webflow/src/main/java/org/springframework/webflow/mvc/InternalFlowResourceMvcViewFactory.java b/spring-webflow/src/main/java/org/springframework/webflow/mvc/InternalFlowResourceMvcViewFactory.java new file mode 100644 index 00000000..f3f72331 --- /dev/null +++ b/spring-webflow/src/main/java/org/springframework/webflow/mvc/InternalFlowResourceMvcViewFactory.java @@ -0,0 +1,60 @@ +package org.springframework.webflow.mvc; + +import org.springframework.binding.expression.Expression; +import org.springframework.context.ApplicationContext; +import org.springframework.core.io.ContextResource; +import org.springframework.core.io.ResourceLoader; +import org.springframework.util.ClassUtils; +import org.springframework.web.servlet.view.InternalResourceView; +import org.springframework.web.servlet.view.JstlView; +import org.springframework.webflow.execution.RequestContext; +import org.springframework.webflow.execution.View; +import org.springframework.webflow.execution.ViewFactory; + +/** + * View factory implementation that creates a Spring-MVC Internal Resource view to render a flow-relative view resource + * such as a JSP or Velocity template. + * @author Keith Donald + */ +class InternalFlowResourceMvcViewFactory implements ViewFactory { + private static final boolean JSTL_PRESENT = ClassUtils.isPresent("javax.servlet.jsp.jstl.fmt.LocalizationContext"); + + private Expression viewIdExpression; + + private ApplicationContext applicationContext; + + private ResourceLoader resourceLoader; + + public InternalFlowResourceMvcViewFactory(Expression viewIdExpression, ApplicationContext context, + ResourceLoader resourceLoader) { + this.viewIdExpression = viewIdExpression; + this.applicationContext = context; + this.resourceLoader = resourceLoader; + } + + public View getView(RequestContext context) { + String viewId = (String) viewIdExpression.getValue(context); + if (viewId.startsWith("/")) { + return getViewInternal(viewId, context); + } else { + ContextResource viewResource = (ContextResource) resourceLoader.getResource(viewId); + return getViewInternal(viewResource.getPathWithinContext(), context); + } + } + + private View getViewInternal(String viewPath, RequestContext context) { + if (viewPath.endsWith(".jsp")) { + if (JSTL_PRESENT) { + JstlView view = new JstlView(viewPath); + view.setApplicationContext(applicationContext); + return new MvcView(view, context); + } else { + InternalResourceView view = new InternalResourceView(viewPath); + view.setApplicationContext(applicationContext); + return new MvcView(view, context); + } + } else { + throw new IllegalArgumentException("Unsupported view type " + viewPath + " only types supported are [.jsp]"); + } + } +} \ No newline at end of file diff --git a/spring-webflow/src/main/java/org/springframework/webflow/mvc/MvcView.java b/spring-webflow/src/main/java/org/springframework/webflow/mvc/MvcView.java new file mode 100644 index 00000000..709579d5 --- /dev/null +++ b/spring-webflow/src/main/java/org/springframework/webflow/mvc/MvcView.java @@ -0,0 +1,249 @@ +/* + * Copyright 2004-2007 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.webflow.mvc; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.binding.collection.MapAdaptable; +import org.springframework.binding.expression.Expression; +import org.springframework.binding.expression.ExpressionParser; +import org.springframework.binding.expression.support.ParserContextImpl; +import org.springframework.binding.format.FormatterRegistry; +import org.springframework.binding.format.factories.DateFormatterFactory; +import org.springframework.binding.format.factories.NumberFormatterFactory; +import org.springframework.binding.format.impl.FormatterRegistryImpl; +import org.springframework.binding.mapping.MappingResult; +import org.springframework.binding.mapping.MappingResults; +import org.springframework.binding.mapping.MappingResultsCriteria; +import org.springframework.binding.mapping.impl.DefaultMapper; +import org.springframework.binding.mapping.impl.DefaultMapping; +import org.springframework.binding.mapping.results.TargetAccessError; +import org.springframework.validation.BindingResult; +import org.springframework.webflow.core.collection.ParameterMap; +import org.springframework.webflow.engine.Transition; +import org.springframework.webflow.engine.TransitionableState; +import org.springframework.webflow.execution.Event; +import org.springframework.webflow.execution.RequestContext; +import org.springframework.webflow.execution.View; +import org.springframework.webflow.expression.DefaultExpressionParserFactory; + +class MvcView implements View { + + private static final MappingResultsCriteria PROPERTY_NOT_FOUND_ERRORS = new PropertyNotFoundErrors(); + + private org.springframework.web.servlet.View view; + + private RequestContext context; + + private ExpressionParser expressionParser = DefaultExpressionParserFactory.getExpressionParser(); + + private FormatterRegistry formatterRegistry; + + private MappingResults mappingResults; + + private boolean postbackSuccess; + + private String eventId; + + public MvcView(org.springframework.web.servlet.View view, RequestContext context) { + this.view = view; + this.context = context; + this.formatterRegistry = createDefaultFormatterRegistry(); + } + + public void setExpressionParser(ExpressionParser expressionParser) { + this.expressionParser = expressionParser; + } + + public void setFormatterRegistry(FormatterRegistry formatterRegistry) { + this.formatterRegistry = formatterRegistry; + } + + public void render() throws IOException { + Map model = new HashMap(); + model.putAll(flowScopes()); + exposeBindingModel(model); + model.put("flowRequestContext", context); + model.put("flowExecutionKey", context.getFlowExecutionContext().getKey().toString()); + model.put("flowExecutionUrl", context.getFlowExecutionUrl()); + model.put("currentUser", context.getExternalContext().getCurrentUser()); + try { + view.render(model, (HttpServletRequest) context.getExternalContext().getNativeRequest(), + (HttpServletResponse) context.getExternalContext().getNativeResponse()); + } catch (IOException e) { + throw e; + } catch (Exception e) { + throw new RuntimeException("Unexpected exception occurred rendering view " + view, e); + } + } + + public void postback() { + determineEventId(context); + Object model = getModelObject(); + if (model == null) { + postbackSuccess = true; + return; + } + if (shouldBind(model)) { + mappingResults = bind(model); + if (!mappingResults.hasErrorResults()) { + postbackSuccess = true; + } else { + if (onlyPropertyNotFoundErrors()) { + postbackSuccess = true; + } else { + postbackSuccess = false; + } + } + } + } + + private boolean onlyPropertyNotFoundErrors() { + return mappingResults.getResults(PROPERTY_NOT_FOUND_ERRORS).size() == mappingResults.getErrorResults().size(); + } + + public boolean eventSignaled() { + return postbackSuccess && eventId != null; + } + + public Event getEvent() { + return new Event(this, eventId, context.getRequestParameters().asAttributeMap()); + } + + protected FormatterRegistry createDefaultFormatterRegistry() { + FormatterRegistryImpl registry = new FormatterRegistryImpl(); + registry.registerFormatter(new NumberFormatterFactory()); + registry.registerFormatter(new DateFormatterFactory()); + return registry; + } + + private Map flowScopes() { + return context.getConversationScope().union(context.getFlowScope()).union(context.getFlashScope()).union( + context.getRequestScope()).asMap(); + } + + private void exposeBindingModel(Map model) { + Object modelObject = getModelObject(); + if (modelObject != null) { + BindingModel bindingModel = new BindingModel(modelObject, expressionParser, formatterRegistry, context + .getMessageContext()); + bindingModel.setMappingResults(mappingResults); + model.put(BindingResult.MODEL_KEY_PREFIX + getModelExpression().getExpressionString(), model); + } + } + + private Object getModelObject() { + Expression model = getModelExpression(); + if (model != null) { + return model.getValue(context); + } else { + return null; + } + } + + private Expression getModelExpression() { + return (Expression) context.getCurrentState().getAttributes().get("model"); + } + + private boolean shouldBind(Object model) { + // this downcast is not ideal + TransitionableState currentState = (TransitionableState) context.getCurrentState(); + // TODO this won't work because last event isn't set yet... + Transition transition = currentState.getRequiredTransition(context); + if (transition.getAttributes().contains("bind")) { + return transition.getAttributes().getBoolean("bind").booleanValue(); + } else { + return false; + } + } + + private MappingResults bind(Object model) { + DefaultMapper mapper = new DefaultMapper(); + addDefaultMappings(mapper, context.getRequestParameters(), model); + return mapper.map(context.getRequestParameters(), model); + } + + private void addDefaultMappings(DefaultMapper mapper, ParameterMap requestParameters, Object model) { + for (Iterator it = requestParameters.asMap().keySet().iterator(); it.hasNext();) { + String name = (String) it.next(); + Expression source = expressionParser + .parseExpression(name, new ParserContextImpl().eval(MapAdaptable.class)); + Expression target = expressionParser.parseExpression(name, new ParserContextImpl().eval(model.getClass())); + mapper.addMapping(new DefaultMapping(source, target)); + } + } + + private void determineEventId(RequestContext context) { + eventId = findParameter("_eventId", context.getRequestParameters()); + } + + /** + * Obtain a named parameter from the request parameters. This method will try to obtain a parameter value using the + * following algorithm: + *

    + *
  1. Try to get the parameter value using just the given logical name. This handles parameters of the + * form logicalName = value. For normal parameters, e.g. submitted using a hidden HTML form field, this + * will return the requested value.
  2. + *
  3. Try to obtain the parameter value from the parameter name, where the parameter name in the request is of the + * form logicalName_value = xyz with "_" being the configured delimiter. This deals with parameter values + * submitted using an HTML form submit button.
  4. + *
  5. If the value obtained in the previous step has a ".x" or ".y" suffix, remove that. This handles cases where + * the value was submitted using an HTML form image button. In this case the parameter in the request would actually + * be of the form logicalName_value.x = 123.
  6. + *
+ * @param logicalParameterName the logical name of the request parameter + * @param parameters the available parameter map + * @return the value of the parameter, or null if the parameter does not exist in given request + */ + private String findParameter(String logicalParameterName, ParameterMap parameters) { + // first try to get it as a normal name=value parameter + String value = parameters.get(logicalParameterName); + if (value != null) { + return value; + } + // if no value yet, try to get it as a name_value=xyz parameter + String prefix = logicalParameterName + "_"; + Iterator paramNames = parameters.asMap().keySet().iterator(); + while (paramNames.hasNext()) { + String paramName = (String) paramNames.next(); + if (paramName.startsWith(prefix)) { + String strValue = paramName.substring(prefix.length()); + // support images buttons, which would submit parameters as + // name_value.x=123 + if (strValue.endsWith(".x") || strValue.endsWith(".y")) { + strValue = strValue.substring(0, strValue.length() - 2); + } + return strValue; + } + } + // we couldn't find the parameter value + return null; + } + + private static class PropertyNotFoundErrors implements MappingResultsCriteria { + public boolean test(MappingResult result) { + return result.getResult() instanceof TargetAccessError + && result.getResult().getErrorCode().equals("propertyNotFound"); + } + } + +} \ No newline at end of file diff --git a/spring-webflow/src/main/java/org/springframework/webflow/mvc/MvcViewFactoryCreator.java b/spring-webflow/src/main/java/org/springframework/webflow/mvc/MvcViewFactoryCreator.java index 5a48c174..faa2b59c 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/mvc/MvcViewFactoryCreator.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/mvc/MvcViewFactoryCreator.java @@ -15,33 +15,15 @@ */ package org.springframework.webflow.mvc; -import java.util.HashMap; -import java.util.Iterator; import java.util.List; -import java.util.Map; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import org.springframework.binding.expression.Expression; import org.springframework.binding.expression.ExpressionParser; import org.springframework.binding.format.FormatterRegistry; -import org.springframework.binding.mapping.MappingResults; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; -import org.springframework.context.i18n.LocaleContextHolder; -import org.springframework.core.io.ContextResource; import org.springframework.core.io.ResourceLoader; -import org.springframework.util.ClassUtils; -import org.springframework.validation.BindingResult; -import org.springframework.web.servlet.ViewResolver; -import org.springframework.web.servlet.view.InternalResourceView; -import org.springframework.web.servlet.view.JstlView; -import org.springframework.webflow.core.collection.ParameterMap; import org.springframework.webflow.engine.builder.ViewFactoryCreator; -import org.springframework.webflow.execution.Event; -import org.springframework.webflow.execution.RequestContext; -import org.springframework.webflow.execution.View; import org.springframework.webflow.execution.ViewFactory; /** @@ -58,12 +40,8 @@ import org.springframework.webflow.execution.ViewFactory; */ public class MvcViewFactoryCreator implements ViewFactoryCreator, ApplicationContextAware { - private static final boolean jstlPresent = ClassUtils.isPresent("javax.servlet.jsp.jstl.fmt.LocalizationContext"); - - // TODO private ExpressionParser expressionParser; - // TODO private FormatterRegistry formatterRegistry; private List viewResolvers; @@ -96,193 +74,4 @@ public class MvcViewFactoryCreator implements ViewFactoryCreator, ApplicationCon return viewStateId + ".jsp"; } - /** - * View factory implementation that creates a Spring-MVC Internal Resource view to render a flow-relative view - * resource such as a JSP or Velocity template. - * @author Keith Donald - */ - static class InternalFlowResourceMvcViewFactory implements ViewFactory { - private Expression viewIdExpression; - - private ApplicationContext applicationContext; - - private ResourceLoader resourceLoader; - - public InternalFlowResourceMvcViewFactory(Expression viewIdExpression, ApplicationContext context, - ResourceLoader resourceLoader) { - this.viewIdExpression = viewIdExpression; - this.applicationContext = context; - this.resourceLoader = resourceLoader; - } - - public View getView(RequestContext context) { - String viewId = (String) viewIdExpression.getValue(context); - if (viewId.startsWith("/")) { - return getViewInternal(viewId, context); - } else { - ContextResource viewResource = (ContextResource) resourceLoader.getResource(viewId); - return getViewInternal(viewResource.getPathWithinContext(), context); - } - } - - private View getViewInternal(String viewPath, RequestContext context) { - if (viewPath.endsWith(".jsp")) { - if (jstlPresent) { - JstlView view = new JstlView(viewPath); - view.setApplicationContext(applicationContext); - return new MvcView(view, context); - } else { - InternalResourceView view = new InternalResourceView(viewPath); - view.setApplicationContext(applicationContext); - return new MvcView(view, context); - } - } else { - throw new IllegalArgumentException("Unsupported view type " + viewPath - + " only types supported are [.jsp]"); - } - } - } - - /** - * View factory implementation that delegates to the Spring-configured view resolver chain to resolve the Spring MVC - * view implementation to render. - * @author Keith Donald - */ - private static class ViewResolvingMvcViewFactory implements ViewFactory { - private Expression viewIdExpression; - - private List viewResolvers; - - public ViewResolvingMvcViewFactory(Expression viewIdExpression, List viewResolvers) { - this.viewIdExpression = viewIdExpression; - this.viewResolvers = viewResolvers; - } - - public View getView(RequestContext context) { - String view = (String) viewIdExpression.getValue(context); - return new MvcView(resolveView(view), context); - } - - protected org.springframework.web.servlet.View resolveView(String viewName) { - for (Iterator it = viewResolvers.iterator(); it.hasNext();) { - ViewResolver viewResolver = (ViewResolver) it.next(); - try { - org.springframework.web.servlet.View view = viewResolver.resolveViewName(viewName, - LocaleContextHolder.getLocale()); - if (view != null) { - return view; - } - } catch (Exception e) { - throw new IllegalStateException("Exception resolving view with name '" + viewName + "'", e); - } - } - return null; - } - } - - private static class MvcView implements View { - - private RequestContext context; - - private org.springframework.web.servlet.View view; - - private String eventId; - - public MvcView(org.springframework.web.servlet.View view, RequestContext context) { - this.view = view; - this.context = context; - } - - public void render() { - Map model = new HashMap(); - model.putAll(context.getConversationScope().union(context.getFlowScope()).union(context.getFlashScope()) - .union(context.getRequestScope()).asMap()); - exposeBindingModel(model); - model.put("flowRequestContext", context); - model.put("flowExecutionKey", context.getFlowExecutionContext().getKey().toString()); - model.put("flowExecutionUrl", context.getFlowExecutionUrl()); - model.put("currentUser", context.getExternalContext().getCurrentUser()); - try { - view.render(model, (HttpServletRequest) context.getExternalContext().getNativeRequest(), - (HttpServletResponse) context.getExternalContext().getNativeResponse()); - } catch (Exception e) { - throw new IllegalStateException("Exception rendering view", e); - } - } - - public void postback() { - // TODO implement me with real data binding behavior - } - - public boolean eventSignaled() { - determineEventId(context); - return eventId != null; - } - - public Event getEvent() { - return new Event(this, eventId, context.getRequestParameters().asAttributeMap()); - } - - private void exposeBindingModel(Map model) { - Expression boundObjectExpr = (Expression) context.getCurrentState().getAttributes().get("model"); - if (boundObjectExpr != null) { - Object boundObject = boundObjectExpr.getValue(context); - // TODO - BindingModel bindingModel = new BindingModel(boundObject, null, null, context.getMessageContext()); - MappingResults bindResults = (MappingResults) context.getRequestScope().get( - boundObjectExpr.getExpressionString() + "MappingResults"); - bindingModel.setMappingResults(bindResults); - model.put(BindingResult.MODEL_KEY_PREFIX + boundObjectExpr.getExpressionString(), model); - } - } - - private void determineEventId(RequestContext context) { - eventId = findParameter("_eventId", context.getRequestParameters()); - } - - /** - * Obtain a named parameter from the request parameters. This method will try to obtain a parameter value using - * the following algorithm: - *
    - *
  1. Try to get the parameter value using just the given logical name. This handles parameters of the - * form logicalName = value. For normal parameters, e.g. submitted using a hidden HTML form field, - * this will return the requested value.
  2. - *
  3. Try to obtain the parameter value from the parameter name, where the parameter name in the request is of - * the form logicalName_value = xyz with "_" being the configured delimiter. This deals with - * parameter values submitted using an HTML form submit button.
  4. - *
  5. If the value obtained in the previous step has a ".x" or ".y" suffix, remove that. This handles cases - * where the value was submitted using an HTML form image button. In this case the parameter in the request - * would actually be of the form logicalName_value.x = 123.
  6. - *
- * @param logicalParameterName the logical name of the request parameter - * @param parameters the available parameter map - * @return the value of the parameter, or null if the parameter does not exist in given request - */ - private String findParameter(String logicalParameterName, ParameterMap parameters) { - // first try to get it as a normal name=value parameter - String value = parameters.get(logicalParameterName); - if (value != null) { - return value; - } - // if no value yet, try to get it as a name_value=xyz parameter - String prefix = logicalParameterName + "_"; - Iterator paramNames = parameters.asMap().keySet().iterator(); - while (paramNames.hasNext()) { - String paramName = (String) paramNames.next(); - if (paramName.startsWith(prefix)) { - String strValue = paramName.substring(prefix.length()); - // support images buttons, which would submit parameters as - // name_value.x=123 - if (strValue.endsWith(".x") || strValue.endsWith(".y")) { - strValue = strValue.substring(0, strValue.length() - 2); - } - return strValue; - } - } - // we couldn't find the parameter value - return null; - } - - } - } \ No newline at end of file diff --git a/spring-webflow/src/main/java/org/springframework/webflow/mvc/ViewResolvingMvcViewFactory.java b/spring-webflow/src/main/java/org/springframework/webflow/mvc/ViewResolvingMvcViewFactory.java new file mode 100644 index 00000000..1a47ef48 --- /dev/null +++ b/spring-webflow/src/main/java/org/springframework/webflow/mvc/ViewResolvingMvcViewFactory.java @@ -0,0 +1,48 @@ +package org.springframework.webflow.mvc; + +import java.util.Iterator; +import java.util.List; + +import org.springframework.binding.expression.Expression; +import org.springframework.context.i18n.LocaleContextHolder; +import org.springframework.web.servlet.ViewResolver; +import org.springframework.webflow.execution.RequestContext; +import org.springframework.webflow.execution.View; +import org.springframework.webflow.execution.ViewFactory; + +/** + * View factory implementation that delegates to the Spring-configured view resolver chain to resolve the Spring MVC + * view implementation to render. + * @author Keith Donald + */ +class ViewResolvingMvcViewFactory implements ViewFactory { + private Expression viewIdExpression; + + private List viewResolvers; + + public ViewResolvingMvcViewFactory(Expression viewIdExpression, List viewResolvers) { + this.viewIdExpression = viewIdExpression; + this.viewResolvers = viewResolvers; + } + + public View getView(RequestContext context) { + String view = (String) viewIdExpression.getValue(context); + return new MvcView(resolveView(view), context); + } + + protected org.springframework.web.servlet.View resolveView(String viewName) { + for (Iterator it = viewResolvers.iterator(); it.hasNext();) { + ViewResolver viewResolver = (ViewResolver) it.next(); + try { + org.springframework.web.servlet.View view = viewResolver.resolveViewName(viewName, + LocaleContextHolder.getLocale()); + if (view != null) { + return view; + } + } catch (Exception e) { + throw new IllegalStateException("Exception resolving view with name '" + viewName + "'", e); + } + } + return null; + } +} \ No newline at end of file