33
33
import java .util .Arrays ;
34
34
import java .util .Collections ;
35
35
import java .util .List ;
36
+ import java .util .function .BiFunction ;
36
37
37
38
import org .springframework .lang .Nullable ;
38
39
import org .springframework .util .Assert ;
@@ -205,6 +206,8 @@ static class ValueBoxing {
205
206
206
207
private final KFunction <?> wrapperConstructor ;
207
208
209
+ private final KProperty <?> valueProperty ;
210
+
208
211
private final boolean applyBoxing ;
209
212
210
213
private final @ Nullable ValueBoxing next ;
@@ -263,7 +266,6 @@ private ValueBoxing(BoxingRules rules, KType type, KClass<?> kClass, boolean opt
263
266
boolean applyBoxing ;
264
267
265
268
if (kClass .isValue ()) {
266
-
267
269
wrapperConstructor = kClass .getConstructors ().iterator ().next ();
268
270
KParameter nested = wrapperConstructor .getParameters ().get (0 );
269
271
KType nestedType = nested .getType ();
@@ -280,10 +282,12 @@ private ValueBoxing(BoxingRules rules, KType type, KClass<?> kClass, boolean opt
280
282
}
281
283
282
284
Assert .notNull (nestedClass , () -> String .format ("Cannot resolve nested class from type %s" , nestedType ));
283
-
285
+ this .valueProperty = kClass .getMembers ().stream ().filter (it -> it instanceof KProperty <?>)
286
+ .map (KProperty .class ::cast ).findFirst ().get ();
284
287
next = new ValueBoxing (rules , nestedType , nestedClass , nested .isOptional ());
285
288
} else {
286
289
applyBoxing = false ;
290
+ this .valueProperty = null ;
287
291
}
288
292
289
293
this .kClass = kClass ;
@@ -378,20 +382,46 @@ public ValueBoxing getNext() {
378
382
}
379
383
380
384
/**
381
- * Apply wrapping into the boxing wrapper type if applicable .
385
+ * Wrap the value into the boxing wrapper type if requested. Already wrapped values are left unchanged .
382
386
*
383
387
* @param o
384
388
* @return
385
389
*/
386
390
@ Nullable
387
391
public Object wrap (@ Nullable Object o ) {
392
+ return doWrap (o , false , ValueBoxing ::wrap );
393
+ }
394
+
395
+ /**
396
+ * Apply wrapping into the boxing wrapper type if applicable. For types, that do not require wrapping but are
397
+ * wrapped, the component type is being unwrapped.
398
+ *
399
+ * @param o
400
+ * @return
401
+ * @since 3.2.6
402
+ */
403
+ @ Nullable
404
+ Object applyWrapping (@ Nullable Object o ) {
405
+ return doWrap (o , true , ValueBoxing ::applyWrapping );
406
+ }
407
+
408
+ /**
409
+ * Apply staged wrapping into the boxing wrapper type if value boxing is requested. Otherwise, apply unwrapping and
410
+ * pass on the result into {@code nextWrapStage}.
411
+ */
412
+ @ Nullable
413
+ Object doWrap (@ Nullable Object o , boolean unwrap , BiFunction <ValueBoxing , Object , Object > nextWrapStage ) {
388
414
389
415
if (applyBoxing ) {
390
- return o == null || kClass .isInstance (o ) ? o : wrapperConstructor .call (next .wrap (o ));
416
+ return o == null || kClass .isInstance (o ) ? o : wrapperConstructor .call (nextWrapStage .apply (next , o ));
417
+ } else if (unwrap && kClass .isValue ()) {
418
+ if (o != null && kClass .isInstance (o )) {
419
+ o = valueProperty .getGetter ().call (o );
420
+ }
391
421
}
392
422
393
423
if (hasNext ()) {
394
- return next . wrap ( o );
424
+ return nextWrapStage . apply ( next , o );
395
425
}
396
426
397
427
return o ;
0 commit comments