el-based message resolution; expected failure right now
This commit is contained in:
parent
97e7dfb398
commit
d0079c6058
|
|
@ -16,10 +16,22 @@
|
||||||
package org.springframework.ui.message;
|
package org.springframework.ui.message;
|
||||||
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import org.springframework.context.MessageSource;
|
import org.springframework.context.MessageSource;
|
||||||
import org.springframework.context.MessageSourceResolvable;
|
import org.springframework.context.MessageSourceResolvable;
|
||||||
|
import org.springframework.context.expression.MapAccessor;
|
||||||
import org.springframework.core.style.ToStringCreator;
|
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;
|
||||||
|
|
||||||
class DefaultMessageResolver implements MessageResolver, MessageSourceResolvable {
|
class DefaultMessageResolver implements MessageResolver, MessageSourceResolvable {
|
||||||
|
|
||||||
|
|
@ -27,22 +39,41 @@ class DefaultMessageResolver implements MessageResolver, MessageSourceResolvable
|
||||||
|
|
||||||
private String[] codes;
|
private String[] codes;
|
||||||
|
|
||||||
private Object[] args;
|
private Map<String, Object> args;
|
||||||
|
|
||||||
private String defaultText;
|
private String defaultText;
|
||||||
|
|
||||||
public DefaultMessageResolver(Severity severity, String[] codes, Object[] args, String defaultText) {
|
private ExpressionParser expressionParser;
|
||||||
|
|
||||||
|
public DefaultMessageResolver(Severity severity, String[] codes, Map<String, Object> args,
|
||||||
|
String defaultText, ExpressionParser expressionParser) {
|
||||||
this.severity = severity;
|
this.severity = severity;
|
||||||
this.codes = codes;
|
this.codes = codes;
|
||||||
this.args = args;
|
this.args = args;
|
||||||
this.defaultText = defaultText;
|
this.defaultText = defaultText;
|
||||||
|
this.expressionParser = expressionParser;
|
||||||
}
|
}
|
||||||
|
|
||||||
// implementing MessageResolver
|
// implementing MessageResolver
|
||||||
|
|
||||||
public Message resolveMessage(MessageSource messageSource, Locale locale) {
|
public Message resolveMessage(MessageSource messageSource, Locale locale) {
|
||||||
String text = messageSource.getMessage(this, locale);
|
String messageString = messageSource.getMessage(this, locale);
|
||||||
|
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 MapAccessor());
|
||||||
|
context.addPropertyAccessor(new MessageSourceResolvableAccessor(messageSource, locale));
|
||||||
|
String text = (String) message.getValue(context);
|
||||||
return new TextMessage(severity, text);
|
return new TextMessage(severity, text);
|
||||||
|
} catch (EvaluationException e) {
|
||||||
|
throw new MessageResolutionException("Failed to evaluate expression to generate message text", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// implementing MessageSourceResolver
|
// implementing MessageSourceResolver
|
||||||
|
|
@ -52,7 +83,7 @@ class DefaultMessageResolver implements MessageResolver, MessageSourceResolvable
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object[] getArguments() {
|
public Object[] getArguments() {
|
||||||
return args;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getDefaultMessage() {
|
public String getDefaultMessage() {
|
||||||
|
|
@ -60,7 +91,8 @@ class DefaultMessageResolver implements MessageResolver, MessageSourceResolvable
|
||||||
}
|
}
|
||||||
|
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return new ToStringCreator(this).append("severity", severity).append("codes", codes).append("args", args).append("defaultText", defaultText).toString();
|
return new ToStringCreator(this).append("severity", severity).append("codes", codes).append("defaultText",
|
||||||
|
defaultText).toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
static class TextMessage implements Message {
|
static class TextMessage implements Message {
|
||||||
|
|
@ -84,4 +116,38 @@ class DefaultMessageResolver implements MessageResolver, MessageSourceResolvable
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static class MessageSourceResolvableAccessor implements PropertyAccessor {
|
||||||
|
|
||||||
|
private MessageSource messageSource;
|
||||||
|
|
||||||
|
private Locale locale;
|
||||||
|
|
||||||
|
public MessageSourceResolvableAccessor(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 {
|
||||||
|
// TODO this does not get called when resolving MessageSourceResolvable variables; only when accessing properties on MessageSourceResolvable targets.
|
||||||
|
return new TypedValue(messageSource.getMessage((MessageSourceResolvable)target, locale));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean canWrite(EvaluationContext context, Object target, String name) throws AccessException {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
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[] { MessageSourceResolvable.class };
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -15,14 +15,18 @@
|
||||||
*/
|
*/
|
||||||
package org.springframework.ui.message;
|
package org.springframework.ui.message;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
import java.util.List;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.springframework.context.MessageSource;
|
import org.springframework.context.MessageSource;
|
||||||
import org.springframework.context.MessageSourceResolvable;
|
import org.springframework.context.MessageSourceResolvable;
|
||||||
|
import org.springframework.context.expression.MapAccessor;
|
||||||
import org.springframework.core.style.ToStringCreator;
|
import org.springframework.core.style.ToStringCreator;
|
||||||
|
import org.springframework.expression.ExpressionParser;
|
||||||
|
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||||
|
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A convenient builder for building {@link MessageResolver} objects programmatically.
|
* A convenient builder for building {@link MessageResolver} objects programmatically.
|
||||||
|
|
@ -46,16 +50,18 @@ import org.springframework.core.style.ToStringCreator;
|
||||||
*/
|
*/
|
||||||
public class MessageBuilder {
|
public class MessageBuilder {
|
||||||
|
|
||||||
private Set<String> codes = new LinkedHashSet<String>();
|
|
||||||
|
|
||||||
private Severity severity;
|
private Severity severity;
|
||||||
|
|
||||||
private List<Object> args = new ArrayList<Object>();
|
private Set<String> codes = new LinkedHashSet<String>();
|
||||||
|
|
||||||
|
private Map<String, Object> args = new LinkedHashMap<String, Object>();
|
||||||
|
|
||||||
private String defaultText;
|
private String defaultText;
|
||||||
|
|
||||||
|
private ExpressionParser expressionParser = new SpelExpressionParser();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Records the severity of the message.
|
* Set the severity of the message.
|
||||||
* @return this, for fluent API usage
|
* @return this, for fluent API usage
|
||||||
*/
|
*/
|
||||||
public MessageBuilder severity(Severity severity) {
|
public MessageBuilder severity(Severity severity) {
|
||||||
|
|
@ -64,8 +70,8 @@ public class MessageBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Records that the message being built should try and resolve its text using the code provided.
|
* Add a message code to use to resolve the message text.
|
||||||
* Adds the code to the codes list. Successive calls to this method add additional codes.
|
* Successive calls to this method add additional codes.
|
||||||
* Codes are applied in the order they are added.
|
* Codes are applied in the order they are added.
|
||||||
* @param code the message code
|
* @param code the message code
|
||||||
* @return this, for fluent API usage
|
* @return this, for fluent API usage
|
||||||
|
|
@ -76,31 +82,31 @@ public class MessageBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Records that the message being built has a variable argument.
|
* Add a message argument.
|
||||||
* Adds the arg to the args list. Successive calls to this method add additional args.
|
* Successive calls to this method add additional args.
|
||||||
* Args are applied in the order they are added.
|
* @param name the argument name
|
||||||
* @param arg the message argument value
|
* @param value the argument value
|
||||||
* @return this, for fluent API usage
|
* @return this, for fluent API usage
|
||||||
*/
|
*/
|
||||||
public MessageBuilder arg(Object arg) {
|
public MessageBuilder arg(String name, Object value) {
|
||||||
args.add(arg);
|
args.put(name, value);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Records that the message being built has a variable argument, whose display value is also {@link MessageSourceResolvable}.
|
* Add a message argument whose value is a resolvable message code.
|
||||||
* Adds the arg to the args list. Successive calls to this method add additional resolvable args.
|
* Successive calls to this method add additional resolvable arguements.
|
||||||
* Args are applied in the order they are added.
|
* @param name the argument name
|
||||||
* @param arg the resolvable message argument
|
* @param value the argument value
|
||||||
* @return this, for fluent API usage
|
* @return this, for fluent API usage
|
||||||
*/
|
*/
|
||||||
public MessageBuilder resolvableArg(Object arg) {
|
public MessageBuilder resolvableArg(String name, Object value) {
|
||||||
args.add(new ResolvableArgument(arg));
|
args.put(name, new ResolvableArgumentValue(value));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Records the fallback text of the message being built.
|
* Set the fallback text for the message.
|
||||||
* If the message has no codes, this will always be used as the text.
|
* If the message has no codes, this will always be used as the text.
|
||||||
* If the message has codes but none can be resolved, this will always be used as the text.
|
* If the message has codes but none can be resolved, this will always be used as the text.
|
||||||
* @param text the default text
|
* @param text the default text
|
||||||
|
|
@ -113,28 +119,28 @@ public class MessageBuilder {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds the message that will be resolved.
|
* Builds the message that will be resolved.
|
||||||
* Call after recording builder instructions.
|
* Call after recording all builder instructions.
|
||||||
* @return the built message resolver
|
* @return the built message resolver
|
||||||
|
* @throws Illegal
|
||||||
*/
|
*/
|
||||||
public MessageResolver build() {
|
public MessageResolver build() {
|
||||||
if (severity == null) {
|
if (severity == null) {
|
||||||
severity = Severity.INFO;
|
severity = Severity.INFO;
|
||||||
}
|
}
|
||||||
if (codes == null && defaultText == null) {
|
if (codes == null && defaultText == null) {
|
||||||
throw new IllegalArgumentException(
|
throw new IllegalStateException(
|
||||||
"A message code or the message text is required to build this message resolver");
|
"A message code or the message text is required to build this message resolver");
|
||||||
}
|
}
|
||||||
String[] codesArray = (String[]) codes.toArray(new String[codes.size()]);
|
String[] codesArray = (String[]) codes.toArray(new String[codes.size()]);
|
||||||
Object[] argsArray = args.toArray(new Object[args.size()]);
|
return new DefaultMessageResolver(severity, codesArray, args, defaultText, expressionParser);
|
||||||
return new DefaultMessageResolver(severity, codesArray, argsArray, defaultText);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class ResolvableArgument implements MessageSourceResolvable {
|
static class ResolvableArgumentValue implements MessageSourceResolvable {
|
||||||
|
|
||||||
private Object arg;
|
private Object value;
|
||||||
|
|
||||||
public ResolvableArgument(Object arg) {
|
public ResolvableArgumentValue(Object value) {
|
||||||
this.arg = arg;
|
this.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object[] getArguments() {
|
public Object[] getArguments() {
|
||||||
|
|
@ -142,15 +148,15 @@ public class MessageBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
public String[] getCodes() {
|
public String[] getCodes() {
|
||||||
return new String[] { arg.toString() };
|
return new String[] { value.toString() };
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getDefaultMessage() {
|
public String getDefaultMessage() {
|
||||||
return arg.toString();
|
return String.valueOf(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return new ToStringCreator(this).append("arg", arg).toString();
|
return new ToStringCreator(this).append("value", value).toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
package org.springframework.ui.message;
|
||||||
|
|
||||||
|
public class MessageResolutionException extends RuntimeException {
|
||||||
|
|
||||||
|
public MessageResolutionException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -5,17 +5,17 @@ import static org.junit.Assert.assertEquals;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.springframework.context.support.StaticMessageSource;
|
|
||||||
|
|
||||||
public class MessageBuilderTests {
|
public class MessageBuilderTests {
|
||||||
|
|
||||||
private MessageBuilder builder = new MessageBuilder();
|
private MessageBuilder builder = new MessageBuilder();
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void buildMessage() {
|
public void buildMessage() {
|
||||||
MessageResolver resolver = builder.severity(Severity.ERROR).code("invalidFormat").resolvableArg("mathForm.decimalField")
|
MessageResolver resolver = builder.severity(Severity.ERROR).code("invalidFormat").resolvableArg("label", "mathForm.decimalField")
|
||||||
.arg("#,###.##").defaultText("Field must be in format #,###.##").build();
|
.arg("format", "#,###.##").defaultText("Field must be in format #,###.##").build();
|
||||||
StaticMessageSource messageSource = new StaticMessageSource();
|
MockMessageSource messageSource = new MockMessageSource();
|
||||||
messageSource.addMessage("invalidFormat", Locale.US, "{0} must be in format {1}");
|
messageSource.addMessage("invalidFormat", Locale.US, "#{label} must be in format #{format}");
|
||||||
messageSource.addMessage("mathForm.decimalField", Locale.US, "Decimal Field");
|
messageSource.addMessage("mathForm.decimalField", Locale.US, "Decimal Field");
|
||||||
Message message = resolver.resolveMessage(messageSource, Locale.US);
|
Message message = resolver.resolveMessage(messageSource, Locale.US);
|
||||||
assertEquals(Severity.ERROR, message.getSeverity());
|
assertEquals(Severity.ERROR, message.getSeverity());
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
package org.springframework.ui.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -13,7 +13,6 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.springframework.expression;
|
package org.springframework.expression;
|
||||||
|
|
||||||
import org.springframework.core.convert.TypeDescriptor;
|
import org.springframework.core.convert.TypeDescriptor;
|
||||||
|
|
@ -69,13 +68,13 @@ public interface Expression {
|
||||||
public <T> T getValue(EvaluationContext context, Class<T> desiredResultType) throws EvaluationException;
|
public <T> T getValue(EvaluationContext context, Class<T> desiredResultType) throws EvaluationException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set this expression in the provided context to the value provided.
|
* Returns the most general type that can be passed to the {@link #setValue(EvaluationContext, Object)} method using
|
||||||
|
* the default context.
|
||||||
*
|
*
|
||||||
* @param context the context in which to set the value of the expression
|
* @return the most general type of value that can be set on this context
|
||||||
* @param value the new value
|
* @throws EvaluationException if there is a problem determining the type
|
||||||
* @throws EvaluationException if there is a problem during evaluation
|
|
||||||
*/
|
*/
|
||||||
public void setValue(EvaluationContext context, Object value) throws EvaluationException;
|
public Class getValueType() throws EvaluationException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the most general type that can be passed to the {@link #setValue(EvaluationContext, Object)} method for
|
* Returns the most general type that can be passed to the {@link #setValue(EvaluationContext, Object)} method for
|
||||||
|
|
@ -91,10 +90,10 @@ public interface Expression {
|
||||||
* Returns the most general type that can be passed to the {@link #setValue(EvaluationContext, Object)} method using
|
* Returns the most general type that can be passed to the {@link #setValue(EvaluationContext, Object)} method using
|
||||||
* the default context.
|
* the default context.
|
||||||
*
|
*
|
||||||
* @return the most general type of value that can be set on this context
|
* @return a type descriptor for the most general type of value that can be set on this context
|
||||||
* @throws EvaluationException if there is a problem determining the type
|
* @throws EvaluationException if there is a problem determining the type
|
||||||
*/
|
*/
|
||||||
public Class getValueType() throws EvaluationException;
|
public TypeDescriptor getValueTypeDescriptor() throws EvaluationException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the most general type that can be passed to the {@link #setValue(EvaluationContext, Object)} method for
|
* Returns the most general type that can be passed to the {@link #setValue(EvaluationContext, Object)} method for
|
||||||
|
|
@ -107,14 +106,22 @@ public interface Expression {
|
||||||
public TypeDescriptor getValueTypeDescriptor(EvaluationContext context) throws EvaluationException;
|
public TypeDescriptor getValueTypeDescriptor(EvaluationContext context) throws EvaluationException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the most general type that can be passed to the {@link #setValue(EvaluationContext, Object)} method using
|
* Determine if an expression can be written to, i.e. setValue() can be called.
|
||||||
* the default context.
|
|
||||||
*
|
*
|
||||||
* @return a type descriptor for the most general type of value that can be set on this context
|
* @param context the context in which the expression should be checked
|
||||||
* @throws EvaluationException if there is a problem determining the type
|
* @return true if the expression is writable
|
||||||
|
* @throws EvaluationException if there is a problem determining if it is writable
|
||||||
*/
|
*/
|
||||||
public TypeDescriptor getValueTypeDescriptor() throws EvaluationException;
|
public boolean isWritable(EvaluationContext context) throws EvaluationException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set this expression in the provided context to the value provided.
|
||||||
|
*
|
||||||
|
* @param context the context in which to set the value of the expression
|
||||||
|
* @param value the new value
|
||||||
|
* @throws EvaluationException if there is a problem during evaluation
|
||||||
|
*/
|
||||||
|
public void setValue(EvaluationContext context, Object value) throws EvaluationException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the original string used to create this expression, unmodified.
|
* Returns the original string used to create this expression, unmodified.
|
||||||
|
|
@ -123,12 +130,4 @@ public interface Expression {
|
||||||
*/
|
*/
|
||||||
public String getExpressionString();
|
public String getExpressionString();
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine if an expression can be written to, i.e. setValue() can be called.
|
|
||||||
*
|
|
||||||
* @param context the context in which the expression should be checked
|
|
||||||
* @return true if the expression is writable
|
|
||||||
* @throws EvaluationException if there is a problem determining if it is writable
|
|
||||||
*/
|
|
||||||
public boolean isWritable(EvaluationContext context) throws EvaluationException;
|
|
||||||
}
|
}
|
||||||
|
|
@ -31,8 +31,8 @@ public interface ParserContext {
|
||||||
*
|
*
|
||||||
* <pre>
|
* <pre>
|
||||||
* Some literal text
|
* Some literal text
|
||||||
* Hello ${name.firstName}!
|
* Hello #{name.firstName}!
|
||||||
* ${3 + 4}
|
* #{3 + 4}
|
||||||
* </pre>
|
* </pre>
|
||||||
*
|
*
|
||||||
* @return true if the expression is a template, false otherwise
|
* @return true if the expression is a template, false otherwise
|
||||||
|
|
@ -55,4 +55,25 @@ public interface ParserContext {
|
||||||
*/
|
*/
|
||||||
String getExpressionSuffix();
|
String getExpressionSuffix();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default ParserContext implementation that enables template expression parsing mode.
|
||||||
|
* The expression prefix is #{ and the expression suffix is }.
|
||||||
|
* @see #isTemplate()
|
||||||
|
*/
|
||||||
|
public static final ParserContext TEMPLATE_EXPRESSION = new ParserContext() {
|
||||||
|
|
||||||
|
public String getExpressionPrefix() {
|
||||||
|
return "#{";
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getExpressionSuffix() {
|
||||||
|
return "}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isTemplate() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,12 @@ public class TemplateParserContext implements ParserContext {
|
||||||
|
|
||||||
private final String expressionSuffix;
|
private final String expressionSuffix;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new TemplateParserContext with the default #{ prefix and } suffix.
|
||||||
|
*/
|
||||||
|
public TemplateParserContext() {
|
||||||
|
this("#{", "}");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new TemplateParserContext for the given prefix and suffix.
|
* Create a new TemplateParserContext for the given prefix and suffix.
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import org.springframework.expression.Expression;
|
||||||
import org.springframework.expression.common.ExpressionUtils;
|
import org.springframework.expression.common.ExpressionUtils;
|
||||||
import org.springframework.expression.spel.ast.SpelNodeImpl;
|
import org.springframework.expression.spel.ast.SpelNodeImpl;
|
||||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A SpelExpressions represents a parsed (valid) expression that is ready to be evaluated in a specified context. An
|
* A SpelExpressions represents a parsed (valid) expression that is ready to be evaluated in a specified context. An
|
||||||
|
|
@ -48,35 +49,27 @@ public class SpelExpression implements Expression {
|
||||||
this.configuration = configuration;
|
this.configuration = configuration;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// implementing Expression
|
||||||
* @return the expression string that was parsed to create this expression instance
|
|
||||||
*/
|
|
||||||
public String getExpressionString() {
|
|
||||||
return this.expression;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
public Object getValue() throws EvaluationException {
|
public Object getValue() throws EvaluationException {
|
||||||
ExpressionState expressionState = new ExpressionState(new StandardEvaluationContext(),configuration);
|
ExpressionState expressionState = new ExpressionState(new StandardEvaluationContext(), configuration);
|
||||||
return this.ast.getValue(expressionState);
|
return ast.getValue(expressionState);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> T getValue(Class<T> expectedResultType) throws EvaluationException {
|
||||||
|
ExpressionState expressionState = new ExpressionState(new StandardEvaluationContext(), configuration);
|
||||||
|
Object result = ast.getValue(expressionState);
|
||||||
|
return ExpressionUtils.convert(expressionState.getEvaluationContext(), result, expectedResultType);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
public Object getValue(EvaluationContext context) throws EvaluationException {
|
public Object getValue(EvaluationContext context) throws EvaluationException {
|
||||||
return this.ast.getValue(new ExpressionState(context,configuration));
|
Assert.notNull(context, "The EvaluationContext is required");
|
||||||
|
return ast.getValue(new ExpressionState(context, configuration));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public <T> T getValue(EvaluationContext context, Class<T> expectedResultType) throws EvaluationException {
|
public <T> T getValue(EvaluationContext context, Class<T> expectedResultType) throws EvaluationException {
|
||||||
Object result = ast.getValue(new ExpressionState(context,configuration));
|
Object result = ast.getValue(new ExpressionState(context, configuration));
|
||||||
|
|
||||||
if (result != null && expectedResultType != null) {
|
if (result != null && expectedResultType != null) {
|
||||||
Class<?> resultType = result.getClass();
|
Class<?> resultType = result.getClass();
|
||||||
if (!expectedResultType.isAssignableFrom(resultType)) {
|
if (!expectedResultType.isAssignableFrom(resultType)) {
|
||||||
|
|
@ -87,25 +80,49 @@ public class SpelExpression implements Expression {
|
||||||
return (T) result;
|
return (T) result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public Class getValueType() throws EvaluationException {
|
||||||
* {@inheritDoc}
|
return ast.getValueInternal(new ExpressionState(new StandardEvaluationContext(), configuration)).getTypeDescriptor().getType();
|
||||||
*/
|
|
||||||
public void setValue(EvaluationContext context, Object value) throws EvaluationException {
|
|
||||||
this.ast.setValue(new ExpressionState(context,configuration), value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public Class getValueType(EvaluationContext context) throws EvaluationException {
|
||||||
* {@inheritDoc}
|
Assert.notNull(context, "The EvaluationContext is required");
|
||||||
*/
|
ExpressionState eState = new ExpressionState(context, configuration);
|
||||||
public boolean isWritable(EvaluationContext context) throws EvaluationException {
|
TypeDescriptor typeDescriptor = ast.getValueInternal(eState).getTypeDescriptor();
|
||||||
return this.ast.isWritable(new ExpressionState(context,configuration));
|
return typeDescriptor.getType();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TypeDescriptor getValueTypeDescriptor() throws EvaluationException {
|
||||||
|
return ast.getValueInternal(new ExpressionState(new StandardEvaluationContext(), configuration)).getTypeDescriptor();
|
||||||
|
}
|
||||||
|
|
||||||
|
public TypeDescriptor getValueTypeDescriptor(EvaluationContext context) throws EvaluationException {
|
||||||
|
Assert.notNull(context, "The EvaluationContext is required");
|
||||||
|
ExpressionState eState = new ExpressionState(context, configuration);
|
||||||
|
TypeDescriptor typeDescriptor = ast.getValueInternal(eState).getTypeDescriptor();
|
||||||
|
return typeDescriptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getExpressionString() {
|
||||||
|
return expression;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isWritable(EvaluationContext context) throws EvaluationException {
|
||||||
|
Assert.notNull(context, "The EvaluationContext is required");
|
||||||
|
return ast.isWritable(new ExpressionState(context, configuration));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValue(EvaluationContext context, Object value) throws EvaluationException {
|
||||||
|
Assert.notNull(context, "The EvaluationContext is required");
|
||||||
|
ast.setValue(new ExpressionState(context, configuration), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// impl only
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return return the Abstract Syntax Tree for the expression
|
* @return return the Abstract Syntax Tree for the expression
|
||||||
*/
|
*/
|
||||||
public SpelNode getAST() {
|
public SpelNode getAST() {
|
||||||
return this.ast;
|
return ast;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -116,49 +133,7 @@ public class SpelExpression implements Expression {
|
||||||
* @return the string representation of the AST
|
* @return the string representation of the AST
|
||||||
*/
|
*/
|
||||||
public String toStringAST() {
|
public String toStringAST() {
|
||||||
return this.ast.toStringAST();
|
return ast.toStringAST();
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
public Class getValueType(EvaluationContext context) throws EvaluationException {
|
|
||||||
ExpressionState eState = new ExpressionState(context,configuration);
|
|
||||||
TypeDescriptor typeDescriptor = this.ast.getValueInternal(eState).getTypeDescriptor();
|
|
||||||
return typeDescriptor.getType();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
public TypeDescriptor getValueTypeDescriptor(EvaluationContext context) throws EvaluationException {
|
|
||||||
ExpressionState eState = new ExpressionState(context,configuration);
|
|
||||||
TypeDescriptor typeDescriptor = this.ast.getValueInternal(eState).getTypeDescriptor();
|
|
||||||
return typeDescriptor;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
public Class getValueType() throws EvaluationException {
|
|
||||||
return this.ast.getValueInternal(new ExpressionState(new StandardEvaluationContext(),configuration)).getTypeDescriptor().getType();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
public TypeDescriptor getValueTypeDescriptor() throws EvaluationException {
|
|
||||||
return this.ast.getValueInternal(new ExpressionState(new StandardEvaluationContext(),configuration)).getTypeDescriptor();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
public <T> T getValue(Class<T> expectedResultType) throws EvaluationException {
|
|
||||||
ExpressionState expressionState = new ExpressionState(new StandardEvaluationContext(),configuration);
|
|
||||||
Object result = this.ast.getValue(expressionState);
|
|
||||||
return ExpressionUtils.convert(expressionState.getEvaluationContext(), result, expectedResultType);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue