001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.juneau.junit.bct; 018 019import java.util.function.*; 020 021/** 022 * Functional interface for pre-processing and transforming objects before conversion. 023 * 024 * <p>Swappers provide object transformation logic that runs before stringification or 025 * listification. They enable unwrapping, preprocessing, and value extraction from 026 * wrapper objects, containers, and lazy evaluation constructs.</p> 027 * 028 * <h5 class='section'>Key Features:</h5> 029 * <ul> 030 * <li><b>Pre-processing:</b> Transform objects before string/list conversion</li> 031 * <li><b>Wrapper unwrapping:</b> Extract values from Optional, Supplier, Future, etc.</li> 032 * <li><b>Lazy evaluation:</b> Trigger computation of lazy values</li> 033 * <li><b>Type transformation:</b> Convert between related types for better testing</li> 034 * </ul> 035 * 036 * <h5 class='section'>Common Use Cases:</h5> 037 * <ul> 038 * <li><b>Optional unwrapping:</b> Extract values from Optional containers</li> 039 * <li><b>Supplier evaluation:</b> Call get() on Supplier objects</li> 040 * <li><b>Future resolution:</b> Extract completed values from Future objects</li> 041 * <li><b>Proxy unwrapping:</b> Extract underlying objects from proxies</li> 042 * <li><b>Value extraction:</b> Pull relevant data from complex wrapper objects</li> 043 * </ul> 044 * 045 * <h5 class='section'>Usage Examples:</h5> 046 * <p class='bjava'> 047 * <jc>// Future value extraction</jc> 048 * Swapper<CompletableFuture> <jv>futureSwapper</jv> = (<jp>conv</jp>, <jp>future</jp>) -> { 049 * <jk>try</jk> { 050 * <jk>return</jk> <jp>future</jp>.isDone() ? <jp>future</jp>.get() : <js>"<pending>"</js>; 051 * } <jk>catch</jk> (Exception <jv>e</jv>) { 052 * <jk>return</jk> <js>"<error: "</js> + <jv>e</jv>.getMessage() + <js>">"</js>; 053 * } 054 * }; 055 * 056 * <jc>// Custom wrapper unwrapping</jc> 057 * Swapper<LazyValue> <jv>lazySwapper</jv> = (<jp>conv</jp>, <jp>lazy</jp>) -> 058 * <jp>lazy</jp>.isEvaluated() ? <jp>lazy</jp>.getValue() : <js>"<unevaluated>"</js>; 059 * 060 * <jc>// Entity to DTO conversion</jc> 061 * Swapper<UserEntity> <jv>entitySwapper</jv> = (<jp>conv</jp>, <jp>entity</jp>) -> 062 * <jk>new</jk> UserDTO(<jp>entity</jp>.getId(), <jp>entity</jp>.getName(), <jp>entity</jp>.getEmail()); 063 * </p> 064 * 065 * <h5 class='section'>Registration:</h5> 066 * <p class='bjava'> 067 * <jk>var</jk> <jv>converter</jv> = BasicBeanConverter.<jsm>builder</jsm>() 068 * .defaultSettings() 069 * .addSwapper(CompletableFuture.<jk>class</jk>, <jv>futureSwapper</jv>) 070 * .addSwapper(LazyValue.<jk>class</jk>, <jv>lazySwapper</jv>) 071 * .addSwapper(UserEntity.<jk>class</jk>, <jv>entitySwapper</jv>) 072 * .build(); 073 * </p> 074 * 075 * <h5 class='section'>Execution Flow:</h5> 076 * <p>Swappers are applied early in the conversion process:</p> 077 * <ol> 078 * <li><b>Object received</b> for conversion</li> 079 * <li><b>Swapper applied</b> if one is registered for the object's type</li> 080 * <li><b>Result processed</b> through normal stringification/listification</li> 081 * <li><b>Chain continues</b> recursively for nested objects</li> 082 * </ol> 083 * 084 * <h5 class='section'>Best Practices:</h5> 085 * <ul> 086 * <li><b>Handle exceptions</b> gracefully, returning error indicators rather than throwing</li> 087 * <li><b>Preserve semantics</b> - the swapped object should represent the same logical value</li> 088 * <li><b>Consider performance</b> - swappers are called frequently during conversion</li> 089 * <li><b>Return meaningful values</b> for null or invalid states</li> 090 * <li><b>Avoid recursion</b> - ensure swappers don't create circular transformations that could 091 * lead to {@link StackOverflowError}. The framework does not detect recursion, so developers 092 * must ensure swapped objects don't trigger the same or related swappers in an endless cycle</li> 093 * </ul> 094 * 095 * @param <T> The type of object this swapper handles 096 * @see BasicBeanConverter.Builder#addSwapper(Class, Swapper) 097 * @see BeanConverter#swap(Object) 098 */ 099@FunctionalInterface 100public interface Swapper<T> extends BiFunction<BeanConverter,T,Object> {}