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&lt;CompletableFuture&gt; <jv>futureSwapper</jv> = (<jp>conv</jp>, <jp>future</jp>) -&gt; {
049 *       <jk>try</jk> {
050 *          <jk>return</jk> <jp>future</jp>.isDone() ? <jp>future</jp>.get() : <js>"&lt;pending&gt;"</js>;
051 *       } <jk>catch</jk> (Exception <jv>e</jv>) {
052 *          <jk>return</jk> <js>"&lt;error: "</js> + <jv>e</jv>.getMessage() + <js>"&gt;"</js>;
053 *       }
054 *    };
055 *
056 *    <jc>// Custom wrapper unwrapping</jc>
057 *    Swapper&lt;LazyValue&gt; <jv>lazySwapper</jv> = (<jp>conv</jp>, <jp>lazy</jp>) -&gt;
058 *       <jp>lazy</jp>.isEvaluated() ? <jp>lazy</jp>.getValue() : <js>"&lt;unevaluated&gt;"</js>;
059 *
060 *    <jc>// Entity to DTO conversion</jc>
061 *    Swapper&lt;UserEntity&gt; <jv>entitySwapper</jv> = (<jp>conv</jp>, <jp>entity</jp>) -&gt;
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> {}