@Test(expected = SpelEvaluationException.class) public void test_unaryPlusWithStringLiteral() { ExpressionState expressionState = new ExpressionState(new StandardEvaluationContext()); StringLiteral str = new StringLiteral("word", -1, -1, "word"); OpPlus o = new OpPlus(-1, -1, str); o.getValueInternal(expressionState); }
@Override public TypedValue getValueInternal(ExpressionState state) throws EvaluationException { SpelNodeImpl leftOp = getLeftOperand(); TypedValue operandTwoValue = getRightOperand().getValueInternal(state); Object rightOperand = operandTwoValue.getValue(); leftOperand + (rightOperand == null ? "null" : convertTypedValueToString(operandTwoValue, state))); (leftOperand == null ? "null" : convertTypedValueToString(operandOneValue, state)) + rightOperand);
/** * Walk through a possible tree of nodes that combine strings and append * them all to the same (on stack) StringBuilder. */ private void walk(MethodVisitor mv, CodeFlow cf, @Nullable SpelNodeImpl operand) { if (operand instanceof OpPlus) { OpPlus plus = (OpPlus)operand; walk(mv, cf, plus.getLeftOperand()); walk(mv, cf, plus.getRightOperand()); } else if (operand != null) { cf.enterCompilationScope(); operand.generateCode(mv,cf); if (!"Ljava/lang/String".equals(cf.lastDescriptor())) { mv.visitTypeInsn(CHECKCAST, "java/lang/String"); } cf.exitCompilationScope(); mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false); } }
@Override public boolean isCompilable() { if (!getLeftOperand().isCompilable()) { return false; } if (this.children.length > 1) { if (!getRightOperand().isCompilable()) { return false; } } return (this.exitTypeDescriptor != null); }
@Test(expected = IllegalArgumentException.class) public void test_emptyOperands() { new OpPlus(-1, -1); }
@Override public String toStringAST() { if (this.children.length < 2) { // unary plus return "+" + getLeftOperand().toStringAST(); } return super.toStringAST(); }
@Nullable private SpelNodeImpl eatSumExpression() { SpelNodeImpl expr = eatProductExpression(); while (peekToken(TokenKind.PLUS, TokenKind.MINUS, TokenKind.INC)) { Token t = takeToken(); //consume PLUS or MINUS or INC SpelNodeImpl rhExpr = eatProductExpression(); checkRightOperand(t, rhExpr); if (t.kind == TokenKind.PLUS) { expr = new OpPlus(t.startPos, t.endPos, expr, rhExpr); } else if (t.kind == TokenKind.MINUS) { expr = new OpMinus(t.startPos, t.endPos, expr, rhExpr); } } return expr; }
@Override public boolean isCompilable() { if (!getLeftOperand().isCompilable()) { return false; } if (this.children.length > 1) { if (!getRightOperand().isCompilable()) { return false; } } return (this.exitTypeDescriptor != null); }
@Override public String toStringAST() { if (this.children.length < 2) { // unary plus return "+" + getLeftOperand().toStringAST(); } return super.toStringAST(); }
/** * Walk through a possible tree of nodes that combine strings and append * them all to the same (on stack) StringBuilder. */ private void walk(MethodVisitor mv, CodeFlow cf, @Nullable SpelNodeImpl operand) { if (operand instanceof OpPlus) { OpPlus plus = (OpPlus)operand; walk(mv, cf, plus.getLeftOperand()); walk(mv, cf, plus.getRightOperand()); } else if (operand != null) { cf.enterCompilationScope(); operand.generateCode(mv,cf); if (!"Ljava/lang/String".equals(cf.lastDescriptor())) { mv.visitTypeInsn(CHECKCAST, "java/lang/String"); } cf.exitCompilationScope(); mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false); } }
@Override public TypedValue getValueInternal(ExpressionState state) throws EvaluationException { SpelNodeImpl leftOp = getLeftOperand(); TypedValue operandTwoValue = getRightOperand().getValueInternal(state); Object rightOperand = operandTwoValue.getValue(); leftOperand + (rightOperand == null ? "null" : convertTypedValueToString(operandTwoValue, state))); (leftOperand == null ? "null" : convertTypedValueToString(operandOneValue, state)) + rightOperand);
@Test public void test_binaryPlusWithTime_ToString() { ExpressionState expressionState = new ExpressionState(new StandardEvaluationContext()); Time time = new Time(new Date().getTime()); VariableReference var = new VariableReference("timeVar", -1, -1); var.setValue(expressionState, time); StringLiteral n2 = new StringLiteral("\" is now\"", -1, -1, "\" is now\""); OpPlus o = new OpPlus(-1, -1, var, n2); TypedValue value = o.getValueInternal(expressionState); assertEquals(String.class, value.getTypeDescriptor().getObjectType()); assertEquals(String.class, value.getTypeDescriptor().getType()); assertEquals(time + " is now", value.getValue()); }
@Nullable private SpelNodeImpl eatUnaryExpression() { if (peekToken(TokenKind.PLUS, TokenKind.MINUS, TokenKind.NOT)) { Token t = takeToken(); SpelNodeImpl expr = eatUnaryExpression(); Assert.state(expr != null, "No node"); if (t.kind == TokenKind.NOT) { return new OperatorNot(t.startPos, t.endPos, expr); } if (t.kind == TokenKind.PLUS) { return new OpPlus(t.startPos, t.endPos, expr); } Assert.isTrue(t.kind == TokenKind.MINUS, "Minus token expected"); return new OpMinus(t.startPos, t.endPos, expr); } if (peekToken(TokenKind.INC, TokenKind.DEC)) { Token t = takeToken(); SpelNodeImpl expr = eatUnaryExpression(); if (t.getKind() == TokenKind.INC) { return new OpInc(t.startPos, t.endPos, false, expr); } return new OpDec(t.startPos, t.endPos, false, expr); } return eatPrimaryExpression(); }
@Override public boolean isCompilable() { if (!getLeftOperand().isCompilable()) { return false; } if (this.children.length > 1) { if (!getRightOperand().isCompilable()) { return false; } } return (this.exitTypeDescriptor != null); }
@Override public String toStringAST() { if (this.children.length < 2) { // unary plus return "+" + getLeftOperand().toStringAST(); } return super.toStringAST(); }
mv.visitInsn(DUP); mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "()V", false); walk(mv, cf, getLeftOperand()); walk(mv, cf, getRightOperand()); mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false);
@Override public TypedValue getValueInternal(ExpressionState state) throws EvaluationException { SpelNodeImpl leftOp = getLeftOperand(); TypedValue operandTwoValue = getRightOperand().getValueInternal(state); Object rightOperand = operandTwoValue.getValue(); leftOperand + (rightOperand == null ? "null" : convertTypedValueToString(operandTwoValue, state))); (leftOperand == null ? "null" : convertTypedValueToString(operandOneValue, state)) + rightOperand);
@Test public void test_binaryPlusWithTimeConverted() { SimpleDateFormat format = new SimpleDateFormat("hh :--: mm :--: ss", Locale.ENGLISH); GenericConversionService conversionService = new GenericConversionService(); conversionService.addConverter(Time.class, String.class, format::format); StandardEvaluationContext evaluationContextConverter = new StandardEvaluationContext(); evaluationContextConverter.setTypeConverter(new StandardTypeConverter(conversionService)); ExpressionState expressionState = new ExpressionState(evaluationContextConverter); Time time = new Time(new Date().getTime()); VariableReference var = new VariableReference("timeVar", -1, -1); var.setValue(expressionState, time); StringLiteral n2 = new StringLiteral("\" is now\"", -1, -1, "\" is now\""); OpPlus o = new OpPlus(-1, -1, var, n2); TypedValue value = o.getValueInternal(expressionState); assertEquals(String.class, value.getTypeDescriptor().getObjectType()); assertEquals(String.class, value.getTypeDescriptor().getType()); assertEquals(format.format(time) + " is now", value.getValue()); }
@Nullable private SpelNodeImpl eatSumExpression() { SpelNodeImpl expr = eatProductExpression(); while (peekToken(TokenKind.PLUS, TokenKind.MINUS, TokenKind.INC)) { Token t = takeToken(); //consume PLUS or MINUS or INC SpelNodeImpl rhExpr = eatProductExpression(); checkRightOperand(t, rhExpr); if (t.kind == TokenKind.PLUS) { expr = new OpPlus(toPos(t), expr, rhExpr); } else if (t.kind == TokenKind.MINUS) { expr = new OpMinus(toPos(t), expr, rhExpr); } } return expr; }
mv.visitInsn(DUP); mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "()V", false); walk(mv, cf, getLeftOperand()); walk(mv, cf, getRightOperand()); mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false);