001/* 002 * Copyright 2002-2014 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.web.servlet.tags; 018 019import java.io.IOException; 020import javax.servlet.jsp.JspException; 021import javax.servlet.jsp.PageContext; 022 023import org.springframework.context.expression.BeanFactoryResolver; 024import org.springframework.context.expression.EnvironmentAccessor; 025import org.springframework.context.expression.MapAccessor; 026import org.springframework.core.convert.ConversionService; 027import org.springframework.expression.AccessException; 028import org.springframework.expression.EvaluationContext; 029import org.springframework.expression.Expression; 030import org.springframework.expression.ExpressionParser; 031import org.springframework.expression.PropertyAccessor; 032import org.springframework.expression.TypedValue; 033import org.springframework.expression.spel.standard.SpelExpressionParser; 034import org.springframework.expression.spel.support.StandardEvaluationContext; 035import org.springframework.expression.spel.support.StandardTypeConverter; 036import org.springframework.util.ObjectUtils; 037import org.springframework.web.util.JavaScriptUtils; 038import org.springframework.web.util.TagUtils; 039 040/** 041 * JSP tag for evaluating expressions with the Spring Expression Language (SpEL). 042 * Supports the standard JSP evaluation context consisting of implicit variables and scoped attributes. 043 * 044 * @author Keith Donald 045 * @author Juergen Hoeller 046 * @since 3.0.1 047 */ 048@SuppressWarnings("serial") 049public class EvalTag extends HtmlEscapingAwareTag { 050 051 /** 052 * {@link javax.servlet.jsp.PageContext} attribute for the 053 * page-level {@link EvaluationContext} instance. 054 */ 055 private static final String EVALUATION_CONTEXT_PAGE_ATTRIBUTE = 056 "org.springframework.web.servlet.tags.EVALUATION_CONTEXT"; 057 058 059 private final ExpressionParser expressionParser = new SpelExpressionParser(); 060 061 private Expression expression; 062 063 private String var; 064 065 private int scope = PageContext.PAGE_SCOPE; 066 067 private boolean javaScriptEscape = false; 068 069 070 /** 071 * Set the expression to evaluate. 072 */ 073 public void setExpression(String expression) { 074 this.expression = this.expressionParser.parseExpression(expression); 075 } 076 077 /** 078 * Set the variable name to expose the evaluation result under. 079 * Defaults to rendering the result to the current JspWriter. 080 */ 081 public void setVar(String var) { 082 this.var = var; 083 } 084 085 /** 086 * Set the scope to export the evaluation result to. 087 * This attribute has no meaning unless var is also defined. 088 */ 089 public void setScope(String scope) { 090 this.scope = TagUtils.getScope(scope); 091 } 092 093 /** 094 * Set JavaScript escaping for this tag, as boolean value. 095 * Default is "false". 096 */ 097 public void setJavaScriptEscape(boolean javaScriptEscape) throws JspException { 098 this.javaScriptEscape = javaScriptEscape; 099 } 100 101 102 @Override 103 public int doStartTagInternal() throws JspException { 104 return EVAL_BODY_INCLUDE; 105 } 106 107 @Override 108 public int doEndTag() throws JspException { 109 EvaluationContext evaluationContext = 110 (EvaluationContext) this.pageContext.getAttribute(EVALUATION_CONTEXT_PAGE_ATTRIBUTE); 111 if (evaluationContext == null) { 112 evaluationContext = createEvaluationContext(this.pageContext); 113 this.pageContext.setAttribute(EVALUATION_CONTEXT_PAGE_ATTRIBUTE, evaluationContext); 114 } 115 if (this.var != null) { 116 Object result = this.expression.getValue(evaluationContext); 117 this.pageContext.setAttribute(this.var, result, this.scope); 118 } 119 else { 120 try { 121 String result = this.expression.getValue(evaluationContext, String.class); 122 result = ObjectUtils.getDisplayString(result); 123 result = htmlEscape(result); 124 result = (this.javaScriptEscape ? JavaScriptUtils.javaScriptEscape(result) : result); 125 this.pageContext.getOut().print(result); 126 } 127 catch (IOException ex) { 128 throw new JspException(ex); 129 } 130 } 131 return EVAL_PAGE; 132 } 133 134 private EvaluationContext createEvaluationContext(PageContext pageContext) { 135 StandardEvaluationContext context = new StandardEvaluationContext(); 136 context.addPropertyAccessor(new JspPropertyAccessor(pageContext)); 137 context.addPropertyAccessor(new MapAccessor()); 138 context.addPropertyAccessor(new EnvironmentAccessor()); 139 context.setBeanResolver(new BeanFactoryResolver(getRequestContext().getWebApplicationContext())); 140 ConversionService conversionService = getConversionService(pageContext); 141 if (conversionService != null) { 142 context.setTypeConverter(new StandardTypeConverter(conversionService)); 143 } 144 return context; 145 } 146 147 private ConversionService getConversionService(PageContext pageContext) { 148 return (ConversionService) pageContext.getRequest().getAttribute(ConversionService.class.getName()); 149 } 150 151 152 @SuppressWarnings("deprecation") 153 private static class JspPropertyAccessor implements PropertyAccessor { 154 155 private final PageContext pageContext; 156 157 private final javax.servlet.jsp.el.VariableResolver variableResolver; 158 159 public JspPropertyAccessor(PageContext pageContext) { 160 this.pageContext = pageContext; 161 this.variableResolver = pageContext.getVariableResolver(); 162 } 163 164 @Override 165 public Class<?>[] getSpecificTargetClasses() { 166 return null; 167 } 168 169 @Override 170 public boolean canRead(EvaluationContext context, Object target, String name) throws AccessException { 171 return (target == null && 172 (resolveImplicitVariable(name) != null || this.pageContext.findAttribute(name) != null)); 173 } 174 175 @Override 176 public TypedValue read(EvaluationContext context, Object target, String name) throws AccessException { 177 Object implicitVar = resolveImplicitVariable(name); 178 if (implicitVar != null) { 179 return new TypedValue(implicitVar); 180 } 181 return new TypedValue(this.pageContext.findAttribute(name)); 182 } 183 184 @Override 185 public boolean canWrite(EvaluationContext context, Object target, String name) { 186 return false; 187 } 188 189 @Override 190 public void write(EvaluationContext context, Object target, String name, Object newValue) { 191 throw new UnsupportedOperationException(); 192 } 193 194 private Object resolveImplicitVariable(String name) throws AccessException { 195 if (this.variableResolver == null) { 196 return null; 197 } 198 try { 199 return this.variableResolver.resolveVariable(name); 200 } 201 catch (Exception ex) { 202 throw new AccessException( 203 "Unexpected exception occurred accessing '" + name + "' as an implicit variable", ex); 204 } 205 } 206 } 207 208}