removed 3.1 feature classes

This commit is contained in:
Juergen Hoeller 2009-09-24 22:53:17 +00:00
parent 2781b876d2
commit a59d205b36
41 changed files with 0 additions and 2788 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.context.alert;
/**
* Communicates an event of interest to the user.
* For example, an alert may inform a user of a 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.context.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.context.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#FATAL
*/
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,51 +0,0 @@
/*
* Copyright 2004-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.context.alert;
/**
* The set of alert severities.
* @author Keith Donald
* @since 3.0
* @see Alert
*/
public enum Severity {
/**
* The "Informational" severity.
* Indicates a successful operation or result.
*/
INFO,
/**
* The "Warning" severity.
* Indicates 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.
* Indicates a significant problem like a business rule violation.
*/
ERROR,
/**
* The "Fatal" severity.
* Indicates 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.context.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.context.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.context.alert.Alert;
import org.springframework.context.alert.AlertContext;
import org.springframework.core.style.ToStringCreator;
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.context.alert.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.context.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.context.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.context.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 messageResolverBuilder = 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) {
messageResolverBuilder.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) {
messageResolverBuilder.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) {
messageResolverBuilder.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) {
messageResolverBuilder.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 messageResolverBuilder.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.context.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.context.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,130 +0,0 @@
/*
* Copyright 2004-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.context.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>
* @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.context.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.context.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.context.message;

View File

@ -1,34 +0,0 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.mapping;
/**
* Maps between a source and target.
* @author Keith Donald
* @param <S> the source type mapped from
* @param <T> the target type mapped to
*/
public interface Mapper<S, T> {
/**
* Map the source to the target.
* @param source the source to map from
* @param target the target to map to
* @throws MappingException if the mapping process failed
*/
void map(S source, T target);
}

View File

@ -1,28 +0,0 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.mapping;
/**
* Base runtime exception for the mapping system.
* @author Keith Donald
*/
public class MappingException extends RuntimeException {
public MappingException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@ -1,13 +0,0 @@
package org.springframework.mapping.support;
import java.util.Set;
import org.springframework.expression.EvaluationContext;
interface MappableType {
Set<String> getMappableFields(Object instance);
EvaluationContext getMappingContext(Object instance);
}

View File

@ -1,7 +0,0 @@
package org.springframework.mapping.support;
import org.springframework.core.convert.converter.Converter;
public interface MappingConfiguration {
MappingConfiguration setConverter(Converter converter);
}

View File

@ -1,222 +0,0 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.mapping.support;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import org.springframework.context.expression.MapAccessor;
import org.springframework.core.convert.converter.Converter;
import org.springframework.expression.EvaluationContext;
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.support.StandardEvaluationContext;
import org.springframework.mapping.Mapper;
import org.springframework.mapping.MappingException;
/**
* A generic object mapper implementation based on the Spring Expression Language (SpEL).
* @author Keith Donald
*/
public class SpelMapper implements Mapper<Object, Object> {
private static final MappableType MAP_MAPPABLE_TYPE = new MapMappableType();
private static final MappableType BEAN_MAPPABLE_TYPE = new BeanMappableType();
private static final ExpressionParser expressionParser = new SpelExpressionParser();
private Set<Mapping> mappings = new LinkedHashSet<Mapping>();
private boolean autoMappingEnabled = true;
public void setAutoMappingEnabled(boolean autoMappingEnabled) {
this.autoMappingEnabled = autoMappingEnabled;
}
public MappingConfiguration addMapping(String sourceExpression, String targetExpression) {
Expression sourceExp;
try {
sourceExp = expressionParser.parseExpression(sourceExpression);
} catch (ParseException e) {
throw new IllegalArgumentException("The mapping source '" + sourceExpression
+ "' is not a parseable value expression", e);
}
Expression targetExp;
try {
targetExp = expressionParser.parseExpression(targetExpression);
} catch (ParseException e) {
throw new IllegalArgumentException("The mapping target '" + sourceExpression
+ "' is not a parseable property expression", e);
}
Mapping mapping = new Mapping(sourceExp, targetExp);
if (mappings == null) {
mappings = new LinkedHashSet<Mapping>();
}
mappings.add(mapping);
return mapping;
}
public void map(Object source, Object target) throws MappingException {
EvaluationContext sourceContext = getMappingContext(source);
EvaluationContext targetContext = getMappingContext(target);
for (Mapping mapping : mappings) {
mapping.map(sourceContext, targetContext);
}
Set<Mapping> autoMappings = getAutoMappings(source);
for (Mapping mapping : autoMappings) {
mapping.map(sourceContext, targetContext);
}
}
protected EvaluationContext getMappingContext(Object object) {
if (object instanceof Map) {
return MAP_MAPPABLE_TYPE.getMappingContext(object);
} else {
return BEAN_MAPPABLE_TYPE.getMappingContext(object);
}
}
protected Set<String> getMappableFields(Object object) {
if (object instanceof Map) {
return MAP_MAPPABLE_TYPE.getMappableFields(object);
} else {
return BEAN_MAPPABLE_TYPE.getMappableFields(object);
}
}
private Set<Mapping> getAutoMappings(Object source) {
if (autoMappingEnabled) {
Set<Mapping> autoMappings = new LinkedHashSet<Mapping>();
Set<String> fields = getMappableFields(source);
for (String field : fields) {
if (!explicitlyMapped(field)) {
Expression exp;
try {
exp = expressionParser.parseExpression(field);
} catch (ParseException e) {
throw new IllegalArgumentException("The mapping source '" + source
+ "' is not a parseable value expression", e);
}
Mapping mapping = new Mapping(exp, exp);
autoMappings.add(mapping);
}
}
return autoMappings;
} else {
return Collections.emptySet();
}
}
private boolean explicitlyMapped(String field) {
for (Mapping mapping : mappings) {
if (mapping.source.getExpressionString().equals(field)) {
return true;
}
}
return false;
}
private static class Mapping implements MappingConfiguration {
private Expression source;
private Expression target;
private Converter converter;
public Mapping(Expression source, Expression target) {
this.source = source;
this.target = target;
}
public MappingConfiguration setConverter(Converter converter) {
this.converter = converter;
return this;
}
public void map(EvaluationContext sourceContext, EvaluationContext targetContext) throws MappingException {
try {
Object value = source.getValue(sourceContext);
if (converter != null) {
value = converter.convert(value);
}
target.setValue(targetContext, value);
} catch (Exception e) {
throw new MappingException("Could not perform mapping", e);
}
}
public int hashCode() {
return source.getExpressionString().hashCode() + target.getExpressionString().hashCode();
}
public boolean equals(Object o) {
if (!(o instanceof Mapping)) {
return false;
}
Mapping m = (Mapping) o;
return source.getExpressionString().equals(m.source.getExpressionString())
&& target.getExpressionString().equals(m.source.getExpressionString());
}
public String toString() {
return source.getExpressionString() + " -> " + target.getExpressionString();
}
}
static class MapMappableType implements MappableType {
StandardEvaluationContext evaluationContext = new StandardEvaluationContext();
public Set<String> getMappableFields(Object instance) {
Map map = (Map) instance;
LinkedHashSet<String> fields = new LinkedHashSet<String>(map.size(), 1);
for (Object key : map.keySet()) {
fields.add(key.toString());
}
return fields;
}
public EvaluationContext getMappingContext(Object instance) {
StandardEvaluationContext context = new StandardEvaluationContext(instance);
context.addPropertyAccessor(new MapAccessor());
return context;
}
}
static class BeanMappableType implements MappableType {
StandardEvaluationContext evaluationContext = new StandardEvaluationContext();
public Set<String> getMappableFields(Object instance) {
// TODO
return null;
}
public EvaluationContext getMappingContext(Object instance) {
return new StandardEvaluationContext(instance);
}
}
}

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; an entry key is a field name, the associated entry value is the submitted value for that field
* @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.context.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,68 +0,0 @@
/*
* Copyright 2004-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF 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 org.springframework.context.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.
*/
List<BindingResult> successes();
/**
* The subset of BindingResults that failed.
*/
List<BindingResult> 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.
*/
List<BindingResult> 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);
/**
* The BindingResult for the specified field.
* Returns null if no result exists for the fieldName specified.
*/
BindingResult get(String fieldName);
}

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 = 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.context.alert.Alert;
import org.springframework.context.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,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.binder.support;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.springframework.context.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 List<BindingResult> successes() {
List<BindingResult> results = new ArrayList<BindingResult>();
for (BindingResult result : this) {
if (!result.isFailure()) {
results.add(result);
}
}
return results;
}
public List<BindingResult> failures() {
List<BindingResult> results = new ArrayList<BindingResult>();
for (BindingResult result : this) {
if (result.isFailure()) {
results.add(result);
}
}
return results;
}
public boolean hasErrors() {
return errors().size() > 0;
}
public List<BindingResult> errors() {
List<BindingResult> results = new ArrayList<BindingResult>();
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 BindingResult get(String fieldName) {
for (BindingResult result : results) {
if (result.getFieldName().equals(fieldName)) {
return result;
}
}
return null;
}
public int size() {
return results.size();
}
public String toString() {
return "[BindingResults = " + results.toString() + "]";
}
}

View File

@ -1,35 +0,0 @@
/*
* Copyright 2004-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF 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
* @since 3.0
* @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.context.alert.Alert;
import org.springframework.context.alert.Severity;
import org.springframework.context.message.DefaultMessageFactory;
import org.springframework.context.message.MessageBuilder;
import org.springframework.context.message.ResolvableArgument;
import org.springframework.model.binder.BindingResult;
/**
* Indicates a field failed to bind because it was not editable/writeable.
* @author Keith Donald
* @since 3.0
*/
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 " + 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.context.alert.Alert;
import org.springframework.context.alert.Severity;
import org.springframework.context.message.DefaultMessageFactory;
import org.springframework.context.message.MessageBuilder;
import org.springframework.context.message.ResolvableArgument;
import org.springframework.model.binder.BindingResult;
/**
* Indicates field failed to bind because it was not found.
* @author Keith Donald
* @since 3.0
*/
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 " + submittedValue + "; no field '" + fieldName + "' found";
}
});
return builder.build();
}
};
}
public String toString() {
return getAlert().toString();
}
}

View File

@ -1,212 +0,0 @@
/*
* Copyright 2004-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF 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.context.alert.Alert;
import org.springframework.context.alert.Severity;
import org.springframework.context.expression.MapAccessor;
import org.springframework.context.message.DefaultMessageFactory;
import org.springframework.context.message.MessageBuilder;
import org.springframework.context.message.ResolvableArgument;
import org.springframework.core.convert.ConversionFailedException;
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.SpelEvaluationException;
import org.springframework.expression.spel.SpelMessage;
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.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
* @author Keith Donald
* @since 3.0
*/
public class GenericBinder extends AbstractBinder<Object> {
private final ExpressionParser expressionParser = new SpelExpressionParser(
SpelExpressionParserConfiguration.CreateObjectIfAttemptToReferenceNull
| SpelExpressionParserConfiguration.GrowListsOnIndexBeyondSize);
@Override
protected FieldBinder createFieldBinder(Object model) {
StandardEvaluationContext context = new StandardEvaluationContext();
context.addPropertyAccessor(new MapAccessor());
context.setRootObject(model);
return new ExpressionFieldBinder(getMessageSource(), expressionParser, context);
}
private static class ExpressionFieldBinder implements FieldBinder {
private final MessageSource messageSource;
private final ExpressionParser expressionParser;
private final EvaluationContext evaluationContext;
private ExpressionFieldBinder(MessageSource messageSource, ExpressionParser expressionParser,
EvaluationContext evaluationContext) {
this.messageSource = messageSource;
this.expressionParser = expressionParser;
this.evaluationContext = evaluationContext;
}
public BindingResult bind(String fieldName, Object value) {
Alert alert = null;
try {
Expression exp = expressionParser.parseExpression(fieldName);
if (!exp.isWritable(evaluationContext)) {
return new FieldNotEditableResult(fieldName, value, messageSource);
}
exp.setValue(evaluationContext, value);
alert = new BindSuccessAlert(fieldName, value, messageSource);
} catch (ParseException e) {
alert = new InternalErrorAlert(e);
} catch (EvaluationException e) {
SpelEvaluationException spelException = (SpelEvaluationException) e;
if (spelException.getMessageCode() == SpelMessage.EXCEPTION_DURING_PROPERTY_WRITE) {
ConversionFailedException conversionFailure = findConversionFailureCause(spelException);
if (conversionFailure != null) {
alert = new TypeMismatchAlert(fieldName, value, conversionFailure, messageSource);
}
}
if (alert == null) {
alert = new InternalErrorAlert(e);
}
}
return new AlertBindingResult(fieldName, value, alert);
}
private ConversionFailedException findConversionFailureCause(Exception e) {
Throwable cause = e.getCause();
while (cause != null) {
if (cause instanceof ConversionFailedException) {
return (ConversionFailedException) cause;
}
cause = cause.getCause();
}
return null;
}
}
private static class BindSuccessAlert implements Alert {
private final String fieldName;
private final Object value;
private MessageSource messageSource;
public BindSuccessAlert(String fieldName, Object value, MessageSource messageSource) {
this.fieldName = fieldName;
this.value = value;
this.messageSource = messageSource;
}
public String getCode() {
return "bindSuccess";
}
public String getMessage() {
MessageBuilder builder = new MessageBuilder(messageSource);
builder.code(getCode());
builder.arg("label", new ResolvableArgument(fieldName));
builder.arg("value", value);
builder.defaultMessage(new DefaultMessageFactory() {
public String createDefaultMessage() {
return "Successfully bound submitted value " + value + " to field '" + fieldName + "'";
}
});
return builder.build();
}
public Severity getSeverity() {
return Severity.INFO;
}
}
private static class TypeMismatchAlert implements Alert {
private final String fieldName;
private final Object value;
private final ConversionFailedException cause;
private MessageSource messageSource;
public TypeMismatchAlert(String fieldName, Object value, ConversionFailedException cause,
MessageSource messageSource) {
this.fieldName = fieldName;
this.value = value;
this.cause = cause;
this.messageSource = messageSource;
}
public String getCode() {
return "typeMismatch";
}
public String getMessage() {
MessageBuilder builder = new MessageBuilder(messageSource);
builder.code(getCode());
builder.arg("label", new ResolvableArgument(fieldName));
builder.arg("value", value);
builder.defaultMessage(new DefaultMessageFactory() {
public String createDefaultMessage() {
return "Failed to bind submitted value " + value + " to field '" + fieldName
+ "'; value could not be converted to type [" + cause.getTargetType().getName() + "]";
}
});
return builder.build();
}
public Severity getSeverity() {
return Severity.ERROR;
}
}
private static class InternalErrorAlert implements Alert {
private final Exception cause;
public InternalErrorAlert(Exception cause) {
this.cause = cause;
}
public String getCode() {
return "internalError";
}
public String getMessage() {
return "An internal error occurred; message = [" + cause.getMessage() + "]";
}
public Severity getSeverity() {
return Severity.FATAL;
}
}
}

View File

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

View File

@ -1,34 +0,0 @@
package org.springframework.context.alert;
import static org.junit.Assert.assertEquals;
import static org.springframework.context.alert.Alerts.error;
import static org.springframework.context.alert.Alerts.fatal;
import static org.springframework.context.alert.Alerts.info;
import static org.springframework.context.alert.Alerts.warning;
import org.junit.Test;
import org.springframework.context.alert.Alert;
import org.springframework.context.alert.Severity;
public class AlertsTests {
@Test
public void testFactoryMethods() {
Alert a1 = info("alert 1");
assertEquals(Severity.INFO, a1.getSeverity());
assertEquals("alert 1", a1.getMessage());
Alert a2 = warning("alert 2");
assertEquals(Severity.WARNING, a2.getSeverity());
assertEquals("alert 2", a2.getMessage());
Alert a3 = error("alert 3");
assertEquals(Severity.ERROR, a3.getSeverity());
assertEquals("alert 3", a3.getMessage());
Alert a4 = fatal("alert 4");
assertEquals(Severity.FATAL, a4.getSeverity());
assertEquals("alert 4", a4.getMessage());
}
}

View File

@ -1,39 +0,0 @@
package org.springframework.context.alert.support;
import static org.junit.Assert.assertEquals;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.alert.Alert;
import org.springframework.context.alert.Severity;
import org.springframework.context.alert.support.DefaultAlertContext;
public class DefaultAlertContextTests {
private DefaultAlertContext context;
@Before
public void setUp() {
context = new DefaultAlertContext();
}
@Test
public void addAlert() {
Alert alert = new Alert() {
public String getCode() {
return "invalidFormat";
}
public String getMessage() {
return "Please enter a value in format yyyy-dd-mm";
}
public Severity getSeverity() {
return Severity.ERROR;
}
};
context.add("form.property", alert);
assertEquals(1, context.getAlerts().size());
assertEquals("invalidFormat", context.getAlerts("form.property").get(0).getCode());
}
}

View File

@ -1,23 +0,0 @@
package org.springframework.context.message;
import static org.junit.Assert.assertEquals;
import java.util.Locale;
import org.junit.Test;
import org.springframework.context.message.MessageBuilder;
import org.springframework.context.message.ResolvableArgument;
public class MessageBuilderTests {
@Test
public void buildMessage() {
MockMessageSource messageSource = new MockMessageSource();
messageSource.addMessage("invalidFormat", Locale.US, "#{label} must be in format #{format}");
messageSource.addMessage("mathForm.decimalField", Locale.US, "Decimal Field");
MessageBuilder builder = new MessageBuilder(messageSource);
String message = builder.code("invalidFormat").arg("label", new ResolvableArgument("mathForm.decimalField"))
.arg("format", "#,###.##").locale(Locale.US).defaultMessage("Field must be in format #,###.##").build();
assertEquals("Decimal Field must be in format #,###.##", message);
}
}

View File

@ -1,26 +0,0 @@
package org.springframework.context.message;
import static org.junit.Assert.assertEquals;
import java.util.Locale;
import org.junit.Test;
import org.springframework.context.message.MessageResolver;
import org.springframework.context.message.MessageResolverBuilder;
import org.springframework.context.message.ResolvableArgument;
public class MessageResolverBuilderTests {
private MessageResolverBuilder builder = new MessageResolverBuilder();
@Test
public void buildMessage() {
MessageResolver resolver = builder.code("invalidFormat").arg("label", new ResolvableArgument("mathForm.decimalField"))
.arg("format", "#,###.##").defaultMessage("Field must be in format #,###.##").build();
MockMessageSource messageSource = new MockMessageSource();
messageSource.addMessage("invalidFormat", Locale.US, "#{label} must be in format #{format}");
messageSource.addMessage("mathForm.decimalField", Locale.US, "Decimal Field");
String message = resolver.resolveMessage(messageSource, Locale.US);
assertEquals("Decimal Field must be in format #,###.##", message);
}
}

View File

@ -1,47 +0,0 @@
package org.springframework.context.message;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import org.springframework.context.support.AbstractMessageSource;
import org.springframework.util.Assert;
public class MockMessageSource extends AbstractMessageSource {
/** Map from 'code + locale' keys to message Strings */
private final Map<String, String> messages = new HashMap<String, String>();
@Override
protected MessageFormat resolveCode(String code, Locale locale) {
throw new IllegalStateException("Should not be called");
}
@Override
protected String resolveCodeWithoutArguments(String code, Locale locale) {
return this.messages.get(code + "_" + locale.toString());
}
/**
* Associate the given message with the given code.
* @param code the lookup code
* * @param locale the locale that the message should be found within
* @param msg the message associated with this lookup code
*/
public void addMessage(String code, Locale locale, String msg) {
Assert.notNull(code, "Code must not be null");
Assert.notNull(locale, "Locale must not be null");
Assert.notNull(msg, "Message must not be null");
this.messages.put(code + "_" + locale.toString(), msg);
if (logger.isDebugEnabled()) {
logger.debug("Added message [" + msg + "] for code [" + code + "] and Locale [" + locale + "]");
}
}
@Override
public String toString() {
return getClass().getName() + ": " + this.messages;
}
}

View File

@ -1,149 +0,0 @@
package org.springframework.mapping.support;
import static org.junit.Assert.assertEquals;
import java.util.HashMap;
import java.util.Map;
import org.junit.Test;
import org.springframework.mapping.MappingException;
import org.springframework.mapping.support.SpelMapper;
public class SpelMapperTests {
private SpelMapper mapper = new SpelMapper();
@Test
public void mapAutomatic() {
Map<String, Object> source = new HashMap<String, Object>();
source.put("name", "Keith");
source.put("age", 31);
Person target = new Person();
mapper.map(source, target);
assertEquals("Keith", target.name);
assertEquals(31, target.age);
}
@Test
public void mapExplicit() throws MappingException {
mapper.setAutoMappingEnabled(false);
mapper.addMapping("name", "name");
Map<String, Object> source = new HashMap<String, Object>();
source.put("name", "Keith");
source.put("age", 31);
Person target = new Person();
mapper.map(source, target);
assertEquals("Keith", target.name);
assertEquals(0, target.age);
}
@Test
public void mapAutomaticWithExplictOverrides() {
mapper.addMapping("test", "age");
Map<String, Object> source = new HashMap<String, Object>();
source.put("name", "Keith");
source.put("test", "3");
source.put("favoriteSport", "FOOTBALL");
Person target = new Person();
mapper.map(source, target);
assertEquals("Keith", target.name);
assertEquals(3, target.age);
assertEquals(Sport.FOOTBALL, target.favoriteSport);
}
@Test
public void mapSameSourceFieldToMultipleTargets() {
mapper.addMapping("test", "name");
mapper.addMapping("test", "favoriteSport");
Map<String, Object> source = new HashMap<String, Object>();
source.put("test", "FOOTBALL");
Person target = new Person();
mapper.map(source, target);
assertEquals("FOOTBALL", target.name);
assertEquals(0, target.age);
assertEquals(Sport.FOOTBALL, target.favoriteSport);
}
@Test
public void bean() {
}
public static class Employee {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
public static class Person {
private String name;
private int age;
private Sport favoriteSport;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Sport getFavoriteSport() {
return favoriteSport;
}
public void setFavoriteSport(Sport favoriteSport) {
this.favoriteSport = favoriteSport;
}
}
public enum Sport {
FOOTBALL, BASKETBALL
}
}

View File

@ -1,278 +0,0 @@
/*
* Copyright 2004-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.model.binder.support;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Locale;
import java.util.Map;
import org.junit.Test;
import org.springframework.context.alert.Severity;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.context.message.MockMessageSource;
import org.springframework.core.style.ToStringCreator;
import org.springframework.model.binder.Binder;
import org.springframework.model.binder.BindingResults;
import org.springframework.model.binder.MissingFieldException;
/**
* @author Mark Fisher
* @since 3.0
*/
public class GenericBinderTests {
@Test
public void simpleValues() {
Person person = new Person();
Map<String, Object> map = new LinkedHashMap<String, Object>();
map.put("name", "John Doe");
map.put("age", 42);
map.put("male", true);
Binder<Object> binder = new GenericBinder();
BindingResults results = binder.bind(map, person);
assertEquals(3, results.size());
assertEquals(3, results.successes().size());
assertEquals(0, results.failures().size());
assertEquals(0, results.errors().size());
assertEquals("name", results.get(0).getFieldName());
assertEquals("John Doe", results.get(0).getSubmittedValue());
assertEquals(false, results.get(0).isFailure());
assertEquals(Severity.INFO, results.get(0).getAlert().getSeverity());
assertEquals("bindSuccess", results.get(0).getAlert().getCode());
assertEquals("Successfully bound submitted value John Doe to field 'name'", results.get(0).getAlert().getMessage());
assertEquals("name", results.get("name").getFieldName());
assertEquals("John Doe", results.get("name").getSubmittedValue());
assertEquals("John Doe", person.name);
assertEquals(42, person.age);
assertTrue(person.male);
}
@Test
public void nestedValues() {
Person person = new Person();
Map<String, Object> map = new HashMap<String, Object>();
map.put("pob.city", "Rome");
map.put("pob.country", "Italy");
Binder<Object> binder = new GenericBinder();
binder.bind(map, person);
assertNotNull(person.pob);
assertEquals("Rome", person.pob.city);
assertEquals("Italy", person.pob.country);
}
@Test
public void mapValues() {
Person person = new Person();
Map<String, Object> map = new HashMap<String, Object>();
map.put("jobHistory['0']", "Clerk");
map.put("jobHistory['1']", "Plumber");
Binder<Object> binder = new GenericBinder();
binder.bind(map, person);
assertEquals("Clerk", person.jobHistory.get(0));
assertEquals("Plumber", person.jobHistory.get(1));
}
@Test
public void typeMismatch() {
Person person = new Person();
Map<String, Object> map = new HashMap<String, Object>();
map.put("male", "bogus");
Binder<Object> binder = new GenericBinder();
BindingResults results = binder.bind(map, person);
assertEquals(1, results.size());
assertEquals(0, results.successes().size());
assertEquals(1, results.failures().size());
assertEquals(1, results.errors().size());
assertEquals("bogus", results.get(0).getSubmittedValue());
assertEquals("typeMismatch", results.get(0).getAlert().getCode());
assertEquals(Severity.ERROR, results.get(0).getAlert().getSeverity());
assertEquals("Failed to bind submitted value bogus to field 'male'; value could not be converted to type [boolean]", results.get(0).getAlert().getMessage());
}
@Test
public void internalError() {
Person person = new Person();
Map<String, Object> map = new HashMap<String, Object>();
map.put("bogus", "bogus");
Binder<Object> binder = new GenericBinder();
BindingResults results = binder.bind(map, person);
assertEquals(1, results.size());
assertEquals(0, results.successes().size());
assertEquals(1, results.failures().size());
assertEquals(1, results.errors().size());
assertEquals("bogus", results.get(0).getSubmittedValue());
assertEquals("internalError", results.get(0).getAlert().getCode());
assertEquals(Severity.FATAL, results.get(0).getAlert().getSeverity());
assertEquals("An internal error occurred; message = [EL1034E:(pos 0): A problem occurred whilst attempting to set the property 'bogus': Unable to access property 'bogus' through setter]", results.get(0).getAlert().getMessage());
}
@Test
public void fieldNotEditable() {
Person person = new Person();
Map<String, Object> map = new HashMap<String, Object>();
map.put("readOnly", "whatever");
Binder<Object> binder = new GenericBinder();
BindingResults results = binder.bind(map, person);
assertEquals(1, results.size());
assertEquals(0, results.successes().size());
assertEquals(1, results.failures().size());
assertEquals(0, results.errors().size());
assertEquals("whatever", results.get(0).getSubmittedValue());
assertEquals("fieldNotEditable", results.get(0).getAlert().getCode());
assertEquals(Severity.WARNING, results.get(0).getAlert().getSeverity());
assertEquals("Failed to bind submitted value whatever; field 'readOnly' is not editable", results.get(0).getAlert().getMessage());
}
@Test
public void messageSource() {
Person person = new Person();
Map<String, Object> map = new HashMap<String, Object>();
map.put("male", "bogus");
GenericBinder binder = new GenericBinder();
MockMessageSource messageSource = new MockMessageSource();
messageSource.addMessage("typeMismatch", Locale.US, "Please enter true or false for the value of the #{label} field; you entered #{value}");
binder.setMessageSource(messageSource);
LocaleContextHolder.setLocale(Locale.US);
BindingResults results = binder.bind(map, person);
assertEquals("Please enter true or false for the value of the male field; you entered bogus", results.get(0).getAlert().getMessage());
LocaleContextHolder.setLocale(null);
}
@Test
public void missingFields() {
Person person = new Person();
Map<String, Object> map = new HashMap<String, Object>();
GenericBinder binder = new GenericBinder();
binder.setRequiredFields(new String[] { "name", "age", "male" });
try {
binder.bind(map, person);
} catch (MissingFieldException e) {
assertEquals(3, e.getMissing().size());
assertEquals("name", e.getMissing().get(0));
assertEquals("age", e.getMissing().get(1));
assertEquals("male", e.getMissing().get(2));
}
}
public static class Person {
private String name;
private int age;
private boolean male;
private PlaceOfBirth pob;
private Map<Integer, String> jobHistory;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public boolean isMale() {
return male;
}
public void setMale(boolean male) {
this.male = male;
}
public PlaceOfBirth getPob() {
return pob;
}
public void setPob(PlaceOfBirth pob) {
this.pob = pob;
}
public Map<Integer, String> getJobHistory() {
return jobHistory;
}
public void setJobHistory(Map<Integer, String> jobHistory) {
this.jobHistory = jobHistory;
}
public void setBogus(String bogus) {
throw new RuntimeException("internal error");
}
public boolean isReadOnly() {
return true;
}
public String toString() {
return new ToStringCreator(this)
.append("name", name)
.append("age", age)
.append("male", male)
.append("pob", pob)
.toString();
}
}
public static class PlaceOfBirth {
private String city;
private String country;
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
public String toString() {
return new ToStringCreator(this)
.append("city", city)
.append("country", country)
.toString();
}
}
}