Package org.springframework.expression.spel

Source Code of org.springframework.expression.spel.ConstructorInvocationTests

/*
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.expression.spel;

import java.util.ArrayList;
import java.util.List;

import org.junit.Ignore;
import org.junit.Test;

import org.springframework.core.convert.TypeDescriptor;
import org.springframework.expression.AccessException;
import org.springframework.expression.ConstructorExecutor;
import org.springframework.expression.ConstructorResolver;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.expression.spel.testresources.PlaceOfBirth;

import static org.junit.Assert.*;

/**
* Tests invocation of constructors.
*
* @author Andy Clement
*/
public class ConstructorInvocationTests extends AbstractExpressionTests {

  @Test
  public void testTypeConstructors() {
    evaluate("new String('hello world')", "hello world", String.class);
  }

  @Test
  public void testNonExistentType() {
    evaluateAndCheckError("new FooBar()", SpelMessage.CONSTRUCTOR_INVOCATION_PROBLEM);
  }


  @SuppressWarnings("serial")
  static class TestException extends Exception {

  }

  static class Tester {

    public static int counter;
    public int i;


    public Tester() {
    }

    public Tester(int i) throws Exception {
      counter++;
      if (i == 1) {
        throw new IllegalArgumentException("IllegalArgumentException for 1");
      }
      if (i == 2) {
        throw new RuntimeException("RuntimeException for 2");
      }
      if (i == 4) {
        throw new TestException();
      }
      this.i = i;
    }

    public Tester(PlaceOfBirth pob) {

    }

  }


  @Test
  public void testConstructorThrowingException_SPR6760() {
    // Test ctor on inventor:
    // On 1 it will throw an IllegalArgumentException
    // On 2 it will throw a RuntimeException
    // On 3 it will exit normally
    // In each case it increments the Tester field 'counter' when invoked

    SpelExpressionParser parser = new SpelExpressionParser();
    Expression expr = parser.parseExpression("new org.springframework.expression.spel.ConstructorInvocationTests$Tester(#bar).i");

    // Normal exit
    StandardEvaluationContext eContext = TestScenarioCreator.getTestEvaluationContext();
    eContext.setRootObject(new Tester());
    eContext.setVariable("bar", 3);
    Object o = expr.getValue(eContext);
    assertEquals(o, 3);
    assertEquals(1, parser.parseExpression("counter").getValue(eContext));

    // Now the expression has cached that throwException(int) is the right thing to
    // call. Let's change 'bar' to be a PlaceOfBirth which indicates the cached
    // reference is out of date.
    eContext.setVariable("bar", new PlaceOfBirth("London"));
    o = expr.getValue(eContext);
    assertEquals(0, o);
    // That confirms the logic to mark the cached reference stale and retry is working

    // Now let's cause the method to exit via exception and ensure it doesn't cause
    // a retry.

    // First, switch back to throwException(int)
    eContext.setVariable("bar", 3);
    o = expr.getValue(eContext);
    assertEquals(3, o);
    assertEquals(2, parser.parseExpression("counter").getValue(eContext));

    // 4 will make it throw a checked exception - this will be wrapped by spel on the
    // way out
    eContext.setVariable("bar", 4);
    try {
      o = expr.getValue(eContext);
      fail("Should have failed");
    }
    catch (Exception e) {
      // A problem occurred whilst attempting to construct an object of type
      // 'org.springframework.expression.spel.ConstructorInvocationTests$Tester'
      // using arguments '(java.lang.Integer)'
      int idx = e.getMessage().indexOf("Tester");
      if (idx == -1) {
        fail("Expected reference to Tester in :" + e.getMessage());
      }
      // normal
    }
    // If counter is 4 then the method got called twice!
    assertEquals(3, parser.parseExpression("counter").getValue(eContext));

    // 1 will make it throw a RuntimeException - SpEL will let this through
    eContext.setVariable("bar", 1);
    try {
      o = expr.getValue(eContext);
      fail("Should have failed");
    }
    catch (Exception e) {
      // A problem occurred whilst attempting to construct an object of type
      // 'org.springframework.expression.spel.ConstructorInvocationTests$Tester'
      // using arguments '(java.lang.Integer)'
      if (e instanceof SpelEvaluationException) {
        e.printStackTrace();
        fail("Should not have been wrapped");
      }
    }
    // If counter is 5 then the method got called twice!
    assertEquals(4, parser.parseExpression("counter").getValue(eContext));
  }

  @Test
  public void testAddingConstructorResolvers() {
    StandardEvaluationContext ctx = new StandardEvaluationContext();

    // reflective constructor accessor is the only one by default
    List<ConstructorResolver> constructorResolvers = ctx.getConstructorResolvers();
    assertEquals(1, constructorResolvers.size());

    ConstructorResolver dummy = new DummyConstructorResolver();
    ctx.addConstructorResolver(dummy);
    assertEquals(2, ctx.getConstructorResolvers().size());

    List<ConstructorResolver> copy = new ArrayList<ConstructorResolver>();
    copy.addAll(ctx.getConstructorResolvers());
    assertTrue(ctx.removeConstructorResolver(dummy));
    assertFalse(ctx.removeConstructorResolver(dummy));
    assertEquals(1, ctx.getConstructorResolvers().size());

    ctx.setConstructorResolvers(copy);
    assertEquals(2, ctx.getConstructorResolvers().size());
  }


  static class DummyConstructorResolver implements ConstructorResolver {

    @Override
    public ConstructorExecutor resolve(EvaluationContext context, String typeName,
        List<TypeDescriptor> argumentTypes) throws AccessException {
      throw new UnsupportedOperationException("Auto-generated method stub");
    }

  }


  @Test
  public void testVarargsInvocation01() {
    // Calling 'Fruit(String... strings)'
    evaluate("new org.springframework.expression.spel.testresources.Fruit('a','b','c').stringscount()", 3,
      Integer.class);
    evaluate("new org.springframework.expression.spel.testresources.Fruit('a').stringscount()", 1, Integer.class);
    evaluate("new org.springframework.expression.spel.testresources.Fruit().stringscount()", 0, Integer.class);
    // all need converting to strings
    evaluate("new org.springframework.expression.spel.testresources.Fruit(1,2,3).stringscount()", 3, Integer.class);
    // needs string conversion
    evaluate("new org.springframework.expression.spel.testresources.Fruit(1).stringscount()", 1, Integer.class);
    // first and last need conversion
    evaluate("new org.springframework.expression.spel.testresources.Fruit(1,'a',3.0d).stringscount()", 3,
      Integer.class);
  }

  @Test
  public void testVarargsInvocation02() {
    // Calling 'Fruit(int i, String... strings)' - returns int+length_of_strings
    evaluate("new org.springframework.expression.spel.testresources.Fruit(5,'a','b','c').stringscount()", 8,
      Integer.class);
    evaluate("new org.springframework.expression.spel.testresources.Fruit(2,'a').stringscount()", 3, Integer.class);
    evaluate("new org.springframework.expression.spel.testresources.Fruit(4).stringscount()", 4, Integer.class);
    evaluate("new org.springframework.expression.spel.testresources.Fruit(8,2,3).stringscount()", 10, Integer.class);
    evaluate("new org.springframework.expression.spel.testresources.Fruit(9).stringscount()", 9, Integer.class);
    evaluate("new org.springframework.expression.spel.testresources.Fruit(2,'a',3.0d).stringscount()", 4,
      Integer.class);
    evaluate(
      "new org.springframework.expression.spel.testresources.Fruit(8,stringArrayOfThreeItems).stringscount()",
      11, Integer.class);
  }

  /*
   * These tests are attempting to call constructors where we need to widen or convert
   * the argument in order to satisfy a suitable constructor.
   */
  @Test
  public void testWidening01() {
    // widening of int 3 to double 3 is OK
    evaluate("new Double(3)", 3.0d, Double.class);
    // widening of int 3 to long 3 is OK
    evaluate("new Long(3)", 3L, Long.class);
  }

  @Test
  @Ignore
  public void testArgumentConversion01() {
    // Closest ctor will be new String(String) and converter supports Double>String
    // TODO currently failing as with new ObjectToArray converter closest constructor
    // matched becomes String(byte[]) which fails...
    evaluate("new String(3.0d)", "3.0", String.class);
  }

}
TOP

Related Classes of org.springframework.expression.spel.ConstructorInvocationTests

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.