Skip to content

Commit 860e2bc

Browse files
committed
Revised isInstanceOf/isAssignable message concatenation
Issue: SPR-15196 (cherry picked from commit 22322fd)
1 parent 0623172 commit 860e2bc

File tree

2 files changed

+116
-42
lines changed

2 files changed

+116
-42
lines changed

spring-core/src/main/java/org/springframework/util/Assert.java

Lines changed: 79 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,30 @@
5555
*/
5656
public abstract class Assert {
5757

58+
/**
59+
* Assert a boolean expression, throwing an {@code IllegalStateException}
60+
* if the expression evaluates to {@code false}.
61+
* <p>Call {@link #isTrue} if you wish to throw an {@code IllegalArgumentException}
62+
* on an assertion failure.
63+
* <pre class="code">Assert.state(id == null, "The id property must not already be initialized");</pre>
64+
* @param expression a boolean expression
65+
* @param message the exception message to use if the assertion fails
66+
* @throws IllegalStateException if {@code expression} is {@code false}
67+
*/
68+
public static void state(boolean expression, String message) {
69+
if (!expression) {
70+
throw new IllegalStateException(message);
71+
}
72+
}
73+
74+
/**
75+
* @deprecated as of 4.3.7, in favor of {@link #state(boolean, String)}
76+
*/
77+
@Deprecated
78+
public static void state(boolean expression) {
79+
state(expression, "[Assertion failed] - this state invariant must be true");
80+
}
81+
5882
/**
5983
* Assert a boolean expression, throwing an {@code IllegalArgumentException}
6084
* if the expression evaluates to {@code false}.
@@ -287,21 +311,20 @@ public static void notEmpty(Map<?, ?> map) {
287311

288312
/**
289313
* Assert that the provided object is an instance of the provided class.
290-
* <pre class="code">Assert.instanceOf(Foo.class, foo, "Processing Foo:");</pre>
314+
* <pre class="code">Assert.instanceOf(Foo.class, foo, "Foo expected");</pre>
291315
* @param type the type to check against
292316
* @param obj the object to check
293-
* @param message a message which will be prepended to the message generated
294-
* by this method in order to provide further context. It should normally end
295-
* in ":" or "." so that the generated message looks OK when appended to it.
317+
* @param message a message which will be prepended to provide further context.
318+
* If it is empty or ends in ":" or ";" or "," or ".", a full exception message
319+
* will be appended. If it ends in a space, the name of the offending object's
320+
* type will be appended. In any other case, a ":" with a space and the name
321+
* of the offending object's type will be appended.
296322
* @throws IllegalArgumentException if the object is not an instance of type
297-
* @see Class#isInstance
298323
*/
299324
public static void isInstanceOf(Class<?> type, Object obj, String message) {
300325
notNull(type, "Type to check against must not be null");
301326
if (!type.isInstance(obj)) {
302-
String className = (obj != null ? obj.getClass().getName() : "null");
303-
throw new IllegalArgumentException(StringUtils.hasLength(message) ? message + ": " + className :
304-
"Object of class [" + className + "] must be an instance of " + type);
327+
instanceCheckFailed(type, obj, message);
305328
}
306329
}
307330

@@ -311,27 +334,27 @@ public static void isInstanceOf(Class<?> type, Object obj, String message) {
311334
* @param type the type to check against
312335
* @param obj the object to check
313336
* @throws IllegalArgumentException if the object is not an instance of type
314-
* @see Class#isInstance
315337
*/
316338
public static void isInstanceOf(Class<?> type, Object obj) {
317339
isInstanceOf(type, obj, "");
318340
}
319341

320342
/**
321343
* Assert that {@code superType.isAssignableFrom(subType)} is {@code true}.
322-
* <pre class="code">Assert.isAssignable(Number.class, myClass);</pre>
344+
* <pre class="code">Assert.isAssignable(Number.class, myClass, "Number expected");</pre>
323345
* @param superType the super type to check against
324346
* @param subType the sub type to check
325-
* @param message a message which will be prepended to the message generated
326-
* by this method in order to provide further context. It should normally end
327-
* in ":" or "." so that the generated message looks OK when appended to it.
347+
* @param message a message which will be prepended to provide further context.
348+
* If it is empty or ends in ":" or ";" or "," or ".", a full exception message
349+
* will be appended. If it ends in a space, the name of the offending sub type
350+
* will be appended. In any other case, a ":" with a space and the name of the
351+
* offending sub type will be appended.
328352
* @throws IllegalArgumentException if the classes are not assignable
329353
*/
330354
public static void isAssignable(Class<?> superType, Class<?> subType, String message) {
331355
notNull(superType, "Super type to check against must not be null");
332356
if (subType == null || !superType.isAssignableFrom(subType)) {
333-
throw new IllegalArgumentException(StringUtils.hasLength(message) ? message + ": " + subType :
334-
subType + " is not assignable to " + superType);
357+
assignableCheckFailed(superType, subType, message);
335358
}
336359
}
337360

@@ -346,28 +369,50 @@ public static void isAssignable(Class<?> superType, Class<?> subType) {
346369
isAssignable(superType, subType, "");
347370
}
348371

349-
/**
350-
* Assert a boolean expression, throwing an {@code IllegalStateException}
351-
* if the expression evaluates to {@code false}.
352-
* <p>Call {@link #isTrue} if you wish to throw an {@code IllegalArgumentException}
353-
* on an assertion failure.
354-
* <pre class="code">Assert.state(id == null, "The id property must not already be initialized");</pre>
355-
* @param expression a boolean expression
356-
* @param message the exception message to use if the assertion fails
357-
* @throws IllegalStateException if {@code expression} is {@code false}
358-
*/
359-
public static void state(boolean expression, String message) {
360-
if (!expression) {
361-
throw new IllegalStateException(message);
372+
373+
private static void instanceCheckFailed(Class<?> type, Object obj, String msg) {
374+
String className = (obj != null ? obj.getClass().getName() : "null");
375+
String result = "";
376+
boolean defaultMessage = true;
377+
if (StringUtils.hasLength(msg)) {
378+
if (endsWithSeparator(msg)) {
379+
result = msg + " ";
380+
}
381+
else {
382+
result = messageWithTypeName(msg, className);
383+
defaultMessage = false;
384+
}
385+
}
386+
if (defaultMessage) {
387+
result = result + ("Object of class [" + className + "] must be an instance of " + type);
362388
}
389+
throw new IllegalArgumentException(result);
363390
}
364391

365-
/**
366-
* @deprecated as of 4.3.7, in favor of {@link #state(boolean, String)}
367-
*/
368-
@Deprecated
369-
public static void state(boolean expression) {
370-
state(expression, "[Assertion failed] - this state invariant must be true");
392+
private static void assignableCheckFailed(Class<?> superType, Class<?> subType, String msg) {
393+
String result = "";
394+
boolean defaultMessage = true;
395+
if (StringUtils.hasLength(msg)) {
396+
if (endsWithSeparator(msg)) {
397+
result = msg + " ";
398+
}
399+
else {
400+
result = messageWithTypeName(msg, subType);
401+
defaultMessage = false;
402+
}
403+
}
404+
if (defaultMessage) {
405+
result = result + (subType + " is not assignable to " + superType);
406+
}
407+
throw new IllegalArgumentException(result);
408+
}
409+
410+
private static boolean endsWithSeparator(String msg) {
411+
return (msg.endsWith(":") || msg.endsWith(";") || msg.endsWith(",") || msg.endsWith("."));
412+
}
413+
414+
private static String messageWithTypeName(String msg, Object typeName) {
415+
return msg + (msg.endsWith(" ") ? "" : ": ") + typeName;
371416
}
372417

373418
}

spring-core/src/test/java/org/springframework/util/AssertTests.java

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,18 @@ public class AssertTests {
4141
public final ExpectedException thrown = ExpectedException.none();
4242

4343

44+
@Test
45+
public void state() {
46+
Assert.state(true, "enigma");
47+
}
48+
49+
@Test
50+
public void stateWithFalseExpression() {
51+
thrown.expect(IllegalStateException.class);
52+
thrown.expectMessage("enigma");
53+
Assert.state(false, "enigma");
54+
}
55+
4456
@Test
4557
public void isTrueWithMessage() {
4658
Assert.isTrue(true, "enigma");
@@ -233,6 +245,21 @@ public void isInstanceOfWithTypeMismatchAndCustomMessage() {
233245
Assert.isInstanceOf(String.class, 42L, "Custom message");
234246
}
235247

248+
@Test
249+
public void isInstanceOfWithTypeMismatchAndCustomMessageWithSeparator() {
250+
thrown.expect(IllegalArgumentException.class);
251+
thrown.expectMessage(
252+
"Custom message: Object of class [java.lang.Long] must be an instance of class java.lang.String");
253+
Assert.isInstanceOf(String.class, 42L, "Custom message:");
254+
}
255+
256+
@Test
257+
public void isInstanceOfWithTypeMismatchAndCustomMessageWithSpace() {
258+
thrown.expect(IllegalArgumentException.class);
259+
thrown.expectMessage("Custom message for java.lang.Long");
260+
Assert.isInstanceOf(String.class, 42L, "Custom message for ");
261+
}
262+
236263
@Test
237264
public void isAssignable() {
238265
Assert.isAssignable(Number.class, Integer.class, "enigma");
@@ -262,20 +289,22 @@ public void isAssignableWithTypeMismatchAndNullMessage() {
262289
@Test
263290
public void isAssignableWithTypeMismatchAndCustomMessage() {
264291
thrown.expect(IllegalArgumentException.class);
265-
thrown.expectMessage("enigma: class java.lang.Integer");
266-
Assert.isAssignable(String.class, Integer.class, "enigma");
292+
thrown.expectMessage("Custom message: class java.lang.Integer");
293+
Assert.isAssignable(String.class, Integer.class, "Custom message");
267294
}
268295

269296
@Test
270-
public void state() {
271-
Assert.state(true, "enigma");
297+
public void isAssignableWithTypeMismatchAndCustomMessageWithSeparator() {
298+
thrown.expect(IllegalArgumentException.class);
299+
thrown.expectMessage("Custom message: class java.lang.Integer is not assignable to class java.lang.String");
300+
Assert.isAssignable(String.class, Integer.class, "Custom message:");
272301
}
273302

274303
@Test
275-
public void stateWithFalseExpression() {
276-
thrown.expect(IllegalStateException.class);
277-
thrown.expectMessage("enigma");
278-
Assert.state(false, "enigma");
304+
public void isAssignableWithTypeMismatchAndCustomMessageWithSpace() {
305+
thrown.expect(IllegalArgumentException.class);
306+
thrown.expectMessage("Custom message for class java.lang.Integer");
307+
Assert.isAssignable(String.class, Integer.class, "Custom message for ");
279308
}
280309

281310
}

0 commit comments

Comments
 (0)