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.expression.spel.ast;
018
019import org.springframework.asm.Label;
020import org.springframework.asm.MethodVisitor;
021import org.springframework.expression.EvaluationException;
022import org.springframework.expression.spel.CodeFlow;
023import org.springframework.expression.spel.ExpressionState;
024import org.springframework.expression.spel.SpelEvaluationException;
025import org.springframework.expression.spel.SpelMessage;
026import org.springframework.expression.spel.support.BooleanTypedValue;
027
028/**
029 * Represents the boolean OR operation.
030 *
031 * @author Andy Clement
032 * @author Mark Fisher
033 * @author Oliver Becker
034 * @since 3.0
035 */
036public class OpOr extends Operator {
037
038        public OpOr(int pos, SpelNodeImpl... operands) {
039                super("or", pos, operands);
040                this.exitTypeDescriptor = "Z";
041        }
042
043
044        @Override
045        public BooleanTypedValue getValueInternal(ExpressionState state) throws EvaluationException {
046                if (getBooleanValue(state, getLeftOperand())) {
047                        // no need to evaluate right operand
048                        return BooleanTypedValue.TRUE;
049                }
050                return BooleanTypedValue.forValue(getBooleanValue(state, getRightOperand()));
051        }
052
053        private boolean getBooleanValue(ExpressionState state, SpelNodeImpl operand) {
054                try {
055                        Boolean value = operand.getValue(state, Boolean.class);
056                        assertValueNotNull(value);
057                        return value;
058                }
059                catch (SpelEvaluationException ee) {
060                        ee.setPosition(operand.getStartPosition());
061                        throw ee;
062                }
063        }
064
065        private void assertValueNotNull(Boolean value) {
066                if (value == null) {
067                        throw new SpelEvaluationException(SpelMessage.TYPE_CONVERSION_ERROR, "null", "boolean");
068                }
069        }
070
071        @Override
072        public boolean isCompilable() {
073                SpelNodeImpl left = getLeftOperand();
074                SpelNodeImpl right = getRightOperand();
075                return (left.isCompilable() && right.isCompilable() &&
076                                CodeFlow.isBooleanCompatible(left.exitTypeDescriptor) &&
077                                CodeFlow.isBooleanCompatible(right.exitTypeDescriptor));
078        }
079        
080        @Override
081        public void generateCode(MethodVisitor mv, CodeFlow cf) {
082                // pseudo: if (leftOperandValue) { result=true; } else { result=rightOperandValue; }
083                Label elseTarget = new Label();
084                Label endOfIf = new Label();
085                cf.enterCompilationScope();
086                getLeftOperand().generateCode(mv, cf);
087                cf.unboxBooleanIfNecessary(mv);
088                cf.exitCompilationScope();
089                mv.visitJumpInsn(IFEQ, elseTarget);
090                mv.visitLdcInsn(1); // TRUE
091                mv.visitJumpInsn(GOTO,endOfIf);
092                mv.visitLabel(elseTarget);
093                cf.enterCompilationScope();
094                getRightOperand().generateCode(mv, cf);
095                cf.unboxBooleanIfNecessary(mv);
096                cf.exitCompilationScope();
097                mv.visitLabel(endOfIf);
098                cf.pushDescriptor(this.exitTypeDescriptor);
099        }
100        
101}