Package org.springframework.expression.spel.support

Source Code of org.springframework.expression.spel.support.ReflectionHelperTests$Super

/*
* 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.support;

import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

import org.junit.Test;

import org.springframework.core.convert.TypeDescriptor;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.ParseException;
import org.springframework.expression.PropertyAccessor;
import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.AbstractExpressionTests;
import org.springframework.expression.spel.SpelUtilities;
import org.springframework.expression.spel.ast.FormatHelper;
import org.springframework.expression.spel.standard.SpelExpression;
import org.springframework.expression.spel.support.ReflectionHelper.ArgumentsMatchKind;

import static org.junit.Assert.*;

/**
* Tests for any helper code.
*
* @author Andy Clement
*/
public class ReflectionHelperTests extends AbstractExpressionTests {

  @Test
  public void testFormatHelperForClassName() {
    assertEquals("java.lang.String",FormatHelper.formatClassNameForMessage(String.class));
    assertEquals("java.lang.String[]",FormatHelper.formatClassNameForMessage(new String[1].getClass()));
    assertEquals("int[]",FormatHelper.formatClassNameForMessage(new int[1].getClass()));
    assertEquals("int[][]",FormatHelper.formatClassNameForMessage(new int[1][2].getClass()));
    assertEquals("null",FormatHelper.formatClassNameForMessage(null));
  }

  /*
  @Test
  public void testFormatHelperForMethod() {
    assertEquals("foo(java.lang.String)",FormatHelper.formatMethodForMessage("foo", String.class));
    assertEquals("goo(java.lang.String,int[])",FormatHelper.formatMethodForMessage("goo", String.class, new int[1].getClass()));
    assertEquals("boo()",FormatHelper.formatMethodForMessage("boo"));
  }
  */

  @Test
  public void testUtilities() throws ParseException {
    SpelExpression expr = (SpelExpression)parser.parseExpression("3+4+5+6+7-2");
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    PrintStream ps = new PrintStream(baos);
    SpelUtilities.printAbstractSyntaxTree(ps, expr);
    ps.flush();
    String s = baos.toString();
//    ===> Expression '3+4+5+6+7-2' - AST start
//    OperatorMinus  value:(((((3 + 4) + 5) + 6) + 7) - 2)  #children:2
//      OperatorPlus  value:((((3 + 4) + 5) + 6) + 7)  #children:2
//        OperatorPlus  value:(((3 + 4) + 5) + 6)  #children:2
//          OperatorPlus  value:((3 + 4) + 5)  #children:2
//            OperatorPlus  value:(3 + 4)  #children:2
//              CompoundExpression  value:3
//                IntLiteral  value:3
//              CompoundExpression  value:4
//                IntLiteral  value:4
//            CompoundExpression  value:5
//              IntLiteral  value:5
//          CompoundExpression  value:6
//            IntLiteral  value:6
//        CompoundExpression  value:7
//          IntLiteral  value:7
//      CompoundExpression  value:2
//        IntLiteral  value:2
//    ===> Expression '3+4+5+6+7-2' - AST end
    assertTrue(s.contains("===> Expression '3+4+5+6+7-2' - AST start"));
    assertTrue(s.contains(" OpPlus  value:((((3 + 4) + 5) + 6) + 7)  #children:2"));
  }

  @Test
  public void testTypedValue() {
    TypedValue tv1 = new TypedValue("hello");
    TypedValue tv2 = new TypedValue("hello");
    TypedValue tv3 = new TypedValue("bye");
    assertEquals(String.class, tv1.getTypeDescriptor().getType());
    assertEquals("TypedValue: 'hello' of [java.lang.String]", tv1.toString());
    assertEquals(tv1, tv2);
    assertEquals(tv2, tv1);
    assertNotEquals(tv1, tv3);
    assertNotEquals(tv2, tv3);
    assertNotEquals(tv3, tv1);
    assertNotEquals(tv3, tv2);
    assertEquals(tv1.hashCode(), tv2.hashCode());
    assertNotEquals(tv1.hashCode(), tv3.hashCode());
    assertNotEquals(tv2.hashCode(), tv3.hashCode());
  }

  @Test
  public void testReflectionHelperCompareArguments_ExactMatching() {
    StandardTypeConverter tc = new StandardTypeConverter();

    // Calling foo(String) with (String) is exact match
    checkMatch(new Class[] {String.class}, new Class[] {String.class}, tc, ReflectionHelper.ArgumentsMatchKind.EXACT);

    // Calling foo(String,Integer) with (String,Integer) is exact match
    checkMatch(new Class[] {String.class, Integer.class}, new Class[] {String.class, Integer.class}, tc, ArgumentsMatchKind.EXACT);
  }

  @Test
  public void testReflectionHelperCompareArguments_CloseMatching() {
    StandardTypeConverter tc = new StandardTypeConverter();

    // Calling foo(List) with (ArrayList) is close match (no conversion required)
    checkMatch(new Class[] {ArrayList.class}, new Class[] {List.class}, tc, ArgumentsMatchKind.CLOSE);

    // Passing (Sub,String) on call to foo(Super,String) is close match
    checkMatch(new Class[] {Sub.class, String.class}, new Class[] {Super.class, String.class}, tc, ArgumentsMatchKind.CLOSE);

    // Passing (String,Sub) on call to foo(String,Super) is close match
    checkMatch(new Class[] {String.class, Sub.class}, new Class[] {String.class, Super.class}, tc, ArgumentsMatchKind.CLOSE);
  }

  @Test
  public void testReflectionHelperCompareArguments_RequiresConversionMatching() {
    StandardTypeConverter tc = new StandardTypeConverter();

    // Calling foo(String,int) with (String,Integer) requires boxing conversion of argument one
    checkMatch(new Class[] {String.class, Integer.TYPE}, new Class[] {String.class,Integer.class},tc, ArgumentsMatchKind.CLOSE);

    // Passing (int,String) on call to foo(Integer,String) requires boxing conversion of argument zero
    checkMatch(new Class[] {Integer.TYPE, String.class}, new Class[] {Integer.class, String.class},tc, ArgumentsMatchKind.CLOSE);

    // Passing (int,Sub) on call to foo(Integer,Super) requires boxing conversion of argument zero
    checkMatch(new Class[] {Integer.TYPE, Sub.class}, new Class[] {Integer.class, Super.class}, tc, ArgumentsMatchKind.CLOSE);

    // Passing (int,Sub,boolean) on call to foo(Integer,Super,Boolean) requires boxing conversion of arguments zero and two
    // TODO checkMatch(new Class[] {Integer.TYPE, Sub.class, Boolean.TYPE}, new Class[] {Integer.class, Super.class, Boolean.class}, tc, ArgsMatchKind.REQUIRES_CONVERSION);
  }

  @Test
  public void testReflectionHelperCompareArguments_NotAMatch() {
    StandardTypeConverter typeConverter = new StandardTypeConverter();

    // Passing (Super,String) on call to foo(Sub,String) is not a match
    checkMatch(new Class[] {Super.class,String.class}, new Class[] {Sub.class,String.class},typeConverter,null);
  }

  @Test
  public void testReflectionHelperCompareArguments_Varargs_ExactMatching() {
    StandardTypeConverter tc = new StandardTypeConverter();
    Class<?> stringArrayClass = new String[0].getClass();
    Class<?> integerArrayClass = new Integer[0].getClass();

    // Passing (String[]) on call to (String[]) is exact match
    checkMatch2(new Class[] {stringArrayClass}, new Class[] {stringArrayClass}, tc, ArgumentsMatchKind.EXACT);

    // Passing (Integer, String[]) on call to (Integer, String[]) is exact match
    checkMatch2(new Class[] {Integer.class, stringArrayClass}, new Class[] {Integer.class, stringArrayClass}, tc, ArgumentsMatchKind.EXACT);

    // Passing (String, Integer, String[]) on call to (String, String, String[]) is exact match
    checkMatch2(new Class[] {String.class, Integer.class, stringArrayClass}, new Class[] {String.class,Integer.class, stringArrayClass}, tc, ArgumentsMatchKind.EXACT);

    // Passing (Sub, String[]) on call to (Super, String[]) is exact match
    checkMatch2(new Class[] {Sub.class, stringArrayClass}, new Class[] {Super.class,stringArrayClass}, tc, ArgumentsMatchKind.CLOSE);

    // Passing (Integer, String[]) on call to (String, String[]) is exact match
    checkMatch2(new Class[] {Integer.class, stringArrayClass}, new Class[] {String.class, stringArrayClass}, tc, ArgumentsMatchKind.REQUIRES_CONVERSION);

    // Passing (Integer, Sub, String[]) on call to (String, Super, String[]) is exact match
    checkMatch2(new Class[] {Integer.class, Sub.class, String[].class}, new Class[] {String.class,Super .class, String[].class}, tc, ArgumentsMatchKind.REQUIRES_CONVERSION);

    // Passing (String) on call to (String[]) is exact match
    checkMatch2(new Class[] {String.class}, new Class[] {stringArrayClass}, tc, ArgumentsMatchKind.EXACT);

    // Passing (Integer,String) on call to (Integer,String[]) is exact match
    checkMatch2(new Class[] {Integer.class, String.class}, new Class[] {Integer.class, stringArrayClass}, tc, ArgumentsMatchKind.EXACT);

    // Passing (String) on call to (Integer[]) is conversion match (String to Integer)
    checkMatch2(new Class[] {String.class}, new Class[] {integerArrayClass}, tc, ArgumentsMatchKind.REQUIRES_CONVERSION);

    // Passing (Sub) on call to (Super[]) is close match
    checkMatch2(new Class[] {Sub.class}, new Class[] {new Super[0].getClass()}, tc, ArgumentsMatchKind.CLOSE);

    // Passing (Super) on call to (Sub[]) is not a match
    checkMatch2(new Class[] {Super.class}, new Class[] {new Sub[0].getClass()}, tc, null);

    checkMatch2(new Class[] {Unconvertable.class, String.class}, new Class[] {Sub.class, Super[].class}, tc, null);

    checkMatch2(new Class[] {Integer.class, Integer.class, String.class}, new Class[] {String.class, String.class, Super[].class}, tc, null);

    checkMatch2(new Class[] {Unconvertable.class, String.class}, new Class[] {Sub.class, Super[].class}, tc, null);

    checkMatch2(new Class[] {Integer.class, Integer.class, String.class}, new Class[] {String.class, String.class, Super[].class}, tc, null);

    checkMatch2(new Class[] {Integer.class, Integer.class, Sub.class}, new Class[] {String.class, String.class, Super[].class}, tc, ArgumentsMatchKind.REQUIRES_CONVERSION);

    checkMatch2(new Class[] {Integer.class, Integer.class, Integer.class}, new Class[] {Integer.class, String[].class}, tc, ArgumentsMatchKind.REQUIRES_CONVERSION);
    // what happens on (Integer,String) passed to (Integer[]) ?
  }

  @Test
  public void testConvertArguments() throws Exception {
    StandardTypeConverter tc = new StandardTypeConverter();
    Method oneArg = TestInterface.class.getMethod("oneArg", String.class);
    Method twoArg = TestInterface.class.getMethod("twoArg", String.class, String[].class);

    // basic conversion int>String
    Object[] args = new Object[] {3};
    ReflectionHelper.convertArguments(tc, args, oneArg, null);
    checkArguments(args, "3");

    // varargs but nothing to convert
    args = new Object[] {3};
    ReflectionHelper.convertArguments(tc, args, twoArg, 1);
    checkArguments(args, "3");

    // varargs with nothing needing conversion
    args = new Object[] {3, "abc", "abc"};
    ReflectionHelper.convertArguments(tc, args, twoArg, 1);
    checkArguments(args, "3","abc","abc");

    // varargs with conversion required
    args = new Object[] {3, false ,3.0d};
    ReflectionHelper.convertArguments(tc, args, twoArg, 1);
    checkArguments(args, "3","false","3.0");
  }

  @Test
  public void testConvertArguments2() throws Exception {
    StandardTypeConverter tc = new StandardTypeConverter();
    Method oneArg = TestInterface.class.getMethod("oneArg", String.class);
    Method twoArg = TestInterface.class.getMethod("twoArg", String.class, String[].class);

    // Simple conversion: int to string
    Object[] args = new Object[] {3};
    ReflectionHelper.convertAllArguments(tc, args, oneArg);
    checkArguments(args,"3");

    // varargs conversion
    args = new Object[] {3, false, 3.0f};
    ReflectionHelper.convertAllArguments(tc, args, twoArg);
    checkArguments(args,"3","false","3.0");

    // varargs conversion but no varargs
    args = new Object[] {3};
    ReflectionHelper.convertAllArguments(tc, args, twoArg);
    checkArguments(args,"3");

    // null value
    args = new Object[] {3, null, 3.0f};
    ReflectionHelper.convertAllArguments(tc, args, twoArg);
    checkArguments(args,"3",null,"3.0");
  }

  @Test
  public void testSetupArguments() {
    Object[] newArray = ReflectionHelper.setupArgumentsForVarargsInvocation(new Class[] {new String[0].getClass()},"a","b","c");

    assertEquals(1, newArray.length);
    Object firstParam = newArray[0];
    assertEquals(String.class,firstParam.getClass().getComponentType());
    Object[] firstParamArray = (Object[])firstParam;
    assertEquals(3,firstParamArray.length);
    assertEquals("a",firstParamArray[0]);
    assertEquals("b",firstParamArray[1]);
    assertEquals("c",firstParamArray[2]);
  }

  @Test
  public void testReflectivePropertyResolver() throws Exception {
    ReflectivePropertyAccessor rpr = new ReflectivePropertyAccessor();
    Tester t = new Tester();
    t.setProperty("hello");
    EvaluationContext ctx = new StandardEvaluationContext(t);
    assertTrue(rpr.canRead(ctx, t, "property"));
    assertEquals("hello",rpr.read(ctx, t, "property").getValue());
    assertEquals("hello",rpr.read(ctx, t, "property").getValue()); // cached accessor used

    assertTrue(rpr.canRead(ctx, t, "field"));
    assertEquals(3,rpr.read(ctx, t, "field").getValue());
    assertEquals(3,rpr.read(ctx, t, "field").getValue()); // cached accessor used

    assertTrue(rpr.canWrite(ctx, t, "property"));
    rpr.write(ctx, t, "property","goodbye");
    rpr.write(ctx, t, "property","goodbye"); // cached accessor used

    assertTrue(rpr.canWrite(ctx, t, "field"));
    rpr.write(ctx, t, "field",12);
    rpr.write(ctx, t, "field",12);

    // Attempted write as first activity on this field and property to drive testing
    // of populating type descriptor cache
    rpr.write(ctx,t,"field2",3);
    rpr.write(ctx, t, "property2","doodoo");
    assertEquals(3,rpr.read(ctx,t,"field2").getValue());

    // Attempted read as first activity on this field and property (no canRead before them)
    assertEquals(0,rpr.read(ctx,t,"field3").getValue());
    assertEquals("doodoo",rpr.read(ctx,t,"property3").getValue());

    // Access through is method
//    assertEquals(0,rpr.read(ctx,t,"field3").getValue());
    assertEquals(false,rpr.read(ctx,t,"property4").getValue());
    assertTrue(rpr.canRead(ctx,t,"property4"));

    // repro SPR-9123, ReflectivePropertyAccessor JavaBean property names compliance tests
    assertEquals("iD",rpr.read(ctx,t,"iD").getValue());
    assertTrue(rpr.canRead(ctx,t,"iD"));
    assertEquals("id",rpr.read(ctx,t,"id").getValue());
    assertTrue(rpr.canRead(ctx,t,"id"));
    assertEquals("ID",rpr.read(ctx,t,"ID").getValue());
    assertTrue(rpr.canRead(ctx,t,"ID"));
    // note: "Id" is not a valid JavaBean name, nevertheless it is treated as "id"
    assertEquals("id",rpr.read(ctx,t,"Id").getValue());
    assertTrue(rpr.canRead(ctx,t,"Id"));

    // repro SPR-10994
    assertEquals("xyZ",rpr.read(ctx,t,"xyZ").getValue());
    assertTrue(rpr.canRead(ctx,t,"xyZ"));
    assertEquals("xY",rpr.read(ctx,t,"xY").getValue());
    assertTrue(rpr.canRead(ctx,t,"xY"));

    // SPR-10122, ReflectivePropertyAccessor JavaBean property names compliance tests - setters
    rpr.write(ctx, t, "pEBS","Test String");
    assertEquals("Test String",rpr.read(ctx,t,"pEBS").getValue());
  }

  @Test
  public void testOptimalReflectivePropertyResolver() throws Exception {
    ReflectivePropertyAccessor rpr = new ReflectivePropertyAccessor();
    Tester t = new Tester();
    t.setProperty("hello");
    EvaluationContext ctx = new StandardEvaluationContext(t);
//    assertTrue(rpr.canRead(ctx, t, "property"));
//    assertEquals("hello",rpr.read(ctx, t, "property").getValue());
//    assertEquals("hello",rpr.read(ctx, t, "property").getValue()); // cached accessor used

    PropertyAccessor optA = rpr.createOptimalAccessor(ctx, t, "property");
    assertTrue(optA.canRead(ctx, t, "property"));
    assertFalse(optA.canRead(ctx, t, "property2"));
    try {
      optA.canWrite(ctx, t, "property");
      fail();
    }
    catch (UnsupportedOperationException uoe) {
      // success
    }
    try {
      optA.canWrite(ctx, t, "property2");
      fail();
    }
    catch (UnsupportedOperationException uoe) {
      // success
    }
    assertEquals("hello",optA.read(ctx, t, "property").getValue());
    assertEquals("hello",optA.read(ctx, t, "property").getValue()); // cached accessor used

    try {
      optA.getSpecificTargetClasses();
      fail();
    }
    catch (UnsupportedOperationException uoe) {
      // success
    }
    try {
      optA.write(ctx,t,"property",null);
      fail();
    }
    catch (UnsupportedOperationException uoe) {
      // success
    }

    optA = rpr.createOptimalAccessor(ctx, t, "field");
    assertTrue(optA.canRead(ctx, t, "field"));
    assertFalse(optA.canRead(ctx, t, "field2"));
    try {
      optA.canWrite(ctx, t, "field");
      fail();
    }
    catch (UnsupportedOperationException uoe) {
      // success
    }
    try {
      optA.canWrite(ctx, t, "field2");
      fail();
    }
    catch (UnsupportedOperationException uoe) {
      // success
    }
    assertEquals(3,optA.read(ctx, t, "field").getValue());
    assertEquals(3,optA.read(ctx, t, "field").getValue()); // cached accessor used

    try {
      optA.getSpecificTargetClasses();
      fail();
    }
    catch (UnsupportedOperationException uoe) {
      // success
    }
    try {
      optA.write(ctx,t,"field",null);
      fail();
    }
    catch (UnsupportedOperationException uoe) {
      // success
    }
  }


  // test classes
  static class Tester {
    String property;
    public int field = 3;
    public int field2;
    public int field3 = 0;
    String property2;
    String property3 = "doodoo";
    boolean property4 = false;
    String iD = "iD";
    String id = "id";
    String ID = "ID";
    String pEBS = "pEBS";
    String xY = "xY";
    String xyZ = "xyZ";

    public String getProperty() { return property; }
    public void setProperty(String value) { property = value; }

    public void setProperty2(String value) { property2 = value; }

    public String getProperty3() { return property3; }

    public boolean isProperty4() { return property4; }

    public String getiD() { return iD; }

    public String getId() { return id; }

    public String getID() { return ID; }

    public String getXY() { return xY; }

    public String getXyZ() { return xyZ; }

    public String getpEBS() {
      return pEBS;
    }

    public void setpEBS(String pEBS) {
      this.pEBS = pEBS;
    }
  }

  static class Super {
  }

  static class Sub extends Super {
  }

  static class Unconvertable {}

  // ---

  /**
   * Used to validate the match returned from a compareArguments call.
   */
  private void checkMatch(Class<?>[] inputTypes, Class<?>[] expectedTypes, StandardTypeConverter typeConverter, ArgumentsMatchKind expectedMatchKind) {
    ReflectionHelper.ArgumentsMatchInfo matchInfo = ReflectionHelper.compareArguments(getTypeDescriptors(expectedTypes), getTypeDescriptors(inputTypes), typeConverter);
    if (expectedMatchKind == null) {
      assertNull("Did not expect them to match in any way", matchInfo);
    }
    else {
      assertNotNull("Should not be a null match", matchInfo);
    }

    if (expectedMatchKind == ArgumentsMatchKind.EXACT) {
      assertTrue(matchInfo.isExactMatch());
    }
    else if (expectedMatchKind == ArgumentsMatchKind.CLOSE) {
      assertTrue(matchInfo.isCloseMatch());
    }
    else if (expectedMatchKind == ArgumentsMatchKind.REQUIRES_CONVERSION) {
      assertTrue("expected to be a match requiring conversion, but was " + matchInfo, matchInfo.isMatchRequiringConversion());
    }
  }

  /**
   * Used to validate the match returned from a compareArguments call.
   */
  private void checkMatch2(Class<?>[] inputTypes, Class<?>[] expectedTypes, StandardTypeConverter typeConverter, ArgumentsMatchKind expectedMatchKind) {
    ReflectionHelper.ArgumentsMatchInfo matchInfo = ReflectionHelper.compareArgumentsVarargs(getTypeDescriptors(expectedTypes), getTypeDescriptors(inputTypes), typeConverter);
    if (expectedMatchKind == null) {
      assertNull("Did not expect them to match in any way: " + matchInfo, matchInfo);
    }
    else {
      assertNotNull("Should not be a null match", matchInfo);
    }

    if (expectedMatchKind == ArgumentsMatchKind.EXACT) {
      assertTrue(matchInfo.isExactMatch());
    }
    else if (expectedMatchKind == ArgumentsMatchKind.CLOSE) {
      assertTrue(matchInfo.isCloseMatch());
    }
    else if (expectedMatchKind == ArgumentsMatchKind.REQUIRES_CONVERSION) {
      assertTrue("expected to be a match requiring conversion, but was " + matchInfo, matchInfo.isMatchRequiringConversion());
    }
  }

  private void checkArguments(Object[] args, Object... expected) {
    assertEquals(expected.length,args.length);
    for (int i = 0; i < expected.length; i++) {
      checkArgument(expected[i],args[i]);
    }
  }

  private void checkArgument(Object expected, Object actual) {
    assertEquals(expected,actual);
  }

  private List<TypeDescriptor> getTypeDescriptors(Class<?>... types) {
    List<TypeDescriptor> typeDescriptors = new ArrayList<TypeDescriptor>(types.length);
    for (Class<?> type : types) {
      typeDescriptors.add(TypeDescriptor.valueOf(type));
    }
    return typeDescriptors;
  }


  public interface TestInterface {

    void oneArg(String arg1);

    void twoArg(String arg1, String... arg2);
  }

}
TOP

Related Classes of org.springframework.expression.spel.support.ReflectionHelperTests$Super

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.