94 changed files with 0 additions and 6892 deletions
@ -1,42 +0,0 @@
@@ -1,42 +0,0 @@
|
||||
/* |
||||
* 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.model.alert; |
||||
|
||||
/** |
||||
* Communicates an event of interest to the user. |
||||
* For example, an alert may inform a user of a web application a business rule was violated. |
||||
* @author Keith Donald |
||||
* @since 3.0 |
||||
*/ |
||||
public interface Alert { |
||||
|
||||
/** |
||||
* The code uniquely identifying this kind of alert; for example, "weakPassword". |
||||
* May be used as a key to lookup additional alert details. |
||||
*/ |
||||
public String getCode(); |
||||
|
||||
/** |
||||
* The level of impact this alert has on the user. |
||||
*/ |
||||
public Severity getSeverity(); |
||||
|
||||
/** |
||||
* The localized message to display to the user; for example, "Please enter a stronger password". |
||||
*/ |
||||
public String getMessage(); |
||||
|
||||
} |
||||
@ -1,49 +0,0 @@
@@ -1,49 +0,0 @@
|
||||
/* |
||||
* 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.model.alert; |
||||
|
||||
import java.util.List; |
||||
import java.util.Map; |
||||
|
||||
/** |
||||
* A context for adding and getting alerts for display in a user interface. |
||||
* @author Keith Donald |
||||
* @since 3.0 |
||||
*/ |
||||
public interface AlertContext { |
||||
|
||||
/** |
||||
* Return all alerts in this context indexed by the UI element they are associated with. |
||||
* @return the message map |
||||
*/ |
||||
public Map<String, List<Alert>> getAlerts(); |
||||
|
||||
/** |
||||
* Get all alerts on the UI element provided. |
||||
* Returns an empty list if no alerts have been added for the element. |
||||
* Alerts are returned in the order they were added. |
||||
* @param element the id of the element to lookup alerts against |
||||
*/ |
||||
public List<Alert> getAlerts(String element); |
||||
|
||||
/** |
||||
* Add an alert to this context. |
||||
* @param the element this alert is associated with |
||||
* @param alert the alert to add |
||||
*/ |
||||
public void add(String element, Alert alert); |
||||
|
||||
} |
||||
@ -1,104 +0,0 @@
@@ -1,104 +0,0 @@
|
||||
/* |
||||
* 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.model.alert; |
||||
|
||||
/** |
||||
* A static factory for conveniently constructing Alerts. |
||||
* Usage example: |
||||
* <pre> |
||||
* import static org.springframework.ui.alert.Alerts.*; |
||||
* |
||||
* public void example() { |
||||
* info("An info alert"); |
||||
* warning("A warning alert"); |
||||
* error("An error alert"); |
||||
* fatal("A fatal alert"); |
||||
* } |
||||
* </pre> |
||||
* @author Keith Donald |
||||
* @since 3.0 |
||||
*/ |
||||
public final class Alerts { |
||||
|
||||
/** |
||||
* Creates a new info alert. |
||||
* @param message the alert message |
||||
* @return the info alert |
||||
* @see Severity#INFO |
||||
*/ |
||||
public static Alert info(String message) { |
||||
return new GenericAlert(Severity.INFO, message); |
||||
} |
||||
|
||||
/** |
||||
* Creates a new warning alert. |
||||
* @param message the alert message |
||||
* @return the info alert |
||||
* @see Severity#WARNING |
||||
*/ |
||||
public static Alert warning(String message) { |
||||
return new GenericAlert(Severity.WARNING, message); |
||||
} |
||||
|
||||
/** |
||||
* Creates a new error alert. |
||||
* @param message the alert message |
||||
* @return the info alert |
||||
* @see Severity#ERROR |
||||
*/ |
||||
public static Alert error(String message) { |
||||
return new GenericAlert(Severity.ERROR, message); |
||||
} |
||||
|
||||
/** |
||||
* Creates a new fatal alert. |
||||
* @param message the alert message |
||||
* @return the info alert |
||||
* @see Severity#ERROR |
||||
*/ |
||||
public static Alert fatal(String message) { |
||||
return new GenericAlert(Severity.FATAL, message); |
||||
} |
||||
|
||||
private static class GenericAlert implements Alert { |
||||
|
||||
private Severity severity; |
||||
|
||||
private String message; |
||||
|
||||
public GenericAlert(Severity severity, String message) { |
||||
this.severity = severity; |
||||
this.message = message; |
||||
} |
||||
|
||||
public String getCode() { |
||||
return null; |
||||
} |
||||
|
||||
public Severity getSeverity() { |
||||
return severity; |
||||
} |
||||
|
||||
public String getMessage() { |
||||
return message; |
||||
} |
||||
|
||||
public String toString() { |
||||
return getSeverity() + ": " + getMessage(); |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -1,47 +0,0 @@
@@ -1,47 +0,0 @@
|
||||
/* |
||||
* 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.model.alert; |
||||
|
||||
/** |
||||
* The set of alert severities. |
||||
* @author Keith Donald |
||||
* @since 3.0 |
||||
* @see Alert |
||||
*/ |
||||
public enum Severity { |
||||
|
||||
/** |
||||
* The "Informational" severity. Used to indicate a successful operation or result. |
||||
*/ |
||||
INFO, |
||||
|
||||
/** |
||||
* The "Warning" severity. Used to indicate there is a minor problem, or to inform the user of possible |
||||
* misuse, or to indicate a problem may arise in the future. |
||||
*/ |
||||
WARNING, |
||||
|
||||
/** |
||||
* The "Error" severity. Used to indicate a significant problem like a business rule violation. |
||||
*/ |
||||
ERROR, |
||||
|
||||
/** |
||||
* The "Fatal" severity. Used to indicate a fatal problem like a system error or runtime exception. |
||||
*/ |
||||
FATAL |
||||
|
||||
} |
||||
@ -1,5 +0,0 @@
@@ -1,5 +0,0 @@
|
||||
/** |
||||
* A general-purpose Alerting API to communicate events of interest. |
||||
*/ |
||||
package org.springframework.model.alert; |
||||
|
||||
@ -1,66 +0,0 @@
@@ -1,66 +0,0 @@
|
||||
/* |
||||
* 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.model.alert.support; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.Collections; |
||||
import java.util.LinkedHashMap; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
|
||||
import org.springframework.core.style.ToStringCreator; |
||||
import org.springframework.model.alert.Alert; |
||||
import org.springframework.model.alert.AlertContext; |
||||
import org.springframework.util.CachingMapDecorator; |
||||
|
||||
/** |
||||
* The default alert context implementation. |
||||
* @author Keith Donald |
||||
* @since 3.0 |
||||
*/ |
||||
public class DefaultAlertContext implements AlertContext { |
||||
|
||||
@SuppressWarnings("serial") |
||||
private Map<String, List<Alert>> alerts = new CachingMapDecorator<String, List<Alert>>(new LinkedHashMap<String, List<Alert>>()) { |
||||
protected List<Alert> create(String element) { |
||||
return new ArrayList<Alert>(); |
||||
} |
||||
}; |
||||
|
||||
// implementing AlertContext
|
||||
|
||||
public Map<String, List<Alert>> getAlerts() { |
||||
return Collections.unmodifiableMap(alerts); |
||||
} |
||||
|
||||
public List<Alert> getAlerts(String element) { |
||||
List<Alert> messages = alerts.get(element); |
||||
if (messages.isEmpty()) { |
||||
return Collections.emptyList(); |
||||
} |
||||
return Collections.unmodifiableList(messages); |
||||
} |
||||
|
||||
public void add(String element, Alert alert) { |
||||
List<Alert> alerts = this.alerts.get(element); |
||||
alerts.add(alert); |
||||
} |
||||
|
||||
public String toString() { |
||||
return new ToStringCreator(this).append("alerts", alerts).toString(); |
||||
} |
||||
|
||||
} |
||||
@ -1,5 +0,0 @@
@@ -1,5 +0,0 @@
|
||||
/** |
||||
* AlertContext implementation suitable for use in most environments. |
||||
*/ |
||||
package org.springframework.model.alert.support; |
||||
|
||||
@ -1,37 +0,0 @@
@@ -1,37 +0,0 @@
|
||||
/* |
||||
* 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.model.binder; |
||||
|
||||
import java.util.Map; |
||||
|
||||
/** |
||||
* Bind to fields of a model object. |
||||
* @author Keith Donald |
||||
* @since 3.0 |
||||
* @param <M> The type of model this binder binds to |
||||
*/ |
||||
public interface Binder<M> { |
||||
|
||||
/** |
||||
* Bind submitted field values. |
||||
* @param fieldValues the field values to bind |
||||
* @param model the model to bind to |
||||
* @return the results of the binding operation |
||||
* @throws MissingFieldException when the fieldValues Map is missing required fields |
||||
*/ |
||||
BindingResults bind(Map<String, ? extends Object> fieldValues, M model); |
||||
|
||||
} |
||||
@ -1,53 +0,0 @@
@@ -1,53 +0,0 @@
|
||||
/* |
||||
* 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.model.binder; |
||||
|
||||
import org.springframework.model.alert.Alert; |
||||
|
||||
/** |
||||
* The result of a bind operation. |
||||
* @author Keith Donald |
||||
* @since 3.0 |
||||
* @see Binder#bind(java.util.Map, Object) |
||||
*/ |
||||
public interface BindingResult { |
||||
|
||||
/** |
||||
* The name of the field this binding result is for. |
||||
* @see Binder#getNested(String) |
||||
*/ |
||||
String getFieldName(); |
||||
|
||||
/** |
||||
* The raw submitted value for which binding was attempted. |
||||
* If not a failure, this value was successfully bound to the model. |
||||
* @see #isFailure() |
||||
*/ |
||||
Object getSubmittedValue(); |
||||
|
||||
/** |
||||
* Indicates if the binding failed. |
||||
*/ |
||||
boolean isFailure(); |
||||
|
||||
/** |
||||
* Gets the alert for this binding result, appropriate for rendering the result to the user. |
||||
* An alert describing a successful binding will have info severity. |
||||
* An alert describing a failed binding will have either warning, error, or fatal severity. |
||||
*/ |
||||
Alert getAlert(); |
||||
|
||||
} |
||||
@ -1,60 +0,0 @@
@@ -1,60 +0,0 @@
|
||||
/* |
||||
* 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.model.binder; |
||||
|
||||
import org.springframework.model.alert.Severity; |
||||
|
||||
/** |
||||
* The results of a bind operation. |
||||
* @author Keith Donald |
||||
* @since 3.0 |
||||
* @see Binder#bind(java.util.Map, Object) |
||||
*/ |
||||
public interface BindingResults extends Iterable<BindingResult> { |
||||
|
||||
/** |
||||
* The subset of BindingResults that were successful. |
||||
*/ |
||||
BindingResults successes(); |
||||
|
||||
/** |
||||
* The subset of BindingResults that failed. |
||||
*/ |
||||
BindingResults failures(); |
||||
|
||||
/** |
||||
* If there is at least one failure with a Severity equal to or greater than {@link Severity#ERROR}. |
||||
* @see BindingResults#failures() |
||||
*/ |
||||
boolean hasErrors(); |
||||
|
||||
/** |
||||
* The subset of BindingResults that failed with {@link Severity#ERROR} or greater. |
||||
*/ |
||||
BindingResults errors(); |
||||
|
||||
/** |
||||
* The total number of results. |
||||
*/ |
||||
int size(); |
||||
|
||||
/** |
||||
* The BindingResult at the specified index. |
||||
* @throws IndexOutOfBoundsException if the index is out of bounds |
||||
*/ |
||||
BindingResult get(int index); |
||||
|
||||
} |
||||
@ -1,58 +0,0 @@
@@ -1,58 +0,0 @@
|
||||
/* |
||||
* 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.model.binder; |
||||
|
||||
import java.util.List; |
||||
import java.util.Map; |
||||
|
||||
/** |
||||
* Exception thrown by a Binder when a required source value is missing unexpectedly from the sourceValues map. |
||||
* Indicates a client configuration error. |
||||
* @author Keith Donald |
||||
* @since 3.0 |
||||
* @see Binder#bind(Map, Object) |
||||
*/ |
||||
@SuppressWarnings("serial") |
||||
public class MissingFieldException extends RuntimeException { |
||||
|
||||
private List<String> missing; |
||||
|
||||
/** |
||||
* Creates a new missing field exceptions. |
||||
* @param missing |
||||
* @param fieldValues |
||||
*/ |
||||
public MissingFieldException(List<String> missing, Map<String, ? extends Object> fieldValues) { |
||||
super(getMessage(missing, fieldValues)); |
||||
this.missing = missing; |
||||
} |
||||
|
||||
/** |
||||
* The names of the fields that are missing. |
||||
*/ |
||||
public List<String> getMissing() { |
||||
return missing; |
||||
} |
||||
|
||||
private static String getMessage(List<String> missingRequired, Map<String, ? extends Object> sourceValues) { |
||||
if (missingRequired.size() == 1) { |
||||
return "Missing a field [" + missingRequired.get(0) + "]; fieldValues map contained " + sourceValues.keySet(); |
||||
} else { |
||||
return "Missing fields " + missingRequired + "; fieldValues map contained " + sourceValues.keySet(); |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -1,5 +0,0 @@
@@ -1,5 +0,0 @@
|
||||
/** |
||||
* API for binding submitted field values in a single batch operation. |
||||
*/ |
||||
package org.springframework.model.binder; |
||||
|
||||
@ -1,127 +0,0 @@
@@ -1,127 +0,0 @@
|
||||
/* |
||||
* 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.model.binder.support; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
|
||||
import org.springframework.context.MessageSource; |
||||
import org.springframework.model.binder.Binder; |
||||
import org.springframework.model.binder.BindingResult; |
||||
import org.springframework.model.binder.BindingResults; |
||||
import org.springframework.model.binder.MissingFieldException; |
||||
import org.springframework.util.Assert; |
||||
|
||||
/** |
||||
* Base Binder implementation that defines common structural elements. |
||||
* Subclasses should be parameterized & implement {@link #bind(Map, Object)}. |
||||
* @author Keith Donald |
||||
* @since 3.0 |
||||
* @see #setRequiredFields(String[]) |
||||
* @see #setMessageSource(MessageSource) |
||||
* @see #createFieldBinder() |
||||
* @see #bind(Map, Object) |
||||
*/ |
||||
public abstract class AbstractBinder<M> implements Binder<M> { |
||||
|
||||
private MessageSource messageSource; |
||||
|
||||
private String[] requiredFields; |
||||
|
||||
/** |
||||
* Configure the fields for which values must be present in each bind attempt. |
||||
* @param fieldNames the required field names |
||||
* @see MissingFieldException |
||||
*/ |
||||
public void setRequiredFields(String[] fieldNames) { |
||||
this.requiredFields = fieldNames; |
||||
} |
||||
|
||||
/** |
||||
* 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; |
||||
} |
||||
|
||||
/** |
||||
* The configured MessageSource that resolves binding result alert messages. |
||||
*/ |
||||
protected MessageSource getMessageSource() { |
||||
return messageSource; |
||||
} |
||||
|
||||
// Binder implementation
|
||||
|
||||
public final BindingResults bind(Map<String, ? extends Object> fieldValues, M model) { |
||||
fieldValues = filter(fieldValues, model); |
||||
checkRequired(fieldValues); |
||||
FieldBinder fieldBinder = this.createFieldBinder(model); |
||||
ArrayListBindingResults results = new ArrayListBindingResults(fieldValues.size()); |
||||
for (Map.Entry<String, ? extends Object> fieldValue : fieldValues.entrySet()) { |
||||
results.add(fieldBinder.bind(fieldValue.getKey(), fieldValue.getValue())); |
||||
} |
||||
return results; |
||||
} |
||||
|
||||
// subclass hooks
|
||||
|
||||
/** |
||||
* Subclasses must implement this method to create the {@link FieldBinder} |
||||
* instance for the given model. |
||||
*/ |
||||
protected abstract FieldBinder createFieldBinder(M model); |
||||
|
||||
/** |
||||
* Filter the fields to bind. |
||||
* Allows for pre-processing the fieldValues Map before any binding occurs. |
||||
* For example, you might insert empty or default values for fields that are not present. |
||||
* As another example, you might collapse multiple fields into a single field. |
||||
* Default implementation simply returns the fieldValues Map unchanged. |
||||
* @param fieldValues the original fieldValues Map provided by the caller |
||||
* @return the filtered fieldValues Map that will be used to bind |
||||
*/ |
||||
protected Map<String, ? extends Object> filter(Map<String, ? extends Object> fieldValues, M model) { |
||||
return fieldValues; |
||||
} |
||||
|
||||
// internal helpers
|
||||
|
||||
private void checkRequired(Map<String, ? extends Object> fieldValues) { |
||||
if (requiredFields == null) { |
||||
return; |
||||
} |
||||
List<String> missingRequired = new ArrayList<String>(); |
||||
for (String required : requiredFields) { |
||||
boolean found = false; |
||||
for (String property : fieldValues.keySet()) { |
||||
if (property.equals(required)) { |
||||
found = true; |
||||
} |
||||
} |
||||
if (!found) { |
||||
missingRequired.add(required); |
||||
} |
||||
} |
||||
if (!missingRequired.isEmpty()) { |
||||
throw new MissingFieldException(missingRequired, fieldValues); |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -1,56 +0,0 @@
@@ -1,56 +0,0 @@
|
||||
/* |
||||
* 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.model.binder.support; |
||||
|
||||
import org.springframework.model.alert.Alert; |
||||
import org.springframework.model.alert.Severity; |
||||
import org.springframework.model.binder.BindingResult; |
||||
|
||||
public class AlertBindingResult implements BindingResult { |
||||
|
||||
private String fieldName; |
||||
|
||||
private Object submittedValue; |
||||
|
||||
private Alert alert; |
||||
|
||||
public AlertBindingResult(String fieldName, Object sourceValue, Alert alert) { |
||||
this.fieldName = fieldName; |
||||
this.submittedValue = sourceValue; |
||||
this.alert = alert; |
||||
} |
||||
|
||||
public String getFieldName() { |
||||
return fieldName; |
||||
} |
||||
|
||||
public Object getSubmittedValue() { |
||||
return submittedValue; |
||||
} |
||||
|
||||
public boolean isFailure() { |
||||
return alert.getSeverity().compareTo(Severity.INFO) > 1; |
||||
} |
||||
|
||||
public Alert getAlert() { |
||||
return alert; |
||||
} |
||||
|
||||
public String toString() { |
||||
return getAlert().toString(); |
||||
} |
||||
|
||||
} |
||||
@ -1,95 +0,0 @@
@@ -1,95 +0,0 @@
|
||||
/* |
||||
* 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.model.binder.support; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.Iterator; |
||||
import java.util.List; |
||||
|
||||
import org.springframework.model.alert.Severity; |
||||
import org.springframework.model.binder.BindingResult; |
||||
import org.springframework.model.binder.BindingResults; |
||||
|
||||
class ArrayListBindingResults implements BindingResults { |
||||
|
||||
private List<BindingResult> results; |
||||
|
||||
public ArrayListBindingResults() { |
||||
results = new ArrayList<BindingResult>(); |
||||
} |
||||
|
||||
public ArrayListBindingResults(int size) { |
||||
results = new ArrayList<BindingResult>(size); |
||||
} |
||||
|
||||
public void add(BindingResult result) { |
||||
results.add(result); |
||||
} |
||||
|
||||
// implementing Iterable
|
||||
|
||||
public Iterator<BindingResult> iterator() { |
||||
return results.iterator(); |
||||
} |
||||
|
||||
// implementing BindingResults
|
||||
|
||||
public BindingResults successes() { |
||||
ArrayListBindingResults results = new ArrayListBindingResults(); |
||||
for (BindingResult result : this) { |
||||
if (!result.isFailure()) { |
||||
results.add(result); |
||||
} |
||||
} |
||||
return results; |
||||
} |
||||
|
||||
public BindingResults failures() { |
||||
ArrayListBindingResults results = new ArrayListBindingResults(); |
||||
for (BindingResult result : this) { |
||||
if (result.isFailure()) { |
||||
results.add(result); |
||||
} |
||||
} |
||||
return results; |
||||
} |
||||
|
||||
public boolean hasErrors() { |
||||
return errors().size() > 0; |
||||
} |
||||
|
||||
public BindingResults errors() { |
||||
ArrayListBindingResults results = new ArrayListBindingResults(); |
||||
for (BindingResult result : this) { |
||||
if (result.isFailure() && result.getAlert().getSeverity().compareTo(Severity.ERROR) >= 0) { |
||||
results.add(result); |
||||
} |
||||
} |
||||
return results; |
||||
} |
||||
|
||||
public BindingResult get(int index) { |
||||
return results.get(index); |
||||
} |
||||
|
||||
public int size() { |
||||
return results.size(); |
||||
} |
||||
|
||||
public String toString() { |
||||
return "[BindingResults = " + results.toString() + "]"; |
||||
} |
||||
} |
||||
@ -1,34 +0,0 @@
@@ -1,34 +0,0 @@
|
||||
/* |
||||
* 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.model.binder.support; |
||||
|
||||
import org.springframework.model.binder.BindingResult; |
||||
|
||||
/** |
||||
* Binder callback interface for binding a single field value. |
||||
* @author Keith Donald |
||||
* @see AbstractBinder#createFieldBinder(Object) |
||||
*/ |
||||
public interface FieldBinder { |
||||
|
||||
/** |
||||
* Bind a single field. |
||||
* @param fieldName the field name |
||||
* @param value the field value |
||||
* @return the binding result |
||||
*/ |
||||
BindingResult bind(String fieldName, Object value); |
||||
} |
||||
@ -1,86 +0,0 @@
@@ -1,86 +0,0 @@
|
||||
/* |
||||
* 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.model.binder.support; |
||||
|
||||
import org.springframework.context.MessageSource; |
||||
import org.springframework.core.style.StylerUtils; |
||||
import org.springframework.model.alert.Alert; |
||||
import org.springframework.model.alert.Severity; |
||||
import org.springframework.model.binder.BindingResult; |
||||
import org.springframework.model.message.DefaultMessageFactory; |
||||
import org.springframework.model.message.MessageBuilder; |
||||
import org.springframework.model.message.ResolvableArgument; |
||||
|
||||
/** |
||||
* Indicates a field failed to bind because it was not editable/writeable. |
||||
* @author Keith Donald |
||||
*/ |
||||
public class FieldNotEditableResult implements BindingResult { |
||||
|
||||
private String fieldName; |
||||
|
||||
private Object submittedValue; |
||||
|
||||
private MessageSource messageSource; |
||||
|
||||
public FieldNotEditableResult(String fieldName, Object submittedValue, MessageSource messageSource) { |
||||
this.fieldName = fieldName; |
||||
this.submittedValue = submittedValue; |
||||
this.messageSource = messageSource; |
||||
} |
||||
|
||||
public String getFieldName() { |
||||
return fieldName; |
||||
} |
||||
|
||||
public Object getSubmittedValue() { |
||||
return submittedValue; |
||||
} |
||||
|
||||
public boolean isFailure() { |
||||
return true; |
||||
} |
||||
|
||||
public Alert getAlert() { |
||||
return new Alert() { |
||||
public String getCode() { |
||||
return "fieldNotEditable"; |
||||
} |
||||
|
||||
public Severity getSeverity() { |
||||
return Severity.WARNING; |
||||
} |
||||
|
||||
public String getMessage() { |
||||
MessageBuilder builder = new MessageBuilder(messageSource); |
||||
builder.code(getCode()); |
||||
builder.arg("label", new ResolvableArgument(fieldName)); |
||||
builder.arg("value", submittedValue); |
||||
builder.defaultMessage(new DefaultMessageFactory() { |
||||
public String createDefaultMessage() { |
||||
return "Failed to bind submitted value " + StylerUtils.style(submittedValue) + "; field '" + fieldName + "' is not editable"; |
||||
} |
||||
}); |
||||
return builder.build(); |
||||
} |
||||
}; |
||||
} |
||||
|
||||
public String toString() { |
||||
return getAlert().toString(); |
||||
} |
||||
|
||||
} |
||||
@ -1,86 +0,0 @@
@@ -1,86 +0,0 @@
|
||||
/* |
||||
* 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.model.binder.support; |
||||
|
||||
import org.springframework.context.MessageSource; |
||||
import org.springframework.core.style.StylerUtils; |
||||
import org.springframework.model.alert.Alert; |
||||
import org.springframework.model.alert.Severity; |
||||
import org.springframework.model.binder.BindingResult; |
||||
import org.springframework.model.message.DefaultMessageFactory; |
||||
import org.springframework.model.message.MessageBuilder; |
||||
import org.springframework.model.message.ResolvableArgument; |
||||
|
||||
/** |
||||
* Indicates field failed to bind because it was not found. |
||||
* @author Keith Donald |
||||
*/ |
||||
public class FieldNotFoundResult implements BindingResult { |
||||
|
||||
private String fieldName; |
||||
|
||||
private Object submittedValue; |
||||
|
||||
private MessageSource messageSource; |
||||
|
||||
public FieldNotFoundResult(String fieldName, Object submittedValue, MessageSource messageSource) { |
||||
this.fieldName = fieldName; |
||||
this.submittedValue = submittedValue; |
||||
this.messageSource = messageSource; |
||||
} |
||||
|
||||
public String getFieldName() { |
||||
return fieldName; |
||||
} |
||||
|
||||
public Object getSubmittedValue() { |
||||
return submittedValue; |
||||
} |
||||
|
||||
public boolean isFailure() { |
||||
return true; |
||||
} |
||||
|
||||
public Alert getAlert() { |
||||
return new Alert() { |
||||
public String getCode() { |
||||
return "fieldNotFound"; |
||||
} |
||||
|
||||
public Severity getSeverity() { |
||||
return Severity.WARNING; |
||||
} |
||||
|
||||
public String getMessage() { |
||||
MessageBuilder builder = new MessageBuilder(messageSource); |
||||
builder.code(getCode()); |
||||
builder.arg("label", new ResolvableArgument(fieldName)); |
||||
builder.arg("value", submittedValue); |
||||
builder.defaultMessage(new DefaultMessageFactory() { |
||||
public String createDefaultMessage() { |
||||
return "Failed to bind submitted value " + StylerUtils.style(submittedValue) + "; no field '" + fieldName + "' found"; |
||||
} |
||||
}); |
||||
return builder.build(); |
||||
} |
||||
}; |
||||
} |
||||
|
||||
public String toString() { |
||||
return getAlert().toString(); |
||||
} |
||||
|
||||
} |
||||
@ -1,134 +0,0 @@
@@ -1,134 +0,0 @@
|
||||
/* |
||||
* 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.model.binder.support; |
||||
|
||||
import org.springframework.expression.EvaluationContext; |
||||
import org.springframework.expression.EvaluationException; |
||||
import org.springframework.expression.Expression; |
||||
import org.springframework.expression.ExpressionParser; |
||||
import org.springframework.expression.ParseException; |
||||
import org.springframework.expression.spel.standard.SpelExpressionParser; |
||||
import org.springframework.expression.spel.standard.SpelExpressionParserConfiguration; |
||||
import org.springframework.expression.spel.support.StandardEvaluationContext; |
||||
import org.springframework.model.alert.Alert; |
||||
import org.springframework.model.alert.Severity; |
||||
import org.springframework.model.binder.Binder; |
||||
import org.springframework.model.binder.BindingResult; |
||||
|
||||
/** |
||||
* A {@link Binder} implementation that accepts any target object and uses |
||||
* Spring's Expression Language (SpEL) to evaluate the keys in the field |
||||
* value Map. |
||||
* @author Mark Fisher |
||||
* @since 3.0 |
||||
*/ |
||||
public class GenericBinder extends AbstractBinder<Object> { |
||||
|
||||
private final ExpressionParser parser = new SpelExpressionParser( |
||||
SpelExpressionParserConfiguration.CreateObjectIfAttemptToReferenceNull |
||||
| SpelExpressionParserConfiguration.GrowListsOnIndexBeyondSize); |
||||
|
||||
@Override |
||||
protected FieldBinder createFieldBinder(Object model) { |
||||
StandardEvaluationContext context = new StandardEvaluationContext(); |
||||
context.setRootObject(model); |
||||
return new EvaluationContextFieldBinder(parser, context); |
||||
} |
||||
|
||||
private static class EvaluationContextFieldBinder implements FieldBinder { |
||||
|
||||
private final ExpressionParser parser; |
||||
|
||||
private final EvaluationContext context; |
||||
|
||||
private EvaluationContextFieldBinder(ExpressionParser parser, EvaluationContext context) { |
||||
this.parser = parser; |
||||
this.context = context; |
||||
} |
||||
|
||||
public BindingResult bind(String key, Object value) { |
||||
Alert alert = null; |
||||
try { |
||||
Expression e = this.parser.parseExpression(key); |
||||
e.setValue(this.context, value); |
||||
alert = new BindSuccessAlert(); |
||||
} catch (ParseException e) { |
||||
alert = new ParseFailureAlert(e); |
||||
} catch (EvaluationException e) { |
||||
alert = new EvaluationFailureAlert(e); |
||||
} |
||||
return new AlertBindingResult(key, value, alert); |
||||
} |
||||
} |
||||
|
||||
private static class BindSuccessAlert implements Alert { |
||||
|
||||
public String getCode() { |
||||
return "bindSuccess"; |
||||
} |
||||
|
||||
public String getMessage() { |
||||
return "Binding successful"; |
||||
} |
||||
|
||||
public Severity getSeverity() { |
||||
return Severity.INFO; |
||||
} |
||||
} |
||||
|
||||
private static class ParseFailureAlert implements Alert { |
||||
|
||||
private final ParseException exception; |
||||
|
||||
ParseFailureAlert(ParseException exception) { |
||||
this.exception = exception; |
||||
} |
||||
|
||||
public String getCode() { |
||||
return "parserFailed"; |
||||
} |
||||
|
||||
public String getMessage() { |
||||
return exception.getMessage(); |
||||
} |
||||
|
||||
public Severity getSeverity() { |
||||
return Severity.ERROR; |
||||
} |
||||
} |
||||
|
||||
private static class EvaluationFailureAlert implements Alert { |
||||
|
||||
private final EvaluationException exception; |
||||
|
||||
EvaluationFailureAlert(EvaluationException exception) { |
||||
this.exception = exception; |
||||
} |
||||
|
||||
public String getCode() { |
||||
return "evaluationFailed"; |
||||
} |
||||
|
||||
public String getMessage() { |
||||
return exception.getMessage(); |
||||
} |
||||
|
||||
public Severity getSeverity() { |
||||
return Severity.ERROR; |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -1,5 +0,0 @@
@@ -1,5 +0,0 @@
|
||||
/** |
||||
* Binder API implementation support. |
||||
*/ |
||||
package org.springframework.model.binder.support; |
||||
|
||||
@ -1,31 +0,0 @@
@@ -1,31 +0,0 @@
|
||||
/* |
||||
* 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.model.message; |
||||
|
||||
/** |
||||
* A factory for a default message to return if no message could be resolved. |
||||
* Allows the message String to be created lazily, only when it is needed. |
||||
* @author Keith Donald |
||||
* @since 3.0 |
||||
* @see MessageBuilder |
||||
*/ |
||||
public interface DefaultMessageFactory { |
||||
|
||||
/** |
||||
* Create the default message. |
||||
*/ |
||||
String createDefaultMessage(); |
||||
} |
||||
@ -1,153 +0,0 @@
@@ -1,153 +0,0 @@
|
||||
/* |
||||
* 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.model.message; |
||||
|
||||
import java.util.Locale; |
||||
import java.util.Map; |
||||
|
||||
import org.springframework.context.MessageSource; |
||||
import org.springframework.context.MessageSourceResolvable; |
||||
import org.springframework.context.NoSuchMessageException; |
||||
import org.springframework.core.style.ToStringCreator; |
||||
import org.springframework.expression.AccessException; |
||||
import org.springframework.expression.EvaluationContext; |
||||
import org.springframework.expression.EvaluationException; |
||||
import org.springframework.expression.Expression; |
||||
import org.springframework.expression.ExpressionParser; |
||||
import org.springframework.expression.ParseException; |
||||
import org.springframework.expression.ParserContext; |
||||
import org.springframework.expression.PropertyAccessor; |
||||
import org.springframework.expression.TypedValue; |
||||
import org.springframework.expression.spel.support.StandardEvaluationContext; |
||||
|
||||
final class DefaultMessageResolver implements MessageResolver, MessageSourceResolvable { |
||||
|
||||
private String[] codes; |
||||
|
||||
private Map<String, Object> args; |
||||
|
||||
private DefaultMessageFactory defaultMessageFactory; |
||||
|
||||
private ExpressionParser expressionParser; |
||||
|
||||
public DefaultMessageResolver(String[] codes, Map<String, Object> args, |
||||
DefaultMessageFactory defaultMessageFactory, ExpressionParser expressionParser) { |
||||
this.codes = codes; |
||||
this.args = args; |
||||
this.defaultMessageFactory = defaultMessageFactory; |
||||
this.expressionParser = expressionParser; |
||||
} |
||||
|
||||
// implementing MessageResolver
|
||||
|
||||
public String resolveMessage(MessageSource messageSource, Locale locale) { |
||||
if (messageSource == null) { |
||||
if (defaultMessageFactory != null) { |
||||
return defaultMessageFactory.createDefaultMessage(); |
||||
} else { |
||||
throw new MessageResolutionException( |
||||
"Unable to resolve message; MessagSource argument is null and no defaultMessage is configured"); |
||||
} |
||||
} |
||||
String messageString; |
||||
try { |
||||
messageString = messageSource.getMessage(this, locale); |
||||
} catch (NoSuchMessageException e) { |
||||
throw new MessageResolutionException("Unable to resolve message in" + messageSource, e); |
||||
} |
||||
Expression message; |
||||
try { |
||||
message = expressionParser.parseExpression(messageString, ParserContext.TEMPLATE_EXPRESSION); |
||||
} catch (ParseException e) { |
||||
throw new MessageResolutionException("Failed to parse message expression", e); |
||||
} |
||||
try { |
||||
StandardEvaluationContext context = new StandardEvaluationContext(); |
||||
context.setRootObject(args); |
||||
context.addPropertyAccessor(new MessageArgumentAccessor(messageSource, locale)); |
||||
return (String) message.getValue(context); |
||||
} catch (EvaluationException e) { |
||||
throw new MessageResolutionException("Failed to evaluate message expression '" |
||||
+ message.getExpressionString() + "' to generate final message text", e); |
||||
} |
||||
} |
||||
|
||||
// implementing MessageSourceResolver
|
||||
|
||||
public String[] getCodes() { |
||||
return codes; |
||||
} |
||||
|
||||
public Object[] getArguments() { |
||||
return null; |
||||
} |
||||
|
||||
public String getDefaultMessage() { |
||||
return defaultMessageFactory.createDefaultMessage(); |
||||
} |
||||
|
||||
public String toString() { |
||||
return new ToStringCreator(this).append("codes", codes).append("args", args).append("defaultMessageFactory", |
||||
defaultMessageFactory).toString(); |
||||
} |
||||
|
||||
@SuppressWarnings("unchecked") |
||||
static class MessageArgumentAccessor implements PropertyAccessor { |
||||
|
||||
private MessageSource messageSource; |
||||
|
||||
private Locale locale; |
||||
|
||||
public MessageArgumentAccessor(MessageSource messageSource, Locale locale) { |
||||
this.messageSource = messageSource; |
||||
this.locale = locale; |
||||
} |
||||
|
||||
public boolean canRead(EvaluationContext context, Object target, String name) throws AccessException { |
||||
return true; |
||||
} |
||||
|
||||
public TypedValue read(EvaluationContext context, Object target, String name) throws AccessException { |
||||
Map map = (Map) target; |
||||
Object o = map.get(name); |
||||
if (o == null) { |
||||
throw new AccessException("No message argument named '" + name |
||||
+ "' is defined in the argument map; arguments available are " + map.keySet(), null); |
||||
} |
||||
if (o instanceof MessageSourceResolvable) { |
||||
String message = messageSource.getMessage((MessageSourceResolvable) o, locale); |
||||
return new TypedValue(message); |
||||
} else { |
||||
return new TypedValue(o); |
||||
} |
||||
} |
||||
|
||||
public boolean canWrite(EvaluationContext context, Object target, String name) throws AccessException { |
||||
return false; |
||||
} |
||||
|
||||
public void write(EvaluationContext context, Object target, String name, Object newValue) |
||||
throws AccessException { |
||||
throw new UnsupportedOperationException("Should not be called"); |
||||
} |
||||
|
||||
public Class[] getSpecificTargetClasses() { |
||||
return new Class[] { Map.class }; |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
@ -1,148 +0,0 @@
@@ -1,148 +0,0 @@
|
||||
/* |
||||
* 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.model.message; |
||||
|
||||
import java.util.Locale; |
||||
|
||||
import org.springframework.context.MessageSource; |
||||
import org.springframework.context.i18n.LocaleContextHolder; |
||||
|
||||
/** |
||||
* Builds a localized message for display in a user interface. |
||||
* Allows convenient specification of the codes to try to resolve the message. |
||||
* Also supports named arguments that can inserted into a message template using eval #{expressions}. |
||||
* <p> |
||||
* Usage example: |
||||
* <pre> |
||||
* String message = new MessageBuilder(messageSource). |
||||
* code("invalidFormat"). |
||||
* arg("label", new ResolvableArgument("mathForm.decimalField")). |
||||
* arg("format", "#,###.##"). |
||||
* defaultMessage("The decimal field must be in format #,###.##"). |
||||
* build(); |
||||
* </pre> |
||||
* Example messages.properties loaded by the MessageSource: |
||||
* <pre> |
||||
* invalidFormat=The #{label} must be in format #{format}. |
||||
* mathForm.decimalField=Decimal Field |
||||
* </pre> |
||||
* @author Keith Donald |
||||
* @since 3.0 |
||||
* @see #code(String) |
||||
* @see #arg(String, Object) |
||||
* @see #defaultMessage(String) |
||||
* @see #locale(Locale) |
||||
*/ |
||||
public class MessageBuilder { |
||||
|
||||
private MessageSource messageSource; |
||||
|
||||
private Locale locale; |
||||
|
||||
private MessageResolverBuilder builder = new MessageResolverBuilder(); |
||||
|
||||
/** |
||||
* Create a new MessageBuilder that builds messages from message templates defined in the MessageSource |
||||
* @param messageSource the message source |
||||
*/ |
||||
public MessageBuilder(MessageSource messageSource) { |
||||
this.messageSource = messageSource; |
||||
} |
||||
|
||||
/** |
||||
* Add a code that will be tried to lookup the message template used to create the localized message. |
||||
* Successive calls to this method add additional codes. |
||||
* Codes are tried in the order they are added. |
||||
* @param code a message code to try |
||||
* @return this, for fluent API usage |
||||
*/ |
||||
public MessageBuilder code(String code) { |
||||
builder.code(code); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Add an argument to insert into the message. |
||||
* Named arguments are inserted by eval #{expressions} denoted within the message template. |
||||
* For example, the value of the 'format' argument would be inserted where a corresponding #{format} expression is defined in the message template. |
||||
* Successive calls to this method add additional arguments. |
||||
* May also add {@link ResolvableArgument resolvable arguments} whose values are resolved against the MessageSource. |
||||
* @param name the argument name |
||||
* @param value the argument value |
||||
* @return this, for fluent API usage |
||||
* @see ResolvableArgument |
||||
*/ |
||||
public MessageBuilder arg(String name, Object value) { |
||||
builder.arg(name, value); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Set the default message. |
||||
* If there are no codes to try, this will be used as the message. |
||||
* If there are codes to try but none of those resolve to a message, this will be used as the message. |
||||
* @param message the default text |
||||
* @return this, for fluent API usage |
||||
*/ |
||||
public MessageBuilder defaultMessage(String message) { |
||||
builder.defaultMessage(message); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Set the default message. |
||||
* If there are no codes to try, this will be used as the message. |
||||
* If there are codes to try but none of those resolve to a message, this will be used as the message. |
||||
* @param message the default text |
||||
* @return this, for fluent API usage |
||||
*/ |
||||
public MessageBuilder defaultMessage(DefaultMessageFactory defaultMessageFactory) { |
||||
builder.defaultMessage(defaultMessageFactory); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Set the message locale. |
||||
* If not set, the default locale the Locale of the current request obtained from {@link LocaleContextHolder#getLocale()}. |
||||
* @param message the locale |
||||
* @return this, for fluent API usage |
||||
*/ |
||||
public MessageBuilder locale(Locale locale) { |
||||
this.locale = locale; |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Builds the resolver for the message. |
||||
* Call after recording all builder instructions. |
||||
* @return the built message resolver |
||||
* @throws IllegalStateException if no codes have been added and there is no default message |
||||
*/ |
||||
public String build() { |
||||
return builder.build().resolveMessage(messageSource, getLocale()); |
||||
} |
||||
|
||||
// internal helpers
|
||||
|
||||
private Locale getLocale() { |
||||
if (locale != null) { |
||||
return locale; |
||||
} else { |
||||
return LocaleContextHolder.getLocale(); |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -1,43 +0,0 @@
@@ -1,43 +0,0 @@
|
||||
/* |
||||
* 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.model.message; |
||||
|
||||
/** |
||||
* Runtime exception thrown by a {@link MessageResolver} if a message resolution fails. |
||||
* @author Keith Donald |
||||
* @since 3.0 |
||||
*/ |
||||
@SuppressWarnings("serial") |
||||
public class MessageResolutionException extends RuntimeException { |
||||
|
||||
/** |
||||
* Creates a new message resolution exception. |
||||
* @param message a messaging describing the failure |
||||
*/ |
||||
public MessageResolutionException(String message) { |
||||
super(message); |
||||
} |
||||
|
||||
/** |
||||
* Creates a new message resolution exception. |
||||
* @param message a messaging describing the failure |
||||
* @param cause the cause of the failure |
||||
*/ |
||||
public MessageResolutionException(String message, Throwable cause) { |
||||
super(message, cause); |
||||
} |
||||
|
||||
} |
||||
@ -1,38 +0,0 @@
@@ -1,38 +0,0 @@
|
||||
/* |
||||
* 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.model.message; |
||||
|
||||
import java.util.Locale; |
||||
|
||||
import org.springframework.context.MessageSource; |
||||
|
||||
/** |
||||
* A factory for a localized message resolved from a MessageSource. |
||||
* @author Keith Donald |
||||
* @since 3.0 |
||||
* @see MessageSource |
||||
*/ |
||||
public interface MessageResolver { |
||||
|
||||
/** |
||||
* Resolve the message from the message source for the locale. |
||||
* @param messageSource the message source, an abstraction for a resource bundle |
||||
* @param locale the locale of this request |
||||
* @return the resolved message |
||||
* @throws MessageResolutionException if a resolution failure occurs |
||||
*/ |
||||
public String resolveMessage(MessageSource messageSource, Locale locale); |
||||
} |
||||
@ -1,131 +0,0 @@
@@ -1,131 +0,0 @@
|
||||
/* |
||||
* 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.model.message; |
||||
|
||||
import java.util.LinkedHashMap; |
||||
import java.util.LinkedHashSet; |
||||
import java.util.Map; |
||||
import java.util.Set; |
||||
|
||||
import org.springframework.expression.ExpressionParser; |
||||
import org.springframework.expression.spel.standard.SpelExpressionParser; |
||||
|
||||
/** |
||||
* Builds a {@link MessageResolver} that can resolve a localized message for display in a user interface. |
||||
* Allows convenient specification of the codes to try to resolve the message. |
||||
* Also supports named arguments that can inserted into a message template using eval #{expressions}. |
||||
* <p> |
||||
* Usage example: |
||||
* <pre> |
||||
* MessageResolver resolver = new MessageResolverBuilder(). |
||||
* code("invalidFormat"). |
||||
* arg("label", new ResolvableArgument("mathForm.decimalField")). |
||||
* arg("format", "#,###.##"). |
||||
* defaultMessage("The decimal field must be in format #,###.##"). |
||||
* build(); |
||||
* String message = resolver.resolveMessage(messageSource, locale); |
||||
* </pre> |
||||
* Example messages.properties loaded by the MessageSource: |
||||
* <pre> |
||||
* invalidFormat=The #{label} must be in format #{format}. |
||||
* mathForm.decimalField=Decimal Field |
||||
* </pre> |
||||
* TODO favor MessageBuilder accepting message source |
||||
* @author Keith Donald |
||||
* @since 3.0 |
||||
* @see #code(String) |
||||
* @see #arg(String, Object) |
||||
* @see #defaultMessage(String) |
||||
*/ |
||||
public class MessageResolverBuilder { |
||||
|
||||
private Set<String> codes = new LinkedHashSet<String>(); |
||||
|
||||
private Map<String, Object> args = new LinkedHashMap<String, Object>(); |
||||
|
||||
private DefaultMessageFactory defaultMessageFactory; |
||||
|
||||
private ExpressionParser expressionParser = new SpelExpressionParser(); |
||||
|
||||
/** |
||||
* Add a code that will be tried to lookup the message template used to create the localized message. |
||||
* Successive calls to this method add additional codes. |
||||
* Codes are tried in the order they are added. |
||||
* @param code a message code to try |
||||
* @return this, for fluent API usage |
||||
*/ |
||||
public MessageResolverBuilder code(String code) { |
||||
codes.add(code); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Add an argument to insert into the message. |
||||
* Named arguments are inserted by eval #{expressions} denoted within the message template. |
||||
* For example, the value of the 'format' argument would be inserted where a corresponding #{format} expression is defined in the message template. |
||||
* Successive calls to this method add additional arguments. |
||||
* May also add {@link ResolvableArgument resolvable arguments} whose values are resolved against the MessageSource passed to |
||||
* {@link MessageResolver#resolveMessage(org.springframework.context.MessageSource, java.util.Locale)}. |
||||
* @param name the argument name |
||||
* @param value the argument value |
||||
* @return this, for fluent API usage |
||||
* @see ResolvableArgument |
||||
*/ |
||||
public MessageResolverBuilder arg(String name, Object value) { |
||||
args.put(name, value); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Set the default message. |
||||
* If the MessageResolver has no codes to try, this will be used as the message. |
||||
* If the MessageResolver has codes to try but none of those resolve to a message, this will be used as the message. |
||||
* @param message the default text |
||||
* @return this, for fluent API usage |
||||
*/ |
||||
public MessageResolverBuilder defaultMessage(String message) { |
||||
return defaultMessage(new StaticDefaultMessageFactory(message)); |
||||
} |
||||
|
||||
/** |
||||
* Set the default message. |
||||
* If the MessageResolver has no codes to try, this will be used as the message. |
||||
* If the MessageResolver has codes to try but none of those resolve to a message, this will be used as the message. |
||||
* @param message the default text |
||||
* @return this, for fluent API usage |
||||
*/ |
||||
public MessageResolverBuilder defaultMessage(DefaultMessageFactory defaultMessageFactory) { |
||||
this.defaultMessageFactory = defaultMessageFactory; |
||||
return this; |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Builds the resolver for the message. |
||||
* Call after recording all builder instructions. |
||||
* @return the built message resolver |
||||
* @throws IllegalStateException if no codes have been added and there is no default message |
||||
*/ |
||||
public MessageResolver build() { |
||||
if (codes == null && defaultMessageFactory == null) { |
||||
throw new IllegalStateException( |
||||
"A message code or the message text is required to build this message resolver"); |
||||
} |
||||
String[] codesArray = (String[]) codes.toArray(new String[codes.size()]); |
||||
return new DefaultMessageResolver(codesArray, args, defaultMessageFactory, expressionParser); |
||||
} |
||||
|
||||
} |
||||
@ -1,56 +0,0 @@
@@ -1,56 +0,0 @@
|
||||
/* |
||||
* 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.model.message; |
||||
|
||||
import org.springframework.context.MessageSource; |
||||
import org.springframework.context.MessageSourceResolvable; |
||||
import org.springframework.core.style.ToStringCreator; |
||||
|
||||
/** |
||||
* A message argument value that is resolved from a MessageSource. |
||||
* Allows the value to be localized. |
||||
* @see MessageSource |
||||
* @author Keith Donald |
||||
*/ |
||||
public class ResolvableArgument implements MessageSourceResolvable { |
||||
|
||||
private String code; |
||||
|
||||
/** |
||||
* Creates a resolvable argument. |
||||
* @param code the code that will be used to lookup the argument value from the message source |
||||
*/ |
||||
public ResolvableArgument(String code) { |
||||
this.code = code; |
||||
} |
||||
|
||||
public String[] getCodes() { |
||||
return new String[] { code.toString() }; |
||||
} |
||||
|
||||
public Object[] getArguments() { |
||||
return null; |
||||
} |
||||
|
||||
public String getDefaultMessage() { |
||||
return String.valueOf(code); |
||||
} |
||||
|
||||
public String toString() { |
||||
return new ToStringCreator(this).append("code", code).toString(); |
||||
} |
||||
|
||||
} |
||||
@ -1,30 +0,0 @@
@@ -1,30 +0,0 @@
|
||||
/* |
||||
* 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.model.message; |
||||
|
||||
class StaticDefaultMessageFactory implements DefaultMessageFactory { |
||||
|
||||
private String defaultMessage; |
||||
|
||||
public StaticDefaultMessageFactory(String defaultMessage) { |
||||
this.defaultMessage = defaultMessage; |
||||
} |
||||
|
||||
public String createDefaultMessage() { |
||||
return defaultMessage; |
||||
} |
||||
|
||||
} |
||||
@ -1,5 +0,0 @@
@@ -1,5 +0,0 @@
|
||||
/** |
||||
* An API for creating localized messages. |
||||
*/ |
||||
package org.springframework.model.message; |
||||
|
||||
@ -1,50 +0,0 @@
@@ -1,50 +0,0 @@
|
||||
/* |
||||
* 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.model.ui; |
||||
|
||||
/** |
||||
* FieldModel binding states. |
||||
* @author Keith Donald |
||||
* @since 3.0 |
||||
* @see FieldModel#getBindingStatus() |
||||
*/ |
||||
public enum BindingStatus { |
||||
|
||||
/** |
||||
* Initial state: No value is buffered, and there is a direct channel to the model value. |
||||
*/ |
||||
CLEAN, |
||||
|
||||
/** |
||||
* An invalid submitted value is applied. |
||||
*/ |
||||
INVALID_SUBMITTED_VALUE, |
||||
|
||||
/** |
||||
* The binding buffer contains a valid value that has not been committed. |
||||
*/ |
||||
DIRTY, |
||||
|
||||
/** |
||||
* The buffered value has been committed. |
||||
*/ |
||||
COMMITTED, |
||||
|
||||
/** |
||||
* The buffered value failed to commit. |
||||
*/ |
||||
COMMIT_FAILURE |
||||
} |
||||
@ -1,174 +0,0 @@
@@ -1,174 +0,0 @@
|
||||
/* |
||||
* 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.model.ui; |
||||
|
||||
import org.springframework.model.alert.Alert; |
||||
|
||||
/** |
||||
* A model for a single data field containing dynamic information to display in the view. |
||||
* @author Keith Donald |
||||
* @since 3.0 |
||||
*/ |
||||
public interface FieldModel { |
||||
|
||||
/** |
||||
* The model value formatted for display in a single field in the UI. |
||||
* Is the formatted model value if {@link BindingStatus#CLEAN} or {@link BindingStatus#COMMITTED}. |
||||
* Is the formatted buffered value if {@link BindingStatus#DIRTY} or {@link BindingStatus#COMMIT_FAILURE}. |
||||
*/ |
||||
String getRenderValue(); |
||||
|
||||
/** |
||||
* The field model value. |
||||
*/ |
||||
Object getValue(); |
||||
|
||||
/** |
||||
* The field model value type. |
||||
*/ |
||||
Class<?> getValueType(); |
||||
|
||||
/** |
||||
* If editable. |
||||
* Used to determine if the user can edit the field value. |
||||
* A Binding that is not editable cannot have submitted values applied and cannot be committed. |
||||
*/ |
||||
boolean isEditable(); |
||||
|
||||
/** |
||||
* If enabled. |
||||
* Used to determine if the user can interact with the field at all. |
||||
* A Binding that is not enabled cannot have submitted values applied and cannot be committed. |
||||
*/ |
||||
boolean isEnabled(); |
||||
|
||||
/** |
||||
* If visible. |
||||
* Used to determine if the user can see the field. |
||||
*/ |
||||
boolean isVisible(); |
||||
|
||||
/** |
||||
* The current field binding status. |
||||
* Initially {@link BindingStatus#CLEAN clean}. |
||||
* Is {@link BindingStatus#DIRTY} after applying a submitted value to the value buffer. |
||||
* Is {@link BindingStatus#COMMITTED} after successfully committing the buffered value. |
||||
* Is {@link BindingStatus#INVALID_SUBMITTED_VALUE} if a submitted value could not be applied. |
||||
* Is {@link BindingStatus#COMMIT_FAILURE} if a buffered value could not be committed. |
||||
*/ |
||||
BindingStatus getBindingStatus(); |
||||
|
||||
/** |
||||
* The current field validation status. |
||||
* Initially {@link ValidationStatus#NOT_VALIDATED}. |
||||
* Is {@link ValidationStatus#VALID} after value is successfully validated. |
||||
* Is {@link ValidationStatus#INVALID} after value fails validation. |
||||
* Resets to {@value ValidationStatus#NOT_VALIDATED} when value changes. |
||||
*/ |
||||
ValidationStatus getValidationStatus(); |
||||
|
||||
/** |
||||
* An alert that communicates current FieldModel status to the user. |
||||
* Returns <code>null</code> if {@link BindingStatus#CLEAN} and {@link ValidationStatus#NOT_VALIDATED}. |
||||
* Returns a {@link Severity#INFO} Alert with code <code>bindSuccess</code> when {@link BindingStatus#COMMITTED}. |
||||
* Returns a {@link Severity#ERROR} Alert with code <code>typeMismatch</code> when {@link BindingStatus#INVALID_SUBMITTED_VALUE} or {@link BindingStatus#COMMIT_FAILURE} due to a value parse / type conversion error. |
||||
* Returns a {@link Severity#FATAL} Alert with code <code>internalError</code> when {@link BindingStatus#COMMIT_FAILURE} due to a unexpected runtime exception. |
||||
* Returns a {@link Severity#INFO} Alert describing results of validation if {@link ValidationStatus#VALID} or {@link ValidationStatus#INVALID}. |
||||
*/ |
||||
Alert getStatusAlert(); |
||||
|
||||
/** |
||||
* Apply a submitted value to this FieldModel. |
||||
* The submitted value is parsed and stored in the value buffer. |
||||
* Sets to {@link BindingStatus#DIRTY} if succeeds. |
||||
* Sets to {@link BindingStatus#INVALID_SUBMITTED_VALUE} if fails. |
||||
* @param submittedValue |
||||
* @throws IllegalStateException if not editable or not enabled |
||||
*/ |
||||
void applySubmittedValue(Object submittedValue); |
||||
|
||||
/** |
||||
* If {@link BindingStatus#INVALID_SUBMITTED_VALUE}, returns the invalid submitted value. |
||||
* Returns null otherwise. |
||||
* @return the invalid submitted value |
||||
*/ |
||||
Object getInvalidSubmittedValue(); |
||||
|
||||
/** |
||||
* Validate the model value. |
||||
* Sets to {@link ValidationStatus#VALID} if succeeds. |
||||
* Sets to {@link ValidationStatus#INVALID} if fails. |
||||
*/ |
||||
void validate(); |
||||
|
||||
/** |
||||
* Commit the buffered value to the model. |
||||
* Sets to {@link BindingStatus#COMMITTED} if succeeds. |
||||
* Sets to {@link BindingStatus#COMMIT_FAILURE} if fails. |
||||
* @throws IllegalStateException if not editable, not enabled, or not dirty |
||||
*/ |
||||
void commit(); |
||||
|
||||
/** |
||||
* Clear the buffered value without committing. |
||||
* @throws IllegalStateException if BindingStatus is CLEAN or COMMITTED. |
||||
*/ |
||||
void revert(); |
||||
|
||||
/** |
||||
* Get a model for a nested field. |
||||
* @param fieldName the nested field name, such as "foo"; should not be a property path like "foo.bar" |
||||
* @return the nested field model |
||||
* @throws IllegalStateException if {@link #isList()} |
||||
* @throws FieldNotFoundException if no such nested field exists |
||||
*/ |
||||
FieldModel getNested(String fieldName); |
||||
|
||||
/** |
||||
* If an indexable {@link java.util.List} or array. |
||||
*/ |
||||
boolean isList(); |
||||
|
||||
/** |
||||
* If {@link #isList()}, get a FieldModel for a element in the list.. |
||||
* @param index the element index |
||||
* @return the indexed binding |
||||
* @throws IllegalStateException if not a list |
||||
*/ |
||||
FieldModel getListElement(int index); |
||||
|
||||
/** |
||||
* If a Map. |
||||
*/ |
||||
boolean isMap(); |
||||
|
||||
/** |
||||
* If {@link #isMap()}, get FieldModel for a value in the Map. |
||||
* @param key the map key |
||||
* @return the keyed binding |
||||
* @throws IllegalStateException if not a map |
||||
*/ |
||||
FieldModel getMapValue(Object key); |
||||
|
||||
/** |
||||
* Format a potential model value for display. |
||||
* If {@link #isList()}, expects the value to be a potential list element & uses the configured element formatter. |
||||
* If {@link #isMap()}, expects the value to be a potential map value & uses the configured map value formatter. |
||||
* @param potentialValue the potential value |
||||
* @return the formatted string |
||||
*/ |
||||
String formatValue(Object potentialValue); |
||||
} |
||||
@ -1,43 +0,0 @@
@@ -1,43 +0,0 @@
|
||||
/* |
||||
* 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.model.ui; |
||||
|
||||
|
||||
/** |
||||
* Thrown when a PresentationModel field cannot be found. |
||||
* @author Keith Donald |
||||
* @since 3.0 |
||||
* @see PresentationModel#getFieldModel(String) |
||||
* @see FieldModel#getNested(String) |
||||
*/ |
||||
@SuppressWarnings("serial") |
||||
public class FieldNotFoundException extends RuntimeException { |
||||
|
||||
private String field; |
||||
|
||||
/** |
||||
* Creates a new FieldNotFoundException. |
||||
* @param fieldName the field not found exception |
||||
*/ |
||||
public FieldNotFoundException(String fieldName) { |
||||
super("No field '" + fieldName + "' found"); |
||||
} |
||||
|
||||
public String getField() { |
||||
return field; |
||||
} |
||||
|
||||
} |
||||
@ -1,52 +0,0 @@
@@ -1,52 +0,0 @@
|
||||
/* |
||||
* 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.model.ui; |
||||
|
||||
/** |
||||
* Represents the state and behavior of a presentation independently of the GUI controls used in the interface. |
||||
* Pulls the state and behavior of a view out into a model class that is part of the presentation. |
||||
* Coordinates with the domain layer and provides an interface to the view that minimizes decision making in the view. |
||||
* @author Keith Donald |
||||
* @since 3.0 |
||||
*/ |
||||
public interface PresentationModel { |
||||
|
||||
/** |
||||
* Get the model for the field. |
||||
* @param fieldName the field name. |
||||
* @throws FieldNotFoundException if no such field exists |
||||
*/ |
||||
FieldModel getFieldModel(String fieldName); |
||||
|
||||
/** |
||||
* Validate all fields. |
||||
* Skips any fields with {@link BindingStatus#INVALID_SUBMITTED_VALUE invalid submitted values}. |
||||
*/ |
||||
void validate(); |
||||
|
||||
/** |
||||
* If errors are present on this PresentationModel. |
||||
* Returns true if at least one FieldModel has {@link BindingStatus#INVALID_SUBMITTED_VALUE invalid submitted values} or is {@link ValidationStatus#INVALID invalid}. |
||||
*/ |
||||
boolean hasErrors(); |
||||
|
||||
/** |
||||
* Commit any {@link BindingStatus#DIRTY dirty} fields. |
||||
* @throws IllegalStateException if there are field models that have {@link BindingStatus#INVALID_SUBMITTED_VALUE invalid submitted values} or are {@link ValidationStatus#INVALID invalid}. |
||||
*/ |
||||
void commit(); |
||||
|
||||
} |
||||
@ -1,34 +0,0 @@
@@ -1,34 +0,0 @@
|
||||
/* |
||||
* 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.model.ui; |
||||
|
||||
/** |
||||
* A factory for domain object PresentationModels. |
||||
* Makes it easy for clients to lookup PresentationModels for domain objects they need to bind to. |
||||
* @author Keith Donald |
||||
* @since 3.0 |
||||
*/ |
||||
public interface PresentationModelFactory { |
||||
|
||||
/** |
||||
* Get the PresentationModel for the domain object. |
||||
* If none exists, one is created and cached. |
||||
* Never returns <code>null</code>. |
||||
* @param domainObject the model object |
||||
* @return the presentation model |
||||
*/ |
||||
public PresentationModel getPresentationModel(Object domainObject); |
||||
} |
||||
@ -1,40 +0,0 @@
@@ -1,40 +0,0 @@
|
||||
/* |
||||
* 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.model.ui; |
||||
|
||||
/** |
||||
* FieldModel Validation states. |
||||
* @author Keith Donald |
||||
* @since 3.0 |
||||
* @see FieldModel#getValidationStatus() |
||||
*/ |
||||
public enum ValidationStatus { |
||||
|
||||
/** |
||||
* Initial state: No validation has run. |
||||
*/ |
||||
NOT_VALIDATED, |
||||
|
||||
/** |
||||
* Validation has succeeded. |
||||
*/ |
||||
VALID, |
||||
|
||||
/** |
||||
* Validation has failed. |
||||
*/ |
||||
INVALID |
||||
} |
||||
@ -1,58 +0,0 @@
@@ -1,58 +0,0 @@
|
||||
/* |
||||
* 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.model.ui.config; |
||||
|
||||
/** |
||||
* A SPI interface that lets you configure a BindingLifecycle for a model, then execute it. |
||||
* Hides details about the source of submitted field values. |
||||
* @author Keith Donald |
||||
* @since 3.0 |
||||
* @param <M> the type of model this lifecycle is for |
||||
*/ |
||||
public interface BindingLifecycle<M> { |
||||
|
||||
/** |
||||
* Configure the model object to bind to. |
||||
* Optional operation. |
||||
* If not called, the model be a new instance of <M> created by invoking it's default constructor. |
||||
* @param model the model |
||||
*/ |
||||
void setModel(M model); |
||||
|
||||
/** |
||||
* Execute this binding lifecycle. |
||||
* The steps are: |
||||
* <ul> |
||||
* <li>Get a PresentationModel for model M.</li> |
||||
* <li>Bind submitted values to the PresentationModel</li> |
||||
* <li>Validate the PresentationModel</li>. |
||||
* <li>Commit changes to M if no bind and validation errors occur.</li> |
||||
* </ul> |
||||
* @throws IllegalStateExeption if no model was set and no default constructor was found on M. |
||||
*/ |
||||
void execute(); |
||||
|
||||
/** |
||||
* If executing the lifecycle produced errors. |
||||
*/ |
||||
boolean hasErrors(); |
||||
|
||||
/** |
||||
* Get the model instance this lifecycle executed against. |
||||
*/ |
||||
M getModel(); |
||||
|
||||
} |
||||
@ -1,52 +0,0 @@
@@ -1,52 +0,0 @@
|
||||
/* |
||||
* 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.model.ui.config; |
||||
|
||||
import org.springframework.model.ui.FieldModel; |
||||
|
||||
/** |
||||
* A FieldModel condition. |
||||
* @author Keith Donald |
||||
* @see FieldModel#isEnabled() |
||||
* @see FieldModel#isEditable() |
||||
* @see FieldModel#isVisible() |
||||
*/ |
||||
public interface Condition { |
||||
|
||||
/** |
||||
* Is the condition true or false? |
||||
*/ |
||||
boolean isTrue(); |
||||
|
||||
/** |
||||
* The condition is always true. |
||||
*/ |
||||
static final Condition ALWAYS_TRUE = new Condition() { |
||||
public boolean isTrue() { |
||||
return true; |
||||
} |
||||
}; |
||||
|
||||
/** |
||||
* The condition is always false. |
||||
*/ |
||||
static final Condition ALWAYS_FALSE = new Condition() { |
||||
public boolean isTrue() { |
||||
return false; |
||||
} |
||||
}; |
||||
|
||||
} |
||||
@ -1,57 +0,0 @@
@@ -1,57 +0,0 @@
|
||||
/* |
||||
* 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.model.ui.config; |
||||
|
||||
import org.springframework.model.ui.FieldModel; |
||||
import org.springframework.model.ui.format.Formatter; |
||||
|
||||
/** |
||||
* A fluent interface for configuring a {@link FieldModel}. |
||||
* @author Keith Donald |
||||
* @since 3.0 |
||||
*/ |
||||
public interface FieldModelConfiguration { |
||||
|
||||
/** |
||||
* Set the Formatter to use to format bound property values. |
||||
*/ |
||||
FieldModelConfiguration formatWith(Formatter<?> formatter); |
||||
|
||||
/** |
||||
* If a Map field, set the Formatter to use to format map keys. |
||||
*/ |
||||
FieldModelConfiguration formatKeysWith(Formatter<?> formatter); |
||||
|
||||
/** |
||||
* If a List, array, or Map, set the Formatter to use to format indexed elements. |
||||
*/ |
||||
FieldModelConfiguration formatElementsWith(Formatter<?> formatter); |
||||
|
||||
/** |
||||
* Set when the binding is editable. |
||||
*/ |
||||
FieldModelConfiguration editableWhen(Condition condition); |
||||
|
||||
/** |
||||
* Set when the binding is enabled. |
||||
*/ |
||||
FieldModelConfiguration enabledWhen(Condition condition); |
||||
|
||||
/** |
||||
* Set when the binding is visible. |
||||
*/ |
||||
FieldModelConfiguration visibleWhen(Condition condition); |
||||
} |
||||
@ -1,5 +0,0 @@
@@ -1,5 +0,0 @@
|
||||
/** |
||||
* PresentationModel configuration SPI. |
||||
*/ |
||||
package org.springframework.model.ui.config; |
||||
|
||||
@ -1,37 +0,0 @@
@@ -1,37 +0,0 @@
|
||||
/* |
||||
* 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.model.ui.format; |
||||
|
||||
import java.lang.annotation.Annotation; |
||||
|
||||
/** |
||||
* A factory that creates {@link Formatter formatters} to format property values on properties annotated with a particular format {@link Annotation}. |
||||
* For example, a <code>CurrencyAnnotationFormatterFactory</code> might create a <code>Formatter</code> that formats a <code>BigDecimal</code> value set on a property annotated with <code>@CurrencyFormat</code>. |
||||
* @author Keith Donald |
||||
* @since 3.0 |
||||
* @param <A> The type of Annotation this factory uses to create Formatter instances |
||||
* @param <T> The type of Object Formatters created by this factory format |
||||
*/ |
||||
public interface AnnotationFormatterFactory<A extends Annotation, T> { |
||||
|
||||
/** |
||||
* Get the Formatter that will format the value of the property annotated with the provided annotation. |
||||
* The annotation instance can contain properties that may be used to configure the Formatter that is returned. |
||||
* @param annotation the annotation instance |
||||
* @return the Formatter to use to format values of properties annotated with the annotation. |
||||
*/ |
||||
Formatter<T> getFormatter(A annotation); |
||||
} |
||||
@ -1,38 +0,0 @@
@@ -1,38 +0,0 @@
|
||||
/* |
||||
* 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.model.ui.format; |
||||
|
||||
import java.lang.annotation.Documented; |
||||
import java.lang.annotation.ElementType; |
||||
import java.lang.annotation.Retention; |
||||
import java.lang.annotation.RetentionPolicy; |
||||
import java.lang.annotation.Target; |
||||
|
||||
/** |
||||
* A type that can be formatted as a String for display in a UI. |
||||
* @author Keith Donald |
||||
* @since 3.0 |
||||
*/ |
||||
@Target({ElementType.TYPE}) |
||||
@Retention(RetentionPolicy.RUNTIME) |
||||
@Documented |
||||
public @interface Formatted { |
||||
|
||||
/** |
||||
* The Formatter that handles the formatting. |
||||
*/ |
||||
Class<?> value(); |
||||
} |
||||
@ -1,45 +0,0 @@
@@ -1,45 +0,0 @@
|
||||
/* |
||||
* 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.model.ui.format; |
||||
|
||||
import java.text.ParseException; |
||||
import java.util.Locale; |
||||
|
||||
/** |
||||
* Formats objects of type T for display. |
||||
* @author Keith Donald |
||||
* @since 3.0 |
||||
* @param <T> the type of object this formatter can format |
||||
*/ |
||||
public interface Formatter<T> { |
||||
|
||||
/** |
||||
* Format the object of type T for display. |
||||
* @param object the object to format |
||||
* @param locale the user's locale |
||||
* @return the formatted display string |
||||
*/ |
||||
String format(T object, Locale locale); |
||||
|
||||
/** |
||||
* Parse an object from its formatted representation. |
||||
* @param formatted a formatted representation |
||||
* @param locale the user's locale |
||||
* @return the parsed object |
||||
* @throws ParseException when a parse exception occurs |
||||
*/ |
||||
T parse(String formatted, Locale locale) throws ParseException; |
||||
} |
||||
@ -1,90 +0,0 @@
@@ -1,90 +0,0 @@
|
||||
/* |
||||
* 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.model.ui.format.date; |
||||
|
||||
import java.text.DateFormat; |
||||
import java.text.ParseException; |
||||
import java.text.SimpleDateFormat; |
||||
import java.util.Date; |
||||
import java.util.Locale; |
||||
|
||||
import org.apache.commons.logging.Log; |
||||
import org.apache.commons.logging.LogFactory; |
||||
import org.springframework.model.ui.format.Formatter; |
||||
|
||||
/** |
||||
* A formatter for {@link Date} types. |
||||
* Allows the configuration of an explicit date pattern and locale. |
||||
* @author Keith Donald |
||||
* @since 3.0 |
||||
* @see SimpleDateFormat |
||||
*/ |
||||
public class DateFormatter implements Formatter<Date> { |
||||
|
||||
private static Log logger = LogFactory.getLog(DateFormatter.class); |
||||
|
||||
/** |
||||
* The default date pattern. |
||||
*/ |
||||
private static final String DEFAULT_PATTERN = "yyyy-MM-dd"; |
||||
|
||||
private String pattern; |
||||
|
||||
/** |
||||
* Sets the pattern to use to format date values. |
||||
* If not specified, the default pattern 'yyyy-MM-dd' is used. |
||||
* @param pattern the date formatting pattern |
||||
*/ |
||||
public void setPattern(String pattern) { |
||||
this.pattern = pattern; |
||||
} |
||||
|
||||
public String format(Date date, Locale locale) { |
||||
if (date == null) { |
||||
return ""; |
||||
} |
||||
return getDateFormat(locale).format(date); |
||||
} |
||||
|
||||
public Date parse(String formatted, Locale locale) throws ParseException { |
||||
if (formatted.length() == 0) { |
||||
return null; |
||||
} |
||||
return getDateFormat(locale).parse(formatted); |
||||
} |
||||
|
||||
// subclassing hookings
|
||||
|
||||
protected DateFormat getDateFormat(Locale locale) { |
||||
DateFormat format = DateFormat.getDateInstance(DateFormat.SHORT, locale); |
||||
format.setLenient(false); |
||||
if (format instanceof SimpleDateFormat) { |
||||
String pattern = determinePattern(this.pattern); |
||||
((SimpleDateFormat) format).applyPattern(pattern); |
||||
} else { |
||||
logger.warn("Unable to apply format pattern '" + pattern |
||||
+ "'; Returned DateFormat is not a SimpleDateFormat"); |
||||
} |
||||
return format; |
||||
} |
||||
|
||||
// internal helpers
|
||||
|
||||
private String determinePattern(String pattern) { |
||||
return pattern != null ? pattern : DEFAULT_PATTERN; |
||||
} |
||||
|
||||
} |
||||
@ -1,5 +0,0 @@
@@ -1,5 +0,0 @@
|
||||
/** |
||||
* Formatters for <code>java.util.Date</code> fields. |
||||
*/ |
||||
package org.springframework.model.ui.format.date; |
||||
|
||||
@ -1,34 +0,0 @@
@@ -1,34 +0,0 @@
|
||||
/* |
||||
* 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.model.ui.format.number; |
||||
|
||||
import java.lang.annotation.Documented; |
||||
import java.lang.annotation.ElementType; |
||||
import java.lang.annotation.Retention; |
||||
import java.lang.annotation.RetentionPolicy; |
||||
import java.lang.annotation.Target; |
||||
|
||||
/** |
||||
* A annotation to apply to a BigDecimal property to have its value formatted as currency amount using a {@link CurrencyFormatter}. |
||||
* @author Keith Donald |
||||
* @since 3.0 |
||||
*/ |
||||
@Target( { ElementType.METHOD, ElementType.FIELD }) |
||||
@Retention(RetentionPolicy.RUNTIME) |
||||
@Documented |
||||
public @interface CurrencyFormat { |
||||
|
||||
} |
||||
@ -1,71 +0,0 @@
@@ -1,71 +0,0 @@
|
||||
/* |
||||
* 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.model.ui.format.number; |
||||
|
||||
import java.math.BigDecimal; |
||||
import java.math.RoundingMode; |
||||
import java.text.NumberFormat; |
||||
import java.text.ParseException; |
||||
import java.text.ParsePosition; |
||||
import java.util.Locale; |
||||
|
||||
import org.springframework.model.ui.format.Formatter; |
||||
|
||||
/** |
||||
* A BigDecimal formatter for currency values. |
||||
* Delegates to {@link NumberFormat#getCurrencyInstance(Locale)}. |
||||
* Configures BigDecimal parsing so there is no loss of precision. |
||||
* Sets the scale of parsed BigDecimal values to {@link NumberFormat#getMaximumFractionDigits()}. |
||||
* Applies {@link RoundingMode#DOWN} to parsed values. |
||||
* @author Keith Donald |
||||
* @since 3.0 |
||||
*/ |
||||
public class CurrencyFormatter implements Formatter<BigDecimal> { |
||||
|
||||
private CurrencyNumberFormatFactory currencyFormatFactory = new CurrencyNumberFormatFactory(); |
||||
|
||||
private boolean lenient; |
||||
|
||||
public String format(BigDecimal decimal, Locale locale) { |
||||
if (decimal == null) { |
||||
return ""; |
||||
} |
||||
NumberFormat format = currencyFormatFactory.getNumberFormat(locale); |
||||
return format.format(decimal); |
||||
} |
||||
|
||||
public BigDecimal parse(String formatted, Locale locale) |
||||
throws ParseException { |
||||
if (formatted.length() == 0) { |
||||
return null; |
||||
} |
||||
NumberFormat format = currencyFormatFactory.getNumberFormat(locale); |
||||
ParsePosition position = new ParsePosition(0); |
||||
BigDecimal decimal = (BigDecimal) format.parse(formatted, position); |
||||
if (position.getErrorIndex() != -1) { |
||||
throw new ParseException(formatted, position.getIndex()); |
||||
} |
||||
if (!lenient) { |
||||
if (formatted.length() != position.getIndex()) { |
||||
// indicates a part of the string that was not parsed
|
||||
throw new ParseException(formatted, position.getIndex()); |
||||
} |
||||
} |
||||
decimal = decimal.setScale(format.getMaximumFractionDigits(), format.getRoundingMode()); |
||||
return decimal; |
||||
} |
||||
|
||||
} |
||||
@ -1,39 +0,0 @@
@@ -1,39 +0,0 @@
|
||||
/* |
||||
* 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.model.ui.format.number; |
||||
|
||||
import java.math.RoundingMode; |
||||
import java.text.DecimalFormat; |
||||
import java.text.NumberFormat; |
||||
import java.util.Locale; |
||||
|
||||
/** |
||||
* Produces NumberFormat instances that format currency values. |
||||
* @author Keith Donald |
||||
* @since 3.0 |
||||
* @see NumberFormat |
||||
*/ |
||||
final class CurrencyNumberFormatFactory extends NumberFormatFactory { |
||||
|
||||
private RoundingMode roundingMode = RoundingMode.DOWN; |
||||
|
||||
public NumberFormat getNumberFormat(Locale locale) { |
||||
DecimalFormat format = (DecimalFormat) NumberFormat.getCurrencyInstance(locale); |
||||
format.setParseBigDecimal(true); |
||||
format.setRoundingMode(roundingMode); |
||||
return format; |
||||
} |
||||
} |
||||
@ -1,81 +0,0 @@
@@ -1,81 +0,0 @@
|
||||
/* |
||||
* 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.model.ui.format.number; |
||||
|
||||
import java.math.BigDecimal; |
||||
import java.text.NumberFormat; |
||||
import java.text.ParseException; |
||||
import java.text.ParsePosition; |
||||
import java.util.Locale; |
||||
|
||||
import org.springframework.model.ui.format.Formatter; |
||||
|
||||
/** |
||||
* A BigDecimal formatter for decimal values. |
||||
* Delegates to {@link NumberFormat#getInstance(Locale)}. |
||||
* Configures BigDecimal parsing so there is no loss in precision. |
||||
* Allows configuration over the decimal number pattern; see {@link #DecimalFormatter(String)}. |
||||
* @author Keith Donald |
||||
* @since 3.0 |
||||
*/ |
||||
public class DecimalFormatter implements Formatter<BigDecimal> { |
||||
|
||||
private DefaultNumberFormatFactory formatFactory = new DefaultNumberFormatFactory(); |
||||
|
||||
private boolean lenient; |
||||
|
||||
public DecimalFormatter() { |
||||
initDefaults(); |
||||
} |
||||
|
||||
public DecimalFormatter(String pattern) { |
||||
initDefaults(); |
||||
formatFactory.setPattern(pattern); |
||||
} |
||||
|
||||
public String format(BigDecimal decimal, Locale locale) { |
||||
if (decimal == null) { |
||||
return ""; |
||||
} |
||||
NumberFormat format = formatFactory.getNumberFormat(locale); |
||||
return format.format(decimal); |
||||
} |
||||
|
||||
public BigDecimal parse(String formatted, Locale locale) |
||||
throws ParseException { |
||||
if (formatted.length() == 0) { |
||||
return null; |
||||
} |
||||
NumberFormat format = formatFactory.getNumberFormat(locale); |
||||
ParsePosition position = new ParsePosition(0); |
||||
BigDecimal decimal = (BigDecimal) format.parse(formatted, position); |
||||
if (position.getErrorIndex() != -1) { |
||||
throw new ParseException(formatted, position.getIndex()); |
||||
} |
||||
if (!lenient) { |
||||
if (formatted.length() != position.getIndex()) { |
||||
// indicates a part of the string that was not parsed
|
||||
throw new ParseException(formatted, position.getIndex()); |
||||
} |
||||
} |
||||
return decimal; |
||||
} |
||||
|
||||
private void initDefaults() { |
||||
formatFactory.setParseBigDecimal(true); |
||||
} |
||||
|
||||
} |
||||
@ -1,80 +0,0 @@
@@ -1,80 +0,0 @@
|
||||
/* |
||||
* 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.model.ui.format.number; |
||||
|
||||
import java.text.DecimalFormat; |
||||
import java.text.NumberFormat; |
||||
import java.util.Locale; |
||||
|
||||
import org.apache.commons.logging.Log; |
||||
import org.apache.commons.logging.LogFactory; |
||||
|
||||
/** |
||||
* Works with a general purpose {@link DecimalFormat} instance returned by calling |
||||
* {@link NumberFormat#getInstance(Locale)} by default. |
||||
* @author Keith Donald |
||||
* @see NumberFormat |
||||
* @see DecimalFormat |
||||
* @since 3.0 |
||||
*/ |
||||
class DefaultNumberFormatFactory extends NumberFormatFactory { |
||||
|
||||
private static Log logger = LogFactory.getLog(DefaultNumberFormatFactory.class); |
||||
|
||||
private String pattern; |
||||
|
||||
private Boolean parseBigDecimal; |
||||
|
||||
/** |
||||
* Sets the pattern to use to format number values. |
||||
* If not specified, the default DecimalFormat pattern is used. |
||||
* @param pattern the format pattern |
||||
* @see DecimalFormat#applyPattern(String) |
||||
*/ |
||||
public void setPattern(String pattern) { |
||||
this.pattern = pattern; |
||||
} |
||||
|
||||
/** |
||||
* Sets whether the format should always parse a big decimal. |
||||
* @param parseBigDecimal the big decimal parse status |
||||
* @see DecimalFormat#setParseBigDecimal(boolean) |
||||
*/ |
||||
public void setParseBigDecimal(boolean parseBigDecimal) { |
||||
this.parseBigDecimal = parseBigDecimal; |
||||
} |
||||
|
||||
public NumberFormat getNumberFormat(Locale locale) { |
||||
NumberFormat format = NumberFormat.getInstance(locale); |
||||
if (pattern != null) { |
||||
if (format instanceof DecimalFormat) { |
||||
((DecimalFormat) format).applyPattern(pattern); |
||||
} else { |
||||
logger.warn("Unable to apply format pattern '" + pattern |
||||
+ "'; Returned NumberFormat is not a DecimalFormat"); |
||||
} |
||||
} |
||||
if (parseBigDecimal != null) { |
||||
if (format instanceof DecimalFormat) { |
||||
((DecimalFormat) format).setParseBigDecimal(parseBigDecimal); |
||||
} else { |
||||
logger.warn("Unable to call setParseBigDecimal; not a DecimalFormat"); |
||||
} |
||||
} |
||||
return format; |
||||
} |
||||
|
||||
} |
||||
@ -1,65 +0,0 @@
@@ -1,65 +0,0 @@
|
||||
/* |
||||
* 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.model.ui.format.number; |
||||
|
||||
import java.text.NumberFormat; |
||||
import java.text.ParseException; |
||||
import java.text.ParsePosition; |
||||
import java.util.Locale; |
||||
|
||||
import org.springframework.model.ui.format.Formatter; |
||||
|
||||
/** |
||||
* A Long formatter for whole integer values. |
||||
* Delegates to {@link NumberFormat#getIntegerInstance(Locale)}. |
||||
* @author Keith Donald |
||||
* @since 3.0 |
||||
*/ |
||||
public class IntegerFormatter implements Formatter<Long> { |
||||
|
||||
private IntegerNumberFormatFactory formatFactory = new IntegerNumberFormatFactory(); |
||||
|
||||
private boolean lenient; |
||||
|
||||
public String format(Long integer, Locale locale) { |
||||
if (integer == null) { |
||||
return ""; |
||||
} |
||||
NumberFormat format = formatFactory.getNumberFormat(locale); |
||||
return format.format(integer); |
||||
} |
||||
|
||||
public Long parse(String formatted, Locale locale) |
||||
throws ParseException { |
||||
if (formatted.length() == 0) { |
||||
return null; |
||||
} |
||||
NumberFormat format = formatFactory.getNumberFormat(locale); |
||||
ParsePosition position = new ParsePosition(0); |
||||
Long integer = (Long) format.parse(formatted, position); |
||||
if (position.getErrorIndex() != -1) { |
||||
throw new ParseException(formatted, position.getIndex()); |
||||
} |
||||
if (!lenient) { |
||||
if (formatted.length() != position.getIndex()) { |
||||
// indicates a part of the string that was not parsed
|
||||
throw new ParseException(formatted, position.getIndex()); |
||||
} |
||||
} |
||||
return integer; |
||||
} |
||||
|
||||
} |
||||
@ -1,31 +0,0 @@
@@ -1,31 +0,0 @@
|
||||
/* |
||||
* 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.model.ui.format.number; |
||||
|
||||
import java.text.NumberFormat; |
||||
import java.util.Locale; |
||||
|
||||
/** |
||||
* Produces NumberFormat instances that format integer values. |
||||
* @author Keith Donald |
||||
* @see NumberFormat |
||||
* @since 3.0 |
||||
*/ |
||||
final class IntegerNumberFormatFactory extends NumberFormatFactory { |
||||
public NumberFormat getNumberFormat(Locale locale) { |
||||
return NumberFormat.getIntegerInstance(locale); |
||||
} |
||||
} |
||||
@ -1,36 +0,0 @@
@@ -1,36 +0,0 @@
|
||||
/* |
||||
* 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.model.ui.format.number; |
||||
|
||||
import java.text.NumberFormat; |
||||
import java.util.Locale; |
||||
|
||||
/** |
||||
* A factory for {@link NumberFormat} objects. |
||||
* Conceals the complexity associated with configuring, constructing, and/or caching number format instances. |
||||
* @author Keith Donald |
||||
* @since 3.0 |
||||
*/ |
||||
abstract class NumberFormatFactory { |
||||
|
||||
/** |
||||
* Factory method that returns a fully-configured {@link NumberFormat} instance to use to format an object for |
||||
* display. |
||||
* @return the number format |
||||
*/ |
||||
public abstract NumberFormat getNumberFormat(Locale locale); |
||||
|
||||
} |
||||
@ -1,67 +0,0 @@
@@ -1,67 +0,0 @@
|
||||
/* |
||||
* 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.model.ui.format.number; |
||||
|
||||
import java.math.BigDecimal; |
||||
import java.text.NumberFormat; |
||||
import java.text.ParseException; |
||||
import java.text.ParsePosition; |
||||
import java.util.Locale; |
||||
|
||||
import org.springframework.model.ui.format.Formatter; |
||||
|
||||
/** |
||||
* A BigDecimal formatter for percent values. |
||||
* Delegates to {@link NumberFormat#getPercentInstance(Locale)}. |
||||
* Configures BigDecimal parsing so there is no loss in precision. |
||||
* @author Keith Donald |
||||
* @since 3.0 |
||||
*/ |
||||
public class PercentFormatter implements Formatter<BigDecimal> { |
||||
|
||||
private PercentNumberFormatFactory percentFormatFactory = new PercentNumberFormatFactory(); |
||||
|
||||
private boolean lenient; |
||||
|
||||
public String format(BigDecimal decimal, Locale locale) { |
||||
if (decimal == null) { |
||||
return ""; |
||||
} |
||||
NumberFormat format = percentFormatFactory.getNumberFormat(locale); |
||||
return format.format(decimal); |
||||
} |
||||
|
||||
public BigDecimal parse(String formatted, Locale locale) |
||||
throws ParseException { |
||||
if (formatted.length() == 0) { |
||||
return null; |
||||
} |
||||
NumberFormat format = percentFormatFactory.getNumberFormat(locale); |
||||
ParsePosition position = new ParsePosition(0); |
||||
BigDecimal decimal = (BigDecimal) format.parse(formatted, position); |
||||
if (position.getErrorIndex() != -1) { |
||||
throw new ParseException(formatted, position.getIndex()); |
||||
} |
||||
if (!lenient) { |
||||
if (formatted.length() != position.getIndex()) { |
||||
// indicates a part of the string that was not parsed
|
||||
throw new ParseException(formatted, position.getIndex()); |
||||
} |
||||
} |
||||
return decimal; |
||||
} |
||||
|
||||
} |
||||
@ -1,34 +0,0 @@
@@ -1,34 +0,0 @@
|
||||
/* |
||||
* 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.model.ui.format.number; |
||||
|
||||
import java.text.DecimalFormat; |
||||
import java.text.NumberFormat; |
||||
import java.util.Locale; |
||||
|
||||
/** |
||||
* Produces NumberFormat instances that format percent values. |
||||
* @see NumberFormat |
||||
* @author Keith Donald |
||||
* @since 3.0 |
||||
*/ |
||||
final class PercentNumberFormatFactory extends NumberFormatFactory { |
||||
public NumberFormat getNumberFormat(Locale locale) { |
||||
DecimalFormat format = (DecimalFormat) NumberFormat.getPercentInstance(locale); |
||||
format.setParseBigDecimal(true); |
||||
return format; |
||||
} |
||||
} |
||||
@ -1,5 +0,0 @@
@@ -1,5 +0,0 @@
|
||||
/** |
||||
* Formatters for <code>java.lang.Number</code> properties. |
||||
*/ |
||||
package org.springframework.model.ui.format.number; |
||||
|
||||
@ -1,5 +0,0 @@
@@ -1,5 +0,0 @@
|
||||
/** |
||||
* A SPI for defining Formatters to format field model values for display in a UI. |
||||
*/ |
||||
package org.springframework.model.ui.format; |
||||
|
||||
@ -1,5 +0,0 @@
@@ -1,5 +0,0 @@
|
||||
/** |
||||
* Spring's PresentationModel public API. |
||||
*/ |
||||
package org.springframework.model.ui; |
||||
|
||||
@ -1,64 +0,0 @@
@@ -1,64 +0,0 @@
|
||||
/* |
||||
* 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.model.ui.support; |
||||
|
||||
import org.springframework.util.Assert; |
||||
import org.springframework.util.ObjectUtils; |
||||
|
||||
/** |
||||
* A type descriptor for a parameterizable collection type such as a java.util.List<?>. |
||||
* @author Keith Donald |
||||
* @since 3.0 |
||||
*/ |
||||
public class CollectionTypeDescriptor { |
||||
|
||||
private Class<?> type; |
||||
|
||||
private Class<?> elementType; |
||||
|
||||
public CollectionTypeDescriptor(Class<?> type, Class<?> elementType) { |
||||
Assert.notNull(type, "The collection type is required"); |
||||
this.type = type; |
||||
this.elementType = elementType; |
||||
} |
||||
|
||||
/** |
||||
* The collection type. |
||||
*/ |
||||
public Class<?> getType() { |
||||
return type; |
||||
} |
||||
|
||||
/** |
||||
* The parameterized collection element type. |
||||
*/ |
||||
public Class<?> getElementType() { |
||||
return elementType; |
||||
} |
||||
|
||||
public boolean equals(Object o) { |
||||
if (!(o instanceof CollectionTypeDescriptor)) { |
||||
return false; |
||||
} |
||||
CollectionTypeDescriptor type = (CollectionTypeDescriptor) o; |
||||
return type.equals(type.type) |
||||
&& ObjectUtils.nullSafeEquals(elementType, type.elementType); |
||||
} |
||||
|
||||
public int hashCode() { |
||||
return type.hashCode() + elementType.hashCode(); |
||||
} |
||||
} |
||||
@ -1,424 +0,0 @@
@@ -1,424 +0,0 @@
|
||||
/* |
||||
* 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.model.ui.support; |
||||
|
||||
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; |
||||
|
||||
import org.springframework.context.i18n.LocaleContextHolder; |
||||
import org.springframework.core.GenericTypeResolver; |
||||
import org.springframework.core.convert.ConversionFailedException; |
||||
import org.springframework.core.convert.TypeConverter; |
||||
import org.springframework.core.convert.TypeDescriptor; |
||||
import org.springframework.core.style.StylerUtils; |
||||
import org.springframework.model.alert.Alert; |
||||
import org.springframework.model.alert.Severity; |
||||
import org.springframework.model.message.DefaultMessageFactory; |
||||
import org.springframework.model.message.MessageBuilder; |
||||
import org.springframework.model.message.ResolvableArgument; |
||||
import org.springframework.model.ui.BindingStatus; |
||||
import org.springframework.model.ui.FieldModel; |
||||
import org.springframework.model.ui.ValidationStatus; |
||||
import org.springframework.model.ui.format.Formatter; |
||||
|
||||
/** |
||||
* Default FieldModel implementation suitable for use in most environments. |
||||
* @author Keith Donald |
||||
* @since 3.0 |
||||
*/ |
||||
public class DefaultFieldModel implements FieldModel { |
||||
|
||||
private ValueModel valueModel; |
||||
|
||||
private FieldModelContext context; |
||||
|
||||
private ValueBuffer buffer; |
||||
|
||||
private BindingStatus bindingStatus; |
||||
|
||||
private Object submittedValue; |
||||
|
||||
private Exception invalidSubmittedValueCause; |
||||
|
||||
public DefaultFieldModel(ValueModel valueModel, FieldModelContext context) { |
||||
this.valueModel = valueModel; |
||||
this.context = context; |
||||
buffer = new ValueBuffer(valueModel); |
||||
bindingStatus = BindingStatus.CLEAN; |
||||
} |
||||
|
||||
// implementing FieldModel
|
||||
|
||||
public String getRenderValue() { |
||||
return format(getValue(), context.getFormatter()); |
||||
} |
||||
|
||||
public Object getValue() { |
||||
if (bindingStatus == BindingStatus.DIRTY || bindingStatus == BindingStatus.COMMIT_FAILURE) { |
||||
return buffer.getValue(); |
||||
} else { |
||||
return valueModel.getValue(); |
||||
} |
||||
} |
||||
|
||||
public Class<?> getValueType() { |
||||
return valueModel.getValueType(); |
||||
} |
||||
|
||||
public boolean isEditable() { |
||||
return valueModel.isWriteable() && context.getEditableCondition().isTrue(); |
||||
} |
||||
|
||||
public boolean isEnabled() { |
||||
return context.getEnabledCondition().isTrue(); |
||||
} |
||||
|
||||
public boolean isVisible() { |
||||
return context.getVisibleCondition().isTrue(); |
||||
} |
||||
|
||||
@SuppressWarnings("unchecked") |
||||
public void applySubmittedValue(Object submittedValue) { |
||||
assertEditable(); |
||||
assertEnabled(); |
||||
if (submittedValue instanceof String) { |
||||
try { |
||||
Object parsed = context.getFormatter().parse((String) submittedValue, getLocale()); |
||||
buffer.setValue(coerseToValueType(parsed)); |
||||
submittedValue = null; |
||||
bindingStatus = BindingStatus.DIRTY; |
||||
} catch (ParseException e) { |
||||
this.submittedValue = submittedValue; |
||||
invalidSubmittedValueCause = e; |
||||
bindingStatus = BindingStatus.INVALID_SUBMITTED_VALUE; |
||||
} catch (ConversionFailedException e) { |
||||
this.submittedValue = submittedValue; |
||||
invalidSubmittedValueCause = e; |
||||
bindingStatus = BindingStatus.INVALID_SUBMITTED_VALUE; |
||||
} |
||||
} else if (submittedValue instanceof String[]) { |
||||
Object parsed; |
||||
if (isMap()) { |
||||
String[] sourceValues = (String[]) submittedValue; |
||||
Formatter keyFormatter = context.getKeyFormatter(); |
||||
Formatter valueFormatter = context.getElementFormatter(); |
||||
Map map = new LinkedHashMap(sourceValues.length); |
||||
for (int i = 0; i < sourceValues.length; i++) { |
||||
String entryString = sourceValues[i]; |
||||
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.submittedValue = submittedValue; |
||||
invalidSubmittedValueCause = e; |
||||
bindingStatus = BindingStatus.INVALID_SUBMITTED_VALUE; |
||||
break; |
||||
} |
||||
} |
||||
parsed = map; |
||||
} else { |
||||
String[] sourceValues = (String[]) submittedValue; |
||||
List list = new ArrayList(sourceValues.length); |
||||
for (int i = 0; i < sourceValues.length; i++) { |
||||
Object parsedValue; |
||||
try { |
||||
parsedValue = context.getElementFormatter().parse(sourceValues[i], getLocale()); |
||||
list.add(parsedValue); |
||||
} catch (ParseException e) { |
||||
this.submittedValue = submittedValue; |
||||
invalidSubmittedValueCause = e; |
||||
bindingStatus = BindingStatus.INVALID_SUBMITTED_VALUE; |
||||
break; |
||||
} |
||||
} |
||||
parsed = list; |
||||
} |
||||
if (bindingStatus != BindingStatus.INVALID_SUBMITTED_VALUE) { |
||||
try { |
||||
buffer.setValue(coerseToValueType(parsed)); |
||||
submittedValue = null; |
||||
bindingStatus = BindingStatus.DIRTY; |
||||
} catch (ConversionFailedException e) { |
||||
this.submittedValue = submittedValue; |
||||
invalidSubmittedValueCause = e; |
||||
bindingStatus = BindingStatus.INVALID_SUBMITTED_VALUE; |
||||
} |
||||
} |
||||
} else { |
||||
try { |
||||
buffer.setValue(coerseToValueType(submittedValue)); |
||||
submittedValue = null; |
||||
bindingStatus = BindingStatus.DIRTY; |
||||
} catch (ConversionFailedException e) { |
||||
this.submittedValue = submittedValue; |
||||
invalidSubmittedValueCause = e; |
||||
bindingStatus = BindingStatus.INVALID_SUBMITTED_VALUE; |
||||
} |
||||
} |
||||
} |
||||
|
||||
public Object getInvalidSubmittedValue() { |
||||
if (bindingStatus != BindingStatus.INVALID_SUBMITTED_VALUE) { |
||||
throw new IllegalStateException("No invalid submitted value applied to this field"); |
||||
} |
||||
return submittedValue; |
||||
} |
||||
|
||||
public BindingStatus getBindingStatus() { |
||||
return bindingStatus; |
||||
} |
||||
|
||||
public ValidationStatus getValidationStatus() { |
||||
// TODO implementation
|
||||
return ValidationStatus.NOT_VALIDATED; |
||||
} |
||||
|
||||
public Alert getStatusAlert() { |
||||
if (bindingStatus == BindingStatus.INVALID_SUBMITTED_VALUE) { |
||||
return new AbstractAlert() { |
||||
public String getCode() { |
||||
return "typeMismatch"; |
||||
} |
||||
|
||||
public String getMessage() { |
||||
MessageBuilder builder = new MessageBuilder(context.getMessageSource()); |
||||
builder.code(getCode()); |
||||
if (invalidSubmittedValueCause instanceof ParseException) { |
||||
ParseException e = (ParseException) invalidSubmittedValueCause; |
||||
builder.arg("label", context.getLabel()); |
||||
builder.arg("value", submittedValue); |
||||
builder.arg("errorOffset", e.getErrorOffset()); |
||||
builder.defaultMessage(new DefaultMessageFactory() { |
||||
public String createDefaultMessage() { |
||||
return "Failed to bind '" + context.getLabel() + "'; the submitted value " |
||||
+ StylerUtils.style(submittedValue) |
||||
+ " has an invalid format and could no be parsed"; |
||||
} |
||||
}); |
||||
} else { |
||||
final ConversionFailedException e = (ConversionFailedException) invalidSubmittedValueCause; |
||||
builder.arg("label", new ResolvableArgument(context.getLabel())); |
||||
builder.arg("value", submittedValue); |
||||
builder.defaultMessage(new DefaultMessageFactory() { |
||||
public String createDefaultMessage() { |
||||
return "Failed to bind '" + context.getLabel() + "'; the submitted value " |
||||
+ StylerUtils.style(submittedValue) + " has could not be converted to " |
||||
+ e.getTargetType().getName(); |
||||
} |
||||
}); |
||||
} |
||||
return builder.build(); |
||||
} |
||||
|
||||
public Severity getSeverity() { |
||||
return Severity.ERROR; |
||||
} |
||||
}; |
||||
} else if (bindingStatus == BindingStatus.COMMIT_FAILURE) { |
||||
return new AbstractAlert() { |
||||
public String getCode() { |
||||
return "internalError"; |
||||
} |
||||
|
||||
public String getMessage() { |
||||
return "Internal error occurred; message = [" + buffer.getFlushException().getMessage() + "]"; |
||||
} |
||||
|
||||
public Severity getSeverity() { |
||||
return Severity.FATAL; |
||||
} |
||||
}; |
||||
} else if (bindingStatus == BindingStatus.COMMITTED) { |
||||
return new AbstractAlert() { |
||||
public String getCode() { |
||||
return "bindSuccess"; |
||||
} |
||||
|
||||
public String getMessage() { |
||||
MessageBuilder builder = new MessageBuilder(context.getMessageSource()); |
||||
builder.code(getCode()); |
||||
builder.arg("label", context.getLabel()); |
||||
builder.arg("value", submittedValue); |
||||
builder.defaultMessage(new DefaultMessageFactory() { |
||||
public String createDefaultMessage() { |
||||
return "Successfully bound submitted value " + StylerUtils.style(submittedValue) |
||||
+ " to field '" + context.getLabel() + "'"; |
||||
} |
||||
}); |
||||
return builder.build(); |
||||
} |
||||
|
||||
public Severity getSeverity() { |
||||
return Severity.INFO; |
||||
} |
||||
}; |
||||
} else { |
||||
return null; |
||||
} |
||||
} |
||||
|
||||
public void validate() { |
||||
// TODO implementation
|
||||
} |
||||
|
||||
public void commit() { |
||||
assertEditable(); |
||||
assertEnabled(); |
||||
if (bindingStatus == BindingStatus.DIRTY) { |
||||
buffer.flush(); |
||||
if (buffer.flushFailed()) { |
||||
bindingStatus = BindingStatus.COMMIT_FAILURE; |
||||
} else { |
||||
bindingStatus = BindingStatus.COMMITTED; |
||||
} |
||||
} else { |
||||
throw new IllegalStateException("Field is not dirty; nothing to commit"); |
||||
} |
||||
} |
||||
|
||||
public void revert() { |
||||
if (bindingStatus == BindingStatus.INVALID_SUBMITTED_VALUE) { |
||||
submittedValue = null; |
||||
invalidSubmittedValueCause = null; |
||||
bindingStatus = BindingStatus.CLEAN; |
||||
} else if (bindingStatus == BindingStatus.DIRTY || bindingStatus == BindingStatus.COMMIT_FAILURE) { |
||||
buffer.clear(); |
||||
bindingStatus = BindingStatus.CLEAN; |
||||
} else { |
||||
throw new IllegalStateException("Field is clean or committed; nothing to revert"); |
||||
} |
||||
} |
||||
|
||||
public FieldModel getNested(String fieldName) { |
||||
return context.getNested(fieldName); |
||||
} |
||||
|
||||
public boolean isList() { |
||||
return getValueType().isArray() || List.class.isAssignableFrom(getValueType()); |
||||
} |
||||
|
||||
public FieldModel getListElement(int index) { |
||||
return context.getListElement(index); |
||||
} |
||||
|
||||
public boolean isMap() { |
||||
return Map.class.isAssignableFrom(getValueType()); |
||||
} |
||||
|
||||
public FieldModel getMapValue(Object key) { |
||||
if (key instanceof String) { |
||||
try { |
||||
key = context.getKeyFormatter().parse((String) key, getLocale()); |
||||
} catch (ParseException e) { |
||||
throw new IllegalArgumentException("Unable to parse map key '" + key + "'", e); |
||||
} |
||||
} |
||||
return context.getMapValue(key); |
||||
} |
||||
|
||||
@SuppressWarnings("unchecked") |
||||
public String formatValue(Object value) { |
||||
Formatter formatter; |
||||
if (Collection.class.isAssignableFrom(getValueType()) || getValueType().isArray() || isMap()) { |
||||
formatter = context.getElementFormatter(); |
||||
} else { |
||||
formatter = context.getFormatter(); |
||||
} |
||||
return format(value, formatter); |
||||
} |
||||
|
||||
// internal helpers
|
||||
|
||||
@SuppressWarnings("unchecked") |
||||
private String format(Object value, Formatter formatter) { |
||||
Class<?> formattedType = getFormattedObjectType(formatter.getClass()); |
||||
value = context.getTypeConverter().convert(value, formattedType); |
||||
return formatter.format(value, getLocale()); |
||||
} |
||||
|
||||
private Locale getLocale() { |
||||
return LocaleContextHolder.getLocale(); |
||||
} |
||||
|
||||
@SuppressWarnings("unchecked") |
||||
private Class getFormattedObjectType(Class formatterClass) { |
||||
Class classToIntrospect = formatterClass; |
||||
while (classToIntrospect != null) { |
||||
Type[] ifcs = classToIntrospect.getGenericInterfaces(); |
||||
for (Type ifc : ifcs) { |
||||
if (ifc instanceof ParameterizedType) { |
||||
ParameterizedType paramIfc = (ParameterizedType) ifc; |
||||
Type rawType = paramIfc.getRawType(); |
||||
if (Formatter.class.equals(rawType)) { |
||||
Type arg = paramIfc.getActualTypeArguments()[0]; |
||||
if (arg instanceof TypeVariable) { |
||||
arg = GenericTypeResolver.resolveTypeVariable((TypeVariable) arg, formatterClass); |
||||
} |
||||
if (arg instanceof Class) { |
||||
return (Class) arg; |
||||
} |
||||
} else if (Formatter.class.isAssignableFrom((Class) rawType)) { |
||||
return getFormattedObjectType((Class) rawType); |
||||
} |
||||
} else if (Formatter.class.isAssignableFrom((Class) ifc)) { |
||||
return getFormattedObjectType((Class) ifc); |
||||
} |
||||
} |
||||
classToIntrospect = classToIntrospect.getSuperclass(); |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
@SuppressWarnings("unchecked") |
||||
private Object coerseToValueType(Object parsed) { |
||||
TypeDescriptor targetType = valueModel.getValueTypeDescriptor(); |
||||
TypeConverter converter = context.getTypeConverter(); |
||||
if (parsed != null && converter.canConvert(parsed.getClass(), targetType)) { |
||||
return converter.convert(parsed, targetType); |
||||
} else { |
||||
return parsed; |
||||
} |
||||
} |
||||
|
||||
private void assertEditable() { |
||||
if (!isEditable()) { |
||||
throw new IllegalStateException("Field is not editable"); |
||||
} |
||||
} |
||||
|
||||
private void assertEnabled() { |
||||
if (!isEditable()) { |
||||
throw new IllegalStateException("Field is not enabled"); |
||||
} |
||||
} |
||||
|
||||
static abstract class AbstractAlert implements Alert { |
||||
public String toString() { |
||||
return getCode() + " - " + getMessage(); |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -1,611 +0,0 @@
@@ -1,611 +0,0 @@
|
||||
/* |
||||
* 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.model.ui.support; |
||||
|
||||
import java.beans.BeanInfo; |
||||
import java.beans.IntrospectionException; |
||||
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.springframework.context.MessageSource; |
||||
import org.springframework.core.GenericCollectionTypeResolver; |
||||
import org.springframework.core.convert.TypeConverter; |
||||
import org.springframework.core.convert.support.DefaultTypeConverter; |
||||
import org.springframework.model.ui.FieldModel; |
||||
import org.springframework.model.ui.FieldNotFoundException; |
||||
import org.springframework.model.ui.PresentationModel; |
||||
import org.springframework.model.ui.config.Condition; |
||||
import org.springframework.model.ui.config.FieldModelConfiguration; |
||||
import org.springframework.model.ui.format.Formatter; |
||||
import org.springframework.util.Assert; |
||||
|
||||
/** |
||||
* A default PresentationModel implementation suitable for use in most environments. |
||||
* @author Keith Donald |
||||
* @since 3.0 |
||||
* @see #setFormatterRegistry(FormatterRegistry) |
||||
* @see #setMessageSource(MessageSource) |
||||
* @see #setTypeConverter(TypeConverter) |
||||
* @see #field(String) |
||||
*/ |
||||
public class DefaultPresentationModel implements PresentationModel { |
||||
|
||||
private Object domainModel; |
||||
|
||||
private Map<String, PropertyFieldModelRule> fieldModelRules; |
||||
|
||||
private FormatterRegistry formatterRegistry; |
||||
|
||||
private TypeConverter typeConverter; |
||||
|
||||
private MessageSource messageSource; |
||||
|
||||
/** |
||||
* Creates a new presentation model for the domain model. |
||||
* @param domainModel the domain model object |
||||
*/ |
||||
public DefaultPresentationModel(Object domainModel) { |
||||
Assert.notNull(domainModel, "The domain model to bind to is required"); |
||||
this.domainModel = domainModel; |
||||
fieldModelRules = new HashMap<String, PropertyFieldModelRule>(); |
||||
formatterRegistry = new GenericFormatterRegistry(); |
||||
typeConverter = new DefaultTypeConverter(); |
||||
} |
||||
|
||||
/** |
||||
* Configures the registry of Formatters to query when no explicit Formatter has been registered for a field. |
||||
* Allows Formatters to be applied by property type and by property annotation. |
||||
* @param registry the formatter registry |
||||
*/ |
||||
public void setFormatterRegistry(FormatterRegistry formatterRegistry) { |
||||
Assert.notNull(formatterRegistry, "The FormatterRegistry is required"); |
||||
this.formatterRegistry = formatterRegistry; |
||||
} |
||||
|
||||
/** |
||||
* Configure the MessageSource that resolves localized UI alert messages. |
||||
* @param messageSource the message source |
||||
*/ |
||||
public void setMessageSource(MessageSource messageSource) { |
||||
Assert.notNull(messageSource, "The MessageSource is required"); |
||||
this.messageSource = messageSource; |
||||
} |
||||
|
||||
/** |
||||
* Configure the TypeConverter that converts values as required by the binding system. |
||||
* For a {@link FieldModel#applySubmittedValue(Object) applySubmittedValue call}, this TypeConverter will be asked to perform a conversion if the value parsed by the field's Formatter is not assignable to the target value type. |
||||
* For a {@link FieldModel#getRenderValue() getRenderValue call}, this TypeConverter will be asked to perform a conversion if the value type does not match the type T required by the field's Formatter. |
||||
* For a {@link FieldModel#getMapValue(Object) getMapValue call} this TypeConverter will be asked to convert the Map key to the type required if there is no keyFormatter registered for the field. |
||||
* @param typeConverter the type converter used by the binding system |
||||
*/ |
||||
public void setTypeConverter(TypeConverter typeConverter) { |
||||
Assert.notNull(typeConverter, "The TypeConverter is required"); |
||||
this.typeConverter = typeConverter; |
||||
} |
||||
|
||||
/** |
||||
* Add a FieldModel configuration at the path specified. |
||||
* @param fieldPath the domain object property path in format <prop>[.nestedProp] |
||||
* @return a builder for the {@link FieldModel} configuration |
||||
*/ |
||||
public FieldModelConfiguration field(String fieldPath) { |
||||
FieldPath path = new FieldPath(fieldPath); |
||||
PropertyFieldModelRule rule = getRule(path.getFirstElement().getValue()); |
||||
for (FieldPathElement element : path.getNestedElements()) { |
||||
rule = rule.getNestedRule(element.getValue()); |
||||
} |
||||
return rule; |
||||
} |
||||
|
||||
/** |
||||
* The domain-layer model this presentation model coordinates with. |
||||
*/ |
||||
public Object getDomainModel() { |
||||
return domainModel; |
||||
} |
||||
|
||||
// implementing PresentationModel
|
||||
|
||||
public FieldModel getFieldModel(String fieldName) { |
||||
FieldPath path = new FieldPath(fieldName); |
||||
FieldModel field = getRule(path.getFirstElement().getValue()).getFieldModel(domainModel); |
||||
for (FieldPathElement element : path.getNestedElements()) { |
||||
if (element.isIndex()) { |
||||
if (field.isMap()) { |
||||
field = field.getMapValue(element.getValue()); |
||||
} else if (field.isList()) { |
||||
field = field.getListElement(element.getIntValue()); |
||||
} else { |
||||
throw new IllegalArgumentException("Attempted to index a field that is not a List, Array, or a Map"); |
||||
} |
||||
} else { |
||||
field = field.getNested(element.getValue()); |
||||
} |
||||
} |
||||
return field; |
||||
} |
||||
|
||||
public void validate() { |
||||
|
||||
} |
||||
|
||||
public boolean hasErrors() { |
||||
return false; |
||||
} |
||||
|
||||
public void commit() { |
||||
|
||||
} |
||||
|
||||
// internal helpers
|
||||
|
||||
private PropertyFieldModelRule getRule(String fieldName) { |
||||
PropertyFieldModelRule rule = fieldModelRules.get(fieldName); |
||||
if (rule == null) { |
||||
rule = new PropertyFieldModelRule(fieldName, domainModel.getClass()); |
||||
fieldModelRules.put(fieldName, rule); |
||||
} |
||||
return rule; |
||||
} |
||||
|
||||
@SuppressWarnings("unchecked") |
||||
class PropertyFieldModelRule implements FieldModelConfiguration, FieldModelContext { |
||||
|
||||
private Class<?> domainModelClass; |
||||
|
||||
private PropertyDescriptor property; |
||||
|
||||
private Formatter formatter; |
||||
|
||||
private Formatter keyFormatter; |
||||
|
||||
private Formatter elementFormatter; |
||||
|
||||
private Condition editableCondition = Condition.ALWAYS_TRUE; |
||||
|
||||
private Condition enabledCondition = Condition.ALWAYS_TRUE; |
||||
|
||||
private Condition visibleCondition = Condition.ALWAYS_TRUE; |
||||
|
||||
private Map<String, PropertyFieldModelRule> nestedFieldModelRules; |
||||
|
||||
private FieldModel fieldModel; |
||||
|
||||
private Map<Integer, FieldModel> listElements; |
||||
|
||||
private Map<Object, FieldModel> mapValues; |
||||
|
||||
public PropertyFieldModelRule(String property, Class domainModelClass) { |
||||
this.domainModelClass = domainModelClass; |
||||
this.property = findPropertyDescriptor(property); |
||||
} |
||||
|
||||
// implementing FieldModelContext
|
||||
|
||||
public MessageSource getMessageSource() { |
||||
return messageSource; |
||||
} |
||||
|
||||
public TypeConverter getTypeConverter() { |
||||
return typeConverter; |
||||
} |
||||
|
||||
public Formatter<?> getFormatter() { |
||||
if (formatter != null) { |
||||
return formatter; |
||||
} else { |
||||
return formatterRegistry.getFormatter(property); |
||||
} |
||||
} |
||||
|
||||
public Formatter<?> getKeyFormatter() { |
||||
if (keyFormatter != null) { |
||||
return keyFormatter; |
||||
} else { |
||||
return formatterRegistry.getFormatter(getKeyType()); |
||||
} |
||||
} |
||||
|
||||
public Formatter<?> getElementFormatter() { |
||||
if (elementFormatter != null) { |
||||
return formatter; |
||||
} else { |
||||
return formatterRegistry.getFormatter(getElementType()); |
||||
} |
||||
} |
||||
|
||||
public Condition getEnabledCondition() { |
||||
return enabledCondition; |
||||
} |
||||
|
||||
public Condition getEditableCondition() { |
||||
return editableCondition; |
||||
} |
||||
|
||||
public Condition getVisibleCondition() { |
||||
return visibleCondition; |
||||
} |
||||
|
||||
public String getLabel() { |
||||
return property.getName(); |
||||
} |
||||
|
||||
public FieldModel getNested(String fieldName) { |
||||
createValueIfNecessary(); |
||||
return getNestedRule(fieldName, fieldModel.getValueType()).getFieldModel(fieldModel.getValue()); |
||||
} |
||||
|
||||
public FieldModel getListElement(int index) { |
||||
// TODO array support
|
||||
if (listElements == null) { |
||||
listElements = new HashMap<Integer, FieldModel>(); |
||||
} |
||||
growListIfNecessary(index); |
||||
FieldModel field = listElements.get(index); |
||||
if (field == null) { |
||||
FieldModelContext context = new ListElementContext(index, this); |
||||
ValueModel valueModel = new ListElementValueModel(index, getElementType(), (List) fieldModel.getValue()); |
||||
field = new DefaultFieldModel(valueModel, context); |
||||
listElements.put(index, field); |
||||
} |
||||
return field; |
||||
} |
||||
|
||||
public FieldModel getMapValue(Object key) { |
||||
if (mapValues == null) { |
||||
mapValues = new HashMap<Object, FieldModel>(); |
||||
} |
||||
createMapValueIfNecessary(); |
||||
FieldModel field = mapValues.get(key); |
||||
if (field == null) { |
||||
FieldModelContext context = new MapValueContext(key, this); |
||||
ValueModel valueModel = new MapValueValueModel(key, getElementType(), (Map) fieldModel.getValue(), |
||||
context); |
||||
field = new DefaultFieldModel(valueModel, context); |
||||
mapValues.put(key, field); |
||||
} |
||||
return field; |
||||
} |
||||
|
||||
// implementing FieldModelConfiguration
|
||||
|
||||
public FieldModelConfiguration formatWith(Formatter<?> formatter) { |
||||
this.formatter = formatter; |
||||
return this; |
||||
} |
||||
|
||||
public FieldModelConfiguration formatElementsWith(Formatter<?> formatter) { |
||||
if (!List.class.isAssignableFrom(domainModelClass) || domainModelClass.isArray()) { |
||||
throw new IllegalStateException("Field is not a List or an Array; cannot set a element formatter"); |
||||
} |
||||
elementFormatter = formatter; |
||||
return this; |
||||
} |
||||
|
||||
public FieldModelConfiguration formatKeysWith(Formatter<?> formatter) { |
||||
if (!Map.class.isAssignableFrom(domainModelClass)) { |
||||
throw new IllegalStateException("Field is not a Map; cannot set a key formatter"); |
||||
} |
||||
keyFormatter = formatter; |
||||
return this; |
||||
} |
||||
|
||||
public FieldModelConfiguration editableWhen(Condition condition) { |
||||
editableCondition = condition; |
||||
return this; |
||||
} |
||||
|
||||
public FieldModelConfiguration enabledWhen(Condition condition) { |
||||
enabledCondition = condition; |
||||
return this; |
||||
} |
||||
|
||||
public FieldModelConfiguration visibleWhen(Condition condition) { |
||||
visibleCondition = condition; |
||||
return this; |
||||
} |
||||
|
||||
// package private helpers
|
||||
|
||||
PropertyFieldModelRule getNestedRule(String propertyName) { |
||||
return getNestedRule(propertyName, this.property.getPropertyType()); |
||||
} |
||||
|
||||
PropertyFieldModelRule getNestedRule(String propertyName, Class<?> domainModelClass) { |
||||
if (nestedFieldModelRules == null) { |
||||
nestedFieldModelRules = new HashMap<String, PropertyFieldModelRule>(); |
||||
} |
||||
PropertyFieldModelRule rule = nestedFieldModelRules.get(propertyName); |
||||
if (rule == null) { |
||||
rule = new PropertyFieldModelRule(propertyName, domainModelClass); |
||||
nestedFieldModelRules.put(propertyName, rule); |
||||
} |
||||
return rule; |
||||
} |
||||
|
||||
// internal helpers
|
||||
|
||||
private Class<?> getElementType() { |
||||
Class<?> propertyType = property.getPropertyType(); |
||||
if (Map.class.isAssignableFrom(propertyType)) { |
||||
return GenericCollectionTypeResolver.getMapValueReturnType(property.getReadMethod()); |
||||
} else if (propertyType.isArray()) { |
||||
return property.getPropertyType().getComponentType(); |
||||
} else { |
||||
return GenericCollectionTypeResolver.getCollectionReturnType(property.getReadMethod()); |
||||
} |
||||
} |
||||
|
||||
private Class<?> getKeyType() { |
||||
return GenericCollectionTypeResolver.getMapKeyReturnType(property.getReadMethod()); |
||||
} |
||||
|
||||
FieldModel getFieldModel(Object domainObject) { |
||||
if (fieldModel == null) { |
||||
PropertyValueModel valueModel = new PropertyValueModel(property, domainObject); |
||||
fieldModel = new DefaultFieldModel(valueModel, this); |
||||
} |
||||
return fieldModel; |
||||
} |
||||
|
||||
private PropertyDescriptor findPropertyDescriptor(String property) { |
||||
PropertyDescriptor[] propDescs = getBeanInfo(domainModelClass).getPropertyDescriptors(); |
||||
for (PropertyDescriptor propDesc : propDescs) { |
||||
if (propDesc.getName().equals(property)) { |
||||
return propDesc; |
||||
} |
||||
} |
||||
throw new FieldNotFoundException(property); |
||||
} |
||||
|
||||
private BeanInfo getBeanInfo(Class<?> clazz) { |
||||
try { |
||||
return Introspector.getBeanInfo(clazz); |
||||
} catch (IntrospectionException e) { |
||||
throw new IllegalStateException("Unable to introspect model type " + clazz); |
||||
} |
||||
} |
||||
|
||||
private void createValueIfNecessary() { |
||||
Object value = fieldModel.getValue(); |
||||
if (value == null) { |
||||
value = newValue(fieldModel.getValueType()); |
||||
fieldModel.applySubmittedValue(value); |
||||
fieldModel.commit(); |
||||
} |
||||
} |
||||
|
||||
private void createMapValueIfNecessary() { |
||||
Object value = fieldModel.getValue(); |
||||
if (value == null) { |
||||
value = newMapValue(fieldModel.getValueType()); |
||||
fieldModel.applySubmittedValue(value); |
||||
fieldModel.commit(); |
||||
} |
||||
} |
||||
|
||||
private void growListIfNecessary(int index) { |
||||
List list = (List) fieldModel.getValue(); |
||||
if (list == null) { |
||||
list = newListValue(fieldModel.getValueType()); |
||||
fieldModel.applySubmittedValue(list); |
||||
fieldModel.commit(); |
||||
list = (List) fieldModel.getValue(); |
||||
} |
||||
if (index >= list.size()) { |
||||
for (int i = list.size(); i <= index; i++) { |
||||
list.add(newValue(getElementType())); |
||||
} |
||||
} |
||||
} |
||||
|
||||
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); |
||||
} else { |
||||
return (List) newValue(type); |
||||
} |
||||
} |
||||
|
||||
private Object newValue(Class<?> type) { |
||||
try { |
||||
return type.newInstance(); |
||||
} catch (InstantiationException e) { |
||||
throw new IllegalStateException("Could not instantiate element of type [" + type.getName() + "]", e); |
||||
} catch (IllegalAccessException e) { |
||||
throw new IllegalStateException("Could not instantiate element of type [" + type.getName() + "]", e); |
||||
} |
||||
} |
||||
|
||||
} |
||||
|
||||
private static class ListElementContext implements FieldModelContext { |
||||
|
||||
private int index; |
||||
|
||||
private PropertyFieldModelRule listBindingContext; |
||||
|
||||
final Map<String, FieldModel> nestedBindings = new HashMap<String, FieldModel>(); |
||||
|
||||
public ListElementContext(int index, PropertyFieldModelRule listBindingContext) { |
||||
this.index = index; |
||||
this.listBindingContext = listBindingContext; |
||||
} |
||||
|
||||
public MessageSource getMessageSource() { |
||||
return listBindingContext.getMessageSource(); |
||||
} |
||||
|
||||
public TypeConverter getTypeConverter() { |
||||
return listBindingContext.getTypeConverter(); |
||||
} |
||||
|
||||
@SuppressWarnings("unchecked") |
||||
public Formatter getFormatter() { |
||||
return listBindingContext.getElementFormatter(); |
||||
} |
||||
|
||||
@SuppressWarnings("unchecked") |
||||
public Formatter getElementFormatter() { |
||||
// TODO multi-dimensional support
|
||||
return null; |
||||
} |
||||
|
||||
@SuppressWarnings("unchecked") |
||||
public Formatter getKeyFormatter() { |
||||
// TODO multi-dimensional support
|
||||
return null; |
||||
} |
||||
|
||||
public Condition getEditableCondition() { |
||||
return listBindingContext.getEditableCondition(); |
||||
} |
||||
|
||||
public Condition getEnabledCondition() { |
||||
return listBindingContext.getEnabledCondition(); |
||||
} |
||||
|
||||
public Condition getVisibleCondition() { |
||||
return listBindingContext.getVisibleCondition(); |
||||
} |
||||
|
||||
public String getLabel() { |
||||
return listBindingContext.getLabel() + "[" + index + "]"; |
||||
} |
||||
|
||||
public FieldModel getNested(String property) { |
||||
Object model = ((List<?>) listBindingContext.fieldModel.getValue()).get(index); |
||||
Class<?> elementType = listBindingContext.getElementType(); |
||||
if (elementType == null) { |
||||
elementType = model.getClass(); |
||||
} |
||||
PropertyFieldModelRule rule = listBindingContext.getNestedRule(property, elementType); |
||||
FieldModel binding = nestedBindings.get(property); |
||||
if (binding == null) { |
||||
PropertyValueModel valueModel = new PropertyValueModel(rule.property, model); |
||||
binding = new DefaultFieldModel(valueModel, rule); |
||||
nestedBindings.put(property, binding); |
||||
} |
||||
return binding; |
||||
} |
||||
|
||||
public FieldModel getListElement(int index) { |
||||
// TODO multi-dimensional support
|
||||
throw new IllegalArgumentException("Not yet supported"); |
||||
} |
||||
|
||||
public FieldModel getMapValue(Object key) { |
||||
// TODO multi-dimensional support
|
||||
throw new IllegalArgumentException("Not yet supported"); |
||||
} |
||||
}; |
||||
|
||||
private static class MapValueContext implements FieldModelContext { |
||||
|
||||
private Object key; |
||||
|
||||
private PropertyFieldModelRule mapContext; |
||||
|
||||
final Map<String, FieldModel> nestedBindings = new HashMap<String, FieldModel>(); |
||||
|
||||
public MapValueContext(Object key, PropertyFieldModelRule mapContext) { |
||||
this.key = key; |
||||
this.mapContext = mapContext; |
||||
} |
||||
|
||||
public MessageSource getMessageSource() { |
||||
return mapContext.getMessageSource(); |
||||
} |
||||
|
||||
public TypeConverter getTypeConverter() { |
||||
return mapContext.getTypeConverter(); |
||||
} |
||||
|
||||
@SuppressWarnings("unchecked") |
||||
public Formatter getFormatter() { |
||||
return mapContext.getElementFormatter(); |
||||
} |
||||
|
||||
@SuppressWarnings("unchecked") |
||||
public Formatter getElementFormatter() { |
||||
// TODO multi-dimensional support
|
||||
return null; |
||||
} |
||||
|
||||
@SuppressWarnings("unchecked") |
||||
public Formatter getKeyFormatter() { |
||||
// TODO multi-dimensional support
|
||||
return null; |
||||
} |
||||
|
||||
public Condition getEditableCondition() { |
||||
return mapContext.getEditableCondition(); |
||||
} |
||||
|
||||
public Condition getEnabledCondition() { |
||||
return mapContext.getEnabledCondition(); |
||||
} |
||||
|
||||
public Condition getVisibleCondition() { |
||||
return mapContext.getVisibleCondition(); |
||||
} |
||||
|
||||
@SuppressWarnings("unchecked") |
||||
public FieldModel getNested(String property) { |
||||
Object model = ((Map) mapContext.fieldModel.getValue()).get(key); |
||||
Class<?> elementType = mapContext.getElementType(); |
||||
if (elementType == null) { |
||||
elementType = model.getClass(); |
||||
} |
||||
PropertyFieldModelRule rule = mapContext.getNestedRule(property, elementType); |
||||
FieldModel binding = nestedBindings.get(property); |
||||
if (binding == null) { |
||||
PropertyValueModel valueModel = new PropertyValueModel(rule.property, model); |
||||
binding = new DefaultFieldModel(valueModel, rule); |
||||
nestedBindings.put(property, binding); |
||||
} |
||||
return binding; |
||||
} |
||||
|
||||
public FieldModel getListElement(int index) { |
||||
// TODO multi-dimensional support
|
||||
throw new IllegalArgumentException("Not yet supported"); |
||||
} |
||||
|
||||
public FieldModel getMapValue(Object key) { |
||||
// TODO multi-dimensional support
|
||||
throw new IllegalArgumentException("Not yet supported"); |
||||
} |
||||
|
||||
public String getLabel() { |
||||
return mapContext.getLabel() + "[" + key + "]"; |
||||
} |
||||
|
||||
}; |
||||
} |
||||
@ -1,46 +0,0 @@
@@ -1,46 +0,0 @@
|
||||
/* |
||||
* 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.model.ui.support; |
||||
|
||||
import java.util.IdentityHashMap; |
||||
import java.util.Map; |
||||
|
||||
import org.springframework.model.ui.PresentationModel; |
||||
import org.springframework.model.ui.PresentationModelFactory; |
||||
|
||||
/** |
||||
* Default PresentationModelFactory implementation that uses a {@link IdentityHashMap} to map domain models to PresentationModels. |
||||
* @author Keith Donald |
||||
* @since 3.0 |
||||
*/ |
||||
public class DefaultPresentationModelFactory implements PresentationModelFactory { |
||||
|
||||
private Map<Object, PresentationModel> presentationModels = new IdentityHashMap<Object, PresentationModel>(); |
||||
|
||||
public void put(Object domainObject, PresentationModel presentationModel) { |
||||
presentationModels.put(domainObject, presentationModel); |
||||
} |
||||
|
||||
public PresentationModel getPresentationModel(Object domainObject) { |
||||
PresentationModel factory = presentationModels.get(domainObject); |
||||
if (factory == null) { |
||||
factory = new DefaultPresentationModel(domainObject); |
||||
presentationModels.put(domainObject, factory); |
||||
} |
||||
return factory; |
||||
} |
||||
|
||||
} |
||||
@ -1,58 +0,0 @@
@@ -1,58 +0,0 @@
|
||||
/* |
||||
* 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.model.ui.support; |
||||
|
||||
import org.springframework.context.MessageSource; |
||||
import org.springframework.core.convert.TypeConverter; |
||||
import org.springframework.model.ui.FieldModel; |
||||
import org.springframework.model.ui.config.Condition; |
||||
import org.springframework.model.ui.format.Formatter; |
||||
|
||||
/** |
||||
* A context that allows a FieldModel to access its external configuration. |
||||
* @author Keith Donald |
||||
* @since 3.0 |
||||
*/ |
||||
public interface FieldModelContext { |
||||
|
||||
MessageSource getMessageSource(); |
||||
|
||||
TypeConverter getTypeConverter(); |
||||
|
||||
Condition getEditableCondition(); |
||||
|
||||
Condition getEnabledCondition(); |
||||
|
||||
Condition getVisibleCondition(); |
||||
|
||||
@SuppressWarnings("unchecked") |
||||
Formatter getFormatter(); |
||||
|
||||
@SuppressWarnings("unchecked") |
||||
Formatter getElementFormatter(); |
||||
|
||||
@SuppressWarnings("unchecked") |
||||
Formatter getKeyFormatter(); |
||||
|
||||
String getLabel(); |
||||
|
||||
FieldModel getNested(String fieldName); |
||||
|
||||
FieldModel getListElement(int index); |
||||
|
||||
FieldModel getMapValue(Object key); |
||||
|
||||
} |
||||
@ -1,65 +0,0 @@
@@ -1,65 +0,0 @@
|
||||
/* |
||||
* 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.model.ui.support; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.Collections; |
||||
import java.util.Iterator; |
||||
import java.util.List; |
||||
|
||||
class FieldPath implements Iterable<FieldPathElement> { |
||||
|
||||
private List<FieldPathElement> elements = new ArrayList<FieldPathElement>(); |
||||
|
||||
public FieldPath(String propertyPath) { |
||||
// a.b.c[i].d[key].e
|
||||
String[] props = propertyPath.split("\\."); |
||||
if (props.length == 0) { |
||||
props = new String[] { propertyPath }; |
||||
} |
||||
for (String prop : props) { |
||||
if (prop.contains("[")) { |
||||
int start = prop.indexOf('['); |
||||
int end = prop.indexOf(']', start); |
||||
String index = prop.substring(start + 1, end); |
||||
elements.add(new FieldPathElement(prop.substring(0, start), false)); |
||||
elements.add(new FieldPathElement(index, true)); |
||||
} else { |
||||
elements.add(new FieldPathElement(prop, false)); |
||||
} |
||||
} |
||||
} |
||||
|
||||
public FieldPathElement getFirstElement() { |
||||
return elements.get(0); |
||||
} |
||||
|
||||
public List<FieldPathElement> getNestedElements() { |
||||
if (elements.size() > 1) { |
||||
return elements.subList(1, elements.size()); |
||||
} else { |
||||
return Collections.emptyList(); |
||||
} |
||||
} |
||||
|
||||
public Iterator<FieldPathElement> iterator() { |
||||
return elements.iterator(); |
||||
} |
||||
|
||||
public String toString() { |
||||
return elements.toString(); |
||||
} |
||||
} |
||||
@ -1,44 +0,0 @@
@@ -1,44 +0,0 @@
|
||||
/* |
||||
* 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.model.ui.support; |
||||
|
||||
class FieldPathElement { |
||||
|
||||
private String value; |
||||
|
||||
private boolean index; |
||||
|
||||
public FieldPathElement(String value, boolean index) { |
||||
this.value = value; |
||||
this.index = index; |
||||
} |
||||
|
||||
public boolean isIndex() { |
||||
return index; |
||||
} |
||||
|
||||
public String getValue() { |
||||
return value; |
||||
} |
||||
|
||||
public int getIntValue() { |
||||
return Integer.parseInt(value); |
||||
} |
||||
|
||||
public String toString() { |
||||
return value + ";index=" + index; |
||||
} |
||||
} |
||||
@ -1,69 +0,0 @@
@@ -1,69 +0,0 @@
|
||||
/* |
||||
* 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.model.ui.support; |
||||
|
||||
import java.beans.PropertyDescriptor; |
||||
|
||||
import org.springframework.model.ui.format.AnnotationFormatterFactory; |
||||
import org.springframework.model.ui.format.Formatter; |
||||
|
||||
/** |
||||
* A centralized registry of Formatters indexed by property types. |
||||
* TODO - consider a general add(Formatter) method for simplicity |
||||
* @author Keith Donald |
||||
* @since 3.0 |
||||
*/ |
||||
public interface FormatterRegistry { |
||||
|
||||
/** |
||||
* Get the Formatter for the property. |
||||
* @return the Formatter, or <code>null</code> if none is registered |
||||
*/ |
||||
Formatter<?> getFormatter(PropertyDescriptor property); |
||||
|
||||
/** |
||||
* Get the Formatter for the type. |
||||
* @return the Formatter, or <code>null</code> if none is registered |
||||
*/ |
||||
Formatter<?> getFormatter(Class<?> type); |
||||
|
||||
/** |
||||
* Adds a Formatter that will format the values of properties of the provided type. |
||||
* The type should generally be a concrete class for a scalar value, such as BigDecimal, and not a collection value. |
||||
* The type may be an annotation type, which will have the Formatter format values of properties annotated with that annotation. |
||||
* Use {@link #add(AnnotationFormatterFactory)} when the format annotation defines configurable annotation instance values. |
||||
* <p> |
||||
* Note the Formatter's formatted object type does not have to equal the associated property type. |
||||
* When the property type differs from the formatted object type, the caller of the Formatter is expected to coerse a property value to the type expected by the Formatter. |
||||
* @param propertyType the type |
||||
* @param formatter the formatter |
||||
*/ |
||||
void add(Class<?> propertyType, Formatter<?> formatter); |
||||
|
||||
/** |
||||
* Adds a Formatter that will format the values of collection properties of the provided type. |
||||
* @param type the type |
||||
* @param formatter the formatter |
||||
*/ |
||||
void add(CollectionTypeDescriptor type, Formatter<?> formatter); |
||||
|
||||
/** |
||||
* Adds a AnnotationFormatterFactory that will format values of properties annotated with a specific annotation. |
||||
* @param factory the annotation formatter factory |
||||
*/ |
||||
void add(AnnotationFormatterFactory<?, ?> factory); |
||||
|
||||
} |
||||
@ -1,303 +0,0 @@
@@ -1,303 +0,0 @@
|
||||
/* |
||||
* 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.model.ui.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.model.ui.format.AnnotationFormatterFactory; |
||||
import org.springframework.model.ui.format.Formatted; |
||||
import org.springframework.model.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. |
||||
* @author Keith Donald |
||||
* @since 3.0 |
||||
* @see #add(Class, Formatter) |
||||
* @see #add(AnnotationFormatterFactory) |
||||
*/ |
||||
@SuppressWarnings("unchecked") |
||||
public class GenericFormatterRegistry implements FormatterRegistry { |
||||
|
||||
private Map<Class, Formatter> typeFormatters = new ConcurrentHashMap<Class, Formatter>(); |
||||
|
||||
private Map<CollectionTypeDescriptor, Formatter> collectionTypeFormatters = new ConcurrentHashMap<CollectionTypeDescriptor, Formatter>(); |
||||
|
||||
private Map<Class, AnnotationFormatterFactory> annotationFormatters = new HashMap<Class, AnnotationFormatterFactory>(); |
||||
|
||||
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) { |
||||
AnnotationFormatterFactory factory = annotationFormatters.get(a.annotationType()); |
||||
if (factory != null) { |
||||
return factory.getFormatter(a); |
||||
} |
||||
} |
||||
Formatter<?> formatter = null; |
||||
Class<?> type; |
||||
if (propertyType.isCollection() || propertyType.isArray()) { |
||||
CollectionTypeDescriptor collectionType = new CollectionTypeDescriptor(propertyType.getType(), propertyType |
||||
.getElementType()); |
||||
formatter = collectionTypeFormatters.get(collectionType); |
||||
if (formatter != null) { |
||||
return formatter; |
||||
} else { |
||||
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; |
||||
} else { |
||||
Formatted formatted = AnnotationUtils.findAnnotation(type, Formatted.class); |
||||
if (formatted != null) { |
||||
Class formatterClass = formatted.value(); |
||||
try { |
||||
formatter = (Formatter) formatterClass.newInstance(); |
||||
} catch (InstantiationException e) { |
||||
throw new IllegalStateException( |
||||
"Formatter referenced by @Formatted annotation does not have default constructor", e); |
||||
} catch (IllegalAccessException e) { |
||||
throw new IllegalStateException( |
||||
"Formatter referenced by @Formatted annotation does not have public constructor", e); |
||||
} |
||||
typeFormatters.put(type, formatter); |
||||
return formatter; |
||||
} else { |
||||
return new DefaultFormatter(type, typeConverter); |
||||
} |
||||
} |
||||
} |
||||
|
||||
public void add(Class<?> propertyType, Formatter<?> formatter) { |
||||
if (propertyType.isAnnotation()) { |
||||
annotationFormatters.put(propertyType, new SimpleAnnotationFormatterFactory(formatter)); |
||||
} else { |
||||
typeFormatters.put(propertyType, formatter); |
||||
} |
||||
} |
||||
|
||||
public void add(CollectionTypeDescriptor 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) { |
||||
Type[] genericInterfaces = classToIntrospect.getGenericInterfaces(); |
||||
for (Type genericInterface : genericInterfaces) { |
||||
if (genericInterface instanceof ParameterizedType) { |
||||
ParameterizedType pInterface = (ParameterizedType) genericInterface; |
||||
if (AnnotationFormatterFactory.class.isAssignableFrom((Class) pInterface.getRawType())) { |
||||
return getParameterClass(pInterface.getActualTypeArguments()[0], factory.getClass()); |
||||
} |
||||
} |
||||
} |
||||
classToIntrospect = classToIntrospect.getSuperclass(); |
||||
} |
||||
throw new IllegalArgumentException( |
||||
"Unable to extract Annotation type A argument from AnnotationFormatterFactory [" |
||||
+ factory.getClass().getName() + "]; does the factory parameterize the <A> generic type?"); |
||||
} |
||||
|
||||
private Class getParameterClass(Type parameterType, Class converterClass) { |
||||
if (parameterType instanceof TypeVariable) { |
||||
parameterType = GenericTypeResolver.resolveTypeVariable((TypeVariable) parameterType, converterClass); |
||||
} |
||||
if (parameterType instanceof Class) { |
||||
return (Class) parameterType; |
||||
} |
||||
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; |
||||
|
||||
public SimpleAnnotationFormatterFactory(Formatter formatter) { |
||||
this.formatter = formatter; |
||||
} |
||||
|
||||
public Formatter getFormatter(Annotation annotation) { |
||||
return formatter; |
||||
} |
||||
|
||||
} |
||||
|
||||
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 CollectionTypeDescriptor collectionType; |
||||
|
||||
private Formatter elementFormatter; |
||||
|
||||
public DefaultCollectionFormatter(CollectionTypeDescriptor 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.getType().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<? extends Collection> implType = ConversionUtils |
||||
.getCollectionImpl((Class<? extends Collection>) collectionType.getType()); |
||||
return (Collection) implType.newInstance(); |
||||
} catch (InstantiationException e) { |
||||
throw new IllegalStateException("Should not happen", e); |
||||
} catch (IllegalAccessException e) { |
||||
throw new IllegalStateException("Should not happen", e); |
||||
} |
||||
|
||||
} |
||||
}; |
||||
|
||||
} |
||||
@ -1,67 +0,0 @@
@@ -1,67 +0,0 @@
|
||||
/* |
||||
* 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.model.ui.support; |
||||
|
||||
import java.util.List; |
||||
|
||||
import org.springframework.core.convert.TypeDescriptor; |
||||
|
||||
/** |
||||
* A ValueModel for a element in a List. |
||||
* @author Keith Donald |
||||
* @since 3.0 |
||||
*/ |
||||
public class ListElementValueModel implements ValueModel { |
||||
|
||||
@SuppressWarnings("unchecked") |
||||
private List list; |
||||
|
||||
private int index; |
||||
|
||||
private Class<?> elementType; |
||||
|
||||
@SuppressWarnings("unchecked") |
||||
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; |
||||
} |
||||
|
||||
@SuppressWarnings("unchecked") |
||||
public void setValue(Object value) { |
||||
list.set(index, value); |
||||
} |
||||
} |
||||
@ -1,67 +0,0 @@
@@ -1,67 +0,0 @@
|
||||
/* |
||||
* 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.model.ui.support; |
||||
|
||||
import java.util.Map; |
||||
|
||||
import org.springframework.core.convert.TypeDescriptor; |
||||
|
||||
/** |
||||
* A ValueModel for a element in a Map. |
||||
* @author Keith Donald |
||||
* @since 3.0 |
||||
*/ |
||||
public class MapValueValueModel implements ValueModel { |
||||
|
||||
private Object key; |
||||
|
||||
private Class<?> elementType; |
||||
|
||||
@SuppressWarnings("unchecked") |
||||
private Map map; |
||||
|
||||
@SuppressWarnings("unchecked") |
||||
public MapValueValueModel(Object key, Class<?> elementType, Map map, FieldModelContext 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; |
||||
} |
||||
|
||||
@SuppressWarnings("unchecked") |
||||
public void setValue(Object value) { |
||||
map.put(key, value); |
||||
} |
||||
} |
||||
@ -1,89 +0,0 @@
@@ -1,89 +0,0 @@
|
||||
/* |
||||
* 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.model.ui.support; |
||||
|
||||
import java.util.Map; |
||||
|
||||
import org.springframework.context.MessageSource; |
||||
import org.springframework.model.binder.BindingResult; |
||||
import org.springframework.model.binder.support.AbstractBinder; |
||||
import org.springframework.model.binder.support.AlertBindingResult; |
||||
import org.springframework.model.binder.support.FieldBinder; |
||||
import org.springframework.model.binder.support.FieldNotEditableResult; |
||||
import org.springframework.model.binder.support.FieldNotFoundResult; |
||||
import org.springframework.model.ui.BindingStatus; |
||||
import org.springframework.model.ui.FieldModel; |
||||
import org.springframework.model.ui.FieldNotFoundException; |
||||
import org.springframework.model.ui.PresentationModel; |
||||
|
||||
/** |
||||
* Binds field values to PresentationModel objects. |
||||
* @author Keith Donald |
||||
* @since 3.0 |
||||
* @see #setMessageSource(MessageSource) |
||||
* @see #setRequiredFields(String[]) |
||||
* @see #setCommitDirtyValue(boolean) |
||||
* @see #bind(Map, PresentationModel) |
||||
*/ |
||||
public class PresentationModelBinder extends AbstractBinder<PresentationModel> { |
||||
|
||||
private boolean commitDirtyValue; |
||||
|
||||
/** |
||||
* Configures if this PresentationModelBinder should eagerly commit the dirty value after a successful field binding. |
||||
* Default is false. |
||||
*/ |
||||
public void setCommitDirtyValue(boolean commitDirtyValue) { |
||||
this.commitDirtyValue = commitDirtyValue; |
||||
} |
||||
|
||||
// subclass hooks
|
||||
|
||||
@Override |
||||
protected FieldBinder createFieldBinder(PresentationModel model) { |
||||
return new FieldModelBinder(model); |
||||
} |
||||
|
||||
// internal helpers
|
||||
|
||||
private class FieldModelBinder implements FieldBinder { |
||||
|
||||
private PresentationModel presentationModel; |
||||
|
||||
public FieldModelBinder(PresentationModel presentationModel) { |
||||
this.presentationModel = presentationModel; |
||||
} |
||||
|
||||
public BindingResult bind(String fieldName, Object value) { |
||||
FieldModel field; |
||||
try { |
||||
field = presentationModel.getFieldModel(fieldName); |
||||
} catch (FieldNotFoundException e) { |
||||
return new FieldNotFoundResult(fieldName, value, getMessageSource()); |
||||
} |
||||
if (!field.isEditable()) { |
||||
return new FieldNotEditableResult(fieldName, value, getMessageSource()); |
||||
} else { |
||||
field.applySubmittedValue(value); |
||||
if (field.getBindingStatus() == BindingStatus.DIRTY && commitDirtyValue) { |
||||
field.commit(); |
||||
} |
||||
return new AlertBindingResult(fieldName, value, field.getStatusAlert()); |
||||
} |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -1,61 +0,0 @@
@@ -1,61 +0,0 @@
|
||||
/* |
||||
* 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.model.ui.support; |
||||
|
||||
import java.beans.PropertyDescriptor; |
||||
|
||||
import org.springframework.core.MethodParameter; |
||||
import org.springframework.core.convert.TypeDescriptor; |
||||
import org.springframework.util.ReflectionUtils; |
||||
|
||||
/** |
||||
* A ValueModel for a bean property. |
||||
* @author Keith Donald |
||||
* @since 3.0 |
||||
*/ |
||||
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); |
||||
} |
||||
|
||||
} |
||||
@ -1,73 +0,0 @@
@@ -1,73 +0,0 @@
|
||||
/* |
||||
* 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.model.ui.support; |
||||
|
||||
class ValueBuffer { |
||||
|
||||
private Object value; |
||||
|
||||
private boolean hasValue; |
||||
|
||||
private ValueModel model; |
||||
|
||||
private boolean flushFailed; |
||||
|
||||
private Exception flushException; |
||||
|
||||
public ValueBuffer(ValueModel model) { |
||||
this.model = model; |
||||
} |
||||
|
||||
public boolean hasValue() { |
||||
return hasValue; |
||||
} |
||||
|
||||
public Object getValue() { |
||||
if (!hasValue()) { |
||||
throw new IllegalStateException("No value in buffer"); |
||||
} |
||||
return value; |
||||
} |
||||
|
||||
public void setValue(Object value) { |
||||
this.value = value; |
||||
hasValue = true; |
||||
} |
||||
|
||||
public void flush() { |
||||
try { |
||||
model.setValue(value); |
||||
clear(); |
||||
} catch (Exception e) { |
||||
flushFailed = true; |
||||
flushException = e; |
||||
} |
||||
} |
||||
|
||||
public void clear() { |
||||
value = null; |
||||
hasValue = false; |
||||
flushFailed = false; |
||||
} |
||||
|
||||
public boolean flushFailed() { |
||||
return flushFailed; |
||||
} |
||||
|
||||
public Exception getFlushException() { |
||||
return flushException; |
||||
} |
||||
} |
||||
@ -1,52 +0,0 @@
@@ -1,52 +0,0 @@
|
||||
/* |
||||
* 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.model.ui.support; |
||||
|
||||
import org.springframework.core.convert.TypeDescriptor; |
||||
|
||||
/** |
||||
* A interface for reading and writing a value. |
||||
* @author Keith Donald |
||||
* @since 3.0 |
||||
*/ |
||||
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); |
||||
} |
||||
@ -1,89 +0,0 @@
@@ -1,89 +0,0 @@
|
||||
/* |
||||
* 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.model.ui.support; |
||||
|
||||
import java.util.LinkedHashMap; |
||||
import java.util.Map; |
||||
|
||||
import org.springframework.model.ui.FieldModel; |
||||
import org.springframework.model.ui.PresentationModel; |
||||
|
||||
/** |
||||
* A binder designed for use in HTTP (web) environments. |
||||
* Suited for binding user-provided HTTP query parameters to model properties. |
||||
* @author Keith Donald |
||||
* @since 3.0 |
||||
* @see #setDefaultPrefix(String) |
||||
* @see #setPresentPrefix(String) |
||||
* @see #filter(Map, PresentationModel) |
||||
*/ |
||||
public class WebBinder extends PresentationModelBinder { |
||||
|
||||
private String defaultPrefix = "!"; |
||||
|
||||
private String presentPrefix = "_"; |
||||
|
||||
/** |
||||
* Configure the prefix used to detect the default value for a field when no value is submitted. |
||||
* Default is '!'. |
||||
*/ |
||||
public void setDefaultPrefix(String defaultPrefix) { |
||||
this.defaultPrefix = defaultPrefix; |
||||
} |
||||
|
||||
/** |
||||
* Configure the prefix used to detect the presence of a field on the web UI when no value was actually submitted. |
||||
* This is used to configure a <i>empty</i> field when no other {@link #setDefaultPrefix(String) default value} is specified by the client. |
||||
* Default is '_'. |
||||
*/ |
||||
public void setPresentPrefix(String presentPrefix) { |
||||
this.presentPrefix = presentPrefix; |
||||
} |
||||
|
||||
@Override |
||||
protected Map<String, ? extends Object> filter(Map<String, ? extends Object> fieldValues, PresentationModel model) { |
||||
LinkedHashMap<String, Object> filteredValues = new LinkedHashMap<String, Object>(); |
||||
for (Map.Entry<String, ? extends Object> entry : fieldValues.entrySet()) { |
||||
String field = entry.getKey(); |
||||
Object value = entry.getValue(); |
||||
if (field.startsWith(defaultPrefix)) { |
||||
field = field.substring(defaultPrefix.length()); |
||||
if (!fieldValues.containsKey(field)) { |
||||
filteredValues.put(field, value); |
||||
} |
||||
} else if (field.startsWith(presentPrefix)) { |
||||
field = field.substring(presentPrefix.length()); |
||||
if (!fieldValues.containsKey(field) && !fieldValues.containsKey(defaultPrefix + field)) { |
||||
value = getEmptyValue(model.getFieldModel(field)); |
||||
filteredValues.put(field, value); |
||||
} |
||||
} else { |
||||
filteredValues.put(entry.getKey(), entry.getValue()); |
||||
} |
||||
} |
||||
return filteredValues; |
||||
} |
||||
|
||||
protected Object getEmptyValue(FieldModel binding) { |
||||
Class<?> type = binding.getValueType(); |
||||
if (boolean.class.equals(type) || Boolean.class.equals(type)) { |
||||
return Boolean.FALSE; |
||||
} else { |
||||
return null; |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -1,5 +0,0 @@
@@ -1,5 +0,0 @@
|
||||
/** |
||||
* Default implementation of a PresentationModel usable in most environments. |
||||
*/ |
||||
package org.springframework.model.ui.support; |
||||
|
||||
@ -1,33 +0,0 @@
@@ -1,33 +0,0 @@
|
||||
/* |
||||
* 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.model.validation; |
||||
|
||||
import java.lang.annotation.Documented; |
||||
import java.lang.annotation.ElementType; |
||||
import java.lang.annotation.Retention; |
||||
import java.lang.annotation.RetentionPolicy; |
||||
import java.lang.annotation.Target; |
||||
|
||||
import org.springframework.model.alert.Severity; |
||||
|
||||
@Target({ElementType.TYPE}) |
||||
@Retention(RetentionPolicy.RUNTIME) |
||||
@Documented |
||||
public @interface Impact { |
||||
|
||||
Severity value(); |
||||
|
||||
} |
||||
@ -1,31 +0,0 @@
@@ -1,31 +0,0 @@
|
||||
/* |
||||
* 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.model.validation; |
||||
|
||||
import java.lang.annotation.Documented; |
||||
import java.lang.annotation.ElementType; |
||||
import java.lang.annotation.Retention; |
||||
import java.lang.annotation.RetentionPolicy; |
||||
import java.lang.annotation.Target; |
||||
|
||||
@Target({ElementType.TYPE}) |
||||
@Retention(RetentionPolicy.RUNTIME) |
||||
@Documented |
||||
public @interface Message { |
||||
|
||||
String[] value(); |
||||
|
||||
} |
||||
@ -1,20 +0,0 @@
@@ -1,20 +0,0 @@
|
||||
/* |
||||
* 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.model.validation; |
||||
|
||||
public interface ValidationConstraint<T> { |
||||
boolean validate(T value); |
||||
} |
||||
@ -1,5 +0,0 @@
@@ -1,5 +0,0 @@
|
||||
/** |
||||
* Model ValidationConstraint SPI. |
||||
*/ |
||||
package org.springframework.model.validation; |
||||
|
||||
@ -1,34 +0,0 @@
@@ -1,34 +0,0 @@
|
||||
package org.springframework.model.alert; |
||||
|
||||
import static org.junit.Assert.assertEquals; |
||||
import static org.springframework.model.alert.Alerts.error; |
||||
import static org.springframework.model.alert.Alerts.fatal; |
||||
import static org.springframework.model.alert.Alerts.info; |
||||
import static org.springframework.model.alert.Alerts.warning; |
||||
|
||||
import org.junit.Test; |
||||
import org.springframework.model.alert.Alert; |
||||
import org.springframework.model.alert.Severity; |
||||
|
||||
public class AlertsTests { |
||||
|
||||
@Test |
||||
public void testFactoryMethods() { |
||||
Alert a1 = info("alert 1"); |
||||
assertEquals(Severity.INFO, a1.getSeverity()); |
||||
assertEquals("alert 1", a1.getMessage()); |
||||
|
||||
Alert a2 = warning("alert 2"); |
||||
assertEquals(Severity.WARNING, a2.getSeverity()); |
||||
assertEquals("alert 2", a2.getMessage()); |
||||
|
||||
Alert a3 = error("alert 3"); |
||||
assertEquals(Severity.ERROR, a3.getSeverity()); |
||||
assertEquals("alert 3", a3.getMessage()); |
||||
|
||||
Alert a4 = fatal("alert 4"); |
||||
assertEquals(Severity.FATAL, a4.getSeverity()); |
||||
assertEquals("alert 4", a4.getMessage()); |
||||
|
||||
} |
||||
} |
||||
@ -1,39 +0,0 @@
@@ -1,39 +0,0 @@
|
||||
package org.springframework.model.alert.support; |
||||
|
||||
import static org.junit.Assert.assertEquals; |
||||
|
||||
import org.junit.Before; |
||||
import org.junit.Test; |
||||
import org.springframework.model.alert.Alert; |
||||
import org.springframework.model.alert.Severity; |
||||
import org.springframework.model.alert.support.DefaultAlertContext; |
||||
|
||||
public class DefaultAlertContextTests { |
||||
|
||||
private DefaultAlertContext context; |
||||
|
||||
@Before |
||||
public void setUp() { |
||||
context = new DefaultAlertContext(); |
||||
} |
||||
|
||||
@Test |
||||
public void addAlert() { |
||||
Alert alert = new Alert() { |
||||
public String getCode() { |
||||
return "invalidFormat"; |
||||
} |
||||
|
||||
public String getMessage() { |
||||
return "Please enter a value in format yyyy-dd-mm"; |
||||
} |
||||
|
||||
public Severity getSeverity() { |
||||
return Severity.ERROR; |
||||
} |
||||
}; |
||||
context.add("form.property", alert); |
||||
assertEquals(1, context.getAlerts().size()); |
||||
assertEquals("invalidFormat", context.getAlerts("form.property").get(0).getCode()); |
||||
} |
||||
} |
||||
@ -1,148 +0,0 @@
@@ -1,148 +0,0 @@
|
||||
/* |
||||
* 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.model.binder.support; |
||||
|
||||
import static org.junit.Assert.assertEquals; |
||||
import static org.junit.Assert.assertNotNull; |
||||
import static org.junit.Assert.assertTrue; |
||||
|
||||
import java.util.HashMap; |
||||
import java.util.Map; |
||||
|
||||
import org.junit.Test; |
||||
|
||||
import org.springframework.core.style.ToStringCreator; |
||||
import org.springframework.model.binder.Binder; |
||||
|
||||
/** |
||||
* @author Mark Fisher |
||||
* @since 3.0 |
||||
*/ |
||||
public class GenericBinderTests { |
||||
|
||||
@Test |
||||
public void simpleValues() { |
||||
Person person = new Person(); |
||||
Map<String, Object> map = new HashMap<String, Object>(); |
||||
map.put("name", "John Doe"); |
||||
map.put("age", 42); |
||||
map.put("male", true); |
||||
Binder <Object> binder = new GenericBinder(); |
||||
binder.bind(map, person); |
||||
assertEquals("John Doe", person.name); |
||||
assertEquals(42, person.age); |
||||
assertTrue(person.male); |
||||
} |
||||
|
||||
@Test |
||||
public void nestedValues() { |
||||
Person person = new Person(); |
||||
Map<String, Object> map = new HashMap<String, Object>(); |
||||
map.put("pob.city", "Rome"); |
||||
map.put("pob.country", "Italy"); |
||||
Binder<Object> binder = new GenericBinder(); |
||||
binder.bind(map, person); |
||||
assertNotNull(person.pob); |
||||
assertEquals("Rome", person.pob.city); |
||||
assertEquals("Italy", person.pob.country); |
||||
} |
||||
|
||||
|
||||
public static class Person { |
||||
|
||||
private String name; |
||||
|
||||
private int age; |
||||
|
||||
private boolean male; |
||||
|
||||
private PlaceOfBirth pob; |
||||
|
||||
public String getName() { |
||||
return name; |
||||
} |
||||
|
||||
public void setName(String name) { |
||||
this.name = name; |
||||
} |
||||
|
||||
public int getAge() { |
||||
return age; |
||||
} |
||||
|
||||
public void setAge(int age) { |
||||
this.age = age; |
||||
} |
||||
|
||||
public boolean isMale() { |
||||
return male; |
||||
} |
||||
|
||||
public void setMale(boolean male) { |
||||
this.male = male; |
||||
} |
||||
|
||||
public PlaceOfBirth getPob() { |
||||
return pob; |
||||
} |
||||
|
||||
public void setPob(PlaceOfBirth pob) { |
||||
this.pob = pob; |
||||
} |
||||
|
||||
public String toString() { |
||||
return new ToStringCreator(this) |
||||
.append("name", name) |
||||
.append("age", age) |
||||
.append("male", male) |
||||
.append("pob", pob) |
||||
.toString(); |
||||
} |
||||
} |
||||
|
||||
|
||||
public static class PlaceOfBirth { |
||||
|
||||
private String city; |
||||
|
||||
private String country; |
||||
|
||||
public String getCity() { |
||||
return city; |
||||
} |
||||
|
||||
public void setCity(String city) { |
||||
this.city = city; |
||||
} |
||||
|
||||
public String getCountry() { |
||||
return country; |
||||
} |
||||
|
||||
public void setCountry(String country) { |
||||
this.country = country; |
||||
} |
||||
|
||||
public String toString() { |
||||
return new ToStringCreator(this) |
||||
.append("city", city) |
||||
.append("country", country) |
||||
.toString(); |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -1,23 +0,0 @@
@@ -1,23 +0,0 @@
|
||||
package org.springframework.model.message; |
||||
|
||||
import static org.junit.Assert.assertEquals; |
||||
|
||||
import java.util.Locale; |
||||
|
||||
import org.junit.Test; |
||||
import org.springframework.model.message.MessageBuilder; |
||||
import org.springframework.model.message.ResolvableArgument; |
||||
|
||||
public class MessageBuilderTests { |
||||
|
||||
@Test |
||||
public void buildMessage() { |
||||
MockMessageSource messageSource = new MockMessageSource(); |
||||
messageSource.addMessage("invalidFormat", Locale.US, "#{label} must be in format #{format}"); |
||||
messageSource.addMessage("mathForm.decimalField", Locale.US, "Decimal Field"); |
||||
MessageBuilder builder = new MessageBuilder(messageSource); |
||||
String message = builder.code("invalidFormat").arg("label", new ResolvableArgument("mathForm.decimalField")) |
||||
.arg("format", "#,###.##").locale(Locale.US).defaultMessage("Field must be in format #,###.##").build(); |
||||
assertEquals("Decimal Field must be in format #,###.##", message); |
||||
} |
||||
} |
||||
@ -1,26 +0,0 @@
@@ -1,26 +0,0 @@
|
||||
package org.springframework.model.message; |
||||
|
||||
import static org.junit.Assert.assertEquals; |
||||
|
||||
import java.util.Locale; |
||||
|
||||
import org.junit.Test; |
||||
import org.springframework.model.message.MessageResolver; |
||||
import org.springframework.model.message.MessageResolverBuilder; |
||||
import org.springframework.model.message.ResolvableArgument; |
||||
|
||||
public class MessageResolverBuilderTests { |
||||
|
||||
private MessageResolverBuilder builder = new MessageResolverBuilder(); |
||||
|
||||
@Test |
||||
public void buildMessage() { |
||||
MessageResolver resolver = builder.code("invalidFormat").arg("label", new ResolvableArgument("mathForm.decimalField")) |
||||
.arg("format", "#,###.##").defaultMessage("Field must be in format #,###.##").build(); |
||||
MockMessageSource messageSource = new MockMessageSource(); |
||||
messageSource.addMessage("invalidFormat", Locale.US, "#{label} must be in format #{format}"); |
||||
messageSource.addMessage("mathForm.decimalField", Locale.US, "Decimal Field"); |
||||
String message = resolver.resolveMessage(messageSource, Locale.US); |
||||
assertEquals("Decimal Field must be in format #,###.##", message); |
||||
} |
||||
} |
||||
@ -1,48 +0,0 @@
@@ -1,48 +0,0 @@
|
||||
package org.springframework.model.message; |
||||
|
||||
import java.text.MessageFormat; |
||||
import java.util.HashMap; |
||||
import java.util.Locale; |
||||
import java.util.Map; |
||||
|
||||
import org.springframework.context.support.AbstractMessageSource; |
||||
import org.springframework.util.Assert; |
||||
|
||||
public class MockMessageSource extends AbstractMessageSource { |
||||
|
||||
/** Map from 'code + locale' keys to message Strings */ |
||||
private final Map<String, String> messages = new HashMap<String, String>(); |
||||
|
||||
@Override |
||||
protected MessageFormat resolveCode(String code, Locale locale) { |
||||
throw new IllegalStateException("Should not be called"); |
||||
} |
||||
|
||||
@Override |
||||
protected String resolveCodeWithoutArguments(String code, Locale locale) { |
||||
return this.messages.get(code + "_" + locale.toString()); |
||||
} |
||||
|
||||
/** |
||||
* Associate the given message with the given code. |
||||
* @param code the lookup code |
||||
* @param locale the locale that the message should be found within |
||||
* @param msg the message associated with this lookup code |
||||
*/ |
||||
public void addMessage(String code, Locale locale, String msg) { |
||||
Assert.notNull(code, "Code must not be null"); |
||||
Assert.notNull(locale, "Locale must not be null"); |
||||
Assert.notNull(msg, "Message must not be null"); |
||||
this.messages.put(code + "_" + locale.toString(), msg); |
||||
if (logger.isDebugEnabled()) { |
||||
logger.debug("Added message [" + msg + "] for code [" + code + "] and Locale [" + locale + "]"); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public String toString() { |
||||
return getClass().getName() + ": " + this.messages; |
||||
} |
||||
|
||||
} |
||||
|
||||
@ -1,36 +0,0 @@
@@ -1,36 +0,0 @@
|
||||
package org.springframework.model.ui.format.date; |
||||
|
||||
import static org.junit.Assert.assertEquals; |
||||
|
||||
import java.text.ParseException; |
||||
import java.util.Calendar; |
||||
import java.util.Locale; |
||||
|
||||
import org.junit.Test; |
||||
import org.springframework.model.ui.format.date.DateFormatter; |
||||
|
||||
public class DateFormatterTests { |
||||
|
||||
private DateFormatter formatter = new DateFormatter(); |
||||
|
||||
@Test |
||||
public void formatValue() { |
||||
Calendar cal = Calendar.getInstance(Locale.US); |
||||
cal.clear(); |
||||
cal.set(Calendar.YEAR, 2009); |
||||
cal.set(Calendar.MONTH, Calendar.JUNE); |
||||
cal.set(Calendar.DAY_OF_MONTH, 1); |
||||
assertEquals("2009-06-01", formatter.format(cal.getTime(), Locale.US)); |
||||
} |
||||
|
||||
@Test |
||||
public void parseValue() throws ParseException { |
||||
Calendar cal = Calendar.getInstance(Locale.US); |
||||
cal.clear(); |
||||
cal.set(Calendar.YEAR, 2009); |
||||
cal.set(Calendar.MONTH, Calendar.JUNE); |
||||
cal.set(Calendar.DAY_OF_MONTH, 1); |
||||
assertEquals(cal.getTime(), formatter.parse("2009-06-01", Locale.US)); |
||||
} |
||||
|
||||
} |
||||
@ -1,51 +0,0 @@
@@ -1,51 +0,0 @@
|
||||
package org.springframework.model.ui.format.number; |
||||
|
||||
import static org.junit.Assert.assertEquals; |
||||
|
||||
import java.math.BigDecimal; |
||||
import java.text.ParseException; |
||||
import java.util.Locale; |
||||
|
||||
import org.junit.Test; |
||||
import org.springframework.model.ui.format.number.CurrencyFormatter; |
||||
|
||||
public class CurrencyFormatterTests { |
||||
|
||||
private CurrencyFormatter formatter = new CurrencyFormatter(); |
||||
|
||||
@Test |
||||
public void formatValue() { |
||||
assertEquals("$23.00", formatter.format(new BigDecimal("23"), Locale.US)); |
||||
} |
||||
|
||||
@Test |
||||
public void parseValue() throws ParseException { |
||||
assertEquals(new BigDecimal("23.56"), formatter.parse("$23.56", Locale.US)); |
||||
} |
||||
|
||||
@Test |
||||
public void parseEmptyValue() throws ParseException { |
||||
assertEquals(null, formatter.parse("", Locale.US)); |
||||
} |
||||
|
||||
@Test(expected = ParseException.class) |
||||
public void parseBogusValue() throws ParseException { |
||||
formatter.parse("bogus", Locale.US); |
||||
} |
||||
|
||||
@Test |
||||
public void parseValueDefaultRoundDown() throws ParseException { |
||||
assertEquals(new BigDecimal("23.56"), formatter.parse("$23.567", Locale.US)); |
||||
} |
||||
|
||||
@Test |
||||
public void parseWholeValue() throws ParseException { |
||||
assertEquals(new BigDecimal("23.00"), formatter.parse("$23", Locale.US)); |
||||
} |
||||
|
||||
@Test(expected=ParseException.class) |
||||
public void parseValueNotLenientFailure() throws ParseException { |
||||
formatter.parse("$23.56bogus", Locale.US); |
||||
} |
||||
|
||||
} |
||||
@ -1,41 +0,0 @@
@@ -1,41 +0,0 @@
|
||||
package org.springframework.model.ui.format.number; |
||||
|
||||
import static org.junit.Assert.assertEquals; |
||||
|
||||
import java.math.BigDecimal; |
||||
import java.text.ParseException; |
||||
import java.util.Locale; |
||||
|
||||
import org.junit.Test; |
||||
import org.springframework.model.ui.format.number.DecimalFormatter; |
||||
|
||||
public class DecimalFormatterTests { |
||||
|
||||
private DecimalFormatter formatter = new DecimalFormatter(); |
||||
|
||||
@Test |
||||
public void formatValue() { |
||||
assertEquals("23.56", formatter.format(new BigDecimal("23.56"), Locale.US)); |
||||
} |
||||
|
||||
@Test |
||||
public void parseValue() throws ParseException { |
||||
assertEquals(new BigDecimal("23.56"), formatter.parse("23.56", Locale.US)); |
||||
} |
||||
|
||||
@Test |
||||
public void parseEmptyValue() throws ParseException { |
||||
assertEquals(null, formatter.parse("", Locale.US)); |
||||
} |
||||
|
||||
@Test(expected = ParseException.class) |
||||
public void parseBogusValue() throws ParseException { |
||||
formatter.parse("bogus", Locale.US); |
||||
} |
||||
|
||||
@Test(expected = ParseException.class) |
||||
public void parsePercentValueNotLenientFailure() throws ParseException { |
||||
formatter.parse("23.56bogus", Locale.US); |
||||
} |
||||
|
||||
} |
||||
@ -1,40 +0,0 @@
@@ -1,40 +0,0 @@
|
||||
package org.springframework.model.ui.format.number; |
||||
|
||||
import static org.junit.Assert.assertEquals; |
||||
|
||||
import java.text.ParseException; |
||||
import java.util.Locale; |
||||
|
||||
import org.junit.Test; |
||||
import org.springframework.model.ui.format.number.IntegerFormatter; |
||||
|
||||
public class IntegerFormatterTests { |
||||
|
||||
private IntegerFormatter formatter = new IntegerFormatter(); |
||||
|
||||
@Test |
||||
public void formatValue() { |
||||
assertEquals("23", formatter.format(23L, Locale.US)); |
||||
} |
||||
|
||||
@Test |
||||
public void parseValue() throws ParseException { |
||||
assertEquals((Long) 2356L, formatter.parse("2356", Locale.US)); |
||||
} |
||||
|
||||
@Test |
||||
public void parseEmptyValue() throws ParseException { |
||||
assertEquals(null, formatter.parse("", Locale.US)); |
||||
} |
||||
|
||||
@Test(expected = ParseException.class) |
||||
public void parseBogusValue() throws ParseException { |
||||
formatter.parse("bogus", Locale.US); |
||||
} |
||||
|
||||
@Test(expected = ParseException.class) |
||||
public void parsePercentValueNotLenientFailure() throws ParseException { |
||||
formatter.parse("23.56", Locale.US); |
||||
} |
||||
|
||||
} |
||||
@ -1,42 +0,0 @@
@@ -1,42 +0,0 @@
|
||||
package org.springframework.model.ui.format.number; |
||||
|
||||
import static org.junit.Assert.assertEquals; |
||||
|
||||
import java.math.BigDecimal; |
||||
import java.text.ParseException; |
||||
import java.util.Locale; |
||||
|
||||
import org.junit.Test; |
||||
import org.springframework.model.ui.format.number.PercentFormatter; |
||||
|
||||
public class PercentFormatterTests { |
||||
|
||||
private PercentFormatter formatter = new PercentFormatter(); |
||||
|
||||
@Test |
||||
public void formatValue() { |
||||
assertEquals("23%", formatter.format(new BigDecimal(".23"), Locale.US)); |
||||
} |
||||
|
||||
@Test |
||||
public void parseValue() throws ParseException { |
||||
assertEquals(new BigDecimal(".2356"), formatter.parse("23.56%", |
||||
Locale.US)); |
||||
} |
||||
|
||||
@Test |
||||
public void parseEmptyValue() throws ParseException { |
||||
assertEquals(null, formatter.parse("", Locale.US)); |
||||
} |
||||
|
||||
@Test(expected = ParseException.class) |
||||
public void parseBogusValue() throws ParseException { |
||||
formatter.parse("bogus", Locale.US); |
||||
} |
||||
|
||||
@Test(expected = ParseException.class) |
||||
public void parsePercentValueNotLenientFailure() throws ParseException { |
||||
formatter.parse("23.56%bogus", Locale.US); |
||||
} |
||||
|
||||
} |
||||
@ -1,652 +0,0 @@
@@ -1,652 +0,0 @@
|
||||
package org.springframework.model.ui.support; |
||||
|
||||
import static org.junit.Assert.assertEquals; |
||||
import static org.junit.Assert.assertFalse; |
||||
import static org.junit.Assert.assertTrue; |
||||
|
||||
import java.math.BigDecimal; |
||||
import java.text.ParseException; |
||||
import java.util.ArrayList; |
||||
import java.util.Arrays; |
||||
import java.util.Collections; |
||||
import java.util.Date; |
||||
import java.util.LinkedHashMap; |
||||
import java.util.List; |
||||
import java.util.Locale; |
||||
import java.util.Map; |
||||
|
||||
import junit.framework.Assert; |
||||
|
||||
import org.junit.After; |
||||
import org.junit.Before; |
||||
import org.junit.Test; |
||||
import org.springframework.context.i18n.LocaleContextHolder; |
||||
import org.springframework.core.style.ToStringCreator; |
||||
import org.springframework.model.binder.BindingResults; |
||||
import org.springframework.model.binder.MissingFieldException; |
||||
import org.springframework.model.message.MockMessageSource; |
||||
import org.springframework.model.ui.BindingStatus; |
||||
import org.springframework.model.ui.FieldModel; |
||||
import org.springframework.model.ui.format.AnnotationFormatterFactory; |
||||
import org.springframework.model.ui.format.Formatted; |
||||
import org.springframework.model.ui.format.Formatter; |
||||
import org.springframework.model.ui.format.date.DateFormatter; |
||||
import org.springframework.model.ui.format.number.CurrencyFormat; |
||||
import org.springframework.model.ui.format.number.CurrencyFormatter; |
||||
import org.springframework.model.ui.format.number.IntegerFormatter; |
||||
|
||||
public class PresentationModelBinderTests { |
||||
|
||||
private PresentationModelBinder binder; |
||||
|
||||
private DefaultPresentationModel presentationModel; |
||||
|
||||
private TestBean bean; |
||||
|
||||
@Before |
||||
public void setUp() { |
||||
bean = new TestBean(); |
||||
presentationModel = new DefaultPresentationModel(bean); |
||||
binder = new PresentationModelBinder(); |
||||
binder.setCommitDirtyValue(true); |
||||
LocaleContextHolder.setLocale(Locale.US); |
||||
} |
||||
|
||||
@After |
||||
public void tearDown() { |
||||
LocaleContextHolder.setLocale(null); |
||||
} |
||||
|
||||
@Test |
||||
public void bindSingleValuesWithDefaultTypeConverterConversion() { |
||||
Map<String, String> values = new LinkedHashMap<String, String>(); |
||||
values.put("string", "test"); |
||||
values.put("integer", "3"); |
||||
values.put("foo", "BAR"); |
||||
BindingResults results = binder.bind(values, presentationModel); |
||||
assertEquals(3, results.size()); |
||||
|
||||
assertEquals("string", results.get(0).getFieldName()); |
||||
assertFalse(results.get(0).isFailure()); |
||||
assertEquals("test", results.get(0).getSubmittedValue()); |
||||
|
||||
assertEquals("integer", results.get(1).getFieldName()); |
||||
assertFalse(results.get(1).isFailure()); |
||||
assertEquals("3", results.get(1).getSubmittedValue()); |
||||
|
||||
assertEquals("foo", results.get(2).getFieldName()); |
||||
assertFalse(results.get(2).isFailure()); |
||||
assertEquals("BAR", results.get(2).getSubmittedValue()); |
||||
|
||||
assertEquals("test", bean.getString()); |
||||
assertEquals(3, bean.getInteger()); |
||||
assertEquals(FooEnum.BAR, bean.getFoo()); |
||||
} |
||||
|
||||
@Test |
||||
public void bindSingleValuesWithDefaultTypeConversionFailure() { |
||||
Map<String, String> values = new LinkedHashMap<String, String>(); |
||||
values.put("string", "test"); |
||||
// bad value
|
||||
values.put("integer", "bogus"); |
||||
values.put("foo", "BAR"); |
||||
BindingResults results = binder.bind(values, presentationModel); |
||||
assertEquals(3, results.size()); |
||||
assertTrue(results.get(1).isFailure()); |
||||
assertEquals("typeMismatch", results.get(1).getAlert().getCode()); |
||||
} |
||||
|
||||
@Test |
||||
public void bindSingleValuePropertyFormatter() throws ParseException { |
||||
presentationModel.field("date").formatWith(new DateFormatter()); |
||||
binder.bind(Collections.singletonMap("date", "2009-06-01"), presentationModel); |
||||
assertEquals(new DateFormatter().parse("2009-06-01", Locale.US), bean.getDate()); |
||||
} |
||||
|
||||
@Test |
||||
public void bindSingleValuePropertyFormatterParseException() { |
||||
presentationModel.field("date").formatWith(new DateFormatter()); |
||||
BindingResults results = binder.bind(Collections.singletonMap("date", "bogus"), presentationModel); |
||||
assertEquals(1, results.size()); |
||||
assertTrue(results.get(0).isFailure()); |
||||
assertEquals("typeMismatch", results.get(0).getAlert().getCode()); |
||||
} |
||||
|
||||
@Test |
||||
public void bindSingleValueWithFormatterRegistedByType() throws ParseException { |
||||
GenericFormatterRegistry formatterRegistry = new GenericFormatterRegistry(); |
||||
formatterRegistry.add(Date.class, new DateFormatter()); |
||||
presentationModel.setFormatterRegistry(formatterRegistry); |
||||
|
||||
binder.bind(Collections.singletonMap("date", "2009-06-01"), presentationModel); |
||||
assertEquals(new DateFormatter().parse("2009-06-01", Locale.US), bean.getDate()); |
||||
} |
||||
|
||||
@Test |
||||
public void bindSingleValueWithAnnotationFormatterFactoryRegistered() throws ParseException { |
||||
GenericFormatterRegistry formatterRegistry = new GenericFormatterRegistry(); |
||||
formatterRegistry.add(new CurrencyAnnotationFormatterFactory()); |
||||
presentationModel.setFormatterRegistry(formatterRegistry); |
||||
|
||||
binder.bind(Collections.singletonMap("currency", "$23.56"), presentationModel); |
||||
assertEquals(new BigDecimal("23.56"), bean.getCurrency()); |
||||
} |
||||
|
||||
@Test |
||||
public void bindSingleValuePropertyNotFound() throws ParseException { |
||||
BindingResults results = binder.bind(Collections.singletonMap("bogus", "2009-06-01"), presentationModel); |
||||
assertEquals("bogus", results.get(0).getFieldName()); |
||||
assertTrue(results.get(0).isFailure()); |
||||
assertEquals("fieldNotFound", results.get(0).getAlert().getCode()); |
||||
} |
||||
|
||||
@Test(expected = MissingFieldException.class) |
||||
public void bindMissingRequiredSourceValue() { |
||||
binder.setRequiredFields(new String[] { "integer" }); |
||||
// missing "integer" - violated bind contract
|
||||
binder.bind(Collections.singletonMap("string", "test"), presentationModel); |
||||
} |
||||
|
||||
@Test |
||||
public void getBindingCustomFormatter() { |
||||
presentationModel.field("currency").formatWith(new CurrencyFormatter()); |
||||
FieldModel b = presentationModel.getFieldModel("currency"); |
||||
assertFalse(b.isList()); |
||||
assertFalse(b.isMap()); |
||||
assertEquals(null, b.getValue()); |
||||
assertEquals("", b.getRenderValue()); |
||||
b.applySubmittedValue("$23.56"); |
||||
assertEquals(BindingStatus.DIRTY, b.getBindingStatus()); |
||||
assertEquals(new BigDecimal("23.56"), b.getValue()); |
||||
assertEquals("$23.56", b.getRenderValue()); |
||||
b.commit(); |
||||
assertEquals(new BigDecimal("23.56"), b.getValue()); |
||||
assertEquals("$23.56", b.getRenderValue()); |
||||
assertEquals(BindingStatus.COMMITTED, b.getBindingStatus()); |
||||
} |
||||
|
||||
@Test |
||||
public void getBindingCustomFormatterRequiringTypeCoersion() { |
||||
// IntegerFormatter formats Longs, so conversion from Integer -> Long is performed
|
||||
presentationModel.field("integer").formatWith(new IntegerFormatter()); |
||||
FieldModel b = presentationModel.getFieldModel("integer"); |
||||
b.applySubmittedValue("2,300"); |
||||
assertEquals("2,300", b.getRenderValue()); |
||||
b.commit(); |
||||
assertEquals(BindingStatus.COMMITTED, b.getBindingStatus()); |
||||
assertEquals("2,300", b.getRenderValue()); |
||||
} |
||||
|
||||
@Test |
||||
public void invalidFormatBindingResultCustomAlertMessage() { |
||||
MockMessageSource messages = new MockMessageSource(); |
||||
messages.addMessage("typeMismatch", Locale.US, |
||||
"Please enter an integer in format ### for the #{label} field; you entered #{value}"); |
||||
presentationModel.setMessageSource(messages); |
||||
presentationModel.field("integer").formatWith(new IntegerFormatter()); |
||||
FieldModel b = presentationModel.getFieldModel("integer"); |
||||
b.applySubmittedValue("bogus"); |
||||
assertEquals("Please enter an integer in format ### for the integer field; you entered bogus", b |
||||
.getStatusAlert().getMessage()); |
||||
} |
||||
|
||||
@SuppressWarnings("unchecked") |
||||
@Test |
||||
public void getBindingMultiValued() { |
||||
FieldModel b = presentationModel.getFieldModel("foos"); |
||||
assertTrue(b.isList()); |
||||
assertEquals(null, b.getValue()); |
||||
assertEquals("", b.getRenderValue()); |
||||
b.applySubmittedValue(new String[] { "BAR", "BAZ", "BOOP" }); |
||||
b.commit(); |
||||
assertEquals(FooEnum.BAR, bean.getFoos().get(0)); |
||||
assertEquals(FooEnum.BAZ, bean.getFoos().get(1)); |
||||
assertEquals(FooEnum.BOOP, bean.getFoos().get(2)); |
||||
String asString = b.getRenderValue(); |
||||
assertEquals("BAR,BAZ,BOOP", asString); |
||||
List<FooEnum> value = (List<FooEnum>) b.getValue(); |
||||
assertEquals(FooEnum.BAR, value.get(0)); |
||||
assertEquals(FooEnum.BAZ, value.get(1)); |
||||
assertEquals(FooEnum.BOOP, value.get(2)); |
||||
} |
||||
|
||||
@Test |
||||
public void getBindingMultiValuedIndexAccess() { |
||||
bean.setFoos(Arrays.asList(new FooEnum[] { FooEnum.BAR })); |
||||
FieldModel b = presentationModel.getFieldModel("foos[0]"); |
||||
assertFalse(b.isList()); |
||||
assertEquals(FooEnum.BAR, b.getValue()); |
||||
assertEquals("BAR", b.getRenderValue()); |
||||
b.applySubmittedValue("BAZ"); |
||||
assertEquals("BAZ", b.getRenderValue()); |
||||
assertEquals(FooEnum.BAZ, b.getValue()); |
||||
} |
||||
|
||||
@Test |
||||
public void getBindingMultiValuedTypeConversionFailure() { |
||||
FieldModel b = presentationModel.getFieldModel("foos"); |
||||
assertTrue(b.isList()); |
||||
assertEquals(null, b.getValue()); |
||||
b.applySubmittedValue(new String[] { "BAR", "BOGUS", "BOOP" }); |
||||
assertEquals(BindingStatus.INVALID_SUBMITTED_VALUE, b.getBindingStatus()); |
||||
assertEquals("typeMismatch", b.getStatusAlert().getCode()); |
||||
} |
||||
|
||||
@Test |
||||
public void bindToList() { |
||||
Map<String, String[]> values = new LinkedHashMap<String, String[]>(); |
||||
values.put("addresses", new String[] { "4655 Macy Lane:Melbourne:FL:35452", |
||||
"1234 Rostock Circle:Palm Bay:FL:32901", "1977 Bel Aire Estates:Coker:AL:12345" }); |
||||
binder.bind(values, presentationModel); |
||||
assertEquals(3, bean.addresses.size()); |
||||
assertEquals("4655 Macy Lane", bean.addresses.get(0).street); |
||||
assertEquals("Melbourne", bean.addresses.get(0).city); |
||||
assertEquals("FL", bean.addresses.get(0).state); |
||||
assertEquals("35452", bean.addresses.get(0).zip); |
||||
} |
||||
|
||||
@Test |
||||
public void bindToListElements() { |
||||
Map<String, String> values = new LinkedHashMap<String, String>(); |
||||
values.put("addresses[0]", "4655 Macy Lane:Melbourne:FL:35452"); |
||||
values.put("addresses[1]", "1234 Rostock Circle:Palm Bay:FL:32901"); |
||||
values.put("addresses[5]", "1977 Bel Aire Estates:Coker:AL:12345"); |
||||
binder.bind(values, presentationModel); |
||||
Assert.assertEquals(6, bean.addresses.size()); |
||||
assertEquals("4655 Macy Lane", bean.addresses.get(0).street); |
||||
assertEquals("Melbourne", bean.addresses.get(0).city); |
||||
assertEquals("FL", bean.addresses.get(0).state); |
||||
assertEquals("35452", bean.addresses.get(0).zip); |
||||
} |
||||
|
||||
@Test |
||||
public void bindToListSingleString() { |
||||
GenericFormatterRegistry formatterRegistry = new GenericFormatterRegistry(); |
||||
formatterRegistry.add(new CollectionTypeDescriptor(List.class, Address.class), new AddressListFormatter()); |
||||
presentationModel.setFormatterRegistry(formatterRegistry); |
||||
Map<String, String> values = new LinkedHashMap<String, String>(); |
||||
values |
||||
.put("addresses", |
||||
"4655 Macy Lane:Melbourne:FL:35452,1234 Rostock Circle:Palm Bay:FL:32901,1977 Bel Aire Estates:Coker:AL:12345"); |
||||
binder.bind(values, presentationModel); |
||||
Assert.assertEquals(3, bean.addresses.size()); |
||||
assertEquals("4655 Macy Lane", bean.addresses.get(0).street); |
||||
assertEquals("Melbourne", bean.addresses.get(0).city); |
||||
assertEquals("FL", bean.addresses.get(0).state); |
||||
assertEquals("35452", bean.addresses.get(0).zip); |
||||
assertEquals("1234 Rostock Circle", bean.addresses.get(1).street); |
||||
assertEquals("Palm Bay", bean.addresses.get(1).city); |
||||
assertEquals("FL", bean.addresses.get(1).state); |
||||
assertEquals("32901", bean.addresses.get(1).zip); |
||||
assertEquals("1977 Bel Aire Estates", bean.addresses.get(2).street); |
||||
assertEquals("Coker", bean.addresses.get(2).city); |
||||
assertEquals("AL", bean.addresses.get(2).state); |
||||
assertEquals("12345", bean.addresses.get(2).zip); |
||||
} |
||||
|
||||
@Test |
||||
public void bindToListSingleStringNoListFormatter() { |
||||
Map<String, String> values = new LinkedHashMap<String, String>(); |
||||
values |
||||
.put("addresses", |
||||
"4655 Macy Lane:Melbourne:FL:35452,1234 Rostock Circle:Palm Bay:FL:32901,1977 Bel Aire Estates:Coker:AL:12345"); |
||||
binder.bind(values, presentationModel); |
||||
Assert.assertEquals(3, bean.addresses.size()); |
||||
assertEquals("4655 Macy Lane", bean.addresses.get(0).street); |
||||
assertEquals("Melbourne", bean.addresses.get(0).city); |
||||
assertEquals("FL", bean.addresses.get(0).state); |
||||
assertEquals("35452", bean.addresses.get(0).zip); |
||||
assertEquals("1234 Rostock Circle", bean.addresses.get(1).street); |
||||
assertEquals("Palm Bay", bean.addresses.get(1).city); |
||||
assertEquals("FL", bean.addresses.get(1).state); |
||||
assertEquals("32901", bean.addresses.get(1).zip); |
||||
assertEquals("1977 Bel Aire Estates", bean.addresses.get(2).street); |
||||
assertEquals("Coker", bean.addresses.get(2).city); |
||||
assertEquals("AL", bean.addresses.get(2).state); |
||||
assertEquals("12345", bean.addresses.get(2).zip); |
||||
} |
||||
|
||||
@Test |
||||
public void getListAsSingleString() { |
||||
GenericFormatterRegistry formatterRegistry = new GenericFormatterRegistry(); |
||||
formatterRegistry.add(new CollectionTypeDescriptor(List.class, Address.class), new AddressListFormatter()); |
||||
presentationModel.setFormatterRegistry(formatterRegistry); |
||||
Address address1 = new Address(); |
||||
address1.setStreet("s1"); |
||||
address1.setCity("c1"); |
||||
address1.setState("st1"); |
||||
address1.setZip("z1"); |
||||
Address address2 = new Address(); |
||||
address2.setStreet("s2"); |
||||
address2.setCity("c2"); |
||||
address2.setState("st2"); |
||||
address2.setZip("z2"); |
||||
List<Address> addresses = new ArrayList<Address>(2); |
||||
addresses.add(address1); |
||||
addresses.add(address2); |
||||
bean.addresses = addresses; |
||||
String value = presentationModel.getFieldModel("addresses").getRenderValue(); |
||||
assertEquals("s1:c1:st1:z1,s2:c2:st2:z2", value); |
||||
} |
||||
|
||||
@Test |
||||
public void getListAsSingleStringNoFormatter() { |
||||
Address address1 = new Address(); |
||||
address1.setStreet("s1"); |
||||
address1.setCity("c1"); |
||||
address1.setState("st1"); |
||||
address1.setZip("z1"); |
||||
Address address2 = new Address(); |
||||
address2.setStreet("s2"); |
||||
address2.setCity("c2"); |
||||
address2.setState("st2"); |
||||
address2.setZip("z2"); |
||||
List<Address> addresses = new ArrayList<Address>(2); |
||||
addresses.add(address1); |
||||
addresses.add(address2); |
||||
bean.addresses = addresses; |
||||
String value = presentationModel.getFieldModel("addresses").getRenderValue(); |
||||
assertEquals("s1:c1:st1:z1,s2:c2:st2:z2", value); |
||||
} |
||||
|
||||
@Test |
||||
public void bindToListHandleNullValueInNestedPath() { |
||||
Map<String, String> values = new LinkedHashMap<String, String>(); |
||||
// - new addresses List is created if null
|
||||
// - new entries automatically built if List is currently too short - all new entries
|
||||
// are new instances of the type of the list entry, they are not null.
|
||||
values.put("addresses[0].street", "4655 Macy Lane"); |
||||
values.put("addresses[0].city", "Melbourne"); |
||||
values.put("addresses[0].state", "FL"); |
||||
values.put("addresses[0].zip", "35452"); |
||||
|
||||
// Auto adds new Address at 1
|
||||
values.put("addresses[1].street", "1234 Rostock Circle"); |
||||
values.put("addresses[1].city", "Palm Bay"); |
||||
values.put("addresses[1].state", "FL"); |
||||
values.put("addresses[1].zip", "32901"); |
||||
|
||||
// Auto adds new Address at 5 (plus intermediates 2,3,4)
|
||||
values.put("addresses[5].street", "7891 Rostock Circle"); |
||||
values.put("addresses[5].city", "Palm Bay"); |
||||
values.put("addresses[5].state", "FL"); |
||||
values.put("addresses[5].zip", "32901"); |
||||
|
||||
BindingResults results = binder.bind(values, presentationModel); |
||||
Assert.assertEquals(6, bean.addresses.size()); |
||||
Assert.assertEquals("Palm Bay", bean.addresses.get(1).city); |
||||
Assert.assertNotNull(bean.addresses.get(2)); |
||||
assertEquals(12, results.size()); |
||||
} |
||||
|
||||
@Test |
||||
public void bindToMap() { |
||||
Map<String, String[]> values = new LinkedHashMap<String, String[]>(); |
||||
values.put("favoriteFoodsByGroup", new String[] { "DAIRY=Milk", "FRUIT=Peaches", "MEAT=Ham" }); |
||||
binder.bind(values, presentationModel); |
||||
Assert.assertEquals(3, bean.favoriteFoodsByGroup.size()); |
||||
assertEquals("Milk", bean.favoriteFoodsByGroup.get(FoodGroup.DAIRY)); |
||||
assertEquals("Peaches", bean.favoriteFoodsByGroup.get(FoodGroup.FRUIT)); |
||||
assertEquals("Ham", bean.favoriteFoodsByGroup.get(FoodGroup.MEAT)); |
||||
} |
||||
|
||||
@Test |
||||
public void bindToMapElements() { |
||||
Map<String, String> values = new LinkedHashMap<String, String>(); |
||||
values.put("favoriteFoodsByGroup[DAIRY]", "Milk"); |
||||
values.put("favoriteFoodsByGroup[FRUIT]", "Peaches"); |
||||
values.put("favoriteFoodsByGroup[MEAT]", "Ham"); |
||||
binder.bind(values, presentationModel); |
||||
Assert.assertEquals(3, bean.favoriteFoodsByGroup.size()); |
||||
assertEquals("Milk", bean.favoriteFoodsByGroup.get(FoodGroup.DAIRY)); |
||||
assertEquals("Peaches", bean.favoriteFoodsByGroup.get(FoodGroup.FRUIT)); |
||||
assertEquals("Ham", bean.favoriteFoodsByGroup.get(FoodGroup.MEAT)); |
||||
} |
||||
|
||||
@Test |
||||
public void bindToMapSingleString() { |
||||
Map<String, String> values = new LinkedHashMap<String, String>(); |
||||
values.put("favoriteFoodsByGroup", "DAIRY=Milk FRUIT=Peaches MEAT=Ham"); |
||||
binder.bind(values, presentationModel); |
||||
Assert.assertEquals(3, bean.favoriteFoodsByGroup.size()); |
||||
assertEquals("Milk", bean.favoriteFoodsByGroup.get(FoodGroup.DAIRY)); |
||||
assertEquals("Peaches", bean.favoriteFoodsByGroup.get(FoodGroup.FRUIT)); |
||||
assertEquals("Ham", bean.favoriteFoodsByGroup.get(FoodGroup.MEAT)); |
||||
} |
||||
|
||||
@Test |
||||
public void getMapAsSingleString() { |
||||
Map<FoodGroup, String> foods = new LinkedHashMap<FoodGroup, String>(); |
||||
foods.put(FoodGroup.DAIRY, "Milk"); |
||||
foods.put(FoodGroup.FRUIT, "Peaches"); |
||||
foods.put(FoodGroup.MEAT, "Ham"); |
||||
bean.favoriteFoodsByGroup = foods; |
||||
String value = presentationModel.getFieldModel("favoriteFoodsByGroup").getRenderValue(); |
||||
// TODO this is inconsistent with previous test case
|
||||
assertEquals("{DAIRY=Milk, FRUIT=Peaches, MEAT=Ham}", value); |
||||
} |
||||
|
||||
@Test |
||||
public void bindToNullObjectPath() { |
||||
Map<String, String> values = new LinkedHashMap<String, String>(); |
||||
values.put("primaryAddress.city", "Melbourne"); |
||||
binder.bind(values, presentationModel); |
||||
Assert.assertEquals("Melbourne", bean.primaryAddress.city); |
||||
} |
||||
|
||||
@Test |
||||
public void formatPossibleValue() { |
||||
presentationModel.field("currency").formatWith(new CurrencyFormatter()); |
||||
FieldModel b = presentationModel.getFieldModel("currency"); |
||||
assertEquals("$5.00", b.formatValue(new BigDecimal("5"))); |
||||
} |
||||
|
||||
@Test |
||||
public void formatPossibleValueDefault() { |
||||
presentationModel.field("currency"); |
||||
FieldModel b = presentationModel.getFieldModel("currency"); |
||||
assertEquals("5", b.formatValue(new BigDecimal("5"))); |
||||
} |
||||
|
||||
public static enum FooEnum { |
||||
BAR, BAZ, BOOP; |
||||
} |
||||
|
||||
public static enum FoodGroup { |
||||
DAIRY, VEG, FRUIT, BREAD, MEAT |
||||
} |
||||
|
||||
public static class TestBean { |
||||
private String string; |
||||
private int integer; |
||||
private Date date; |
||||
private FooEnum foo; |
||||
private BigDecimal currency; |
||||
private List<FooEnum> foos; |
||||
private List<Address> addresses; |
||||
private Map<FoodGroup, String> favoriteFoodsByGroup; |
||||
private Address primaryAddress; |
||||
|
||||
public TestBean() { |
||||
} |
||||
|
||||
public String getString() { |
||||
return string; |
||||
} |
||||
|
||||
public void setString(String string) { |
||||
this.string = string; |
||||
} |
||||
|
||||
public int getInteger() { |
||||
return integer; |
||||
} |
||||
|
||||
public void setInteger(int integer) { |
||||
this.integer = integer; |
||||
} |
||||
|
||||
public Date getDate() { |
||||
return date; |
||||
} |
||||
|
||||
public void setDate(Date date) { |
||||
this.date = date; |
||||
} |
||||
|
||||
public FooEnum getFoo() { |
||||
return foo; |
||||
} |
||||
|
||||
public void setFoo(FooEnum foo) { |
||||
this.foo = foo; |
||||
} |
||||
|
||||
@CurrencyFormat |
||||
public BigDecimal getCurrency() { |
||||
return currency; |
||||
} |
||||
|
||||
public void setCurrency(BigDecimal currency) { |
||||
this.currency = currency; |
||||
} |
||||
|
||||
public List<FooEnum> getFoos() { |
||||
return foos; |
||||
} |
||||
|
||||
public void setFoos(List<FooEnum> foos) { |
||||
this.foos = foos; |
||||
} |
||||
|
||||
public List<Address> getAddresses() { |
||||
return addresses; |
||||
} |
||||
|
||||
public void setAddresses(List<Address> addresses) { |
||||
this.addresses = addresses; |
||||
} |
||||
|
||||
public Map<FoodGroup, String> getFavoriteFoodsByGroup() { |
||||
return favoriteFoodsByGroup; |
||||
} |
||||
|
||||
public void setFavoriteFoodsByGroup(Map<FoodGroup, String> favoriteFoodsByGroup) { |
||||
this.favoriteFoodsByGroup = favoriteFoodsByGroup; |
||||
} |
||||
|
||||
public Address getPrimaryAddress() { |
||||
return primaryAddress; |
||||
} |
||||
|
||||
public void setPrimaryAddress(Address primaryAddress) { |
||||
this.primaryAddress = primaryAddress; |
||||
} |
||||
|
||||
public String toString() { |
||||
return new ToStringCreator(this).append("addressses", addresses).toString(); |
||||
} |
||||
} |
||||
|
||||
public static class AddressFormatter implements Formatter<Address> { |
||||
|
||||
public String format(Address address, Locale locale) { |
||||
return address.getStreet() + ":" + address.getCity() + ":" + address.getState() + ":" + address.getZip(); |
||||
} |
||||
|
||||
public Address parse(String formatted, Locale locale) throws ParseException { |
||||
Address address = new Address(); |
||||
String[] fields = formatted.split(":"); |
||||
address.setStreet(fields[0]); |
||||
address.setCity(fields[1]); |
||||
address.setState(fields[2]); |
||||
address.setZip(fields[3]); |
||||
return address; |
||||
} |
||||
|
||||
} |
||||
|
||||
public static class AddressListFormatter implements Formatter<List<Address>> { |
||||
|
||||
public String format(List<Address> addresses, Locale locale) { |
||||
StringBuilder builder = new StringBuilder(); |
||||
for (Address address : addresses) { |
||||
builder.append(new AddressFormatter().format(address, locale)); |
||||
builder.append(","); |
||||
} |
||||
return builder.toString(); |
||||
} |
||||
|
||||
public List<Address> parse(String formatted, Locale locale) throws ParseException { |
||||
String[] fields = formatted.split(","); |
||||
List<Address> addresses = new ArrayList<Address>(fields.length); |
||||
for (String field : fields) { |
||||
addresses.add(new AddressFormatter().parse(field, locale)); |
||||
} |
||||
return addresses; |
||||
} |
||||
|
||||
} |
||||
|
||||
@Formatted(AddressFormatter.class) |
||||
public static class Address { |
||||
private String street; |
||||
private String city; |
||||
private String state; |
||||
private String zip; |
||||
private String country; |
||||
|
||||
public String getStreet() { |
||||
return street; |
||||
} |
||||
|
||||
public void setStreet(String street) { |
||||
this.street = street; |
||||
} |
||||
|
||||
public String getCity() { |
||||
return city; |
||||
} |
||||
|
||||
public void setCity(String city) { |
||||
this.city = city; |
||||
} |
||||
|
||||
public String getState() { |
||||
return state; |
||||
} |
||||
|
||||
public void setState(String state) { |
||||
this.state = state; |
||||
} |
||||
|
||||
public String getZip() { |
||||
return zip; |
||||
} |
||||
|
||||
public void setZip(String zip) { |
||||
this.zip = zip; |
||||
} |
||||
|
||||
public String getCountry() { |
||||
return country; |
||||
} |
||||
|
||||
public void setCountry(String country) { |
||||
this.country = country; |
||||
} |
||||
|
||||
public String toString() { |
||||
return new ToStringCreator(this).append("street", street).append("city", city).append("state", state) |
||||
.append("zip", zip).toString(); |
||||
} |
||||
} |
||||
|
||||
public static class CurrencyAnnotationFormatterFactory implements |
||||
AnnotationFormatterFactory<CurrencyFormat, BigDecimal> { |
||||
public Formatter<BigDecimal> getFormatter(CurrencyFormat annotation) { |
||||
return new CurrencyFormatter(); |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -1,214 +0,0 @@
@@ -1,214 +0,0 @@
|
||||
package org.springframework.model.ui.support; |
||||
|
||||
import static org.junit.Assert.assertEquals; |
||||
|
||||
import java.math.BigDecimal; |
||||
import java.text.ParseException; |
||||
import java.util.Date; |
||||
import java.util.LinkedHashMap; |
||||
import java.util.List; |
||||
import java.util.Locale; |
||||
import java.util.Map; |
||||
|
||||
import org.junit.After; |
||||
import org.junit.Before; |
||||
import org.junit.Test; |
||||
import org.springframework.context.i18n.LocaleContextHolder; |
||||
import org.springframework.model.binder.BindingResults; |
||||
import org.springframework.model.ui.format.date.DateFormatter; |
||||
import org.springframework.model.ui.format.number.CurrencyFormat; |
||||
import org.springframework.model.ui.format.number.CurrencyFormatter; |
||||
import org.springframework.model.ui.support.DefaultPresentationModel; |
||||
import org.springframework.model.ui.support.GenericFormatterRegistry; |
||||
import org.springframework.model.ui.support.WebBinder; |
||||
|
||||
public class WebBinderTests { |
||||
|
||||
TestBean bean = new TestBean(); |
||||
|
||||
DefaultPresentationModel presentationModel; |
||||
|
||||
WebBinder binder; |
||||
|
||||
@Before |
||||
public void setUp() { |
||||
LocaleContextHolder.setLocale(Locale.US); |
||||
presentationModel = new DefaultPresentationModel(bean); |
||||
binder = new WebBinder(); |
||||
binder.setCommitDirtyValue(true); |
||||
} |
||||
|
||||
@After |
||||
public void tearDown() { |
||||
LocaleContextHolder.setLocale(null); |
||||
} |
||||
|
||||
@Test |
||||
public void bindUserValuesCreatedFromUserMap() throws ParseException { |
||||
GenericFormatterRegistry registry = new GenericFormatterRegistry(); |
||||
registry.add(Date.class, new DateFormatter()); |
||||
registry.add(CurrencyFormat.class, new CurrencyFormatter()); |
||||
presentationModel.setFormatterRegistry(registry); |
||||
Map<String, String> userMap = new LinkedHashMap<String, String>(); |
||||
userMap.put("string", "test"); |
||||
userMap.put("_integer", "doesn't matter"); |
||||
userMap.put("_bool", "doesn't matter"); |
||||
userMap.put("!date", "2009-06-10"); |
||||
userMap.put("!currency", "$5.00"); |
||||
userMap.put("_currency", "doesn't matter"); |
||||
userMap.put("_addresses", "doesn't matter"); |
||||
BindingResults results = binder.bind(userMap, presentationModel); |
||||
assertEquals(6, results.size()); |
||||
assertEquals("test", results.get(0).getSubmittedValue()); |
||||
assertEquals(null, results.get(1).getSubmittedValue()); |
||||
assertEquals(Boolean.FALSE, results.get(2).getSubmittedValue()); |
||||
assertEquals("2009-06-10", results.get(3).getSubmittedValue()); |
||||
assertEquals("$5.00", results.get(4).getSubmittedValue()); |
||||
assertEquals(null, results.get(5).getSubmittedValue()); |
||||
|
||||
assertEquals("test", bean.getString()); |
||||
assertEquals(0, bean.getInteger()); |
||||
assertEquals(new DateFormatter().parse("2009-06-10", Locale.US), bean.getDate()); |
||||
assertEquals(false, bean.isBool()); |
||||
assertEquals(new BigDecimal("5.00"), bean.getCurrency()); |
||||
assertEquals(null, bean.getAddresses()); |
||||
} |
||||
|
||||
public static enum FooEnum { |
||||
BAR, BAZ, BOOP; |
||||
} |
||||
|
||||
public static class TestBean { |
||||
|
||||
private String string; |
||||
|
||||
private int integer; |
||||
|
||||
private boolean bool; |
||||
|
||||
private Date date; |
||||
|
||||
private FooEnum foo; |
||||
|
||||
private BigDecimal currency; |
||||
|
||||
private List<FooEnum> foos; |
||||
|
||||
private List<Address> addresses; |
||||
|
||||
public String getString() { |
||||
return string; |
||||
} |
||||
|
||||
public void setString(String string) { |
||||
this.string = string; |
||||
} |
||||
|
||||
public int getInteger() { |
||||
return integer; |
||||
} |
||||
|
||||
public void setInteger(int integer) { |
||||
this.integer = integer; |
||||
} |
||||
|
||||
public boolean isBool() { |
||||
return bool; |
||||
} |
||||
|
||||
public void setBool(boolean bool) { |
||||
this.bool = bool; |
||||
} |
||||
|
||||
public Date getDate() { |
||||
return date; |
||||
} |
||||
|
||||
public void setDate(Date date) { |
||||
this.date = date; |
||||
} |
||||
|
||||
public FooEnum getFoo() { |
||||
return foo; |
||||
} |
||||
|
||||
public void setFoo(FooEnum foo) { |
||||
this.foo = foo; |
||||
} |
||||
|
||||
@CurrencyFormat |
||||
public BigDecimal getCurrency() { |
||||
return currency; |
||||
} |
||||
|
||||
public void setCurrency(BigDecimal currency) { |
||||
this.currency = currency; |
||||
} |
||||
|
||||
public List<FooEnum> getFoos() { |
||||
return foos; |
||||
} |
||||
|
||||
public void setFoos(List<FooEnum> foos) { |
||||
this.foos = foos; |
||||
} |
||||
|
||||
public List<Address> getAddresses() { |
||||
return addresses; |
||||
} |
||||
|
||||
public void setAddresses(List<Address> addresses) { |
||||
this.addresses = addresses; |
||||
} |
||||
|
||||
} |
||||
|
||||
public static class Address { |
||||
private String street; |
||||
private String city; |
||||
private String state; |
||||
private String zip; |
||||
private String country; |
||||
|
||||
public String getStreet() { |
||||
return street; |
||||
} |
||||
|
||||
public void setStreet(String street) { |
||||
this.street = street; |
||||
} |
||||
|
||||
public String getCity() { |
||||
return city; |
||||
} |
||||
|
||||
public void setCity(String city) { |
||||
this.city = city; |
||||
} |
||||
|
||||
public String getState() { |
||||
return state; |
||||
} |
||||
|
||||
public void setState(String state) { |
||||
this.state = state; |
||||
} |
||||
|
||||
public String getZip() { |
||||
return zip; |
||||
} |
||||
|
||||
public void setZip(String zip) { |
||||
this.zip = zip; |
||||
} |
||||
|
||||
public String getCountry() { |
||||
return country; |
||||
} |
||||
|
||||
public void setCountry(String country) { |
||||
this.country = country; |
||||
} |
||||
|
||||
} |
||||
} |
||||
@ -1,65 +0,0 @@
@@ -1,65 +0,0 @@
|
||||
/* |
||||
* 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.web.bind.support; |
||||
|
||||
import javax.servlet.ServletRequest; |
||||
|
||||
import org.springframework.model.ui.PresentationModelFactory; |
||||
import org.springframework.model.ui.support.DefaultPresentationModelFactory; |
||||
import org.springframework.web.context.request.WebRequest; |
||||
|
||||
/** |
||||
* Utilities for working with the <code>model.ui</code> PresentationModel system. |
||||
* @author Keith Donald |
||||
*/ |
||||
public final class PresentationModelUtils { |
||||
|
||||
private static final String PRESENTATION_MODEL_FACTORY_ATTRIBUTE = "presentationModelFactory"; |
||||
|
||||
private PresentationModelUtils() { |
||||
} |
||||
|
||||
/** |
||||
* Get the PresentationModelFactory for the current web request. |
||||
* Will create a new one and cache it as a request attribute if one does not exist. |
||||
* @param request the web request |
||||
* @return the presentation model factory |
||||
*/ |
||||
public static PresentationModelFactory getPresentationModelFactory(WebRequest request) { |
||||
PresentationModelFactory factory = (PresentationModelFactory) request.getAttribute(PRESENTATION_MODEL_FACTORY_ATTRIBUTE, WebRequest.SCOPE_REQUEST); |
||||
if (factory == null) { |
||||
factory = new DefaultPresentationModelFactory(); |
||||
request.setAttribute(PRESENTATION_MODEL_FACTORY_ATTRIBUTE, factory, WebRequest.SCOPE_REQUEST); |
||||
} |
||||
return factory; |
||||
} |
||||
|
||||
/** |
||||
* Get the PresentationModelFactory for the current servlet request. |
||||
* Will create a new one and cache it as a request attribute if one does not exist. |
||||
* @param request the servlet |
||||
* @return the presentation model factory |
||||
*/ |
||||
public static PresentationModelFactory getPresentationModelFactory(ServletRequest request) { |
||||
PresentationModelFactory factory = (PresentationModelFactory) request.getAttribute(PRESENTATION_MODEL_FACTORY_ATTRIBUTE); |
||||
if (factory == null) { |
||||
factory = new DefaultPresentationModelFactory(); |
||||
request.setAttribute(PRESENTATION_MODEL_FACTORY_ATTRIBUTE, factory); |
||||
} |
||||
return factory; |
||||
} |
||||
|
||||
} |
||||
Loading…
Reference in new issue