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.reflect.ReflectionUtils.*;
020import static org.apache.juneau.commons.utils.AssertionUtils.*;
021import static org.apache.juneau.commons.utils.ThrowableUtils.*;
022import static org.apache.juneau.commons.utils.Utils.*;
023
024import java.nio.charset.*;
025import java.util.*;
026import java.util.concurrent.*;
027import java.util.function.*;
028
029import org.apache.juneau.commons.function.*;
030import org.apache.juneau.commons.reflect.*;
031
032/**
033 * Encapsulates Java system properties with support for global and per-thread overrides for unit testing.
034 *
035 * <p>
036 * This class provides a thread-safe way to access system properties that can be overridden at both the global level
037 * and per-thread level, making it useful for unit tests that need to temporarily change system property
038 * values without affecting other tests or threads.
039 *
040 * <p>
041 * Settings instances are created using the {@link Builder} pattern. Use {@link #create()} to create a new builder,
042 * or {@link #get()} to get the singleton instance (which is created using default builder settings).
043 *
044 * <h5 class='section'>Lookup Order:</h5>
045 * <p>
046 * When retrieving a property value, the lookup order is:
047 * <ol>
048 *    <li>Per-thread store (if set via {@link #setLocal(String, String)})
049 *    <li>Global store (if set via {@link #setGlobal(String, String)})
050 *    <li>Sources in reverse order (last source added via {@link Builder#addSource(SettingSource)} is checked first)
051 *    <li>System property source (default, always second-to-last)
052 *    <li>System environment variable source (default, always last)
053 * </ol>
054 *
055 * <h5 class='section'>Sources vs Stores:</h5>
056 * <ul class='spaced-list'>
057 *    <li><b>Sources</b> ({@link SettingSource}) - Provide read-only access to property values. Examples: {@link FunctionalSource}
058 *    <li><b>Stores</b> ({@link SettingStore}) - Provide read/write access to property values. Examples: {@link MapStore}, {@link FunctionalStore}
059 *    <li>Stores can be used as sources (they extend {@link SettingSource}), so you can add stores via {@link Builder#addSource(SettingSource)}
060 * </ul>
061 *
062 * <h5 class='section'>Features:</h5>
063 * <ul class='spaced-list'>
064 *    <li>System property access - read Java system properties with type conversion via {@link StringSetting}
065 *    <li>Global overrides - override system properties globally for all threads (stored in a {@link SettingStore})
066 *    <li>Per-thread overrides - override system properties for specific threads (stored in a per-thread {@link SettingStore})
067 *    <li>Custom sources - add arbitrary property sources (e.g., Spring properties, environment variables, config files) via the {@link Builder}
068 *    <li>Disable override support - system property to prevent new global overrides from being set
069 *    <li>Type-safe accessors - type conversion methods on {@link StringSetting} for common types: Integer, Long, Boolean, Double, Float, File, Path, URI, Charset
070 *    <li>Resettable suppliers - settings are returned as {@link StringSetting} instances that can be reset to force recomputation
071 * </ul>
072 *
073 * <h5 class='section'>Usage Examples:</h5>
074 * <p class='bjava'>
075 *    <jc>// Get a system property as a StringSetting (using singleton instance)</jc>
076 *    StringSetting <jv>setting</jv> = Settings.<jsf>get</jsf>().get(<js>"my.property"</js>);
077 *    String <jv>value</jv> = <jv>setting</jv>.get();  <jc>// Get the string value</jc>
078 *
079 *    <jc>// Get with type conversion using StringSetting methods</jc>
080 *    Setting&lt;Integer&gt; <jv>intSetting</jv> = Settings.<jsf>get</jsf>().get(<js>"my.int.property"</js>).asInteger();
081 *    Setting&lt;Long&gt; <jv>longSetting</jv> = Settings.<jsf>get</jsf>().get(<js>"my.long.property"</js>).asLong();
082 *    Setting&lt;Boolean&gt; <jv>boolSetting</jv> = Settings.<jsf>get</jsf>().get(<js>"my.bool.property"</js>).asBoolean();
083 *    Setting&lt;Double&gt; <jv>doubleSetting</jv> = Settings.<jsf>get</jsf>().get(<js>"my.double.property"</js>).asDouble();
084 *    Setting&lt;Float&gt; <jv>floatSetting</jv> = Settings.<jsf>get</jsf>().get(<js>"my.float.property"</js>).asFloat();
085 *    Setting&lt;File&gt; <jv>fileSetting</jv> = Settings.<jsf>get</jsf>().get(<js>"my.file.property"</js>).asFile();
086 *    Setting&lt;Path&gt; <jv>pathSetting</jv> = Settings.<jsf>get</jsf>().get(<js>"my.path.property"</js>).asPath();
087 *    Setting&lt;URI&gt; <jv>uriSetting</jv> = Settings.<jsf>get</jsf>().get(<js>"my.uri.property"</js>).asURI();
088 *    Setting&lt;Charset&gt; <jv>charsetSetting</jv> = Settings.<jsf>get</jsf>().get(<js>"my.charset.property"</js>).asCharset();
089 *
090 *    <jc>// Use custom type conversion</jc>
091 *    Setting&lt;MyCustomType&gt; <jv>customSetting</jv> = Settings.<jsf>get</jsf>().get(<js>"my.custom.property"</js>).asType(MyCustomType.<jk>class</jk>);
092 *
093 *    <jc>// Reset a setting to force recomputation</jc>
094 *    <jv>setting</jv>.reset();
095 *
096 *    <jc>// Override for current thread (useful in unit tests)</jc>
097 *    Settings.<jsf>get</jsf>().setLocal(<js>"my.property"</js>, <js>"test-value"</js>);
098 *    <jc>// ... test code that uses the override ...</jc>
099 *    Settings.<jsf>get</jsf>().unsetLocal(<js>"my.property"</js>);  <jc>// Remove specific override</jc>
100 *    <jc>// OR</jc>
101 *    Settings.<jsf>get</jsf>().clearLocal();  <jc>// Clear all thread-local overrides</jc>
102 *
103 *    <jc>// Set global override (applies to all threads)</jc>
104 *    Settings.<jsf>get</jsf>().setGlobal(<js>"my.property"</js>, <js>"global-value"</js>);
105 *    <jc>// ... code that uses the global override ...</jc>
106 *    Settings.<jsf>get</jsf>().unsetGlobal(<js>"my.property"</js>);  <jc>// Remove specific override</jc>
107 *    <jc>// OR</jc>
108 *    Settings.<jsf>get</jsf>().clearGlobal();  <jc>// Clear all global overrides</jc>
109 *
110 *    <jc>// Create a custom Settings instance with custom sources (e.g., Spring properties)</jc>
111 *    MapStore <jv>springSource</jv> = <jk>new</jk> MapStore();
112 *    <jv>springSource</jv>.set(<js>"spring.datasource.url"</js>, <js>"jdbc:postgresql://localhost/db"</js>);
113 *    Settings <jv>custom</jv> = Settings.<jsf>create</jsf>()
114 *       .addSource(<jv>springSource</jv>)
115 *       .addSource(FunctionalSource.<jsf>of</jsf>(System::getProperty))
116 *       .build();
117 * </p>
118 *
119 * <h5 class='section'>System Properties:</h5>
120 * <ul class='spaced-list'>
121 *    <li><c>juneau.settings.disableGlobal</c> (system property) or <c>JUNEAU_SETTINGS_DISABLEGLOBAL</c> (system env) - If set to <c>true</c>, prevents new global overrides
122 *       from being set via {@link #setGlobal(String, String)}. Existing global overrides will still be
123 *       returned by {@link #get(String)} until explicitly removed.
124 *       <p class='bnote'>
125 *          Note: This property is read once at class initialization time when creating the singleton instance
126 *          and cannot be changed at runtime. Changing the system property after the class has been loaded will
127 *          have no effect on the singleton instance. However, you can create custom Settings instances using
128 *          {@link #create()} that ignore this property.
129 *       </p>
130 * </ul>
131 */
132public class Settings {
133
134   /**
135    * System property source that delegates to {@link System#getProperty(String)}.
136    */
137   public static final SettingSource SYSTEM_PROPERTY_SOURCE = FunctionalSource.of(System::getProperty);
138
139   private static final Set<String> FROM_STRING_METHOD_NAMES = new LinkedHashSet<>(Arrays.asList("fromString", "parse", "forName", "valueOf"));
140
141   /**
142    * System environment variable source that delegates to {@link System#getenv(String)}.
143    */
144   public static final SettingSource SYSTEM_ENV_SOURCE = FunctionalSource.of(System::getenv);
145
146   private static final String DISABLE_GLOBAL_PROP = "juneau.settings.disableGlobal";
147   private static final String MSG_globalDisabled = "Global settings not enabled";
148   private static final String MSG_localDisabled = "Local settings not enabled";
149
150   /**
151    * Returns properties for this Settings object itself.
152    * Note that these are initialized at startup and not changeable through System.setProperty().
153    */
154   private static final Optional<String> initProperty(String property) {
155      var v = SYSTEM_PROPERTY_SOURCE.get(property);
156      if (v != null)
157         return v;  // Not testable
158      v = SYSTEM_ENV_SOURCE.get(property.replace('.', '_').toUpperCase());
159      if (v != null)
160         return v;  // Not testable
161      return opte();
162   }
163
164   /**
165    * Creates a new builder for constructing a Settings instance.
166    *
167    * <p>
168    * This method provides a convenient way to create custom Settings instances with specific
169    * configuration. The builder allows you to configure stores, sources, and other settings
170    * before building the final Settings instance.
171    *
172    * <h5 class='section'>Example:</h5>
173    * <p class='bjava'>
174    *    <jc>// Create a custom Settings instance</jc>
175    *    Settings <jv>custom</jv> = Settings.<jsf>create</jsf>()
176    *       .globalStore(() -&gt; <jk>new</jk> MapStore())
177    *       .localStore(() -&gt; <jk>new</jk> MapStore())
178    *       .addSource(FunctionalSource.<jsf>of</jsf>(System::getProperty))
179    *       .build();
180    * </p>
181    *
182    * @return A new Builder instance.
183    */
184   public static Builder create() {
185      return new Builder();
186   }
187
188   /**
189    * Builder for creating Settings instances.
190    */
191   public static class Builder {
192      private Supplier<SettingStore> globalStoreSupplier = () -> new MapStore();
193      private Supplier<SettingStore> localStoreSupplier = () -> new MapStore();
194      private final List<SettingSource> sources = new ArrayList<>();
195      private final Map<Class<?>,Function<String,?>> customTypeFunctions = new IdentityHashMap<>();
196
197      /**
198       * Sets the supplier for the global store.
199       *
200       * @param supplier The supplier for the global store.  Must not be <c>null</c>.  Can supply null to disable global store.
201       * @return This builder for method chaining.
202       */
203      public Builder globalStore(OptionalSupplier<SettingStore> supplier) {
204         this.globalStoreSupplier = assertArgNotNull("supplier", supplier);
205         return this;
206      }
207
208      /**
209       * Sets the supplier for the local (per-thread) store.
210       *
211       * @param supplier The supplier for the local store. Must not be <c>null</c>.
212       * @return This builder for method chaining.
213       */
214      public Builder localStore(OptionalSupplier<SettingStore> supplier) {
215         this.localStoreSupplier = assertArgNotNull("supplier", supplier);
216         return this;
217      }
218
219      /**
220       * Sets the sources list, replacing any existing sources.
221       *
222       * @param sources The sources to set. Must not be <c>null</c> or contain <c>null</c> elements.
223       * @return This builder for method chaining.
224       */
225      @SafeVarargs
226      public final Builder setSources(SettingSource...sources) {
227         assertArgNoNulls("sources", sources);
228         this.sources.clear();
229         for (var source : sources) {
230            this.sources.add(source);
231         }
232         return this;
233      }
234
235      /**
236       * Adds a source to the sources list.
237       *
238       * @param source The source to add. Must not be <c>null</c>.
239       * @return This builder for method chaining.
240       */
241      public Builder addSource(SettingSource source) {
242         assertArgNotNull("source", source);
243         this.sources.add(source);
244         return this;
245      }
246
247      /**
248       * Adds a functional source to the sources list.
249       *
250       * @param source The functional source to add. Must not be <c>null</c>.
251       * @return This builder for method chaining.
252       */
253      public Builder addSource(FunctionalSource source) {
254         return addSource((SettingSource)source);
255      }
256
257      /**
258       * Registers a custom type conversion function for the specified type.
259       *
260       * <p>
261       * This allows you to add support for converting string values to custom types when using
262       * {@link Settings#get(String, Object)}. The function will be used to convert string values
263       * to the specified type.
264       *
265       * <h5 class='section'>Example:</h5>
266       * <p class='bjava'>
267       *    <jc>// Register a custom converter for a custom type</jc>
268       *    Settings <jv>custom</jv> = Settings.<jsf>create</jsf>()
269       *       .addTypeFunction(Integer.<jk>class</jk>, Integer::valueOf)
270       *       .addTypeFunction(MyCustomType.<jk>class</jk>, MyCustomType::fromString)
271       *       .build();
272       *
273       *    <jc>// Now you can use get() with these types</jc>
274       *    Integer <jv>intValue</jv> = <jv>custom</jv>.get(<js>"my.int.property"</js>, 0);
275       *    MyCustomType <jv>customValue</jv> = <jv>custom</jv>.get(<js>"my.custom.property"</js>, MyCustomType.DEFAULT);
276       * </p>
277       *
278       * @param <T> The type to register a converter for.
279       * @param type The class type to register a converter for. Must not be <c>null</c>.
280       * @param function The function that converts a string to the specified type. Must not be <c>null</c>.
281       * @return This builder for method chaining.
282       */
283      public <T> Builder addTypeFunction(Class<T> type, Function<String,T> function) {
284         assertArgNotNull("type", type);
285         assertArgNotNull("function", function);
286         customTypeFunctions.put(type, function);
287         return this;
288      }
289
290      /**
291       * Builds a Settings instance from this builder.
292       *
293       * @return A new Settings instance.
294       */
295      public Settings build() {
296         return new Settings(this);
297      }
298   }
299
300   private static final Settings INSTANCE = new Builder()
301      .globalStore(initProperty(DISABLE_GLOBAL_PROP).map(Boolean::valueOf).orElse(false) ? () -> null : () -> new MapStore())
302      .setSources(SYSTEM_ENV_SOURCE, SYSTEM_PROPERTY_SOURCE)
303      .build();
304
305   /**
306    * Returns the singleton instance of Settings.
307    *
308    * @return The singleton Settings instance.
309    */
310   public static Settings get() {
311      return INSTANCE;
312   }
313
314   private final ResettableSupplier<SettingStore> globalStore;
315   private final ThreadLocal<SettingStore> localStore;
316   private final List<SettingSource> sources;
317   private final Map<Class<?>,Function<String,?>> toTypeFunctions;
318
319   /**
320    * Constructor.
321    */
322   private Settings(Builder builder) {
323      this.globalStore = memr(builder.globalStoreSupplier);
324      this.localStore = ThreadLocal.withInitial(builder.localStoreSupplier);
325      this.sources = new CopyOnWriteArrayList<>(builder.sources);
326      this.toTypeFunctions = new ConcurrentHashMap<>(builder.customTypeFunctions);
327   }
328
329   /**
330    * Returns a {@link StringSetting} for the specified system property.
331    *
332    * <p>
333    * The returned {@link StringSetting} is a resettable supplier that caches the lookup result.
334    * Use the {@link StringSetting#asInteger()}, {@link StringSetting#asBoolean()}, etc. methods
335    * to convert to different types.
336    *
337    * <p>
338    * The lookup order is:
339    * <ol>
340    *    <li>Per-thread override (if set via {@link #setLocal(String, String)})
341    *    <li>Global override (if set via {@link #setGlobal(String, String)})
342    *    <li>Sources in reverse order (last source added via {@link Builder#addSource(SettingSource)} is checked first)
343    *    <li>System property source (default, always second-to-last)
344    *    <li>System environment variable source (default, always last)
345    * </ol>
346    *
347    * @param name The property name. Must not be <jk>null</jk>.
348    * @return A {@link StringSetting} that provides the property value, or <jk>null</jk> if not found.
349    */
350   public StringSetting get(String name) {
351      assertArgNotNull("name", name);
352      return new StringSetting(this, () -> {
353         // 1. Check thread-local override
354         var v = localStore.get().get(name);
355         if (v != null)
356            return v.orElse(null); // v is Optional.empty() if key exists with null value, or Optional.of(value) if present
357
358         // 2. Check global override
359         v = globalStore.get().get(name);
360         if (v != null)
361            return v.orElse(null); // v is Optional.empty() if key exists with null value, or Optional.of(value) if present
362
363         // 3. Check sources in reverse order (last added first)
364         for (int i = sources.size() - 1; i >= 0; i--) {
365            var source = sources.get(i);
366            var result = source.get(name);
367            if (result != null)
368               return result.orElse(null);
369         }
370
371         return null;
372      });
373   }
374
375   /**
376    * Looks up a system property, returning a default value if not found.
377    *
378    * <p>
379    * This method searches for a value using the same lookup order as {@link #get(String)}.
380    * If a value is found, it is converted to the type of the default value using {@link #toType(String, Class)}.
381    * Supported types include any type that has a static method with signature <c>public static &lt;T&gt; T anyName(String arg)</c>
382    * or a public constructor with signature <c>public T(String arg)</c>, such as {@link Boolean}, {@link Integer}, {@link java.nio.charset.Charset}, {@link java.io.File}, etc.
383    *
384    * <h5 class='section'>Example:</h5>
385    * <p class='bjava'>
386    *    <jc>// System property: -Dmy.property=true</jc>
387    *    Boolean <jv>flag</jv> = get(<js>"my.property"</js>, <jk>false</jk>);  <jc>// true</jc>
388    *
389    *    <jc>// Environment variable: MY_PROPERTY=UTF-8</jc>
390    *    Charset <jv>charset</jv> = get(<js>"my.property"</js>, Charset.defaultCharset());  <jc>// UTF-8</jc>
391    *
392    *    <jc>// Not found, returns default</jc>
393    *    String <jv>value</jv> = get(<js>"nonexistent"</js>, <js>"default"</js>);  <jc>// "default"</jc>
394    * </p>
395    *
396    * @param <T> The type to convert the value to.
397    * @param name The property name.
398    * @param def The default value to return if not found.
399    * @return The found value (converted to type T), or the default value if not found.
400    * @see #get(String)
401    * @see #toType(String, Class)
402    */
403   @SuppressWarnings("unchecked")
404   public <T> T get(String name, T def) {
405      assertArgNotNull("def", def);
406      return get(name).asType((Class<T>)def.getClass()).orElse(def);
407   }
408
409   /**
410    * Sets a global override for the specified property.
411    *
412    * <p>
413    * This override will apply to all threads and takes precedence over system properties.
414    * However, per-thread overrides (set via {@link #setLocal(String, String)}) will still take precedence
415    * over global overrides.
416    *
417    * <p>
418    * If the <c>juneau.settings.disableGlobal</c> system property is set to <c>true</c>,
419    * this method will throw an exception. This allows system administrators
420    * to prevent applications from overriding system properties globally.
421    *
422    * <p>
423    * Setting a value to <c>null</c> will store an empty optional, effectively overriding the system
424    * property to return empty. Use {@link #unsetGlobal(String)} to completely remove the override.
425    *
426    * @param name The property name.
427    * @param value The override value, or <c>null</c> to set an empty override.
428    * @return This object for method chaining.
429    * @see #unsetGlobal(String)
430    * @see #clearGlobal()
431    */
432   public Settings setGlobal(String name, String value) {
433      assertArgNotNull("name", name);
434      globalStore.orElseThrow(()->illegalState(MSG_globalDisabled)).set(name, value);
435      return this;
436   }
437
438   /**
439    * Removes a global override for the specified property.
440    *
441    * <p>
442    * After calling this method, the property will fall back to the system property value
443    * (or per-thread override if one exists).
444    *
445    * @param name The property name.
446    * @see #setGlobal(String, String)
447    * @see #clearGlobal()
448    */
449   public void unsetGlobal(String name) {
450      assertArgNotNull("name", name);
451      globalStore.orElseThrow(()->illegalState(MSG_globalDisabled)).unset(name);
452   }
453
454   /**
455    * Sets a per-thread override for the specified property.
456    *
457    * <p>
458    * This override will only apply to the current thread and takes precedence over global overrides
459    * and system properties. This is particularly useful in unit tests where you need to temporarily
460    * change a system property value without affecting other threads or tests.
461    *
462    * <p>
463    * Setting a value to <c>null</c> will store an empty optional, effectively overriding the property
464    * to return empty for this thread. Use {@link #unsetLocal(String)} to completely remove the override.
465    *
466    * @param name The property name.
467    * @param value The override value, or <c>null</c> to set an empty override.
468    * @return This object for method chaining.
469    * @see #unsetLocal(String)
470    * @see #clearLocal()
471    */
472   public Settings setLocal(String name, String value) {
473      assertArgNotNull("name", name);
474      assertState(nn(localStore.get()), MSG_localDisabled);
475      localStore.get().set(name, value);
476      return this;
477   }
478
479   /**
480    * Removes a per-thread override for the specified property.
481    *
482    * <p>
483    * After calling this method, the property will fall back to the global override (if set)
484    * or the system property value for the current thread.
485    *
486    * @param name The property name.
487    * @see #setLocal(String, String)
488    * @see #clearLocal()
489    */
490   public void unsetLocal(String name) {
491      assertArgNotNull("name", name);
492      assertState(nn(localStore.get()), MSG_localDisabled);
493      localStore.get().unset(name);
494   }
495
496   /**
497    * Clears all per-thread overrides for the current thread.
498    *
499    * <p>
500    * After calling this method, all properties will fall back to global overrides (if set)
501    * or system property values for the current thread.
502    *
503    * <p>
504    * This is typically called in a <c>@AfterEach</c> or <c>@After</c> test method to clean up
505    * thread-local overrides after a test completes.
506    *
507    * @return This object for method chaining.
508    * @see #setLocal(String, String)
509    * @see #unsetLocal(String)
510    */
511   public Settings clearLocal() {
512      assertState(nn(localStore.get()), MSG_localDisabled);
513      localStore.get().clear();
514      return this;
515   }
516
517   /**
518    * Clears all global overrides.
519    *
520    * <p>
521    * After calling this method, all properties will fall back to resolver values
522    * (or per-thread overrides if they exist).
523    *
524    * @return This object for method chaining.
525    * @see #setGlobal(String, String)
526    * @see #unsetGlobal(String)
527    */
528   public Settings clearGlobal() {
529      globalStore.orElseThrow(()->illegalState(MSG_globalDisabled)).clear();
530      return this;
531   }
532
533   /**
534    * Converts a string to the specified type using reflection to find conversion methods or constructors.
535    *
536    * <p>
537    * This method attempts to convert a string to the specified type using the following lookup order:
538    * <ol>
539    *    <li>Custom type functions registered via {@link Builder#addTypeFunction(Class, Function)}</li>
540    *    <li>Special handling for {@link String} (returns the string as-is)</li>
541    *    <li>Special handling for {@link Enum} types (uses {@link Enum#valueOf(Class, String)})</li>
542    *    <li>Reflection lookup for static methods with signature <c>public static &lt;T&gt; T anyName(String arg)</c>
543    *       on the target class</li>
544    *    <li>Reflection lookup for static methods on the superclass (for abstract classes like {@link Charset}
545    *       where concrete implementations need to use the abstract class's static method)</li>
546    *    <li>Reflection lookup for public constructors with signature <c>public T(String arg)</c></li>
547    * </ol>
548    *
549    * <p>
550    * When a conversion method or constructor is found via reflection, it is cached in the
551    * <c>toTypeFunctions</c> map for future use.
552    *
553    * <h5 class='section'>Examples:</h5>
554    * <ul class='spaced-list'>
555    *    <li><c>Boolean</c> - Uses <c>Boolean.valueOf(String)</c> static method
556    *    <li><c>Integer</c> - Uses <c>Integer.valueOf(String)</c> static method
557    *    <li><c>Charset</c> - Uses <c>Charset.forName(String)</c> static method (even for concrete implementations)
558    *    <li><c>File</c> - Uses <c>File(String)</c> constructor
559    * </ul>
560    *
561    * @param <T> The target type.
562    * @param s The string to convert. Must not be <jk>null</jk>.
563    * @param c The target class. Must not be <jk>null</jk>.
564    * @return The converted value.
565    * @throws RuntimeException If the type is not supported for conversion (no static method or constructor found).
566    */
567   @SuppressWarnings({ "unchecked", "rawtypes" })
568   protected <T> T toType(String s, Class<T> c) {
569      assertArgNotNull("s", s);
570      assertArgNotNull("c", c);
571      var f = (Function<String,T>)toTypeFunctions.get(c);
572      if (f == null) {
573         if (c == String.class)
574            return (T)s;
575         if (c.isEnum())
576            return (T)Enum.valueOf((Class<? extends Enum>)c, s);
577         ClassInfoTyped<T> ci = info(c);
578         f = ci.getDeclaredMethod(x -> x.isStatic() && x.hasParameterTypes(String.class) && x.hasReturnType(c) && FROM_STRING_METHOD_NAMES.contains(x.getName())).map(x -> (Function<String,T>)s2 -> x.invoke(null, s2)).orElse(null);
579         if (f == null)
580            f = ci.getPublicConstructor(x -> x.hasParameterTypes(String.class)).map(x -> (Function<String,T>)s2 -> x.newInstance(s2)).orElse(null);
581         if (f != null)
582            toTypeFunctions.putIfAbsent(c, f);
583      }
584      if (f == null)
585         throw rex("Invalid env type: {0}", c);
586      return f.apply(s);
587   }
588}
589