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