001/*
002 * Copyright © 2025 CUI-OpenSource-Software (info@cuioss.de)
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package de.cuioss.http.client.converter;
017
018import lombok.NonNull;
019
020import java.net.http.HttpResponse;
021import java.nio.charset.Charset;
022import java.nio.charset.StandardCharsets;
023import java.util.Optional;
024
025/**
026 * Base class for content converters that process String-based HTTP responses.
027 * <p>
028 * This converter is suitable for text-based content types such as JSON, XML, HTML, and plain text.
029 * It uses HttpResponse.BodyHandlers.ofString() with configurable charset support.
030 * <p>
031 * Subclasses need only implement the conversion logic and empty value provision.
032 * The String raw type handling is managed internally.
033 *
034 * @param <T> the target type for content conversion
035 * @author Oliver Wolff
036 * @since 1.0
037 */
038public abstract class StringContentConverter<T> implements HttpContentConverter<T> {
039
040    private final Charset charset;
041
042    /**
043     * Creates a String content converter with UTF-8 charset.
044     */
045    protected StringContentConverter() {
046        this(StandardCharsets.UTF_8);
047    }
048
049    /**
050     * Creates a String content converter with specified charset.
051     *
052     * @param charset the charset to use for String decoding
053     */
054    protected StringContentConverter(@NonNull Charset charset) {
055        this.charset = charset;
056    }
057
058    @Override
059    public HttpResponse.BodyHandler<?> getBodyHandler() {
060        return HttpResponse.BodyHandlers.ofString(charset);
061    }
062
063    @Override
064    public Optional<T> convert(Object rawContent) {
065        // Cast to String since our BodyHandler produces String content
066        return convertString((String) rawContent);
067    }
068
069    /**
070     * Converts String content to the target type.
071     * This method is called by the public convert method after casting.
072     *
073     * @param rawContent the raw String content from HTTP response
074     * @return Optional containing converted content, or empty if conversion fails
075     */
076    protected abstract Optional<T> convertString(String rawContent);
077
078    /**
079     * Identity converter for String content (no conversion needed).
080     * <p>
081     * This is the most basic String converter that returns the input unchanged,
082     * suitable for cases where the raw String response is the desired result.
083     *
084     * @return converter that returns the input String unchanged
085     */
086    public static StringContentConverter<String> identity() {
087        return new StringContentConverter<String>() {
088            @Override
089            protected Optional<String> convertString(String rawContent) {
090                return Optional.ofNullable(rawContent);
091            }
092
093            @Override
094            @NonNull
095            public String emptyValue() {
096                return "";
097            }
098        };
099    }
100}