pruned presentation model system from trunk to move to dev branch as its a 3.1 feature now
This commit is contained in:
parent
93e99556c0
commit
00f90cd816
|
|
@ -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 @@
|
|||
/*
|
||||
* 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 @@
|
|||
/*
|
||||
* 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 @@
|
|||
/*
|
||||
* 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 @@
|
|||
/**
|
||||
* A general-purpose Alerting API to communicate events of interest.
|
||||
*/
|
||||
package org.springframework.model.alert;
|
||||
|
||||
|
|
@ -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 @@
|
|||
/**
|
||||
* AlertContext implementation suitable for use in most environments.
|
||||
*/
|
||||
package org.springframework.model.alert.support;
|
||||
|
||||
|
|
@ -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 @@
|
|||
/*
|
||||
* 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 @@
|
|||
/*
|
||||
* 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 @@
|
|||
/*
|
||||
* 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 @@
|
|||
/**
|
||||
* API for binding submitted field values in a single batch operation.
|
||||
*/
|
||||
package org.springframework.model.binder;
|
||||
|
||||
|
|
@ -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 @@
|
|||
/*
|
||||
* 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 @@
|
|||
/*
|
||||
* 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 @@
|
|||
/*
|
||||
* 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 @@
|
|||
/*
|
||||
* 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 @@
|
|||
/*
|
||||
* 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 @@
|
|||
/*
|
||||
* 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 @@
|
|||
/**
|
||||
* Binder API implementation support.
|
||||
*/
|
||||
package org.springframework.model.binder.support;
|
||||
|
||||
|
|
@ -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 @@
|
|||
/*
|
||||
* 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 @@
|
|||
/*
|
||||
* 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 @@
|
|||
/*
|
||||
* 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 @@
|
|||
/*
|
||||
* 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 @@
|
|||
/*
|
||||
* 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 @@
|
|||
/*
|
||||
* 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 @@
|
|||
/*
|
||||
* 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 @@
|
|||
/**
|
||||
* An API for creating localized messages.
|
||||
*/
|
||||
package org.springframework.model.message;
|
||||
|
||||
|
|
@ -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 @@
|
|||
/*
|
||||
* 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 @@
|
|||
/*
|
||||
* 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 @@
|
|||
/*
|
||||
* 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 @@
|
|||
/*
|
||||
* 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 @@
|
|||
/*
|
||||
* 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 @@
|
|||
/*
|
||||
* 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 @@
|
|||
/*
|
||||
* 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 @@
|
|||
/*
|
||||
* 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 @@
|
|||
/**
|
||||
* PresentationModel configuration SPI.
|
||||
*/
|
||||
package org.springframework.model.ui.config;
|
||||
|
||||
|
|
@ -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 @@
|
|||
/*
|
||||
* 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 @@
|
|||
/*
|
||||
* 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 @@
|
|||
/*
|
||||
* 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 @@
|
|||
/**
|
||||
* Formatters for <code>java.util.Date</code> fields.
|
||||
*/
|
||||
package org.springframework.model.ui.format.date;
|
||||
|
||||
|
|
@ -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 @@
|
|||
/*
|
||||
* 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 @@
|
|||
/*
|
||||
* 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 @@
|
|||
/*
|
||||
* 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 @@
|
|||
/*
|
||||
* 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 @@
|
|||
/*
|
||||
* 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 @@
|
|||
/*
|
||||
* 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 @@
|
|||
/*
|
||||
* 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 @@
|
|||
/*
|
||||
* 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 @@
|
|||
/*
|
||||
* 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 @@
|
|||
/**
|
||||
* Formatters for <code>java.lang.Number</code> properties.
|
||||
*/
|
||||
package org.springframework.model.ui.format.number;
|
||||
|
||||
|
|
@ -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 @@
|
|||
/**
|
||||
* Spring's PresentationModel public API.
|
||||
*/
|
||||
package org.springframework.model.ui;
|
||||
|
||||
|
|
@ -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 @@
|
|||
/*
|
||||
* 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 @@
|
|||
/*
|
||||
* 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 @@
|
|||
/*
|
||||
* 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 @@
|
|||
/*
|
||||
* 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 @@
|
|||
/*
|
||||
* 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 @@
|
|||
/*
|
||||
* 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 @@
|
|||
/*
|
||||
* 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 @@
|
|||
/*
|
||||
* 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 @@
|
|||
/*
|
||||
* 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 @@
|
|||
/*
|
||||
* 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 @@
|
|||
/*
|
||||
* 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 @@
|
|||
/*
|
||||
* 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 @@
|
|||
/*
|
||||
* 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 @@
|
|||
/*
|
||||
* 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 @@
|
|||
/*
|
||||
* 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 @@
|
|||
/**
|
||||
* Default implementation of a PresentationModel usable in most environments.
|
||||
*/
|
||||
package org.springframework.model.ui.support;
|
||||
|
||||
|
|
@ -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 @@
|
|||
/*
|
||||
* 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 @@
|
|||
/*
|
||||
* 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 @@
|
|||
/**
|
||||
* Model ValidationConstraint SPI.
|
||||
*/
|
||||
package org.springframework.model.validation;
|
||||
|
||||
|
|
@ -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 @@
|
|||
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 @@
|
|||
/*
|
||||
* 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 @@
|
|||
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 @@
|
|||
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 @@
|
|||
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 @@
|
|||
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 @@
|
|||
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 @@
|
|||
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 @@
|
|||
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 @@
|
|||
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 @@
|
|||
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 @@
|
|||
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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -19,9 +19,6 @@ package org.springframework.web.bind.annotation.support;
|
|||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.lang.reflect.TypeVariable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
|
@ -42,11 +39,8 @@ import org.springframework.core.annotation.AnnotationUtils;
|
|||
import org.springframework.http.HttpInputMessage;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.model.ui.PresentationModelFactory;
|
||||
import org.springframework.model.ui.config.BindingLifecycle;
|
||||
import org.springframework.ui.ExtendedModelMap;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.ui.MvcBindingLifecycle;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
|
@ -62,7 +56,6 @@ import org.springframework.web.bind.annotation.RequestBody;
|
|||
import org.springframework.web.bind.annotation.RequestHeader;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.support.DefaultSessionAttributeStore;
|
||||
import org.springframework.web.bind.support.PresentationModelUtils;
|
||||
import org.springframework.web.bind.support.SessionAttributeStore;
|
||||
import org.springframework.web.bind.support.SessionStatus;
|
||||
import org.springframework.web.bind.support.SimpleSessionStatus;
|
||||
|
|
@ -70,7 +63,6 @@ import org.springframework.web.bind.support.WebArgumentResolver;
|
|||
import org.springframework.web.bind.support.WebBindingInitializer;
|
||||
import org.springframework.web.bind.support.WebRequestDataBinder;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.context.request.NativeWebRequestParameterMap;
|
||||
import org.springframework.web.context.request.WebRequest;
|
||||
import org.springframework.web.multipart.MultipartRequest;
|
||||
|
||||
|
|
@ -244,13 +236,6 @@ public class HandlerMethodInvoker {
|
|||
throw new IllegalStateException("Errors/BindingResult argument declared " +
|
||||
"without preceding model attribute. Check your handler method signature!");
|
||||
}
|
||||
// TODO - Code Review - NEW BINDING LIFECYCLE RESOLVABLE ARG
|
||||
else if (BindingLifecycle.class.isAssignableFrom(paramType)) {
|
||||
Class<?> modelType = resolveBindingLifecycleModelType(methodParam);
|
||||
PresentationModelFactory factory = PresentationModelUtils.getPresentationModelFactory(webRequest);
|
||||
Map<String, Object> fieldValues = new NativeWebRequestParameterMap(webRequest);
|
||||
args[i] = new MvcBindingLifecycle(modelType, factory, implicitModel, fieldValues);
|
||||
}
|
||||
else if (BeanUtils.isSimpleProperty(paramType)) {
|
||||
paramName = "";
|
||||
}
|
||||
|
|
@ -723,23 +708,4 @@ public class HandlerMethodInvoker {
|
|||
implicitModel.addAttribute(attrName, returnValue);
|
||||
}
|
||||
|
||||
// TODO - Code Review - BINDING LIFECYCLE RELATED INTERNAL HELPERS
|
||||
|
||||
// TODO - this generic arg identification looping code is duplicated in several places now...
|
||||
private Class<?> resolveBindingLifecycleModelType(MethodParameter methodParam) {
|
||||
Type type = GenericTypeResolver.getTargetType(methodParam);
|
||||
if (type instanceof ParameterizedType) {
|
||||
ParameterizedType paramType = (ParameterizedType) type;
|
||||
Type rawType = paramType.getRawType();
|
||||
Type arg = paramType.getActualTypeArguments()[0];
|
||||
if (arg instanceof TypeVariable) {
|
||||
arg = GenericTypeResolver.resolveTypeVariable((TypeVariable) arg, BindingLifecycle.class);
|
||||
}
|
||||
if (arg instanceof Class) {
|
||||
return (Class) arg;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -24,7 +24,6 @@ Import-Template:
|
|||
org.springframework.beans.*;version="[3.0.0, 3.0.1)",
|
||||
org.springframework.context.*;version="[3.0.0, 3.0.1)",
|
||||
org.springframework.core.*;version="[3.0.0, 3.0.1)",
|
||||
org.springframework.model.*;version="[3.0.0, 3.0.1)",
|
||||
org.springframework.oxm.*;version="[3.0.0, 3.0.1)";resolution:=optional,
|
||||
org.springframework.remoting.*;version="[3.0.0, 3.0.1)";resolution:=optional,
|
||||
org.springframework.ui.*;version="[3.0.0, 3.0.1)",
|
||||
|
|
|
|||
Loading…
Reference in New Issue