001/*
002 * SPDX-License-Identifier: Apache-2.0
003 *
004 * Copyright 2023-2026 The Enola <https://enola.dev> Authors
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License");
007 * you may not use this file except in compliance with the License.
008 * You may obtain a copy of the License at
009 *
010 *     https://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package dev.enola.common.io.resource;
019
020import com.google.common.base.Strings;
021import com.google.common.io.ByteSource;
022import com.google.common.io.CharSource;
023import com.google.common.net.MediaType;
024
025import org.jspecify.annotations.Nullable;
026
027import java.net.URI;
028import java.net.URISyntaxException;
029import java.util.Objects;
030
031/**
032 * Non-standard Enola specific "string:hello" Resource I/O implementation.
033 *
034 * <p>@deprecated Use {@link DataResource} (or {@link MultibaseResource}) instead of this!
035 */
036@Deprecated // TODO Replace all original StringResource usages with DataResource, and remove
037// TODO Cannot be replaced, because data: cannot have #fragment - how about MultibaseResource?
038public class StringResource extends BaseResource implements ReadableButNotWritableResource {
039
040    public static class Provider implements ResourceProvider {
041        @Override
042        public Resource getResource(URI uri) {
043            if (SCHEME.equals(uri.getScheme()))
044                // NOT new StringResource(uriPath, mediaType),
045                // because that is confusing, as it will chop off after # and interpret '?'
046                // which is confusing for users, for this URI scheme. If "literal" resources
047                // WITH MediaType are required, consider adding DataResource for data:
048                return StringResource.of(uri.getSchemeSpecificPart());
049            else return null;
050        }
051    }
052
053    static final String SCHEME = "string";
054
055    private final String string;
056
057    /**
058     * @deprecated Replace with {@link StringResource2#of(String, MediaType, URI)}
059     */
060    @Deprecated
061    public static Resource of(@Nullable String text, MediaType mediaType, URI fragmentURI) {
062        return StringResource2.of(text, mediaType, fragmentURI);
063    }
064
065    /**
066     * @deprecated Replace with {@link DataResource#of(String, MediaType)}
067     */
068    @Deprecated
069    public static Resource of(@Nullable String text, MediaType mediaType) {
070        if (Strings.isNullOrEmpty(text)) {
071            return new EmptyResource(mediaType);
072        } else {
073            return new StringResource(text, mediaType);
074        }
075    }
076
077    /**
078     * @deprecated Replace with {@link DataResource#of(String)}
079     */
080    @Deprecated
081    public static Resource of(String text) {
082        return of(text, MediaType.PLAIN_TEXT_UTF_8);
083    }
084
085    private StringResource(String text, MediaType mediaType) {
086        this(text, mediaType, createURI(text));
087    }
088
089    private static URI createURI(String text) {
090        try {
091            return new URI(SCHEME, text, null);
092        } catch (URISyntaxException e) {
093            // This should never happen if the escaping done within URI is correct...
094            throw new IllegalArgumentException("String is invalid in URI: " + text, e);
095        }
096    }
097
098    protected StringResource(String text, MediaType mediaType, URI uri) {
099        super(uri, mediaType);
100        this.string = Objects.requireNonNull(text, "text");
101        if ("".equals(text)) {
102            throw new IllegalArgumentException(
103                    "Empty string: not supported (because that's an invalid URI); please use #of()"
104                            + " factory method instead");
105        }
106        if (!mediaType.charset().isPresent()) {
107            throw new IllegalArgumentException(
108                    "MediaType is missing required charset: " + mediaType);
109        }
110    }
111
112    @Override
113    public ByteSource byteSource() {
114        return charSource().asByteSource(mediaType().charset().get());
115    }
116
117    @Override
118    public CharSource charSource() {
119        return CharSource.wrap(string);
120    }
121}