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.asm.Label;
020import org.springframework.asm.MethodVisitor;
021import org.springframework.expression.EvaluationContext;
022import org.springframework.expression.EvaluationException;
023import org.springframework.expression.spel.CodeFlow;
024import org.springframework.expression.spel.ExpressionState;
025import org.springframework.expression.spel.support.BooleanTypedValue;
026
027/**
028 * Implements the not-equal operator.
029 *
030 * @author Andy Clement
031 * @since 3.0
032 */
033public class OpNE extends Operator {
034
035        public OpNE(int startPos, int endPos, SpelNodeImpl... operands) {
036                super("!=", startPos, endPos, operands);
037                this.exitTypeDescriptor = "Z";
038        }
039
040
041        @Override
042        public BooleanTypedValue getValueInternal(ExpressionState state) throws EvaluationException {
043                Object leftValue = getLeftOperand().getValueInternal(state).getValue();
044                Object rightValue = getRightOperand().getValueInternal(state).getValue();
045                this.leftActualDescriptor = CodeFlow.toDescriptorFromObject(leftValue);
046                this.rightActualDescriptor = CodeFlow.toDescriptorFromObject(rightValue);
047                return BooleanTypedValue.forValue(!equalityCheck(state.getEvaluationContext(), leftValue, rightValue));
048        }
049
050        // This check is different to the one in the other numeric operators (OpLt/etc)
051        // because we allow simple object comparison
052        @Override
053        public boolean isCompilable() {
054                SpelNodeImpl left = getLeftOperand();
055                SpelNodeImpl right = getRightOperand();
056                if (!left.isCompilable() || !right.isCompilable()) {
057                        return false;
058                }
059
060                String leftDesc = left.exitTypeDescriptor;
061                String rightDesc = right.exitTypeDescriptor;
062                DescriptorComparison dc = DescriptorComparison.checkNumericCompatibility(leftDesc,
063                                rightDesc, this.leftActualDescriptor, this.rightActualDescriptor);
064                return (!dc.areNumbers || dc.areCompatible);
065        }
066
067        @Override
068        public void generateCode(MethodVisitor mv, CodeFlow cf) {
069                cf.loadEvaluationContext(mv);
070                String leftDesc = getLeftOperand().exitTypeDescriptor;
071                String rightDesc = getRightOperand().exitTypeDescriptor;
072                boolean leftPrim = CodeFlow.isPrimitive(leftDesc);
073                boolean rightPrim = CodeFlow.isPrimitive(rightDesc);
074
075                cf.enterCompilationScope();
076                getLeftOperand().generateCode(mv, cf);
077                cf.exitCompilationScope();
078                if (leftPrim) {
079                        CodeFlow.insertBoxIfNecessary(mv, leftDesc.charAt(0));
080                }
081                cf.enterCompilationScope();
082                getRightOperand().generateCode(mv, cf);
083                cf.exitCompilationScope();
084                if (rightPrim) {
085                        CodeFlow.insertBoxIfNecessary(mv, rightDesc.charAt(0));
086                }
087
088                String operatorClassName = Operator.class.getName().replace('.', '/');
089                String evaluationContextClassName = EvaluationContext.class.getName().replace('.', '/');
090                mv.visitMethodInsn(INVOKESTATIC, operatorClassName, "equalityCheck",
091                                "(L" + evaluationContextClassName + ";Ljava/lang/Object;Ljava/lang/Object;)Z", false);
092
093                // Invert the boolean
094                Label notZero = new Label();
095                Label end = new Label();
096                mv.visitJumpInsn(IFNE, notZero);
097                mv.visitInsn(ICONST_1);
098                mv.visitJumpInsn(GOTO, end);
099                mv.visitLabel(notZero);
100                mv.visitInsn(ICONST_0);
101                mv.visitLabel(end);
102
103                cf.pushDescriptor("Z");
104        }
105
106}