diff --git a/org.springframework.context/src/main/java/org/springframework/ui/binding/BindingFactory.java b/org.springframework.context/src/main/java/org/springframework/ui/binding/BindingFactory.java new file mode 100644 index 00000000000..5f739c3fbe2 --- /dev/null +++ b/org.springframework.context/src/main/java/org/springframework/ui/binding/BindingFactory.java @@ -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.ui.binding; + +/** + * A factory for model property bindings. + * @author Keith Donald + */ +public interface BindingFactory { + + /** + * The model object upon which bindings may be accessed. + */ + Object getModel(); + + /** + * Get a binding to a model property. + * @param property the property path + * @throws NoSuchBindingException if no binding to the property exists + */ + Binding getBinding(String property); + +} \ No newline at end of file diff --git a/org.springframework.context/src/main/java/org/springframework/ui/binding/NoSuchBindingException.java b/org.springframework.context/src/main/java/org/springframework/ui/binding/NoSuchBindingException.java deleted file mode 100644 index cfce3af30ba..00000000000 --- a/org.springframework.context/src/main/java/org/springframework/ui/binding/NoSuchBindingException.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.springframework.ui.binding; - -/** - * Thrown by a BindingFactory when no binding to a property exists. - * @author Keith Donald - * @since 3.0 - * @see BindingFactory#getBinding(String) - */ -@SuppressWarnings("serial") -public class NoSuchBindingException extends RuntimeException { - - /** - * Creates a new no such binding exception. - * @param property the requested property for which there is no binding - */ - public NoSuchBindingException(String property) { - super("No binding to property '" + property + "' exists"); - } -} diff --git a/org.springframework.context/src/main/java/org/springframework/ui/binding/support/ArrayListBindingResults.java b/org.springframework.context/src/main/java/org/springframework/ui/binding/binder/ArrayListBindingResults.java similarity index 68% rename from org.springframework.context/src/main/java/org/springframework/ui/binding/support/ArrayListBindingResults.java rename to org.springframework.context/src/main/java/org/springframework/ui/binding/binder/ArrayListBindingResults.java index c1f4984ac20..e9835cacde3 100644 --- a/org.springframework.context/src/main/java/org/springframework/ui/binding/support/ArrayListBindingResults.java +++ b/org.springframework.context/src/main/java/org/springframework/ui/binding/binder/ArrayListBindingResults.java @@ -1,14 +1,24 @@ -/** - * +/* + * 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.ui.binding.support; +package org.springframework.ui.binding.binder; import java.util.ArrayList; import java.util.Iterator; import java.util.List; -import org.springframework.ui.binding.BindingResult; -import org.springframework.ui.binding.BindingResults; class ArrayListBindingResults implements BindingResults { diff --git a/org.springframework.context/src/main/java/org/springframework/ui/binding/Binder.java b/org.springframework.context/src/main/java/org/springframework/ui/binding/binder/Binder.java similarity index 77% rename from org.springframework.context/src/main/java/org/springframework/ui/binding/Binder.java rename to org.springframework.context/src/main/java/org/springframework/ui/binding/binder/Binder.java index f37f61116a6..a42158f515d 100644 --- a/org.springframework.context/src/main/java/org/springframework/ui/binding/Binder.java +++ b/org.springframework.context/src/main/java/org/springframework/ui/binding/binder/Binder.java @@ -13,31 +13,25 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.ui.binding; +package org.springframework.ui.binding.binder; import java.util.Map; +import org.springframework.ui.binding.Binding; + /** * Binds user-entered values to properties of a model object. * @author Keith Donald * @since 3.0 * @see #bind(String) - * @see #getBinding(String) * @see #bind(Map) */ public interface Binder { - + /** - * The model object for which property bindings may be accessed. + * The model this binder binds to. */ - Object getModel(); - - /** - * Get a binding to a model property.. - * @param property the property path - * @throws NoSuchBindingException if no binding to the property exists - */ - Binding getBinding(String property); + public Object getModel(); /** * Bind the source values to the properties of the model. diff --git a/org.springframework.context/src/main/java/org/springframework/ui/binding/BindingResult.java b/org.springframework.context/src/main/java/org/springframework/ui/binding/binder/BindingResult.java similarity index 94% rename from org.springframework.context/src/main/java/org/springframework/ui/binding/BindingResult.java rename to org.springframework.context/src/main/java/org/springframework/ui/binding/binder/BindingResult.java index ec03e0bafb3..caee4d62a5a 100644 --- a/org.springframework.context/src/main/java/org/springframework/ui/binding/BindingResult.java +++ b/org.springframework.context/src/main/java/org/springframework/ui/binding/binder/BindingResult.java @@ -13,9 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.ui.binding; +package org.springframework.ui.binding.binder; import org.springframework.ui.alert.Alert; +import org.springframework.ui.binding.Binding; /** * The result of a bind operation. diff --git a/org.springframework.context/src/main/java/org/springframework/ui/binding/BindingResults.java b/org.springframework.context/src/main/java/org/springframework/ui/binding/binder/BindingResults.java similarity index 96% rename from org.springframework.context/src/main/java/org/springframework/ui/binding/BindingResults.java rename to org.springframework.context/src/main/java/org/springframework/ui/binding/binder/BindingResults.java index ac688b43170..0c2e66c99ea 100644 --- a/org.springframework.context/src/main/java/org/springframework/ui/binding/BindingResults.java +++ b/org.springframework.context/src/main/java/org/springframework/ui/binding/binder/BindingResults.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.ui.binding; +package org.springframework.ui.binding.binder; import java.util.List; diff --git a/org.springframework.context/src/main/java/org/springframework/ui/binding/support/BindingStatusResult.java b/org.springframework.context/src/main/java/org/springframework/ui/binding/binder/BindingStatusResult.java similarity index 53% rename from org.springframework.context/src/main/java/org/springframework/ui/binding/support/BindingStatusResult.java rename to org.springframework.context/src/main/java/org/springframework/ui/binding/binder/BindingStatusResult.java index 0b4222ef5d8..f22810e52a0 100644 --- a/org.springframework.context/src/main/java/org/springframework/ui/binding/support/BindingStatusResult.java +++ b/org.springframework.context/src/main/java/org/springframework/ui/binding/binder/BindingStatusResult.java @@ -1,11 +1,22 @@ -/** - * +/* + * 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.ui.binding.support; +package org.springframework.ui.binding.binder; import org.springframework.ui.alert.Alert; import org.springframework.ui.alert.Severity; -import org.springframework.ui.binding.BindingResult; class BindingStatusResult implements BindingResult { diff --git a/org.springframework.context/src/main/java/org/springframework/ui/binding/binder/GenericBinder.java b/org.springframework.context/src/main/java/org/springframework/ui/binding/binder/GenericBinder.java new file mode 100644 index 00000000000..d196fe05762 --- /dev/null +++ b/org.springframework.context/src/main/java/org/springframework/ui/binding/binder/GenericBinder.java @@ -0,0 +1,145 @@ +/* + * 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.ui.binding.binder; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.springframework.context.MessageSource; +import org.springframework.core.convert.TypeConverter; +import org.springframework.ui.binding.Binding; +import org.springframework.ui.binding.BindingFactory; +import org.springframework.ui.binding.Binding.BindingStatus; +import org.springframework.ui.binding.support.PropertyNotFoundException; +import org.springframework.util.Assert; + +/** + * A generic {@link Binder binder} suitable for use in most environments. + * @author Keith Donald + * @since 3.0 + * @see #setMessageSource(MessageSource) + * @see #setTypeConverter(TypeConverter) + * @see #bind(Map) + */ +public class GenericBinder implements Binder { + + private BindingFactory bindingFactory; + + private String[] requiredProperties; + + private MessageSource messageSource; + + public GenericBinder(BindingFactory bindingFactory) { + Assert.notNull(bindingFactory, "The BindingFactory is required"); + this.bindingFactory = bindingFactory; + } + + /** + * Configure the MessageSource that resolves localized {@link BindingResult} alert messages. + * @param messageSource the message source + */ + public void setMessageSource(MessageSource messageSource) { + Assert.notNull(messageSource, "The MessageSource is required"); + this.messageSource = messageSource; + } + + /** + * Configure the properties for which source values must be present in each bind attempt. + * @param propertyPaths the property path expressions + * @see MissingSourceValuesException + */ + public void setRequired(String[] propertyPaths) { + this.requiredProperties = propertyPaths; + } + + public Object getModel() { + return bindingFactory.getModel(); + } + + protected Binding getBinding(String property) { + return bindingFactory.getBinding(property); + } + + // implementing Binder + + public BindingResults bind(Map sourceValues) { + sourceValues = filter(sourceValues); + checkRequired(sourceValues); + ArrayListBindingResults results = new ArrayListBindingResults(sourceValues.size()); + for (Map.Entry sourceValue : sourceValues.entrySet()) { + try { + Binding binding = getBinding(sourceValue.getKey()); + results.add(bind(sourceValue, binding)); + } catch (PropertyNotFoundException e) { + results.add(new PropertyNotFoundResult(sourceValue.getKey(), sourceValue.getValue(), messageSource)); + } + } + return results; + } + + // subclassing hooks + + /** + * Hook subclasses may use to filter the source values to bind. + * This hook allows the binder to pre-process the source values before binding occurs. + * For example, a Binder might insert empty or default values for fields that are not present. + * As another example, a Binder might collapse multiple source values into a single source value. + * @param sourceValues the original source values map provided by the caller + * @return the filtered source values map that will be used to bind + */ + protected Map filter(Map sourceValues) { + return sourceValues; + } + + // internal helpers + + private void checkRequired(Map sourceValues) { + if (requiredProperties == null) { + return; + } + List missingRequired = new ArrayList(); + for (String required : requiredProperties) { + boolean found = false; + for (String property : sourceValues.keySet()) { + if (property.equals(required)) { + found = true; + } + } + if (!found) { + missingRequired.add(required); + } + } + if (!missingRequired.isEmpty()) { + throw new MissingSourceValuesException(missingRequired, sourceValues); + } + } + + private BindingResult bind(Map.Entry sourceValue, Binding binding) { + String property = sourceValue.getKey(); + Object value = sourceValue.getValue(); + if (!binding.isEditable()) { + return new PropertyNotEditableResult(property, value, messageSource); + } else { + binding.applySourceValue(value); + if (binding.getStatus() == BindingStatus.DIRTY) { + binding.commit(); + } + return new BindingStatusResult(property, value, binding.getStatusAlert()); + } + } + +} \ No newline at end of file diff --git a/org.springframework.context/src/main/java/org/springframework/ui/binding/MissingSourceValuesException.java b/org.springframework.context/src/main/java/org/springframework/ui/binding/binder/MissingSourceValuesException.java similarity index 97% rename from org.springframework.context/src/main/java/org/springframework/ui/binding/MissingSourceValuesException.java rename to org.springframework.context/src/main/java/org/springframework/ui/binding/binder/MissingSourceValuesException.java index ad5a65fd889..9ba6db0084a 100644 --- a/org.springframework.context/src/main/java/org/springframework/ui/binding/MissingSourceValuesException.java +++ b/org.springframework.context/src/main/java/org/springframework/ui/binding/binder/MissingSourceValuesException.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.ui.binding; +package org.springframework.ui.binding.binder; import java.util.List; import java.util.Map; diff --git a/org.springframework.context/src/main/java/org/springframework/ui/binding/support/PropertyNotEditableResult.java b/org.springframework.context/src/main/java/org/springframework/ui/binding/binder/PropertyNotEditableResult.java similarity index 69% rename from org.springframework.context/src/main/java/org/springframework/ui/binding/support/PropertyNotEditableResult.java rename to org.springframework.context/src/main/java/org/springframework/ui/binding/binder/PropertyNotEditableResult.java index a5db886ea1f..201411f5b9b 100644 --- a/org.springframework.context/src/main/java/org/springframework/ui/binding/support/PropertyNotEditableResult.java +++ b/org.springframework.context/src/main/java/org/springframework/ui/binding/binder/PropertyNotEditableResult.java @@ -1,13 +1,24 @@ -/** - * +/* + * 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.ui.binding.support; +package org.springframework.ui.binding.binder; import org.springframework.context.MessageSource; import org.springframework.core.style.StylerUtils; import org.springframework.ui.alert.Alert; import org.springframework.ui.alert.Severity; -import org.springframework.ui.binding.BindingResult; import org.springframework.ui.message.MessageBuilder; import org.springframework.ui.message.ResolvableArgument; diff --git a/org.springframework.context/src/main/java/org/springframework/ui/binding/support/PropertyNotFoundResult.java b/org.springframework.context/src/main/java/org/springframework/ui/binding/binder/PropertyNotFoundResult.java similarity index 69% rename from org.springframework.context/src/main/java/org/springframework/ui/binding/support/PropertyNotFoundResult.java rename to org.springframework.context/src/main/java/org/springframework/ui/binding/binder/PropertyNotFoundResult.java index 055ac75fc5d..7115e04eed8 100644 --- a/org.springframework.context/src/main/java/org/springframework/ui/binding/support/PropertyNotFoundResult.java +++ b/org.springframework.context/src/main/java/org/springframework/ui/binding/binder/PropertyNotFoundResult.java @@ -1,10 +1,24 @@ -package org.springframework.ui.binding.support; +/* + * 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.ui.binding.binder; import org.springframework.context.MessageSource; import org.springframework.core.style.StylerUtils; import org.springframework.ui.alert.Alert; import org.springframework.ui.alert.Severity; -import org.springframework.ui.binding.BindingResult; import org.springframework.ui.message.MessageBuilder; import org.springframework.ui.message.ResolvableArgument; diff --git a/org.springframework.context/src/main/java/org/springframework/ui/binding/support/WebBinder.java b/org.springframework.context/src/main/java/org/springframework/ui/binding/binder/WebBinder.java similarity index 89% rename from org.springframework.context/src/main/java/org/springframework/ui/binding/support/WebBinder.java rename to org.springframework.context/src/main/java/org/springframework/ui/binding/binder/WebBinder.java index 3680cb01d05..a8a9dc121a0 100644 --- a/org.springframework.context/src/main/java/org/springframework/ui/binding/support/WebBinder.java +++ b/org.springframework.context/src/main/java/org/springframework/ui/binding/binder/WebBinder.java @@ -13,11 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.ui.binding.support; +package org.springframework.ui.binding.binder; import java.util.LinkedHashMap; import java.util.Map; +import org.springframework.ui.binding.Binding; +import org.springframework.ui.binding.BindingFactory; + /** * A binder designed for use in HTTP (web) environments. * Suited for binding user-provided HTTP query parameters to model properties. @@ -36,8 +39,8 @@ public class WebBinder extends GenericBinder { * Creates a new web binder for the model object. * @param model the model object containing properties this binder will bind to */ - public WebBinder(Object model) { - super(model); + public WebBinder(BindingFactory bindingFactory) { + super(bindingFactory); } /** @@ -72,7 +75,7 @@ public class WebBinder extends GenericBinder { } else if (field.startsWith(presentPrefix)) { field = field.substring(presentPrefix.length()); if (!sourceValues.containsKey(field) && !sourceValues.containsKey(defaultPrefix + field)) { - value = getEmptyValue((PropertyBinding) getBinding(field)); + value = getEmptyValue(getBinding(field)); filteredValues.put(field, value); } } else { @@ -82,8 +85,8 @@ public class WebBinder extends GenericBinder { return filteredValues; } - protected Object getEmptyValue(PropertyBinding binding) { - Class type = binding.getValueModel().getValueType(); + protected Object getEmptyValue(Binding binding) { + Class type = binding.getValueType(); if (boolean.class.equals(type) || Boolean.class.equals(type)) { return Boolean.FALSE; } else { diff --git a/org.springframework.context/src/main/java/org/springframework/ui/binding/config/Condition.java b/org.springframework.context/src/main/java/org/springframework/ui/binding/config/Condition.java index 7efde4bb2df..2d800fc1a2b 100644 --- a/org.springframework.context/src/main/java/org/springframework/ui/binding/config/Condition.java +++ b/org.springframework.context/src/main/java/org/springframework/ui/binding/config/Condition.java @@ -1,3 +1,18 @@ +/* + * 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.ui.binding.config; public interface Condition { diff --git a/org.springframework.context/src/main/java/org/springframework/ui/binding/support/BindingContext.java b/org.springframework.context/src/main/java/org/springframework/ui/binding/support/BindingContext.java new file mode 100644 index 00000000000..b976f14804b --- /dev/null +++ b/org.springframework.context/src/main/java/org/springframework/ui/binding/support/BindingContext.java @@ -0,0 +1,50 @@ +/* + * 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.ui.binding.support; + +import org.springframework.context.MessageSource; +import org.springframework.core.convert.TypeConverter; +import org.springframework.ui.binding.Binding; +import org.springframework.ui.binding.config.Condition; +import org.springframework.ui.format.Formatter; + +public interface BindingContext { + + MessageSource getMessageSource(); + + TypeConverter getTypeConverter(); + + Condition getEditableCondition(); + + Condition getEnabledCondition(); + + Condition getVisibleCondition(); + + Binding getBinding(String property); + + Formatter getFormatter(); + + Formatter getElementFormatter(); + + Formatter getKeyFormatter(); + + Binding getListElementBinding(int index); + + Binding getMapValueBinding(Object key); + + String getLabel(); + +} \ No newline at end of file diff --git a/org.springframework.context/src/main/java/org/springframework/ui/binding/support/BindingRule.java b/org.springframework.context/src/main/java/org/springframework/ui/binding/support/BindingRule.java index 4e344ab715a..86cd9d3cab3 100644 --- a/org.springframework.context/src/main/java/org/springframework/ui/binding/support/BindingRule.java +++ b/org.springframework.context/src/main/java/org/springframework/ui/binding/support/BindingRule.java @@ -1,3 +1,18 @@ +/* + * 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.ui.binding.support; import org.springframework.ui.binding.config.Condition; diff --git a/org.springframework.context/src/main/java/org/springframework/ui/binding/support/CollectionTypeDescriptor.java b/org.springframework.context/src/main/java/org/springframework/ui/binding/support/CollectionTypeDescriptor.java index cfb1b69697c..ebb4555b5c3 100644 --- a/org.springframework.context/src/main/java/org/springframework/ui/binding/support/CollectionTypeDescriptor.java +++ b/org.springframework.context/src/main/java/org/springframework/ui/binding/support/CollectionTypeDescriptor.java @@ -1,5 +1,17 @@ -/** - * +/* + * 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.ui.binding.support; diff --git a/org.springframework.context/src/main/java/org/springframework/ui/binding/support/DefaultFormatter.java b/org.springframework.context/src/main/java/org/springframework/ui/binding/support/DefaultFormatter.java index 3ace9aba2a2..948a16f3e55 100644 --- a/org.springframework.context/src/main/java/org/springframework/ui/binding/support/DefaultFormatter.java +++ b/org.springframework.context/src/main/java/org/springframework/ui/binding/support/DefaultFormatter.java @@ -1,54 +1,31 @@ -/** - * +/* + * 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.ui.binding.support; -import java.lang.reflect.Array; import java.text.ParseException; -import java.util.Collection; -import java.util.Iterator; import java.util.Locale; import org.springframework.ui.format.Formatter; @SuppressWarnings("unchecked") +// TODO should this delegate to type converter... class DefaultFormatter implements Formatter { public static final Formatter INSTANCE = new DefaultFormatter(); - public static final Formatter COLLECTION_FORMATTER = new Formatter() { - public String format(Object object, Locale locale) { - if (object == null) { - return ""; - } else { - StringBuffer buffer = new StringBuffer(); - if (object.getClass().isArray()) { - int length = Array.getLength(object); - for (int i = 0; i < length; i++) { - buffer.append(INSTANCE.format(Array.get(object, i), locale)); - if (i < length - 1) { - buffer.append(","); - } - } - } else if (Collection.class.isAssignableFrom(object.getClass())) { - Collection c = (Collection) object; - for (Iterator it = c.iterator(); it.hasNext();) { - buffer.append(INSTANCE.format(it.next(), locale)); - if (it.hasNext()) { - buffer.append(","); - } - } - } - return buffer.toString(); - } - } - - public Object parse(String formatted, Locale locale) throws ParseException { - throw new UnsupportedOperationException("Not yet implemented"); - } - - }; - public String format(Object object, Locale locale) { if (object == null) { return ""; diff --git a/org.springframework.context/src/main/java/org/springframework/ui/binding/support/AbstractBinding.java b/org.springframework.context/src/main/java/org/springframework/ui/binding/support/GenericBinding.java similarity index 74% rename from org.springframework.context/src/main/java/org/springframework/ui/binding/support/AbstractBinding.java rename to org.springframework.context/src/main/java/org/springframework/ui/binding/support/GenericBinding.java index 3b7a1c86360..d1a6a17f7af 100644 --- a/org.springframework.context/src/main/java/org/springframework/ui/binding/support/AbstractBinding.java +++ b/org.springframework.context/src/main/java/org/springframework/ui/binding/support/GenericBinding.java @@ -1,11 +1,27 @@ +/* + * 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.ui.binding.support; -import java.lang.reflect.Array; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.text.ParseException; +import java.util.ArrayList; import java.util.Collection; +import java.util.LinkedHashMap; import java.util.List; import java.util.Locale; import java.util.Map; @@ -19,13 +35,14 @@ import org.springframework.core.style.StylerUtils; import org.springframework.ui.alert.Alert; import org.springframework.ui.alert.Severity; import org.springframework.ui.binding.Binding; -import org.springframework.ui.binding.support.GenericBinder.BindingContext; import org.springframework.ui.format.Formatter; import org.springframework.ui.message.MessageBuilder; import org.springframework.ui.message.ResolvableArgument; -public abstract class AbstractBinding implements Binding { +public class GenericBinding implements Binding { + private ValueModel valueModel; + private BindingContext bindingContext; private ValueBuffer buffer; @@ -33,17 +50,18 @@ public abstract class AbstractBinding implements Binding { private BindingStatus status; private Object sourceValue; - + private Exception invalidSourceValueCause; - - public AbstractBinding(BindingContext bindingContext) { + + public GenericBinding(ValueModel valueModel, BindingContext bindingContext) { + this.valueModel = valueModel; this.bindingContext = bindingContext; - buffer = new ValueBuffer(getValueModel()); - status = BindingStatus.CLEAN; + buffer = new ValueBuffer(valueModel); + status = BindingStatus.CLEAN; } // implementing Binding - + public String getRenderValue() { return format(getValue(), bindingContext.getFormatter()); } @@ -52,16 +70,16 @@ public abstract class AbstractBinding implements Binding { if (status == BindingStatus.DIRTY || status == BindingStatus.COMMIT_FAILURE) { return buffer.getValue(); } else { - return getValueModel().getValue(); + return valueModel.getValue(); } } public Class getValueType() { - return getValueModel().getValueType(); + return valueModel.getValueType(); } public boolean isEditable() { - return bindingContext.getEditableCondition().isTrue(); + return valueModel.isWriteable() && bindingContext.getEditableCondition().isTrue(); } public boolean isEnabled() { @@ -91,23 +109,44 @@ public abstract class AbstractBinding implements Binding { status = BindingStatus.INVALID_SOURCE_VALUE; } } else if (sourceValue instanceof String[]) { - String[] sourceValues = (String[]) sourceValue; - Class parsedType = getFormattedObjectType(bindingContext.getElementFormatter().getClass()); - if (parsedType == null) { - parsedType = String.class; - } - Object parsed = Array.newInstance(parsedType, sourceValues.length); - for (int i = 0; i < sourceValues.length; i++) { - Object parsedValue; - try { - parsedValue = bindingContext.getElementFormatter().parse(sourceValues[i], getLocale()); - Array.set(parsed, i, parsedValue); - } catch (ParseException e) { - this.sourceValue = sourceValue; - invalidSourceValueCause = e; - status = BindingStatus.INVALID_SOURCE_VALUE; - break; + Object parsed; + if (isMap()) { + String[] sourceValues = (String[]) sourceValue; + Formatter keyFormatter = bindingContext.getKeyFormatter(); + Formatter valueFormatter = bindingContext.getElementFormatter(); + Map map = new LinkedHashMap(sourceValues.length); + for (int i = 0; i < sourceValues.length; i++) { + String entryString = sourceValues[i]; + Object parsedValue; + try { + String[] keyValue = entryString.split("="); + Object parsedMapKey = keyFormatter.parse(keyValue[0], getLocale()); + Object parsedMapValue = valueFormatter.parse(keyValue[1], getLocale()); + map.put(parsedMapKey, parsedMapValue); + } catch (ParseException e) { + this.sourceValue = sourceValue; + invalidSourceValueCause = e; + status = BindingStatus.INVALID_SOURCE_VALUE; + break; + } } + parsed = map; + } else { + String[] sourceValues = (String[]) sourceValue; + List list = new ArrayList(sourceValues.length); + for (int i = 0; i < sourceValues.length; i++) { + Object parsedValue; + try { + parsedValue = bindingContext.getElementFormatter().parse(sourceValues[i], getLocale()); + list.add(parsedValue); + } catch (ParseException e) { + this.sourceValue = sourceValue; + invalidSourceValueCause = e; + status = BindingStatus.INVALID_SOURCE_VALUE; + break; + } + } + parsed = list; } if (status != BindingStatus.INVALID_SOURCE_VALUE) { try { @@ -150,7 +189,7 @@ public abstract class AbstractBinding implements Binding { public String getCode() { return "typeMismatch"; } - + public String getMessage() { MessageBuilder builder = new MessageBuilder(bindingContext.getMessageSource()); builder.code(getCode()); @@ -168,11 +207,11 @@ public abstract class AbstractBinding implements Binding { builder.defaultMessage("Failed to bind '" + bindingContext.getLabel() + "'; the source value " + StylerUtils.style(sourceValue) + " has could not be converted to " + e.getTargetType().getName()); - + } return builder.build(); } - + public Severity getSeverity() { return Severity.ERROR; } @@ -182,12 +221,12 @@ public abstract class AbstractBinding implements Binding { public String getCode() { return "internalError"; } - + public String getMessage() { buffer.getFlushException().printStackTrace(); return "Internal error occurred; message = [" + buffer.getFlushException().getMessage() + "]"; } - + public Severity getSeverity() { return Severity.FATAL; } @@ -197,11 +236,11 @@ public abstract class AbstractBinding implements Binding { public String getCode() { return "bindSuccess"; } - + public String getMessage() { return "Binding successful"; } - + public Severity getSeverity() { return Severity.INFO; } @@ -256,6 +295,13 @@ public abstract class AbstractBinding implements Binding { } public Binding getMapValueBinding(Object key) { + if (key instanceof String) { + try { + key = bindingContext.getKeyFormatter().parse((String) key, getLocale()); + } catch (ParseException e) { + throw new IllegalArgumentException("Unable to parse map key '" + key + "'", e); + } + } return bindingContext.getMapValueBinding(key); } @@ -263,31 +309,25 @@ public abstract class AbstractBinding implements Binding { public String formatValue(Object value) { Formatter formatter; if (Collection.class.isAssignableFrom(getValueType()) || getValueType().isArray() || isMap()) { - formatter = getBindingContext().getElementFormatter(); + formatter = bindingContext.getElementFormatter(); } else { - formatter = getBindingContext().getFormatter(); + formatter = bindingContext.getFormatter(); } return format(value, formatter); } - - // subclassing hooks - protected BindingContext getBindingContext() { - return bindingContext; - } - - protected abstract ValueModel getValueModel(); + // internal helpers - protected Locale getLocale() { - return LocaleContextHolder.getLocale(); - } - - protected String format(Object value, Formatter formatter) { + private String format(Object value, Formatter formatter) { Class formattedType = getFormattedObjectType(formatter.getClass()); value = bindingContext.getTypeConverter().convert(value, formattedType); return formatter.format(value, getLocale()); } + private Locale getLocale() { + return LocaleContextHolder.getLocale(); + } + private Class getFormattedObjectType(Class formatterClass) { Class classToIntrospect = formatterClass; while (classToIntrospect != null) { @@ -317,7 +357,7 @@ public abstract class AbstractBinding implements Binding { } private Object coerseToValueType(Object parsed) { - TypeDescriptor targetType = getValueModel().getValueTypeDescriptor(); + TypeDescriptor targetType = valueModel.getValueTypeDescriptor(); TypeConverter converter = bindingContext.getTypeConverter(); if (parsed != null && converter.canConvert(parsed.getClass(), targetType)) { return converter.convert(parsed, targetType); @@ -338,39 +378,10 @@ public abstract class AbstractBinding implements Binding { } } - // internal helpers - static abstract class AbstractAlert implements Alert { public String toString() { return getCode() + " - " + getMessage(); } } - /** - * For accessing the raw bound model object. - * @author Keith Donald - */ - public interface ValueModel { - - /** - * The model value. - */ - Object getValue(); - - /** - * The model value type. - */ - Class getValueType(); - - /** - * The model value type descriptor. - */ - TypeDescriptor getValueTypeDescriptor(); - - /** - * Set the model value. - */ - void setValue(Object value); - } - } diff --git a/org.springframework.context/src/main/java/org/springframework/ui/binding/support/GenericBinder.java b/org.springframework.context/src/main/java/org/springframework/ui/binding/support/GenericBindingFactory.java similarity index 64% rename from org.springframework.context/src/main/java/org/springframework/ui/binding/support/GenericBinder.java rename to org.springframework.context/src/main/java/org/springframework/ui/binding/support/GenericBindingFactory.java index 33ae2fd6a50..c9a3bca8e26 100644 --- a/org.springframework.context/src/main/java/org/springframework/ui/binding/support/GenericBinder.java +++ b/org.springframework.context/src/main/java/org/springframework/ui/binding/support/GenericBindingFactory.java @@ -21,19 +21,19 @@ import java.beans.Introspector; import java.beans.PropertyDescriptor; import java.util.ArrayList; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import org.codehaus.groovy.binding.PropertyBinding; import org.springframework.context.MessageSource; import org.springframework.core.GenericCollectionTypeResolver; import org.springframework.core.convert.TypeConverter; import org.springframework.core.convert.support.DefaultTypeConverter; -import org.springframework.ui.binding.Binder; import org.springframework.ui.binding.Binding; -import org.springframework.ui.binding.BindingResult; -import org.springframework.ui.binding.BindingResults; -import org.springframework.ui.binding.MissingSourceValuesException; -import org.springframework.ui.binding.Binding.BindingStatus; +import org.springframework.ui.binding.BindingFactory; +import org.springframework.ui.binding.binder.Binder; +import org.springframework.ui.binding.binder.BindingResult; import org.springframework.ui.binding.config.BindingRuleConfiguration; import org.springframework.ui.binding.config.Condition; import org.springframework.ui.format.Formatter; @@ -47,7 +47,7 @@ import org.springframework.util.Assert; * @see #setTypeConverter(TypeConverter) * @see #bind(Map) */ -public class GenericBinder implements Binder { +public class GenericBindingFactory implements BindingFactory { private Object model; @@ -59,13 +59,11 @@ public class GenericBinder implements Binder { private MessageSource messageSource; - private String[] requiredProperties = new String[0]; - /** * Creates a new binder for the model object. * @param model the model object containing properties this binder will bind to */ - public GenericBinder(Object model) { + public GenericBindingFactory(Object model) { Assert.notNull(model, "The model to bind to is required"); this.model = model; bindingRules = new HashMap(); @@ -104,9 +102,9 @@ public class GenericBinder implements Binder { } /** - * + * Add a new binding rule for the property at the path specified. * @param propertyPath binding rule property path in format prop.nestedProp - * @return + * @return a builder for the binding rule */ public BindingRuleConfiguration bindingRule(String propertyPath) { PropertyPath path = new PropertyPath(propertyPath); @@ -142,55 +140,6 @@ public class GenericBinder implements Binder { return binding; } - public BindingResults bind(Map sourceValues) { - sourceValues = filter(sourceValues); - checkRequired(sourceValues); - ArrayListBindingResults results = new ArrayListBindingResults(sourceValues.size()); - for (Map.Entry sourceValue : sourceValues.entrySet()) { - try { - Binding binding = getBinding(sourceValue.getKey()); - results.add(bind(sourceValue, binding)); - } catch (PropertyNotFoundException e) { - results.add(new PropertyNotFoundResult(sourceValue.getKey(), sourceValue.getValue(), messageSource)); - } - } - return results; - } - - // subclassing hooks - - /** - * Hook subclasses may use to filter the source values to bind. - * This hook allows the binder to pre-process the source values before binding occurs. - * For example, a Binder might insert empty or default values for fields that are not present. - * As another example, a Binder might collapse multiple source values into a single source value. - * @param sourceValues the original source values map provided by the caller - * @return the filtered source values map that will be used to bind - */ - protected Map filter(Map sourceValues) { - return sourceValues; - } - - // internal helpers - - private void checkRequired(Map sourceValues) { - List missingRequired = new ArrayList(); - for (String required : requiredProperties) { - boolean found = false; - for (String property : sourceValues.keySet()) { - if (property.equals(required)) { - found = true; - } - } - if (!found) { - missingRequired.add(required); - } - } - if (!missingRequired.isEmpty()) { - throw new MissingSourceValuesException(missingRequired, sourceValues); - } - } - private GenericBindingRule getBindingRule(String property) { GenericBindingRule rule = bindingRules.get(property); if (rule == null) { @@ -200,20 +149,6 @@ public class GenericBinder implements Binder { return rule; } - private BindingResult bind(Map.Entry sourceValue, Binding binding) { - String property = sourceValue.getKey(); - Object value = sourceValue.getValue(); - if (!binding.isEditable()) { - return new PropertyNotEditableResult(property, value, messageSource); - } else { - binding.applySourceValue(value); - if (binding.getStatus() == BindingStatus.DIRTY) { - binding.commit(); - } - return new BindingStatusResult(property, value, binding.getStatusAlert()); - } - } - @SuppressWarnings("unchecked") class GenericBindingRule implements BindingRuleConfiguration, BindingContext { @@ -223,10 +158,10 @@ public class GenericBinder implements Binder { private Formatter formatter; - private Formatter elementFormatter; - private Formatter keyFormatter; + private Formatter elementFormatter; + private Condition editableCondition = Condition.ALWAYS_TRUE; private Condition enabledCondition = Condition.ALWAYS_TRUE; @@ -239,6 +174,8 @@ public class GenericBinder implements Binder { private Map listElementBindings; + private Map mapValueBindings; + public GenericBindingRule(String property, Class modelClass) { this.modelClass = modelClass; this.property = findPropertyDescriptor(property); @@ -262,14 +199,6 @@ public class GenericBinder implements Binder { } } - public Formatter getElementFormatter() { - if (elementFormatter != null) { - return formatter; - } else { - return formatterRegistry.getFormatter(getElementType()); - } - } - public Formatter getKeyFormatter() { if (keyFormatter != null) { return keyFormatter; @@ -278,6 +207,14 @@ public class GenericBinder implements Binder { } } + public Formatter getElementFormatter() { + if (elementFormatter != null) { + return formatter; + } else { + return formatterRegistry.getFormatter(getElementType()); + } + } + public Condition getEnabledCondition() { return enabledCondition; } @@ -295,79 +232,34 @@ public class GenericBinder implements Binder { return getBindingRule(property, binding.getValueType()).getBinding(binding.getValue()); } - public Binding getListElementBinding(final int index) { + public Binding getListElementBinding(int index) { if (listElementBindings == null) { listElementBindings = new HashMap(); } growListIfNecessary(index); Binding binding = listElementBindings.get(index); if (binding == null) { - final Map nestedBindings = new HashMap(); - BindingContext listContext = new BindingContext() { - public MessageSource getMessageSource() { - return GenericBindingRule.this.getMessageSource(); - } - - public TypeConverter getTypeConverter() { - return GenericBindingRule.this.getTypeConverter(); - } - - public Binding getBinding(String property) { - GenericBindingRule rule = GenericBindingRule.this.getBindingRule(property, getElementType()); - Object model = ((List) GenericBindingRule.this.binding.getValue()).get(index); - Binding binding = nestedBindings.get(property); - if (binding == null) { - binding = new PropertyBinding(rule.property, model, rule); - nestedBindings.put(property, binding); - } - return binding; - } - - public Formatter getFormatter() { - return GenericBindingRule.this.getElementFormatter(); - } - - public Formatter getElementFormatter() { - return null; - } - - public Formatter getKeyFormatter() { - return null; - } - - public Condition getEditableCondition() { - return GenericBindingRule.this.getEditableCondition(); - } - - public Condition getEnabledCondition() { - return GenericBindingRule.this.getEnabledCondition(); - } - - public Condition getVisibleCondition() { - return GenericBindingRule.this.getVisibleCondition(); - } - - public Binding getListElementBinding(int index) { - throw new IllegalArgumentException("Not yet supported"); - } - - public Binding getMapValueBinding(Object key) { - throw new IllegalArgumentException("Not yet supported"); - } - - public String getLabel() { - return GenericBindingRule.this.getLabel() + "[" + index + "]"; - } - - }; - binding = new ListElementBinding(index, getElementType(), (List) this.binding.getValue(), listContext); + BindingContext bindingContext = new ListElementBindingContext(index, this); + ValueModel valueModel = new ListElementValueModel(index, getElementType(), (List) this.binding.getValue()); + binding = new GenericBinding(valueModel, bindingContext); listElementBindings.put(index, binding); } return binding; } public Binding getMapValueBinding(Object key) { - return null; + if (mapValueBindings == null) { + mapValueBindings = new HashMap(); + } + createMapValueIfNecessary(); + Binding binding = mapValueBindings.get(key); + if (binding == null) { + BindingContext bindingContext = new MapValueBindingContext(key, this); + ValueModel valueModel = new MapValueValueModel(key, getElementType(), (Map) this.binding.getValue(), bindingContext); + binding = new GenericBinding(valueModel, bindingContext); + mapValueBindings.put(key, binding); + } + return binding; } public String getLabel() { @@ -416,7 +308,11 @@ public class GenericBinder implements Binder { // internal helpers private Class getElementType() { - return GenericCollectionTypeResolver.getCollectionReturnType(property.getReadMethod()); + if (Map.class.isAssignableFrom(property.getPropertyType())) { + return GenericCollectionTypeResolver.getMapValueReturnType(property.getReadMethod()); + } else { + return GenericCollectionTypeResolver.getCollectionReturnType(property.getReadMethod()); + } } private Class getKeyType() { @@ -443,7 +339,8 @@ public class GenericBinder implements Binder { Binding getBinding(Object model) { if (binding == null) { - binding = new PropertyBinding(property, model, this); + PropertyValueModel valueModel = new PropertyValueModel(property, model); + binding = new GenericBinding(valueModel, this); } return binding; } @@ -475,6 +372,15 @@ public class GenericBinder implements Binder { } } + private void createMapValueIfNecessary() { + Object value = binding.getValue(); + if (value == null) { + value = newMapValue(binding.getValueType()); + binding.applySourceValue(value); + binding.commit(); + } + } + private void growListIfNecessary(int index) { List list = (List) binding.getValue(); if (list == null) { @@ -490,6 +396,14 @@ public class GenericBinder implements Binder { } } + private Map newMapValue(Class type) { + if (type.isInterface()) { + return (Map) newValue(LinkedHashMap.class); + } else { + return (Map) newValue(type); + } + } + private List newListValue(Class type) { if (type.isInterface()) { return (List) newValue(ArrayList.class); @@ -510,36 +424,153 @@ public class GenericBinder implements Binder { } - public interface BindingContext { + private static class ListElementBindingContext implements BindingContext { + + private int index; - MessageSource getMessageSource(); + private GenericBindingRule listBindingContext; + + final Map nestedBindings = new HashMap(); + + public ListElementBindingContext(int index, GenericBindingRule listBindingContext) { + this.index = index; + this.listBindingContext = listBindingContext; + } + + public MessageSource getMessageSource() { + return listBindingContext.getMessageSource(); + } - TypeConverter getTypeConverter(); + public TypeConverter getTypeConverter() { + return listBindingContext.getTypeConverter(); + } - Condition getEditableCondition(); + public Binding getBinding(String property) { + Object model = ((List) listBindingContext.binding.getValue()).get(index); + Class elementType = listBindingContext.getElementType(); + if (elementType == null) { + elementType = model.getClass(); + } + GenericBindingRule rule = listBindingContext.getBindingRule(property, elementType); + Binding binding = nestedBindings.get(property); + if (binding == null) { + PropertyValueModel valueModel = new PropertyValueModel(rule.property, model); + binding = new GenericBinding(valueModel, rule); + nestedBindings.put(property, binding); + } + return binding; + } - Condition getEnabledCondition(); + public Formatter getFormatter() { + return listBindingContext.getElementFormatter(); + } - Condition getVisibleCondition(); + public Formatter getElementFormatter() { + return null; + } - Binding getBinding(String property); + public Formatter getKeyFormatter() { + return null; + } - Formatter getFormatter(); + public Condition getEditableCondition() { + return listBindingContext.getEditableCondition(); + } - Formatter getElementFormatter(); + public Condition getEnabledCondition() { + return listBindingContext.getEnabledCondition(); + } - Formatter getKeyFormatter(); + public Condition getVisibleCondition() { + return listBindingContext.getVisibleCondition(); + } - Binding getListElementBinding(int index); + public Binding getListElementBinding(int index) { + throw new IllegalArgumentException("Not yet supported"); + } - Binding getMapValueBinding(Object key); + public Binding getMapValueBinding(Object key) { + throw new IllegalArgumentException("Not yet supported"); + } - String getLabel(); + public String getLabel() { + return listBindingContext.getLabel() + "[" + index + "]"; + } - } + }; + + private static class MapValueBindingContext implements BindingContext { + + private Object key; - public void setRequired(String[] propertyPaths) { - this.requiredProperties = propertyPaths; - } + private GenericBindingRule mapBindingContext; + + final Map nestedBindings = new HashMap(); + + public MapValueBindingContext(Object key, GenericBindingRule mapBindingContext) { + this.key = key; + this.mapBindingContext = mapBindingContext; + } + + public MessageSource getMessageSource() { + return mapBindingContext.getMessageSource(); + } + public TypeConverter getTypeConverter() { + return mapBindingContext.getTypeConverter(); + } + + public Binding getBinding(String property) { + Object model = ((Map) mapBindingContext.binding.getValue()).get(key); + Class elementType = mapBindingContext.getElementType(); + if (elementType == null) { + elementType = model.getClass(); + } + GenericBindingRule rule = mapBindingContext.getBindingRule(property, elementType); + Binding binding = nestedBindings.get(property); + if (binding == null) { + PropertyValueModel valueModel = new PropertyValueModel(rule.property, model); + binding = new GenericBinding(valueModel, rule); + nestedBindings.put(property, binding); + } + return binding; + } + + public Formatter getFormatter() { + return mapBindingContext.getElementFormatter(); + } + + public Formatter getElementFormatter() { + return null; + } + + public Formatter getKeyFormatter() { + return null; + } + + public Condition getEditableCondition() { + return mapBindingContext.getEditableCondition(); + } + + public Condition getEnabledCondition() { + return mapBindingContext.getEnabledCondition(); + } + + public Condition getVisibleCondition() { + return mapBindingContext.getVisibleCondition(); + } + + public Binding getListElementBinding(int index) { + throw new IllegalArgumentException("Not yet supported"); + } + + public Binding getMapValueBinding(Object key) { + throw new IllegalArgumentException("Not yet supported"); + } + + public String getLabel() { + return mapBindingContext.getLabel() + "[" + key + "]"; + } + + }; } diff --git a/org.springframework.context/src/main/java/org/springframework/ui/binding/support/GenericFormatterRegistry.java b/org.springframework.context/src/main/java/org/springframework/ui/binding/support/GenericFormatterRegistry.java index 8c76fb1b357..a56ce13aa65 100644 --- a/org.springframework.context/src/main/java/org/springframework/ui/binding/support/GenericFormatterRegistry.java +++ b/org.springframework.context/src/main/java/org/springframework/ui/binding/support/GenericFormatterRegistry.java @@ -17,20 +17,31 @@ package org.springframework.ui.binding.support; import java.beans.PropertyDescriptor; import java.lang.annotation.Annotation; +import java.lang.reflect.Array; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; +import java.text.ParseException; +import java.util.Collection; import java.util.HashMap; +import java.util.Iterator; +import java.util.Locale; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.springframework.core.GenericTypeResolver; import org.springframework.core.MethodParameter; import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.core.convert.ConversionFailedException; +import org.springframework.core.convert.TypeConverter; import org.springframework.core.convert.TypeDescriptor; +import org.springframework.core.convert.support.ConversionUtils; +import org.springframework.core.convert.support.DefaultTypeConverter; import org.springframework.ui.format.AnnotationFormatterFactory; import org.springframework.ui.format.Formatted; import org.springframework.ui.format.Formatter; +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; /** * A generic implementation of {@link FormatterRegistry} suitable for use in most binding environments. @@ -48,7 +59,14 @@ public class GenericFormatterRegistry implements FormatterRegistry { private Map annotationFormatters = new HashMap(); + private TypeConverter typeConverter = new DefaultTypeConverter(); + + public void setTypeConverter(TypeConverter typeConverter) { + this.typeConverter = typeConverter; + } + public Formatter getFormatter(PropertyDescriptor property) { + Assert.notNull(property, "The PropertyDescriptor is required"); TypeDescriptor propertyType = new TypeDescriptor(new MethodParameter(property.getReadMethod(), -1)); Annotation[] annotations = propertyType.getAnnotations(); for (Annotation a : annotations) { @@ -60,19 +78,22 @@ public class GenericFormatterRegistry implements FormatterRegistry { Formatter formatter = null; Class type; if (propertyType.isCollection() || propertyType.isArray()) { - formatter = collectionTypeFormatters.get(new GenericCollectionPropertyType(propertyType.getType(), propertyType.getElementType())); + GenericCollectionPropertyType collectionType = new GenericCollectionPropertyType(propertyType.getType(), + propertyType.getElementType()); + formatter = collectionTypeFormatters.get(collectionType); if (formatter != null) { return formatter; } else { - return DefaultFormatter.COLLECTION_FORMATTER; + return new DefaultCollectionFormatter(collectionType, this); } } else { type = propertyType.getType(); } return getFormatter(type); } - + public Formatter getFormatter(Class type) { + Assert.notNull(type, "The Class of the object to format is required"); Formatter formatter = typeFormatters.get(type); if (formatter != null) { return formatter; @@ -83,20 +104,20 @@ public class GenericFormatterRegistry implements FormatterRegistry { try { formatter = (Formatter) formatterClass.newInstance(); } catch (InstantiationException e) { - // TODO better runtime exception - throw new IllegalStateException(e); + throw new IllegalStateException( + "Formatter referenced by @Formatted annotation does not have default constructor", e); } catch (IllegalAccessException e) { - throw new IllegalStateException(e); + throw new IllegalStateException( + "Formatter referenced by @Formatted annotation does not have public constructor", e); } typeFormatters.put(type, formatter); return formatter; } else { - return DefaultFormatter.INSTANCE; + return new DefaultFormatter(type, typeConverter); } - } + } } - public void add(Class propertyType, Formatter formatter) { if (propertyType.isAnnotation()) { annotationFormatters.put(propertyType, new SimpleAnnotationFormatterFactory(formatter)); @@ -108,13 +129,13 @@ public class GenericFormatterRegistry implements FormatterRegistry { public void add(GenericCollectionPropertyType propertyType, Formatter formatter) { collectionTypeFormatters.put(propertyType, formatter); } - + public void add(AnnotationFormatterFactory factory) { annotationFormatters.put(getAnnotationType(factory), factory); } // internal helpers - + private Class getAnnotationType(AnnotationFormatterFactory factory) { Class classToIntrospect = factory.getClass(); while (classToIntrospect != null) { @@ -133,7 +154,7 @@ public class GenericFormatterRegistry implements FormatterRegistry { "Unable to extract Annotation type A argument from AnnotationFormatterFactory [" + factory.getClass().getName() + "]; does the factory parameterize the generic type?"); } - + private Class getParameterClass(Type parameterType, Class converterClass) { if (parameterType instanceof TypeVariable) { parameterType = GenericTypeResolver.resolveTypeVariable((TypeVariable) parameterType, converterClass); @@ -144,7 +165,7 @@ public class GenericFormatterRegistry implements FormatterRegistry { throw new IllegalArgumentException("Unable to obtain the java.lang.Class for parameterType [" + parameterType + "] on Formatter [" + converterClass.getName() + "]"); } - + static class SimpleAnnotationFormatterFactory implements AnnotationFormatterFactory { private Formatter formatter; @@ -159,4 +180,124 @@ public class GenericFormatterRegistry implements FormatterRegistry { } -} + private static class DefaultFormatter implements Formatter { + + public static final Formatter DEFAULT_INSTANCE = new DefaultFormatter(null, null); + + private Class objectType; + + private TypeConverter typeConverter; + + public DefaultFormatter(Class objectType, TypeConverter typeConverter) { + this.objectType = objectType; + this.typeConverter = typeConverter; + } + + public String format(Object object, Locale locale) { + if (object == null) { + return ""; + } else { + if (typeConverter != null && typeConverter.canConvert(object.getClass(), String.class)) { + return typeConverter.convert(object, String.class); + } else { + return object.toString(); + } + } + } + + public Object parse(String formatted, Locale locale) throws ParseException { + if (formatted == "") { + return null; + } else { + if (typeConverter != null && typeConverter.canConvert(String.class, objectType)) { + try { + return typeConverter.convert(formatted, objectType); + } catch (ConversionFailedException e) { + throw new ParseException(formatted, -1); + } + } else { + return formatted; + } + } + } + } + + private static class DefaultCollectionFormatter implements Formatter { + + private GenericCollectionPropertyType collectionType; + + private Formatter elementFormatter; + + public DefaultCollectionFormatter(GenericCollectionPropertyType collectionType, + GenericFormatterRegistry formatterRegistry) { + this.collectionType = collectionType; + this.elementFormatter = collectionType.getElementType() != null ? formatterRegistry + .getFormatter(collectionType.getElementType()) : DefaultFormatter.DEFAULT_INSTANCE; + } + + public String format(Object object, Locale locale) { + if (object == null) { + return ""; + } else { + StringBuffer buffer = new StringBuffer(); + if (object.getClass().isArray()) { + int length = Array.getLength(object); + for (int i = 0; i < length; i++) { + buffer.append(elementFormatter.format(Array.get(object, i), locale)); + if (i < length - 1) { + buffer.append(","); + } + } + } else if (Collection.class.isAssignableFrom(object.getClass())) { + Collection c = (Collection) object; + for (Iterator it = c.iterator(); it.hasNext();) { + buffer.append(elementFormatter.format(it.next(), locale)); + if (it.hasNext()) { + buffer.append(","); + } + } + } + return buffer.toString(); + } + } + + public Object parse(String formatted, Locale locale) throws ParseException { + String[] fields = StringUtils.commaDelimitedListToStringArray(formatted); + if (collectionType.getCollectionType().isArray()) { + Object array = Array.newInstance(getElementType(), fields.length); + for (int i = 0; i < fields.length; i++) { + Array.set(array, i, elementFormatter.parse(fields[i], locale)); + } + return array; + } else { + Collection collection = newCollection(); + for (int i = 0; i < fields.length; i++) { + collection.add(elementFormatter.parse(fields[i], locale)); + } + return collection; + } + } + + private Class getElementType() { + if (collectionType.getElementType() != null) { + return collectionType.getElementType(); + } else { + return String.class; + } + } + + private Collection newCollection() { + try { + Class implType = ConversionUtils + .getCollectionImpl((Class) collectionType.getCollectionType()); + return (Collection) implType.newInstance(); + } catch (InstantiationException e) { + throw new IllegalStateException("Should not happen", e); + } catch (IllegalAccessException e) { + throw new IllegalStateException("Should not happen", e); + } + + } + }; + +} \ No newline at end of file diff --git a/org.springframework.context/src/main/java/org/springframework/ui/binding/support/ListElementBinding.java b/org.springframework.context/src/main/java/org/springframework/ui/binding/support/ListElementBinding.java deleted file mode 100644 index ab55dc6238e..00000000000 --- a/org.springframework.context/src/main/java/org/springframework/ui/binding/support/ListElementBinding.java +++ /dev/null @@ -1,51 +0,0 @@ -package org.springframework.ui.binding.support; - -import java.util.List; - -import org.springframework.core.convert.TypeDescriptor; -import org.springframework.ui.binding.support.GenericBinder.BindingContext; - -public class ListElementBinding extends AbstractBinding { - - private List list; - - private int index; - - private Class elementType; - - private BindingContext bindingContext; - - public ListElementBinding(int index, Class elementType, List list, BindingContext bindingContext) { - super(bindingContext); - this.index = index; - this.elementType = elementType; - this.list = list; - this.bindingContext = bindingContext; - } - - @Override - protected ValueModel getValueModel() { - return new ValueModel() { - public Object getValue() { - return list.get(index); - } - - public Class getValueType() { - if (elementType != null) { - return elementType; - } else { - return getValue().getClass(); - } - } - - public TypeDescriptor getValueTypeDescriptor() { - return TypeDescriptor.valueOf(getValueType()); - } - - public void setValue(Object value) { - list.set(index, value); - } - }; - } - -} \ No newline at end of file diff --git a/org.springframework.context/src/main/java/org/springframework/ui/binding/support/ListElementValueModel.java b/org.springframework.context/src/main/java/org/springframework/ui/binding/support/ListElementValueModel.java new file mode 100644 index 00000000000..28218bdb2ec --- /dev/null +++ b/org.springframework.context/src/main/java/org/springframework/ui/binding/support/ListElementValueModel.java @@ -0,0 +1,59 @@ +/* + * 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.ui.binding.support; + +import java.util.List; + +import org.springframework.core.convert.TypeDescriptor; + +class ListElementValueModel implements ValueModel { + + private List list; + + private int index; + + private Class elementType; + + public ListElementValueModel(int index, Class elementType, List list) { + this.index = index; + this.elementType = elementType; + this.list = list; + } + + public Object getValue() { + return list.get(index); + } + + public Class getValueType() { + if (elementType != null) { + return elementType; + } else { + return getValue().getClass(); + } + } + + public TypeDescriptor getValueTypeDescriptor() { + return TypeDescriptor.valueOf(getValueType()); + } + + public boolean isWriteable() { + return true; + } + + public void setValue(Object value) { + list.set(index, value); + } +} \ No newline at end of file diff --git a/org.springframework.context/src/main/java/org/springframework/ui/binding/support/MapValueValueModel.java b/org.springframework.context/src/main/java/org/springframework/ui/binding/support/MapValueValueModel.java new file mode 100644 index 00000000000..940d2554426 --- /dev/null +++ b/org.springframework.context/src/main/java/org/springframework/ui/binding/support/MapValueValueModel.java @@ -0,0 +1,59 @@ +/* + * 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.ui.binding.support; + +import java.util.Map; + +import org.springframework.core.convert.TypeDescriptor; + +public class MapValueValueModel implements ValueModel { + + private Object key; + + private Class elementType; + + private Map map; + + public MapValueValueModel(Object key, Class elementType, Map map, BindingContext bindingContext) { + this.key = key; + this.elementType = elementType; + this.map = map; + } + + public Object getValue() { + return map.get(key); + } + + public Class getValueType() { + if (elementType != null) { + return elementType; + } else { + return getValue().getClass(); + } + } + + public TypeDescriptor getValueTypeDescriptor() { + return TypeDescriptor.valueOf(getValueType()); + } + + public boolean isWriteable() { + return true; + } + + public void setValue(Object value) { + map.put(key, value); + } +} \ No newline at end of file diff --git a/org.springframework.context/src/main/java/org/springframework/ui/binding/support/PropertyBinding.java b/org.springframework.context/src/main/java/org/springframework/ui/binding/support/PropertyBinding.java deleted file mode 100644 index 0e6aafbbb1d..00000000000 --- a/org.springframework.context/src/main/java/org/springframework/ui/binding/support/PropertyBinding.java +++ /dev/null @@ -1,58 +0,0 @@ -package org.springframework.ui.binding.support; - -import java.beans.PropertyDescriptor; - -import org.springframework.core.MethodParameter; -import org.springframework.core.convert.TypeDescriptor; -import org.springframework.ui.binding.Binding; -import org.springframework.ui.binding.support.GenericBinder.BindingContext; -import org.springframework.util.ReflectionUtils; - -public class PropertyBinding extends AbstractBinding implements Binding { - - private PropertyDescriptor property; - - private Object object; - - public PropertyBinding(PropertyDescriptor property, Object object, BindingContext bindingContext) { - super(bindingContext); - this.property = property; - this.object = object; - } - - // Binding overrides - - @Override - public boolean isEditable() { - return isWriteable() && super.isEditable(); - } - - // implementing - - protected ValueModel getValueModel() { - return new ValueModel() { - public Object getValue() { - return ReflectionUtils.invokeMethod(property.getReadMethod(), object); - } - - public Class getValueType() { - return property.getPropertyType(); - } - - @SuppressWarnings("unchecked") - public TypeDescriptor getValueTypeDescriptor() { - return new TypeDescriptor(new MethodParameter(property.getReadMethod(), -1)); - } - - public void setValue(Object value) { - ReflectionUtils.invokeMethod(property.getWriteMethod(), object, value); - } - }; - } - - // internal helpers - - private boolean isWriteable() { - return property.getWriteMethod() != null; - } -} \ No newline at end of file diff --git a/org.springframework.context/src/main/java/org/springframework/ui/binding/support/PropertyNotFoundException.java b/org.springframework.context/src/main/java/org/springframework/ui/binding/support/PropertyNotFoundException.java index 5217ba503e2..c4fac86a736 100644 --- a/org.springframework.context/src/main/java/org/springframework/ui/binding/support/PropertyNotFoundException.java +++ b/org.springframework.context/src/main/java/org/springframework/ui/binding/support/PropertyNotFoundException.java @@ -1,3 +1,18 @@ +/* + * 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.ui.binding.support; public class PropertyNotFoundException extends RuntimeException { diff --git a/org.springframework.context/src/main/java/org/springframework/ui/binding/support/PropertyPath.java b/org.springframework.context/src/main/java/org/springframework/ui/binding/support/PropertyPath.java index c5e2879aafd..f12ebd3783c 100644 --- a/org.springframework.context/src/main/java/org/springframework/ui/binding/support/PropertyPath.java +++ b/org.springframework.context/src/main/java/org/springframework/ui/binding/support/PropertyPath.java @@ -1,5 +1,17 @@ -/** - * +/* + * 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.ui.binding.support; diff --git a/org.springframework.context/src/main/java/org/springframework/ui/binding/support/PropertyPathElement.java b/org.springframework.context/src/main/java/org/springframework/ui/binding/support/PropertyPathElement.java index 9c6ec0d7988..cc77e965fc4 100644 --- a/org.springframework.context/src/main/java/org/springframework/ui/binding/support/PropertyPathElement.java +++ b/org.springframework.context/src/main/java/org/springframework/ui/binding/support/PropertyPathElement.java @@ -1,5 +1,17 @@ -/** - * +/* + * 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.ui.binding.support; diff --git a/org.springframework.context/src/main/java/org/springframework/ui/binding/support/PropertyValueModel.java b/org.springframework.context/src/main/java/org/springframework/ui/binding/support/PropertyValueModel.java new file mode 100644 index 00000000000..40245c08ee2 --- /dev/null +++ b/org.springframework.context/src/main/java/org/springframework/ui/binding/support/PropertyValueModel.java @@ -0,0 +1,56 @@ +/* + * 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.ui.binding.support; + +import java.beans.PropertyDescriptor; + +import org.springframework.core.MethodParameter; +import org.springframework.core.convert.TypeDescriptor; +import org.springframework.util.ReflectionUtils; + +public class PropertyValueModel implements ValueModel { + + private PropertyDescriptor property; + + private Object object; + + public PropertyValueModel(PropertyDescriptor property, Object object) { + this.property = property; + this.object = object; + } + + public Object getValue() { + return ReflectionUtils.invokeMethod(property.getReadMethod(), object); + } + + public Class getValueType() { + return property.getPropertyType(); + } + + @SuppressWarnings("unchecked") + public TypeDescriptor getValueTypeDescriptor() { + return new TypeDescriptor(new MethodParameter(property.getReadMethod(), -1)); + } + + public boolean isWriteable() { + return property.getWriteMethod() != null; + } + + public void setValue(Object value) { + ReflectionUtils.invokeMethod(property.getWriteMethod(), object, value); + } + +} \ No newline at end of file diff --git a/org.springframework.context/src/main/java/org/springframework/ui/binding/support/ValueBuffer.java b/org.springframework.context/src/main/java/org/springframework/ui/binding/support/ValueBuffer.java index 9f1d1d79e81..06c58abc68c 100644 --- a/org.springframework.context/src/main/java/org/springframework/ui/binding/support/ValueBuffer.java +++ b/org.springframework.context/src/main/java/org/springframework/ui/binding/support/ValueBuffer.java @@ -1,9 +1,20 @@ -/** - * +/* + * 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.ui.binding.support; -import org.springframework.ui.binding.support.AbstractBinding.ValueModel; class ValueBuffer { diff --git a/org.springframework.context/src/main/java/org/springframework/ui/binding/support/ValueModel.java b/org.springframework.context/src/main/java/org/springframework/ui/binding/support/ValueModel.java new file mode 100644 index 00000000000..a4b6f474d27 --- /dev/null +++ b/org.springframework.context/src/main/java/org/springframework/ui/binding/support/ValueModel.java @@ -0,0 +1,36 @@ +package org.springframework.ui.binding.support; + +import org.springframework.core.convert.TypeDescriptor; + +/** + * For accessing the raw bound model object. + * @author Keith Donald + */ +public interface ValueModel { + + /** + * The model value. + */ + Object getValue(); + + /** + * The model value type. + */ + Class getValueType(); + + /** + * The model value type descriptor. + */ + TypeDescriptor getValueTypeDescriptor(); + + /** + * If the model is writeable. + */ + boolean isWriteable(); + + /** + * Set the model value. + * @throws IllegalStateException if not writeable + */ + void setValue(Object value); +} \ No newline at end of file diff --git a/org.springframework.context/src/main/java/org/springframework/ui/lifecycle/BindAndValidateLifecycleImpl.java b/org.springframework.context/src/main/java/org/springframework/ui/lifecycle/BindAndValidateLifecycleImpl.java index d12159673f4..bbbc4eb9101 100644 --- a/org.springframework.context/src/main/java/org/springframework/ui/lifecycle/BindAndValidateLifecycleImpl.java +++ b/org.springframework.context/src/main/java/org/springframework/ui/lifecycle/BindAndValidateLifecycleImpl.java @@ -20,9 +20,9 @@ import java.util.List; import java.util.Map; import org.springframework.ui.alert.AlertContext; -import org.springframework.ui.binding.Binder; -import org.springframework.ui.binding.BindingResult; -import org.springframework.ui.binding.BindingResults; +import org.springframework.ui.binding.binder.Binder; +import org.springframework.ui.binding.binder.BindingResult; +import org.springframework.ui.binding.binder.BindingResults; import org.springframework.ui.validation.ValidationFailure; import org.springframework.ui.validation.Validator; diff --git a/org.springframework.context/src/main/java/org/springframework/ui/lifecycle/ValidationDecider.java b/org.springframework.context/src/main/java/org/springframework/ui/lifecycle/ValidationDecider.java index 84b7aac766c..38255e231e4 100644 --- a/org.springframework.context/src/main/java/org/springframework/ui/lifecycle/ValidationDecider.java +++ b/org.springframework.context/src/main/java/org/springframework/ui/lifecycle/ValidationDecider.java @@ -15,7 +15,7 @@ */ package org.springframework.ui.lifecycle; -import org.springframework.ui.binding.BindingResults; +import org.springframework.ui.binding.binder.BindingResults; /** * Decides if validation should run for an execution of the bind and validate lifecycle.