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}