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}