Polishing

This commit is contained in:
Juergen Hoeller 2018-03-21 16:12:42 +01:00
parent ba5ef6456f
commit c1405ef140
3 changed files with 97 additions and 107 deletions

View File

@ -38,7 +38,7 @@ import org.springframework.expression.spel.support.ReflectionHelper.ArgumentsMat
import static org.junit.Assert.*;
/**
* Tests for any helper code.
* Tests for reflection helper code.
*
* @author Andy Clement
*/
@ -46,23 +46,14 @@ 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("java.lang.String[][]",FormatHelper.formatClassNameForMessage(new String[1][1].getClass()));
assertEquals("int[]",FormatHelper.formatClassNameForMessage(new int[1].getClass()));
assertEquals("int[][]",FormatHelper.formatClassNameForMessage(new int[1][2].getClass()));
assertEquals("null",FormatHelper.formatClassNameForMessage(null));
assertEquals("java.lang.String", FormatHelper.formatClassNameForMessage(String.class));
assertEquals("java.lang.String[]", FormatHelper.formatClassNameForMessage(String[].class));
assertEquals("java.lang.String[][]", FormatHelper.formatClassNameForMessage(String[][].class));
assertEquals("int[]", FormatHelper.formatClassNameForMessage(int[].class));
assertEquals("int[][]", FormatHelper.formatClassNameForMessage(int[][].class));
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");
@ -159,47 +150,45 @@ public class ReflectionHelperTests extends AbstractExpressionTests {
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);
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);
checkMatch2(new Class<?>[] {String[].class}, new Class<?>[] {String[].class}, 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);
checkMatch2(new Class<?>[] {Integer.class, String[].class}, new Class<?>[] {Integer.class, String[].class}, 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);
checkMatch2(new Class<?>[] {String.class, Integer.class, String[].class}, new Class<?>[] {String.class,Integer.class, String[].class}, 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);
checkMatch2(new Class<?>[] {Sub.class, String[].class}, new Class<?>[] {Super.class,String[].class}, 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);
checkMatch2(new Class<?>[] {Integer.class, String[].class}, new Class<?>[] {String.class, String[].class}, 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);
checkMatch2(new Class<?>[] {String.class}, new Class<?>[] {String[].class}, 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);
checkMatch2(new Class<?>[] {Integer.class, String.class}, new Class<?>[] {Integer.class, String[].class}, 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);
checkMatch2(new Class<?>[] {String.class}, new Class<?>[] {Integer[].class}, 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);
checkMatch2(new Class<?>[] {Sub.class}, new Class<?>[] {Super[].class}, 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<?>[] {Super.class}, new Class<?>[] {Sub[].class}, tc, null);
checkMatch2(new Class<?>[] {Unconvertable.class, String.class}, new Class<?>[] {Sub.class, Super[].class}, tc, null);
@ -234,12 +223,12 @@ public class ReflectionHelperTests extends AbstractExpressionTests {
// varargs with nothing needing conversion
args = new Object[] {3, "abc", "abc"};
ReflectionHelper.convertArguments(tc, args, twoArg, 1);
checkArguments(args, "3","abc","abc");
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");
checkArguments(args, "3", "false", "3.0");
}
@Test
@ -251,33 +240,33 @@ public class ReflectionHelperTests extends AbstractExpressionTests {
// Simple conversion: int to string
Object[] args = new Object[] {3};
ReflectionHelper.convertAllArguments(tc, args, oneArg);
checkArguments(args,"3");
checkArguments(args, "3");
// varargs conversion
args = new Object[] {3, false, 3.0f};
ReflectionHelper.convertAllArguments(tc, args, twoArg);
checkArguments(args,"3","false","3.0");
checkArguments(args, "3", "false", "3.0");
// varargs conversion but no varargs
args = new Object[] {3};
ReflectionHelper.convertAllArguments(tc, args, twoArg);
checkArguments(args,"3");
checkArguments(args, "3");
// null value
args = new Object[] {3, null, 3.0f};
ReflectionHelper.convertAllArguments(tc, args, twoArg);
checkArguments(args,"3",null,"3.0");
checkArguments(args, "3", null, "3.0");
}
@Test
public void testSetupArguments() {
Object[] newArray = ReflectionHelper.setupArgumentsForVarargsInvocation(
new Class<?>[] {new String[0].getClass()},"a","b","c");
new Class<?>[] {String[].class}, "a", "b", "c");
assertEquals(1, newArray.length);
Object firstParam = newArray[0];
assertEquals(String.class,firstParam.getClass().getComponentType());
Object[] firstParamArray = (Object[])firstParam;
Object[] firstParamArray = (Object[]) firstParam;
assertEquals(3,firstParamArray.length);
assertEquals("a",firstParamArray[0]);
assertEquals("b",firstParamArray[1]);
@ -285,75 +274,75 @@ public class ReflectionHelperTests extends AbstractExpressionTests {
}
@Test
public void testReflectivePropertyResolver() throws Exception {
ReflectivePropertyAccessor rpr = new ReflectivePropertyAccessor();
public void testReflectivePropertyAccessor() throws Exception {
ReflectivePropertyAccessor rpa = 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(rpa.canRead(ctx, t, "property"));
assertEquals("hello",rpa.read(ctx, t, "property").getValue());
assertEquals("hello",rpa.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(rpa.canRead(ctx, t, "field"));
assertEquals(3,rpa.read(ctx, t, "field").getValue());
assertEquals(3,rpa.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(rpa.canWrite(ctx, t, "property"));
rpa.write(ctx, t, "property", "goodbye");
rpa.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);
assertTrue(rpa.canWrite(ctx, t, "field"));
rpa.write(ctx, t, "field", 12);
rpa.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());
rpa.write(ctx, t, "field2", 3);
rpa.write(ctx, t, "property2", "doodoo");
assertEquals(3,rpa.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());
assertEquals(0,rpa.read(ctx, t, "field3").getValue());
assertEquals("doodoo",rpa.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"));
assertEquals(0,rpa .read(ctx, t, "field3").getValue());
assertEquals(false,rpa.read(ctx, t, "property4").getValue());
assertTrue(rpa.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"));
assertEquals("iD",rpa.read(ctx, t, "iD").getValue());
assertTrue(rpa.canRead(ctx, t, "iD"));
assertEquals("id",rpa.read(ctx, t, "id").getValue());
assertTrue(rpa.canRead(ctx, t, "id"));
assertEquals("ID",rpa.read(ctx, t, "ID").getValue());
assertTrue(rpa.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"));
assertEquals("id",rpa.read(ctx, t, "Id").getValue());
assertTrue(rpa.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"));
assertEquals("xyZ",rpa.read(ctx, t, "xyZ").getValue());
assertTrue(rpa.canRead(ctx, t, "xyZ"));
assertEquals("xY",rpa.read(ctx, t, "xY").getValue());
assertTrue(rpa.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());
rpa.write(ctx, t, "pEBS", "Test String");
assertEquals("Test String",rpa.read(ctx, t, "pEBS").getValue());
}
@Test
public void testOptimalReflectivePropertyResolver() throws Exception {
ReflectivePropertyAccessor rpr = new ReflectivePropertyAccessor();
public void testOptimalReflectivePropertyAccessor() throws Exception {
ReflectivePropertyAccessor rpa = 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(rpa.canRead(ctx, t, "property"));
assertEquals("hello", rpa.read(ctx, t, "property").getValue());
assertEquals("hello", rpa.read(ctx, t, "property").getValue()); // cached accessor used
PropertyAccessor optA = rpr.createOptimalAccessor(ctx, t, "property");
PropertyAccessor optA = rpa.createOptimalAccessor(ctx, t, "property");
assertTrue(optA.canRead(ctx, t, "property"));
assertFalse(optA.canRead(ctx, t, "property2"));
try {
@ -381,14 +370,14 @@ public class ReflectionHelperTests extends AbstractExpressionTests {
// success
}
try {
optA.write(ctx,t,"property",null);
optA.write(ctx, t, "property", null);
fail();
}
catch (UnsupportedOperationException uoe) {
// success
}
optA = rpr.createOptimalAccessor(ctx, t, "field");
optA = rpa.createOptimalAccessor(ctx, t, "field");
assertTrue(optA.canRead(ctx, t, "field"));
assertFalse(optA.canRead(ctx, t, "field2"));
try {
@ -406,7 +395,7 @@ public class ReflectionHelperTests extends AbstractExpressionTests {
// success
}
assertEquals(3,optA.read(ctx, t, "field").getValue());
assertEquals(3,optA.read(ctx, t, "field").getValue()); // cached accessor used
assertEquals(3,optA.read(ctx, t, "field").getValue()); // cached accessor used
try {
optA.getSpecificTargetClasses();
@ -416,7 +405,7 @@ public class ReflectionHelperTests extends AbstractExpressionTests {
// success
}
try {
optA.write(ctx,t,"field",null);
optA.write(ctx, t, "field", null);
fail();
}
catch (UnsupportedOperationException uoe) {

View File

@ -23,7 +23,6 @@ import javax.xml.transform.Source;
import javax.xml.transform.dom.DOMSource;
import org.hamcrest.Matcher;
import org.springframework.test.util.JsonExpectationsHelper;
import org.w3c.dom.Node;
import org.springframework.http.HttpHeaders;
@ -32,6 +31,7 @@ import org.springframework.http.MediaType;
import org.springframework.http.client.ClientHttpRequest;
import org.springframework.http.converter.FormHttpMessageConverter;
import org.springframework.mock.http.client.MockClientHttpRequest;
import org.springframework.test.util.JsonExpectationsHelper;
import org.springframework.test.util.XmlExpectationsHelper;
import org.springframework.test.web.client.RequestMatcher;
import org.springframework.util.MultiValueMap;
@ -218,8 +218,8 @@ public class ContentRequestMatchers {
* regardless of formatting.
* <p>Can compare in two modes, depending on {@code strict} parameter value:
* <ul>
* <li>{@code true}: strict checking. Not extensible, and strict array ordering.</li>
* <li>{@code false}: lenient checking. Extensible, and non-strict array ordering.</li>
* <li>{@code true}: strict checking. Not extensible, and strict array ordering.</li>
* <li>{@code false}: lenient checking. Extensible, and non-strict array ordering.</li>
* </ul>
* <p>Use of this matcher requires the <a
* href="http://jsonassert.skyscreamer.org/">JSONassert<a/> library.
@ -232,8 +232,9 @@ public class ContentRequestMatchers {
try {
MockClientHttpRequest mockRequest = (MockClientHttpRequest) request;
jsonHelper.assertJsonEqual(expectedJsonContent, mockRequest.getBodyAsString(), strict);
} catch (Exception e) {
throw new AssertionError("Failed to parse expected or actual JSON request content", e);
}
catch (Exception ex) {
throw new AssertionError("Failed to parse expected or actual JSON request content", ex);
}
};
}
@ -259,4 +260,3 @@ public class ContentRequestMatchers {
}
}

View File

@ -537,6 +537,24 @@ public class ResourceHttpRequestHandler extends WebContentGenerator
return resource;
}
/**
* Process the given resource path.
* <p>The default implementation replaces:
* <ul>
* <li>Backslash with forward slash.
* <li>Duplicate occurrences of slash with a single slash.
* <li>Any combination of leading slash and control characters (00-1F and 7F)
* with a single "/" or "". For example {@code " / // foo/bar"}
* becomes {@code "/foo/bar"}.
* </ul>
* @since 3.2.12
*/
protected String processPath(String path) {
path = StringUtils.replace(path, "\\", "/");
path = cleanDuplicateSlashes(path);
return cleanLeadingSlash(path);
}
private String cleanDuplicateSlashes(String path) {
StringBuilder sb = null;
char prev = 0;
@ -619,6 +637,7 @@ public class ResourceHttpRequestHandler extends WebContentGenerator
* path starts predictably with a single '/' or does not have one.
* @param path the path to validate
* @return {@code true} if the path is invalid, {@code false} otherwise
* @since 3.0.6
*/
protected boolean isInvalidPath(String path) {
if (path.contains("WEB-INF") || path.contains("META-INF")) {
@ -642,24 +661,6 @@ public class ResourceHttpRequestHandler extends WebContentGenerator
return false;
}
/**
* Process the given resource path.
* <p>The default implementation replaces:
* <ul>
* <li>Backslash with forward slash.
* <li>Duplicate occurrences of slash with a single slash.
* <li>Any combination of leading slash and control characters (00-1F and 7F)
* with a single "/" or "". For example {@code " / // foo/bar"}
* becomes {@code "/foo/bar"}.
* </ul>
* @since 3.2.12
*/
protected String processPath(String path) {
path = StringUtils.replace(path, "\\", "/");
path = cleanDuplicateSlashes(path);
return cleanLeadingSlash(path);
}
/**
* Determine the media type for the given request and the resource matched
* to it. This implementation tries to determine the MediaType based on the