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.commons.settings;
018
019import static org.apache.juneau.commons.utils.AssertionUtils.*;
020import static org.apache.juneau.commons.utils.Utils.*;
021
022import java.io.*;
023import java.net.*;
024import java.nio.charset.*;
025import java.nio.file.*;
026import java.util.*;
027import java.util.function.*;
028
029/**
030 * A specialized {@link Setting} for string values that provides convenience methods for type conversion.
031 *
032 * <p>
033 * This class extends {@link Setting} with type-specific conversion methods such as {@link #asInteger()},
034 * {@link #asBoolean()}, {@link #asCharset()}, etc.
035 */
036public class StringSetting extends Setting<String> {
037
038   /**
039    * Creates a new StringSetting from a Settings instance and a Supplier.
040    *
041    * @param settings The Settings instance that created this setting. Must not be <jk>null</jk>.
042    * @param supplier The supplier that provides the string value. Must not be <jk>null</jk>.
043    */
044   public StringSetting(Settings settings, Supplier<String> supplier) {
045      super(settings, supplier);
046   }
047
048   /**
049    * If a value is present, applies the provided mapping function to it and returns a StringSetting describing the result.
050    *
051    * <p>
052    * This method is specifically for String-to-String mappings. For mappings to other types, use the inherited {@link #map(Function)} method.
053    *
054    * <p>
055    * The returned StringSetting maintains its own cache, independent of this supplier.
056    * Resetting the mapped supplier does not affect this supplier, and vice versa.
057    *
058    * @param mapper A mapping function to apply to the value, if present. Must not be <jk>null</jk>.
059    * @return A StringSetting describing the result of applying a mapping function to the value of this StringSetting, if a value is present, otherwise an empty StringSetting.
060    */
061   public StringSetting mapString(Function<? super String, ? extends String> mapper) {
062      assertArgNotNull("mapper", mapper);
063      return new StringSetting(getSettings(), () -> {
064         String value = get();
065         return nn(value) ? mapper.apply(value) : null;
066      });
067   }
068
069   /**
070    * If a value is present, and the value matches the given predicate, returns a StringSetting describing the value, otherwise returns an empty StringSetting.
071    *
072    * <p>
073    * The returned StringSetting maintains its own cache, independent of this supplier.
074    * Resetting the filtered supplier does not affect this supplier, and vice versa.
075    *
076    * @param predicate A predicate to apply to the value, if present. Must not be <jk>null</jk>.
077    * @return A StringSetting describing the value of this StringSetting if a value is present and the value matches the given predicate, otherwise an empty StringSetting.
078    */
079   @Override
080   public StringSetting filter(Predicate<? super String> predicate) {
081      assertArgNotNull("predicate", predicate);
082      return new StringSetting(getSettings(), () -> {
083         String value = get();
084         return (nn(value) && predicate.test(value)) ? value : null;
085      });
086   }
087
088   /**
089    * Converts the string value to an Integer.
090    *
091    * <p>
092    * The property value is parsed using {@link Integer#valueOf(String)}. If the property is not found
093    * or cannot be parsed as an integer, returns {@link Optional#empty()}.
094    *
095    * @return The property value as an Integer, or {@link Optional#empty()} if not found or not a valid integer.
096    */
097   public Setting<Integer> asInteger() {
098      return map(v -> safeOrNull(() -> Integer.valueOf(v))).filter(Objects::nonNull);
099   }
100
101   /**
102    * Converts the string value to a Long.
103    *
104    * <p>
105    * The property value is parsed using {@link Long#valueOf(String)}. If the property is not found
106    * or cannot be parsed as a long, returns {@link Optional#empty()}.
107    *
108    * @return The property value as a Long, or {@link Optional#empty()} if not found or not a valid long.
109    */
110   public Setting<Long> asLong() {
111      return map(v -> safeOrNull(() -> Long.valueOf(v))).filter(Objects::nonNull);
112   }
113
114   /**
115    * Converts the string value to a Boolean.
116    *
117    * <p>
118    * The property value is parsed using {@link Boolean#parseBoolean(String)}, which returns <c>true</c>
119    * if the value is (case-insensitive) "true", otherwise <c>false</c>. Note that this method will
120    * return <c>Optional.of(false)</c> for any non-empty value that is not "true", and
121    * {@link Optional#empty()} only if the property is not set.
122    *
123    * @return The property value as a Boolean, or {@link Optional#empty()} if not found.
124    */
125   public Setting<Boolean> asBoolean() {
126      return map(v -> Boolean.parseBoolean(v));
127   }
128
129   /**
130    * Converts the string value to a Double.
131    *
132    * <p>
133    * The property value is parsed using {@link Double#valueOf(String)}. If the property is not found
134    * or cannot be parsed as a double, returns {@link Optional#empty()}.
135    *
136    * @return The property value as a Double, or {@link Optional#empty()} if not found or not a valid double.
137    */
138   public Setting<Double> asDouble() {
139      return map(v -> safeOrNull(() -> Double.valueOf(v))).filter(Objects::nonNull);
140   }
141
142   /**
143    * Converts the string value to a Float.
144    *
145    * <p>
146    * The property value is parsed using {@link Float#valueOf(String)}. If the property is not found
147    * or cannot be parsed as a float, returns {@link Optional#empty()}.
148    *
149    * @return The property value as a Float, or {@link Optional#empty()} if not found or not a valid float.
150    */
151   public Setting<Float> asFloat() {
152      return map(v -> safeOrNull(() -> Float.valueOf(v))).filter(Objects::nonNull);
153   }
154
155   /**
156    * Converts the string value to a File.
157    *
158    * <p>
159    * The property value is converted to a {@link File} using the {@link File#File(String)} constructor.
160    * If the property is not found, returns {@link Optional#empty()}. Note that this method does not
161    * validate that the file path is valid or that the file exists.
162    *
163    * @return The property value as a File, or {@link Optional#empty()} if not found.
164    */
165   public Setting<File> asFile() {
166      return map(v -> new File(v));
167   }
168
169   /**
170    * Converts the string value to a Path.
171    *
172    * <p>
173    * The property value is converted to a {@link Path} using {@link Paths#get(String, String...)}.
174    * If the property is not found or the path string is invalid, returns {@link Optional#empty()}.
175    *
176    * @return The property value as a Path, or {@link Optional#empty()} if not found or not a valid path.
177    */
178   public Setting<Path> asPath() {
179      return map(v -> safeOrNull(() -> Paths.get(v))).filter(Objects::nonNull);
180   }
181
182   /**
183    * Converts the string value to a URI.
184    *
185    * <p>
186    * The property value is converted to a {@link URI} using {@link URI#create(String)}.
187    * If the property is not found or the URI string is invalid, returns {@link Optional#empty()}.
188    *
189    * @return The property value as a URI, or {@link Optional#empty()} if not found or not a valid URI.
190    */
191   public Setting<URI> asURI() {
192      return map(v -> safeOrNull(() -> URI.create(v))).filter(Objects::nonNull);
193   }
194
195   /**
196    * Converts the string value to a Charset.
197    *
198    * <p>
199    * The property value is converted to a {@link Charset} using {@link Charset#forName(String)}.
200    * If the property is not found or the charset name is not supported, returns {@link Optional#empty()}.
201    *
202    * @return The property value as a Charset, or {@link Optional#empty()} if not found or not a valid charset.
203    */
204   public Setting<Charset> asCharset() {
205      return map(v -> safeOrNull(() -> Charset.forName(v))).filter(Objects::nonNull);
206   }
207
208   /**
209    * Converts the string value to the specified type using the Settings type conversion functions.
210    *
211    * <p>
212    * The property value is converted using {@link Settings#toType(String, Class)}. If the property is not found
213    * or cannot be converted to the specified type, returns {@link Optional#empty()}.
214    *
215    * @param <T> The target type.
216    * @param c The target class. Must not be <jk>null</jk>.
217    * @return The property value as the specified type, or {@link Optional#empty()} if not found or not a valid conversion.
218    */
219   public <T> Setting<T> asType(Class<T> c) {
220      assertArgNotNull("c", c);
221      return map(v -> getSettings().toType(v, c)).filter(Objects::nonNull);
222   }
223}