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 ;
@@ -200,6 +201,8 @@ static final class ValueBoxing {
200
201
201
202
private final KFunction <?> wrapperConstructor ;
202
203
204
+ private final KProperty <?> valueProperty ;
205
+
203
206
private final boolean applyBoxing ;
204
207
205
208
private final @ Nullable ValueBoxing next ;
@@ -258,7 +261,6 @@ private ValueBoxing(BoxingRules rules, KType type, KClass<?> kClass, boolean opt
258
261
boolean applyBoxing ;
259
262
260
263
if (kClass .isValue ()) {
261
-
262
264
wrapperConstructor = kClass .getConstructors ().iterator ().next ();
263
265
KParameter nested = wrapperConstructor .getParameters ().get (0 );
264
266
KType nestedType = nested .getType ();
@@ -275,10 +277,12 @@ private ValueBoxing(BoxingRules rules, KType type, KClass<?> kClass, boolean opt
275
277
}
276
278
277
279
Assert .notNull (nestedClass , () -> String .format ("Cannot resolve nested class from type %s" , nestedType ));
278
-
280
+ this .valueProperty = kClass .getMembers ().stream ().filter (it -> it instanceof KProperty <?>)
281
+ .map (KProperty .class ::cast ).findFirst ().get ();
279
282
next = new ValueBoxing (rules , nestedType , nestedClass , nested .isOptional ());
280
283
} else {
281
284
applyBoxing = false ;
285
+ this .valueProperty = null ;
282
286
}
283
287
284
288
this .kClass = kClass ;
@@ -373,20 +377,46 @@ public ValueBoxing getNext() {
373
377
}
374
378
375
379
/**
376
- * Apply wrapping into the boxing wrapper type if applicable .
380
+ * Wrap the value into the boxing wrapper type if requested. Already wrapped values are left unchanged .
377
381
*
378
382
* @param o
379
383
* @return
380
384
*/
381
385
@ Nullable
382
386
public Object wrap (@ Nullable Object o ) {
387
+ return doWrap (o , false , ValueBoxing ::wrap );
388
+ }
389
+
390
+ /**
391
+ * Apply wrapping into the boxing wrapper type if applicable. For types, that do not require wrapping but are
392
+ * wrapped, the component type is being unwrapped.
393
+ *
394
+ * @param o
395
+ * @return
396
+ * @since 3.2.6
397
+ */
398
+ @ Nullable
399
+ Object applyWrapping (@ Nullable Object o ) {
400
+ return doWrap (o , true , ValueBoxing ::applyWrapping );
401
+ }
402
+
403
+ /**
404
+ * Apply staged wrapping into the boxing wrapper type if value boxing is requested. Otherwise, apply unwrapping and
405
+ * pass on the result into {@code nextWrapStage}.
406
+ */
407
+ @ Nullable
408
+ Object doWrap (@ Nullable Object o , boolean unwrap , BiFunction <ValueBoxing , Object , Object > nextWrapStage ) {
383
409
384
410
if (applyBoxing ) {
385
- return o == null || kClass .isInstance (o ) ? o : wrapperConstructor .call (next .wrap (o ));
411
+ return o == null || kClass .isInstance (o ) ? o : wrapperConstructor .call (nextWrapStage .apply (next , o ));
412
+ } else if (unwrap && kClass .isValue ()) {
413
+ if (o != null && kClass .isInstance (o )) {
414
+ o = valueProperty .getGetter ().call (o );
415
+ }
386
416
}
387
417
388
418
if (hasNext ()) {
389
- return next . wrap ( o );
419
+ return nextWrapStage . apply ( next , o );
390
420
}
391
421
392
422
return o ;
0 commit comments