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;