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.rest.client;
018
019import java.io.*;
020
021
022/**
023 * Converts a Java object passed to {@link RestRequest#body(Object)} into a {@link TransportBody}.
024 *
025 * <p>
026 * A list of converters is consulted in order; the first converter whose {@link #canConvert(Object)} returns
027 * {@code true} wins. The default converter list (applied automatically by {@link RestClient}) handles
028 * the common cases:
029 * <ol>
030 *    <li>{@link org.apache.juneau.http.HttpBody} — passed through directly (streaming preferred over buffering)
031 *    <li>{@link InputStream} — wrapped in a streaming body
032 *    <li>{@code byte[]} — wrapped in a byte-array body
033 *    <li>{@link File} — wrapped in a file body
034 * </ol>
035 *
036 * <p>
037 * Custom converters are registered on the builder and are prepended to the default list
038 * (checked before the defaults). Use the {@link #of(Class, ThrowingConverter)} factory for concise lambdas:
039 *
040 * <h5 class='section'>Example:</h5>
041 * <p class='bjava'>
042 *    RestClient <jv>client</jv> = RestClient.<jsm>builder</jsm>()
043 *       .bodyConverter(BodyConverter.<jsm>of</jsm>(MyBean.<jk>class</jk>,
044 *          <jv>bean</jv> -&gt; TransportBody.<jsm>of</jsm>(StringBody.<jsm>of</jsm>(<jv>mySerializer</jv>.write(<jv>bean</jv>)))))
045 *       .build();
046 * </p>
047 *
048 * <h5 class='section'>See Also:</h5><ul>
049 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/NextGenRestClient">Next-Generation REST Client</a>
050 * </ul>
051 *
052 * @param <T> The type this converter handles.
053 * @since 9.2.1
054 */
055@SuppressWarnings({
056   "java:S2326" // 'T' is a phantom type parameter binding a converter to its handled type for type-safe registration (BodyConverter<?> lists) and the of() factory; removing it would break this public API.
057})
058public interface BodyConverter<T> {
059
060   /**
061    * Returns {@code true} if this converter can handle the given value.
062    *
063    * @param value The value passed to {@link RestRequest#body(Object)}. May be <jk>null</jk>.
064    * @return {@code true} if {@link #convert(Object)} should be called with this value.
065    */
066   boolean canConvert(Object value);
067
068   /**
069    * Converts the value to a {@link TransportBody}.
070    *
071    * @param value The value. Never <jk>null</jk> (converters are only called when {@link #canConvert} returns true).
072    * @return The transport body. Never <jk>null</jk>.
073    * @throws IOException If conversion fails.
074    */
075   TransportBody convert(Object value) throws IOException;
076
077   /**
078    * Creates a {@link BodyConverter} from a type predicate and a converting lambda.
079    *
080    * <p>
081    * The converter matches any value that is an instance of the given type.
082    *
083    * @param <T> The type to handle.
084    * @param type The type to match via {@code instanceof}. Must not be <jk>null</jk>.
085    * @param converter The conversion function. Must not be <jk>null</jk>.
086    * @return A new converter. Never <jk>null</jk>.
087    */
088   static <T> BodyConverter<T> of(Class<T> type, ThrowingConverter<T> converter) {
089      return new BodyConverter<>() {
090         @Override public boolean canConvert(Object value) { return type.isInstance(value); }
091         @Override public TransportBody convert(Object value) throws IOException { return converter.apply(type.cast(value)); }
092      };
093   }
094
095   /**
096    * A checked function used by {@link BodyConverter#of(Class, ThrowingConverter)}.
097    *
098    * @param <T> The input type.
099    * @since 9.2.1
100    */
101   @FunctionalInterface
102   interface ThrowingConverter<T> {
103
104      /**
105       * Converts the given value to a {@link TransportBody}.
106       *
107       * @param value The value. Never <jk>null</jk>.
108       * @return The transport body. Never <jk>null</jk>.
109       * @throws IOException If conversion fails.
110       */
111      TransportBody apply(T value) throws IOException;
112   }
113}