001/*
002 * Copyright 2002-2019 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.expression.spel.ast;
018
019import org.springframework.expression.TypedValue;
020import org.springframework.expression.spel.SpelEvaluationException;
021import org.springframework.expression.spel.SpelMessage;
022
023/**
024 * Represents a reference to a value.  With a reference it is possible to get or set the
025 * value. Passing around value references rather than the values themselves can avoid
026 * incorrect duplication of operand evaluation. For example in 'list[index++]++' without
027 * a value reference for 'list[index++]' it would be necessary to evaluate list[index++]
028 * twice (once to get the value, once to determine where the value goes) and that would
029 * double increment index.
030 *
031 * @author Andy Clement
032 * @since 3.2
033 */
034public interface ValueRef {
035
036        /**
037         * Returns the value this ValueRef points to, it should not require expression
038         * component re-evaluation.
039         * @return the value
040         */
041        TypedValue getValue();
042
043        /**
044         * Sets the value this ValueRef points to, it should not require expression component
045         * re-evaluation.
046         * @param newValue the new value
047         */
048        void setValue(Object newValue);
049
050        /**
051         * Indicates whether calling setValue(Object) is supported.
052         * @return true if setValue() is supported for this value reference.
053         */
054        boolean isWritable();
055
056
057        /**
058         * A ValueRef for the null value.
059         */
060        class NullValueRef implements ValueRef {
061
062                static final NullValueRef INSTANCE = new NullValueRef();
063
064                @Override
065                public TypedValue getValue() {
066                        return TypedValue.NULL;
067                }
068
069                @Override
070                public void setValue(Object newValue) {
071                        // The exception position '0' isn't right but the overhead of creating
072                        // instances of this per node (where the node is solely for error reporting)
073                        // would be unfortunate.
074                        throw new SpelEvaluationException(0, SpelMessage.NOT_ASSIGNABLE, "null");
075                }
076
077                @Override
078                public boolean isWritable() {
079                        return false;
080                }
081        }
082
083
084        /**
085         * A ValueRef holder for a single value, which cannot be set.
086         */
087        class TypedValueHolderValueRef implements ValueRef {
088
089                private final TypedValue typedValue;
090
091                private final SpelNodeImpl node;  // used only for error reporting
092
093                public TypedValueHolderValueRef(TypedValue typedValue, SpelNodeImpl node) {
094                        this.typedValue = typedValue;
095                        this.node = node;
096                }
097
098                @Override
099                public TypedValue getValue() {
100                        return this.typedValue;
101                }
102
103                @Override
104                public void setValue(Object newValue) {
105                        throw new SpelEvaluationException(
106                                        this.node.getStartPosition(), SpelMessage.NOT_ASSIGNABLE, this.node.toStringAST());
107                }
108
109                @Override
110                public boolean isWritable() {
111                        return false;
112                }
113        }
114
115}