001/* 002 * Copyright 2002-2016 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.context.expression; 018 019import java.util.Map; 020 021import org.springframework.core.DefaultParameterNameDiscoverer; 022import org.springframework.core.ParameterNameDiscoverer; 023import org.springframework.expression.Expression; 024import org.springframework.expression.spel.standard.SpelExpressionParser; 025import org.springframework.util.Assert; 026import org.springframework.util.ObjectUtils; 027 028/** 029 * Shared utility class used to evaluate and cache SpEL expressions that 030 * are defined on {@link java.lang.reflect.AnnotatedElement}. 031 * 032 * @author Stephane Nicoll 033 * @since 4.2 034 * @see AnnotatedElementKey 035 */ 036public abstract class CachedExpressionEvaluator { 037 038 private final SpelExpressionParser parser; 039 040 private final ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer(); 041 042 043 /** 044 * Create a new instance with the specified {@link SpelExpressionParser}. 045 */ 046 protected CachedExpressionEvaluator(SpelExpressionParser parser) { 047 Assert.notNull(parser, "SpelExpressionParser must not be null"); 048 this.parser = parser; 049 } 050 051 /** 052 * Create a new instance with a default {@link SpelExpressionParser}. 053 */ 054 protected CachedExpressionEvaluator() { 055 this(new SpelExpressionParser()); 056 } 057 058 059 /** 060 * Return the {@link SpelExpressionParser} to use. 061 */ 062 protected SpelExpressionParser getParser() { 063 return this.parser; 064 } 065 066 /** 067 * Return a shared parameter name discoverer which caches data internally. 068 * @since 4.3 069 */ 070 protected ParameterNameDiscoverer getParameterNameDiscoverer() { 071 return this.parameterNameDiscoverer; 072 } 073 074 075 /** 076 * Return the {@link Expression} for the specified SpEL value 077 * <p>Parse the expression if it hasn't been already. 078 * @param cache the cache to use 079 * @param elementKey the element on which the expression is defined 080 * @param expression the expression to parse 081 */ 082 protected Expression getExpression(Map<ExpressionKey, Expression> cache, 083 AnnotatedElementKey elementKey, String expression) { 084 085 ExpressionKey expressionKey = createKey(elementKey, expression); 086 Expression expr = cache.get(expressionKey); 087 if (expr == null) { 088 expr = getParser().parseExpression(expression); 089 cache.put(expressionKey, expr); 090 } 091 return expr; 092 } 093 094 private ExpressionKey createKey(AnnotatedElementKey elementKey, String expression) { 095 return new ExpressionKey(elementKey, expression); 096 } 097 098 099 protected static class ExpressionKey implements Comparable<ExpressionKey> { 100 101 private final AnnotatedElementKey element; 102 103 private final String expression; 104 105 protected ExpressionKey(AnnotatedElementKey element, String expression) { 106 this.element = element; 107 this.expression = expression; 108 } 109 110 @Override 111 public boolean equals(Object other) { 112 if (this == other) { 113 return true; 114 } 115 if (!(other instanceof ExpressionKey)) { 116 return false; 117 } 118 ExpressionKey otherKey = (ExpressionKey) other; 119 return (this.element.equals(otherKey.element) && 120 ObjectUtils.nullSafeEquals(this.expression, otherKey.expression)); 121 } 122 123 @Override 124 public int hashCode() { 125 return this.element.hashCode() + (this.expression != null ? this.expression.hashCode() * 29 : 0); 126 } 127 128 @Override 129 public String toString() { 130 return this.element + (this.expression != null ? " with expression \"" + this.expression : "\""); 131 } 132 133 @Override 134 public int compareTo(ExpressionKey other) { 135 int result = this.element.toString().compareTo(other.element.toString()); 136 if (result == 0 && this.expression != null) { 137 result = this.expression.compareTo(other.expression); 138 } 139 return result; 140 } 141 } 142 143}