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