pruned presentation model system from trunk to move to dev branch as its a 3.1 feature now

This commit is contained in:
Keith Donald 2009-08-05 15:25:54 +00:00
parent 93e99556c0
commit 00f90cd816
94 changed files with 0 additions and 6892 deletions

View File

@ -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();
}

View File

@ -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);
}

View File

@ -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();
}
}
}

View File

@ -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
}

View File

@ -1,5 +0,0 @@
/**
* A general-purpose Alerting API to communicate events of interest.
*/
package org.springframework.model.alert;

View File

@ -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();
}
}

View File

@ -1,5 +0,0 @@
/**
* AlertContext implementation suitable for use in most environments.
*/
package org.springframework.model.alert.support;

View File

@ -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);
}

View File

@ -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();
}

View File

@ -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);
}

View File

@ -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();
}
}
}

View File

@ -1,5 +0,0 @@
/**
* API for binding submitted field values in a single batch operation.
*/
package org.springframework.model.binder;

View File

@ -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);
}
}
}

View File

@ -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();
}
}

View File

@ -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() + "]";
}
}

View File

@ -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);
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}
}

View File

@ -1,5 +0,0 @@
/**
* Binder API implementation support.
*/
package org.springframework.model.binder.support;

View File

@ -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();
}

View File

@ -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 };
}
}
}

View File

@ -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();
}
}
}

View File

@ -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);
}
}

View File

@ -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);
}

View File

@ -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);
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -1,5 +0,0 @@
/**
* An API for creating localized messages.
*/
package org.springframework.model.message;

View File

@ -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
}

View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -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();
}

View File

@ -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);
}

View File

@ -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
}

View File

@ -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();
}

View File

@ -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;
}
};
}

View File

@ -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);
}

View File

@ -1,5 +0,0 @@
/**
* PresentationModel configuration SPI.
*/
package org.springframework.model.ui.config;

View File

@ -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);
}

View File

@ -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();
}

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -1,5 +0,0 @@
/**
* Formatters for <code>java.util.Date</code> fields.
*/
package org.springframework.model.ui.format.date;

View File

@ -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 {
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -1,5 +0,0 @@
/**
* Formatters for <code>java.lang.Number</code> properties.
*/
package org.springframework.model.ui.format.number;

View File

@ -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;

View File

@ -1,5 +0,0 @@
/**
* Spring's PresentationModel public API.
*/
package org.springframework.model.ui;

View File

@ -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&lt;?&gt;.
* @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();
}
}

View File

@ -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();
}
}
}

View File

@ -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 &lt;prop&gt;[.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 + "]";
}
};
}

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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);
}
}
};
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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());
}
}
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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;
}
}
}

View File

@ -1,5 +0,0 @@
/**
* Default implementation of a PresentationModel usable in most environments.
*/
package org.springframework.model.ui.support;

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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);
}

View File

@ -1,5 +0,0 @@
/**
* Model ValidationConstraint SPI.
*/
package org.springframework.model.validation;

View File

@ -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());
}
}

View File

@ -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());
}
}

View File

@ -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();
}
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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));
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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();
}
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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)",