    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 */
    017
    018package org.apache.commons.text.lookup;
    019
    020import java.nio.charset.StandardCharsets;
    021import java.util.Base64;
    022import java.util.Collections;
    023import java.util.HashMap;
    024import java.util.Locale;
    025import java.util.Map;
    026import java.util.Properties;
    027import java.util.function.BiFunction;
    028import java.util.function.Function;
    029
    030import javax.xml.xpath.XPathFactory;
    031
    032import org.apache.commons.text.StringSubstitutor;
    033
    034/**
    035 * Create instances of string lookups or access singleton string lookups implemented in this package.
    036 * <p>
    037 * The "classic" look up is {@link #mapStringLookup(Map)}.
    038 * </p>
    039 * <p>
    040 * The methods for variable interpolation (A.K.A. variable substitution) are:
    041 * </p>
    042 * <ul>
    043 * <li>{@link #interpolatorStringLookup()}.</li>
    044 * <li>{@link #interpolatorStringLookup(Map)}.</li>
    045 * <li>{@link #interpolatorStringLookup(StringLookup)}.</li>
    046 * <li>{@link #interpolatorStringLookup(Map, StringLookup, boolean)}.</li>
    047 * </ul>
    048 * <p>
    049 * Unless explicitly requested otherwise, a set of default lookups are included for convenience with these
    050 * variable interpolation methods. These defaults are listed in the table below. However, the exact lookups
    051 * included can be configured through the use of the {@value #DEFAULT_STRING_LOOKUPS_PROPERTY} system property.
    052 * If present, this system property will be parsed as a comma-separated list of lookup names, with the names
    053 * being those defined by the {@link DefaultStringLookup} enum. For example, setting this system property to
    054 * {@code "BASE64_ENCODER,ENVIRONMENT"} will only include the
    055 * {@link DefaultStringLookup#BASE64_ENCODER BASE64_ENCODER} and {@link DefaultStringLookup#ENVIRONMENT ENVIRONMENT}
    056 * lookups. Setting the property to the empty string will cause no defaults to be configured.
    057 * Note that not all lookups defined here and in {@link DefaultStringLookup} are included by default.
    058 * Specifically, lookups that can execute code (e.g., {@link DefaultStringLookup#SCRIPT SCRIPT}) and those
    059 * that can result in contact with remote servers (e.g., {@link DefaultStringLookup#URL URL} and
    060 * {@link DefaultStringLookup#DNS DNS}) are not included by default. The current set of default lookups can
    061 * be accessed directly with {@link #addDefaultStringLookups(Map)}.
    062 * </p>
    063 * <table>
    064 * <caption>Default String Lookups</caption>
    065 * <tr>
    066 * <th>Key</th>
    067 * <th>Interface</th>
    068 * <th>Factory Method</th>
    069 * <th>Since</th>
    070 * </tr>
    071 * <tr>
    072 * <td>{@value #KEY_BASE64_DECODER}</td>
    073 * <td>{@link StringLookup}</td>
    074 * <td>{@link #base64DecoderStringLookup()}</td>
    075 * <td>1.6</td>
    076 * </tr>
    077 * <tr>
    078 * <td>{@value #KEY_BASE64_ENCODER}</td>
    079 * <td>{@link StringLookup}</td>
    080 * <td>{@link #base64EncoderStringLookup()}</td>
    081 * <td>1.6</td>
    082 * </tr>
    083 * <tr>
    084 * <td>{@value #KEY_CONST}</td>
    085 * <td>{@link StringLookup}</td>
    086 * <td>{@link #constantStringLookup()}</td>
    087 * <td>1.5</td>
    088 * </tr>
    089 * <tr>
    090 * <td>{@value #KEY_DATE}</td>
    091 * <td>{@link StringLookup}</td>
    092 * <td>{@link #dateStringLookup()}</td>
    093 * <td>1.5</td>
    094 * </tr>
    095 * <tr>
    096 * <td>{@value #KEY_ENV}</td>
    097 * <td>{@link StringLookup}</td>
    098 * <td>{@link #environmentVariableStringLookup()}</td>
    099 * <td>1.3</td>
    100 * </tr>
    101 * <tr>
    102 * <td>{@value #KEY_FILE}</td>
    103 * <td>{@link StringLookup}</td>
    104 * <td>{@link #fileStringLookup()}</td>
    105 * <td>1.5</td>
    106 * </tr>
    107 * <tr>
    108 * <td>{@value #KEY_JAVA}</td>
    109 * <td>{@link StringLookup}</td>
    110 * <td>{@link #javaPlatformStringLookup()}</td>
    111 * <td>1.5</td>
    112 * </tr>
    113 * <tr>
    114 * <td>{@value #KEY_LOCALHOST}</td>
    115 * <td>{@link StringLookup}</td>
    116 * <td>{@link #localHostStringLookup()}</td>
    117 * <td>1.3</td>
    118 * </tr>
    119 * <tr>
    120 * <td>{@value #KEY_PROPERTIES}</td>
    121 * <td>{@link StringLookup}</td>
    122 * <td>{@link #propertiesStringLookup()}</td>
    123 * <td>1.5</td>
    124 * </tr>
    125 * <tr>
    126 * <td>{@value #KEY_RESOURCE_BUNDLE}</td>
    127 * <td>{@link StringLookup}</td>
    128 * <td>{@link #resourceBundleStringLookup()}</td>
    129 * <td>1.6</td>
    130 * </tr>
    131 * <tr>
    132 * <td>{@value #KEY_SYS}</td>
    133 * <td>{@link StringLookup}</td>
    134 * <td>{@link #systemPropertyStringLookup()}</td>
    135 * <td>1.3</td>
    136 * </tr>
    137 * <tr>
    138 * <td>{@value #KEY_URL_DECODER}</td>
    139 * <td>{@link StringLookup}</td>
    140 * <td>{@link #urlDecoderStringLookup()}</td>
    141 * <td>1.5</td>
    142 * </tr>
    143 * <tr>
    144 * <td>{@value #KEY_URL_ENCODER}</td>
    145 * <td>{@link StringLookup}</td>
    146 * <td>{@link #urlEncoderStringLookup()}</td>
    147 * <td>1.5</td>
    148 * </tr>
    149 * <tr>
    150 * <td>{@value #KEY_XML}</td>
    151 * <td>{@link StringLookup}</td>
    152 * <td>{@link #xmlStringLookup()}</td>
    153 * <td>1.5</td>
    154 * </tr>
    155 * <tr>
    156 * <td>{@value #KEY_XML_DECODER}</td>
    157 * <td>{@link StringLookup}</td>
    158 * <td>{@link #xmlDecoderStringLookup()}</td>
    159 * <td>1.11.0</td>
    160 * </tr>
    161 * <tr>
    162 * <td>{@value #KEY_XML_ENCODER}</td>
    163 * <td>{@link StringLookup}</td>
    164 * <td>{@link #xmlEncoderStringLookup()}</td>
    165 * <td>1.11.0</td>
    166 * </tr>
    167 * </table>
    168 *
    169 * <table>
    170 * <caption>Additional String Lookups (not included by default)</caption>
    171 * <tr>
    172 * <th>Key</th>
    173 * <th>Interface</th>
    174 * <th>Factory Method</th>
    175 * <th>Since</th>
    176 * </tr>
    177 * <tr>
    178 * <td>{@value #KEY_DNS}</td>
    179 * <td>{@link StringLookup}</td>
    180 * <td>{@link #dnsStringLookup()}</td>
    181 * <td>1.8</td>
    182 * </tr>
    183 * <tr>
    184 * <td>{@value #KEY_URL}</td>
    185 * <td>{@link StringLookup}</td>
    186 * <td>{@link #urlStringLookup()}</td>
    187 * <td>1.5</td>
    188 * </tr>
    189 * <tr>
    190 * <td>{@value #KEY_SCRIPT}</td>
    191 * <td>{@link StringLookup}</td>
    192 * <td>{@link #scriptStringLookup()}</td>
    193 * <td>1.5</td>
    194 * </tr>
    195 * </table>
    196 *
    197 * <p>
    198 * This class also provides functional lookups used as building blocks for other lookups.
    199 * <table>
    200 * <caption>Functional String Lookups</caption>
    201 * <tr>
    202 * <th>Interface</th>
    203 * <th>Factory Method</th>
    204 * <th>Since</th>
    205 * </tr>
    206 * <tr>
    207 * <td>{@link BiStringLookup}</td>
    208 * <td>{@link #biFunctionStringLookup(BiFunction)}</td>
    209 * <td>1.9</td>
    210 * </tr>
    211 * <tr>
    212 * <td>{@link StringLookup}</td>
    213 * <td>{@link #functionStringLookup(Function)}</td>
    214 * <td>1.9</td>
    215 * </tr>
    216 * </table>
    217 *
    218 * @since 1.3
    219 */
    220public final class StringLookupFactory {
    221
    222    /**
    223     * Internal class used to construct the default {@link StringLookup} map used by
    224     * {@link StringLookupFactory#addDefaultStringLookups(Map)}.
    225     */
    226    static final class DefaultStringLookupsHolder {
    227
    228        /** Singleton instance, initialized with the system properties. */
    229        static final DefaultStringLookupsHolder INSTANCE = new DefaultStringLookupsHolder(System.getProperties());
    230
    231        /**
    232         * Add the key and string lookup from {@code lookup} to {@code map}, also adding any additional
    233         * key aliases if needed. Keys are normalized using the {@link #toKey(String)} method.
    234         * @param lookup lookup to add
    235         * @param map map to add to
    236         */
    237        private static void addLookup(final DefaultStringLookup lookup, final Map<String, StringLookup> map) {
    238            map.put(toKey(lookup.getKey()), lookup.getStringLookup());
    239
    240            if (DefaultStringLookup.BASE64_DECODER.equals(lookup)) {
    241                // "base64" is deprecated in favor of KEY_BASE64_DECODER.
    242                map.put(toKey("base64"), lookup.getStringLookup());
    243            }
    244        }
    245
    246        /**
    247         * Create the lookup map used when the user has requested no customization.
    248         * @return default lookup map
    249         */
    250        private static Map<String, StringLookup> createDefaultStringLookups() {
    251            final Map<String, StringLookup> lookupMap = new HashMap<>();
    252
    253            addLookup(DefaultStringLookup.BASE64_DECODER, lookupMap);
    254            addLookup(DefaultStringLookup.BASE64_ENCODER, lookupMap);
    255            addLookup(DefaultStringLookup.CONST, lookupMap);
    256            addLookup(DefaultStringLookup.DATE, lookupMap);
    257            addLookup(DefaultStringLookup.ENVIRONMENT, lookupMap);
    258            addLookup(DefaultStringLookup.FILE, lookupMap);
    259            addLookup(DefaultStringLookup.JAVA, lookupMap);
    260            addLookup(DefaultStringLookup.LOCAL_HOST, lookupMap);
    261            addLookup(DefaultStringLookup.PROPERTIES, lookupMap);
    262            addLookup(DefaultStringLookup.RESOURCE_BUNDLE, lookupMap);
    263            addLookup(DefaultStringLookup.SYSTEM_PROPERTIES, lookupMap);
    264            addLookup(DefaultStringLookup.URL_DECODER, lookupMap);
    265            addLookup(DefaultStringLookup.URL_ENCODER, lookupMap);
    266            addLookup(DefaultStringLookup.XML, lookupMap);
    267            addLookup(DefaultStringLookup.XML_DECODER, lookupMap);
    268            addLookup(DefaultStringLookup.XML_ENCODER, lookupMap);
    269
    270            return lookupMap;
    271        }
    272
    273        /**
    274         * Construct a lookup map by parsing the given string. The string is expected to contain
    275         * comma or space-separated names of values from the {@link DefaultStringLookup} enum. If
    276         * the given string is null or empty, an empty map is returned.
    277         * @param str string to parse; may be null or empty
    278         * @return lookup map parsed from the given string
    279         */
    280        private static Map<String, StringLookup> parseStringLookups(final String str) {
    281            final Map<String, StringLookup> lookupMap = new HashMap<>();
    282
    283            try {
    284                for (final String lookupName : str.split("[\\s,]+")) {
    285                    if (!lookupName.isEmpty()) {
    286                        addLookup(DefaultStringLookup.valueOf(lookupName.toUpperCase()), lookupMap);
    287                    }
    288                }
    289            } catch (IllegalArgumentException exc) {
    290                throw new IllegalArgumentException("Invalid default string lookups definition: " + str, exc);
    291            }
    292
    293            return lookupMap;
    294        }
    295
    296        /** Default string lookup map. */
    297        private final Map<String, StringLookup> defaultStringLookups;
    298
    299        /**
    300         * Construct a new instance initialized with the given properties.
    301         * @param props initialization properties
    302         */
    303        DefaultStringLookupsHolder(final Properties props) {
    304            final Map<String, StringLookup> lookups =
    305                    props.containsKey(StringLookupFactory.DEFAULT_STRING_LOOKUPS_PROPERTY)
    306                        ? parseStringLookups(props.getProperty(StringLookupFactory.DEFAULT_STRING_LOOKUPS_PROPERTY))
    307                        : createDefaultStringLookups();
    308
    309            defaultStringLookups = Collections.unmodifiableMap(lookups);
    310        }
    311
    312        /**
    313         * Get the default string lookups map.
    314         * @return default string lookups map
    315         */
    316        Map<String, StringLookup> getDefaultStringLookups() {
    317            return defaultStringLookups;
    318        }
    319    }
    320
    321    /**
    322     * Defines the singleton for this class.
    323     */
    324    public static final StringLookupFactory INSTANCE = new StringLookupFactory();
    325
    326    /**
    327     * Decodes Base64 Strings.
    328     * <p>
    329     * Using a {@link StringLookup} from the {@link StringLookupFactory}:
    330     * </p>
    331     *
    332     * <pre>
    333     * StringLookupFactory.INSTANCE.base64DecoderStringLookup().lookup("SGVsbG9Xb3JsZCE=");
    334     * </pre>
    335     * <p>
    336     * Using a {@link StringSubstitutor}:
    337     * </p>
    338     *
    339     * <pre>
    340     * StringSubstitutor.createInterpolator().replace("... ${base64Decoder:SGVsbG9Xb3JsZCE=} ..."));
    341     * </pre>
    342     * <p>
    343     * The above examples convert {@code "SGVsbG9Xb3JsZCE="} to {@code "HelloWorld!"}.
    344     * </p>
    345     */
    346    static final FunctionStringLookup<String> INSTANCE_BASE64_DECODER = FunctionStringLookup
    347        .on(key -> new String(Base64.getDecoder().decode(key), StandardCharsets.ISO_8859_1));
    348
    349    /**
    350     * Encodes Base64 Strings.
    351     * <p>
    352     * Using a {@link StringLookup} from the {@link StringLookupFactory}:
    353     * </p>
    354     *
    355     * <pre>
    356     * StringLookupFactory.INSTANCE.base64EncoderStringLookup().lookup("HelloWorld!");
    357     * </pre>
    358     * <p>
    359     * Using a {@link StringSubstitutor}:
    360     * </p>
    361     *
    362     * <pre>
    363     * StringSubstitutor.createInterpolator().replace("... ${base64Encoder:HelloWorld!} ..."));
    364     * </pre>
    365     * <p>
    366     * The above examples convert {@code "HelloWorld!"} to {@code "SGVsbG9Xb3JsZCE="}.
    367     * </p>
    368     * Defines the singleton for this class.
    369     */
    370    static final FunctionStringLookup<String> INSTANCE_BASE64_ENCODER = FunctionStringLookup
    371        .on(key -> Base64.getEncoder().encodeToString(key.getBytes(StandardCharsets.ISO_8859_1)));
    372
    373    /**
    374     * Looks up keys from environment variables.
    375     * <p>
    376     * Using a {@link StringLookup} from the {@link StringLookupFactory}:
    377     * </p>
    378     *
    379     * <pre>
    380     * StringLookupFactory.INSTANCE.environmentVariableStringLookup().lookup("USER");
    381     * </pre>
    382     * <p>
    383     * Using a {@link StringSubstitutor}:
    384     * </p>
    385     *
    386     * <pre>
    387     * StringSubstitutor.createInterpolator().replace("... ${env:USER} ..."));
    388     * </pre>
    389     * <p>
    390     * The above examples convert (on Linux) {@code "USER"} to the current user name. On Windows 10, you would use
    391     * {@code "USERNAME"} to the same effect.
    392     * </p>
    393     */
    394    static final FunctionStringLookup<String> INSTANCE_ENVIRONMENT_VARIABLES = FunctionStringLookup.on(System::getenv);
    395
    396    /**
    397     * Defines the FunctionStringLookup singleton that always returns null.
    398     */
    399    static final FunctionStringLookup<String> INSTANCE_NULL = FunctionStringLookup.on(key -> null);
    400
    401    /**
    402     * Defines the FunctionStringLookup singleton for looking up system properties.
    403     */
    404    static final FunctionStringLookup<String> INSTANCE_SYSTEM_PROPERTIES = FunctionStringLookup.on(System::getProperty);
    405
    406    /**
    407     * Default lookup key for interpolation {@value #KEY_BASE64_DECODER}.
    408     *
    409     * @since 1.6
    410     */
    411    public static final String KEY_BASE64_DECODER = "base64Decoder";
    412
    413    /**
    414     * Default lookup key for interpolation {@value #KEY_BASE64_ENCODER}.
    415     *
    416     * @since 1.6
    417     */
    418    public static final String KEY_BASE64_ENCODER = "base64Encoder";
    419
    420    /**
    421     * Default lookup key for interpolation {@value #KEY_CONST}.
    422     *
    423     * @since 1.6
    424     */
    425    public static final String KEY_CONST = "const";
    426
    427    /**
    428     * Default lookup key for interpolation {@value #KEY_DATE}.
    429     *
    430     * @since 1.6
    431     */
    432    public static final String KEY_DATE = "date";
    433
    434    /**
    435     * Default lookup key for interpolation {@value #KEY_DNS}.
    436     *
    437     * @since 1.8
    438     */
    439    public static final String KEY_DNS = "dns";
    440
    441    /**
    442     * Default lookup key for interpolation {@value #KEY_ENV}.
    443     *
    444     * @since 1.6
    445     */
    446    public static final String KEY_ENV = "env";
    447
    448    /**
    449     * Default lookup key for interpolation {@value #KEY_FILE}.
    450     *
    451     * @since 1.6
    452     */
    453    public static final String KEY_FILE = "file";
    454
    455    /**
    456     * Default lookup key for interpolation {@value #KEY_JAVA}.
    457     *
    458     * @since 1.6
    459     */
    460    public static final String KEY_JAVA = "java";
    461
    462    /**
    463     * Default lookup key for interpolation {@value #KEY_LOCALHOST}.
    464     *
    465     * @since 1.6
    466     */
    467    public static final String KEY_LOCALHOST = "localhost";
    468
    469    /**
    470     * Default lookup key for interpolation {@value #KEY_PROPERTIES}.
    471     *
    472     * @since 1.6
    473     */
    474    public static final String KEY_PROPERTIES = "properties";
    475
    476    /**
    477     * Default lookup key for interpolation {@value #KEY_RESOURCE_BUNDLE}.
    478     *
    479     * @since 1.6
    480     */
    481    public static final String KEY_RESOURCE_BUNDLE = "resourceBundle";
    482
    483    /**
    484     * Default lookup key for interpolation {@value #KEY_SCRIPT}.
    485     *
    486     * @since 1.6
    487     */
    488    public static final String KEY_SCRIPT = "script";
    489
    490    /**
    491     * Default lookup key for interpolation {@value #KEY_SYS}.
    492     *
    493     * @since 1.6
    494     */
    495    public static final String KEY_SYS = "sys";
    496
    497    /**
    498     * Default lookup key for interpolation {@value #KEY_URL}.
    499     *
    500     * @since 1.6
    501     */
    502    public static final String KEY_URL = "url";
    503
    504    /**
    505     * Default lookup key for interpolation {@value #KEY_URL_DECODER}.
    506     *
    507     * @since 1.6
    508     */
    509    public static final String KEY_URL_DECODER = "urlDecoder";
    510
    511    /**
    512     * Default lookup key for interpolation {@value #KEY_URL_ENCODER}.
    513     *
    514     * @since 1.6
    515     */
    516    public static final String KEY_URL_ENCODER = "urlEncoder";
    517
    518    /**
    519     * Default lookup key for interpolation {@value #KEY_XML}.
    520     *
    521     * @since 1.6
    522     */
    523    public static final String KEY_XML = "xml";
    524
    525    /**
    526     * Default lookup key for interpolation {@value #KEY_XML_DECODER}.
    527     *
    528     * @since 1.11.0
    529     */
    530    public static final String KEY_XML_DECODER = "xmlDecoder";
    531
    532    /**
    533     * Default lookup key for interpolation {@value #KEY_XML_ENCODER}.
    534     *
    535     * @since 1.11.0
    536     */
    537    public static final String KEY_XML_ENCODER = "xmlEncoder";
    538
    539    /**
    540     * Name of the system property used to determine the string lookups added by the
    541     * {@link #addDefaultStringLookups(Map)} method. Use of this property is only required
    542     * in cases where the set of default lookups must be modified. (See the class documentation
    543     * for details.)
    544     *
    545     * @since 1.10.0
    546     */
    547    public static final String DEFAULT_STRING_LOOKUPS_PROPERTY = "org.apache.commons.text.lookup.StringLookupFactory.defaultStringLookups";
    548
    549    /**
    550     * Clears any static resources.
    551     *
    552     * @since 1.5
    553     */
    554    public static void clear() {
    555        ConstantStringLookup.clear();
    556    }
    557
    558    /**
    559     * Get a string suitable for use as a key in the string lookup map.
    560     * @param key string to convert to a string lookup map key
    561     * @return string lookup map key
    562     */
    563    static String toKey(final String key) {
    564        return key.toLowerCase(Locale.ROOT);
    565    }
    566
    567    /**
    568     * Returns the given map if the input is non-null or an empty immutable map if the input is null.
    569     *
    570     * @param <K> the class of the map keys
    571     * @param <V> the class of the map values
    572     * @param map The map to test
    573     * @return the given map if the input is non-null or an empty immutable map if the input is null.
    574     */
    575    static <K, V> Map<K, V> toMap(final Map<K, V> map) {
    576        return map == null ? Collections.emptyMap() : map;
    577    }
    578
    579    /**
    580     * No need to build instances for now.
    581     */
    582    private StringLookupFactory() {
    583        // empty
    584    }
    585
    586    /**
    587     * Adds the default string lookups for this class to {@code stringLookupMap}. The default string
    588     * lookups are a set of built-in lookups added for convenience during string interpolation. The
    589     * defaults may be configured using the {@value #DEFAULT_STRING_LOOKUPS_PROPERTY} system property.
    590     * See the class documentation for details and a list of lookups.
    591     *
    592     * @param stringLookupMap the map of string lookups to edit.
    593     * @since 1.5
    594     */
    595    public void addDefaultStringLookups(final Map<String, StringLookup> stringLookupMap) {
    596        if (stringLookupMap != null) {
    597            stringLookupMap.putAll(DefaultStringLookupsHolder.INSTANCE.getDefaultStringLookups());
    598        }
    599    }
    600
    601    /**
    602     * Returns the Base64DecoderStringLookup singleton instance to decode Base64 strings.
    603     * <p>
    604     * Using a {@link StringLookup} from the {@link StringLookupFactory}:
    605     * </p>
    606     *
    607     * <pre>
    608     * StringLookupFactory.INSTANCE.base64DecoderStringLookup().lookup("SGVsbG9Xb3JsZCE=");
    609     * </pre>
    610     * <p>
    611     * Using a {@link StringSubstitutor}:
    612     * </p>
    613     *
    614     * <pre>
    615     * StringSubstitutor.createInterpolator().replace("... ${base64Decoder:SGVsbG9Xb3JsZCE=} ..."));
    616     * </pre>
    617     * <p>
    618     * The above examples convert {@code "SGVsbG9Xb3JsZCE="} to {@code "HelloWorld!"}.
    619     * </p>
    620     *
    621     * @return The Base64DecoderStringLookup singleton instance.
    622     * @since 1.5
    623     */
    624    public StringLookup base64DecoderStringLookup() {
    625        return StringLookupFactory.INSTANCE_BASE64_DECODER;
    626    }
    627
    628    /**
    629     * Returns the Base64EncoderStringLookup singleton instance to encode strings to Base64.
    630     * <p>
    631     * Using a {@link StringLookup} from the {@link StringLookupFactory}:
    632     * </p>
    633     *
    634     * <pre>
    635     * StringLookupFactory.INSTANCE.base64EncoderStringLookup().lookup("HelloWorld!");
    636     * </pre>
    637     * <p>
    638     * Using a {@link StringSubstitutor}:
    639     * </p>
    640     *
    641     * <pre>
    642     * StringSubstitutor.createInterpolator().replace("... ${base64Encoder:HelloWorld!} ..."));
    643     * </pre>
    644     * <p>
    645     * The above examples convert {@code } to {@code "SGVsbG9Xb3JsZCE="}.
    646     * </p>
    647     *
    648     * @return The Base64EncoderStringLookup singleton instance.
    649     * @since 1.6
    650     */
    651    public StringLookup base64EncoderStringLookup() {
    652        return StringLookupFactory.INSTANCE_BASE64_ENCODER;
    653    }
    654
    655    /**
    656     * Returns the Base64DecoderStringLookup singleton instance to decode Base64 strings.
    657     * <p>
    658     * Using a {@link StringLookup} from the {@link StringLookupFactory}:
    659     * </p>
    660     *
    661     * <pre>
    662     * StringLookupFactory.INSTANCE.base64DecoderStringLookup().lookup("SGVsbG9Xb3JsZCE=");
    663     * </pre>
    664     * <p>
    665     * Using a {@link StringSubstitutor}:
    666     * </p>
    667     *
    668     * <pre>
    669     * StringSubstitutor.createInterpolator().replace("... ${base64Decoder:SGVsbG9Xb3JsZCE=} ..."));
    670     * </pre>
    671     * <p>
    672     * The above examples convert {@code "SGVsbG9Xb3JsZCE="} to {@code "HelloWorld!"}.
    673     * </p>
    674     *
    675     * @return The Base64DecoderStringLookup singleton instance.
    676     * @since 1.5
    677     * @deprecated Use {@link #base64DecoderStringLookup()}.
    678     */
    679    @Deprecated
    680    public StringLookup base64StringLookup() {
    681        return StringLookupFactory.INSTANCE_BASE64_DECODER;
    682    }
    683
    684    /**
    685     * Returns a new function-based lookup where the request for a lookup is answered by applying the function with a
    686     * lookup key.
    687     *
    688     * @param <R> the function return type.
    689     * @param <U> the function's second parameter type.
    690     * @param biFunction the function.
    691     * @return a new MapStringLookup.
    692     * @since 1.9
    693     */
    694    public <R, U> BiStringLookup<U> biFunctionStringLookup(final BiFunction<String, U, R> biFunction) {
    695        return BiFunctionStringLookup.on(biFunction);
    696    }
    697
    698    /**
    699     * Returns the ConstantStringLookup singleton instance to look up the value of a fully-qualified static final value.
    700     * <p>
    701     * Sometimes it is necessary in a configuration file to refer to a constant defined in a class. This can be done
    702     * with this lookup implementation. Variable names must be in the format {@code apackage.AClass.AFIELD}. The
    703     * {@code lookup(String)} method will split the passed in string at the last dot, separating the fully qualified
    704     * class name and the name of the constant (i.e. <b>static final</b>) member field. Then the class is loaded and the
    705     * field's value is obtained using reflection.
    706     * </p>
    707     * <p>
    708     * Once retrieved values are cached for fast access. This class is thread-safe. It can be used as a standard (i.e.
    709     * global) lookup object and serve multiple clients concurrently.
    710     * </p>
    711     * <p>
    712     * Using a {@link StringLookup} from the {@link StringLookupFactory}:
    713     * </p>
    714     *
    715     * <pre>
    716     * StringLookupFactory.INSTANCE.constantStringLookup().lookup("java.awt.event.KeyEvent.VK_ESCAPE");
    717     * </pre>
    718     * <p>
    719     * Using a {@link StringSubstitutor}:
    720     * </p>
    721     *
    722     * <pre>
    723     * StringSubstitutor.createInterpolator().replace("... ${const:java.awt.event.KeyEvent.VK_ESCAPE} ..."));
    724     * </pre>
    725     * <p>
    726     * The above examples convert {@code java.awt.event.KeyEvent.VK_ESCAPE} to {@code "27"}.
    727     * </p>
    728     *
    729     * @return The ConstantStringLookup singleton instance.
    730     * @since 1.5
    731     */
    732    public StringLookup constantStringLookup() {
    733        return ConstantStringLookup.INSTANCE;
    734    }
    735
    736    /**
    737     * Returns the DateStringLookup singleton instance to format the current date with the format given in the key in a
    738     * format compatible with {@link java.text.SimpleDateFormat}.
    739     * <p>
    740     * Using a {@link StringLookup} from the {@link StringLookupFactory}:
    741     * </p>
    742     *
    743     * <pre>
    744     * StringLookupFactory.INSTANCE.dateStringLookup().lookup("yyyy-MM-dd");
    745     * </pre>
    746     * <p>
    747     * Using a {@link StringSubstitutor}:
    748     * </p>
    749     *
    750     * <pre>
    751     * StringSubstitutor.createInterpolator().replace("... ${date:yyyy-MM-dd} ..."));
    752     * </pre>
    753     * <p>
    754     * The above examples convert {@code "yyyy-MM-dd"} to todays's date, for example, {@code "2019-08-04"}.
    755     * </p>
    756     *
    757     * @return The DateStringLookup singleton instance.
    758     */
    759    public StringLookup dateStringLookup() {
    760        return DateStringLookup.INSTANCE;
    761    }
    762
    763    /**
    764     * Returns the DnsStringLookup singleton instance where the lookup key is one of:
    765     * <ul>
    766     * <li><b>name</b>: for the local host name, for example {@code EXAMPLE} but also {@code EXAMPLE.apache.org}.</li>
    767     * <li><b>canonical-name</b>: for the local canonical host name, for example {@code EXAMPLE.apache.org}.</li>
    768     * <li><b>address</b>: for the local host address, for example {@code 192.168.56.1}.</li>
    769     * </ul>
    770     *
    771     * <p>
    772     * Using a {@link StringLookup} from the {@link StringLookupFactory}:
    773     * </p>
    774     *
    775     * <pre>
    776     * StringLookupFactory.INSTANCE.dnsStringLookup().lookup("address|apache.org");
    777     * </pre>
    778     * <p>
    779     * When used through a {@link StringSubstitutor}, this lookup must either be added programmatically
    780     * (as below) or enabled as a default lookup using the {@value #DEFAULT_STRING_LOOKUPS_PROPERTY} system property
    781     * (see class documentation).
    782     * </p>
    783     *
    784     * <pre>
    785     * Map&lt;String, StringLookup&gt; lookupMap = new HashMap&lt;&gt;();
    786     * lookupMap.put("dns", StringLookupFactory.INSTANCE.dnsStringLookup());
    787     *
    788     * StringLookup variableResolver = StringLookupFactory.INSTANCE.interpolatorStringLookup(lookupMap, null, false);
    789     *
    790     * new StringSubstitutor(variableResolver).replace("... ${dns:address|apache.org} ...");
    791     * </pre>
    792     * <p>
    793     * The above examples convert {@code "address|apache.org"} to the IP address of {@code apache.org}.
    794     * </p>
    795     *
    796     * @return the DnsStringLookup singleton instance.
    797     * @since 1.8
    798     */
    799    public StringLookup dnsStringLookup() {
    800        return DnsStringLookup.INSTANCE;
    801    }
    802
    803    /**
    804     * Returns the EnvironmentVariableStringLookup singleton instance where the lookup key is an environment variable
    805     * name.
    806     * <p>
    807     * Using a {@link StringLookup} from the {@link StringLookupFactory}:
    808     * </p>
    809     *
    810     * <pre>
    811     * StringLookupFactory.INSTANCE.environmentVariableStringLookup().lookup("USER");
    812     * </pre>
    813     * <p>
    814     * Using a {@link StringSubstitutor}:
    815     * </p>
    816     *
    817     * <pre>
    818     * StringSubstitutor.createInterpolator().replace("... ${env:USER} ..."));
    819     * </pre>
    820     * <p>
    821     * The above examples convert (on Linux) {@code "USER"} to the current user name. On Windows 10, you would use
    822     * {@code "USERNAME"} to the same effect.
    823     * </p>
    824     *
    825     * @return The EnvironmentVariableStringLookup singleton instance.
    826     */
    827    public StringLookup environmentVariableStringLookup() {
    828        return StringLookupFactory.INSTANCE_ENVIRONMENT_VARIABLES;
    829    }
    830
    831    /**
    832     * Returns the FileStringLookup singleton instance.
    833     * <p>
    834     * Using a {@link StringLookup} from the {@link StringLookupFactory}:
    835     * </p>
    836     *
    837     * <pre>
    838     * StringLookupFactory.INSTANCE.fileStringLookup().lookup("UTF-8:com/domain/document.properties");
    839     * </pre>
    840     * <p>
    841     * Using a {@link StringSubstitutor}:
    842     * </p>
    843     *
    844     * <pre>
    845     * StringSubstitutor.createInterpolator().replace("... ${file:UTF-8:com/domain/document.properties} ..."));
    846     * </pre>
    847     * <p>
    848     * The above examples convert {@code "UTF-8:com/domain/document.properties"} to the contents of the file.
    849     * </p>
    850     *
    851     * @return The FileStringLookup singleton instance.
    852     * @since 1.5
    853     */
    854    public StringLookup fileStringLookup() {
    855        return FileStringLookup.INSTANCE;
    856    }
    857
    858    /**
    859     * Returns a new function-based lookup where the request for a lookup is answered by applying the function with a
    860     * lookup key.
    861     *
    862     * @param <R> the function return type.
    863     * @param function the function.
    864     * @return a new MapStringLookup.
    865     * @since 1.9
    866     */
    867    public <R> StringLookup functionStringLookup(final Function<String, R> function) {
    868        return FunctionStringLookup.on(function);
    869    }
    870
    871    /**
    872     * Returns a {@link InterpolatorStringLookup} containing the configured
    873     * {@link #addDefaultStringLookups(Map) default lookups}. See the class documentation for
    874     * details on how these defaults are configured.
    875     * <p>
    876     * Using a {@link StringLookup} from the {@link StringLookupFactory}:
    877     * </p>
    878     *
    879     * <pre>
    880     * StringLookupFactory.INSTANCE.interpolatorStringLookup().lookup("${sys:os.name}, ${env:USER}");
    881     * </pre>
    882     * <p>
    883     * Using a {@link StringSubstitutor}:
    884     * </p>
    885     *
    886     * <pre>
    887     * StringSubstitutor.createInterpolator().replace("... ${sys:os.name}, ${env:USER} ..."));
    888     * </pre>
    889     * <p>
    890     * The above examples convert {@code "${sys:os.name}, ${env:USER}"} to the OS name and Linux user name.
    891     * </p>
    892     *
    893     * @return the default {@link InterpolatorStringLookup}.
    894     */
    895    public StringLookup interpolatorStringLookup() {
    896        return InterpolatorStringLookup.INSTANCE;
    897    }
    898
    899    /**
    900     * Returns a new InterpolatorStringLookup. If {@code addDefaultLookups} is {@code true}, the configured
    901     * {@link #addDefaultStringLookups(Map) default lookups} are included in addition to the ones
    902     * provided in {@code stringLookupMap}. (See the class documentation for details on how default lookups
    903     * are configured.)
    904     *
    905     * @param stringLookupMap the map of string lookups.
    906     * @param defaultStringLookup the default string lookup; this lookup is used when a variable cannot be
    907     *      resolved using the lookups in {@code stringLookupMap} or the configured default lookups (if enabled)
    908     * @param addDefaultLookups whether to use default lookups as described above.
    909     * @return a new InterpolatorStringLookup.
    910     * @since 1.4
    911     */
    912    public StringLookup interpolatorStringLookup(final Map<String, StringLookup> stringLookupMap,
    913        final StringLookup defaultStringLookup, final boolean addDefaultLookups) {
    914        return new InterpolatorStringLookup(stringLookupMap, defaultStringLookup, addDefaultLookups);
    915    }
    916
    917    /**
    918     * Returns a new InterpolatorStringLookup using the given key-value pairs and the configured
    919     * {@link #addDefaultStringLookups(Map) default lookups} to resolve variables. (See the class
    920     * documentation for details on how default lookups are configured.)
    921     *
    922     * @param <V> the value type the default string lookup's map.
    923     * @param map the default map for string lookups.
    924     * @return a new InterpolatorStringLookup.
    925     */
    926    public <V> StringLookup interpolatorStringLookup(final Map<String, V> map) {
    927        return new InterpolatorStringLookup(map);
    928    }
    929
    930    /**
    931     * Returns a new InterpolatorStringLookup using the given lookup and the configured
    932     * {@link #addDefaultStringLookups(Map) default lookups} to resolve variables. (See the class
    933     * documentation for details on how default lookups are configured.)
    934     *
    935     * @param defaultStringLookup the default string lookup.
    936     * @return a new InterpolatorStringLookup.
    937     */
    938    public StringLookup interpolatorStringLookup(final StringLookup defaultStringLookup) {
    939        return new InterpolatorStringLookup(defaultStringLookup);
    940    }
    941
    942    /**
    943     * Returns the JavaPlatformStringLookup singleton instance. Looks up keys related to Java: Java version, JRE
    944     * version, VM version, and so on.
    945     * <p>
    946     * The lookup keys with examples are:
    947     * </p>
    948     * <ul>
    949     * <li><b>version</b>: "Java version 1.8.0_181"</li>
    950     * <li><b>runtime</b>: "Java(TM) SE Runtime Environment (build 1.8.0_181-b13) from Oracle Corporation"</li>
    951     * <li><b>vm</b>: "Java HotSpot(TM) 64-Bit Server VM (build 25.181-b13, mixed mode)"</li>
    952     * <li><b>os</b>: "Windows 10 10.0, architecture: amd64-64"</li>
    953     * <li><b>hardware</b>: "processors: 4, architecture: amd64-64, instruction sets: amd64"</li>
    954     * <li><b>locale</b>: "default locale: en_US, platform encoding: iso-8859-1"</li>
    955     * </ul>
    956     *
    957     * <p>
    958     * Using a {@link StringLookup} from the {@link StringLookupFactory}:
    959     * </p>
    960     *
    961     * <pre>
    962     * StringLookupFactory.INSTANCE.javaPlatformStringLookup().lookup("version");
    963     * </pre>
    964     * <p>
    965     * Using a {@link StringSubstitutor}:
    966     * </p>
    967     *
    968     * <pre>
    969     * StringSubstitutor.createInterpolator().replace("... ${java:version} ..."));
    970     * </pre>
    971     * <p>
    972     * The above examples convert {@code "version"} to the current VM version, for example,
    973     * {@code "Java version 1.8.0_181"}.
    974     * </p>
    975     *
    976     * @return The JavaPlatformStringLookup singleton instance.
    977     */
    978    public StringLookup javaPlatformStringLookup() {
    979        return JavaPlatformStringLookup.INSTANCE;
    980    }
    981
    982    /**
    983     * Returns the LocalHostStringLookup singleton instance where the lookup key is one of:
    984     * <ul>
    985     * <li><b>name</b>: for the local host name, for example {@code EXAMPLE}.</li>
    986     * <li><b>canonical-name</b>: for the local canonical host name, for example {@code EXAMPLE.apache.org}.</li>
    987     * <li><b>address</b>: for the local host address, for example {@code 192.168.56.1}.</li>
    988     * </ul>
    989     *
    990     * <p>
    991     * Using a {@link StringLookup} from the {@link StringLookupFactory}:
    992     * </p>
    993     *
    994     * <pre>
    995     * StringLookupFactory.INSTANCE.localHostStringLookup().lookup("canonical-name");
    996     * </pre>
    997     * <p>
    998     * Using a {@link StringSubstitutor}:
    999     * </p>
    1000     *
    1001     * <pre>
    1002     * StringSubstitutor.createInterpolator().replace("... ${localhost:canonical-name} ..."));
    1003     * </pre>
    1004     * <p>
    1005     * The above examples convert {@code "canonical-name"} to the current host name, for example,
    1006     * {@code "EXAMPLE.apache.org"}.
    1007     * </p>
    1008     *
    1009     * @return The DateStringLookup singleton instance.
    1010     */
    1011    public StringLookup localHostStringLookup() {
    1012        return LocalHostStringLookup.INSTANCE;
    1013    }
    1014
    1015    /**
    1016     * Returns a new map-based lookup where the request for a lookup is answered with the value for that key.
    1017     *
    1018     * @param <V> the map value type.
    1019     * @param map the map.
    1020     * @return a new MapStringLookup.
    1021     */
    1022    public <V> StringLookup mapStringLookup(final Map<String, V> map) {
    1023        return FunctionStringLookup.on(map);
    1024    }
    1025
    1026    /**
    1027     * Returns the NullStringLookup singleton instance which always returns null.
    1028     *
    1029     * @return The NullStringLookup singleton instance.
    1030     */
    1031    public StringLookup nullStringLookup() {
    1032        return StringLookupFactory.INSTANCE_NULL;
    1033    }
    1034
    1035    /**
    1036     * Returns the PropertiesStringLookup singleton instance.
    1037     * <p>
    1038     * Looks up the value for the key in the format "DocumentPath::MyKey".
    1039     * </p>
    1040     * <p>
    1041     * Note the use of "::" instead of ":" to allow for "C:" drive letters in paths.
    1042     * </p>
    1043     * <p>
    1044     * For example: "com/domain/document.properties::MyKey".
    1045     * </p>
    1046     *
    1047     * <p>
    1048     * Using a {@link StringLookup} from the {@link StringLookupFactory}:
    1049     * </p>
    1050     *
    1051     * <pre>
    1052     * StringLookupFactory.INSTANCE.propertiesStringLookup().lookup("com/domain/document.properties::MyKey");
    1053     * </pre>
    1054     * <p>
    1055     * Using a {@link StringSubstitutor}:
    1056     * </p>
    1057     *
    1058     * <pre>
    1059     * StringSubstitutor.createInterpolator().replace("... ${properties:com/domain/document.properties::MyKey} ..."));
    1060     * </pre>
    1061     * <p>
    1062     * The above examples convert {@code "com/domain/document.properties::MyKey"} to the key value in the properties
    1063     * file at the path "com/domain/document.properties".
    1064     * </p>
    1065     *
    1066     * @return The PropertiesStringLookup singleton instance.
    1067     * @since 1.5
    1068     */
    1069    public StringLookup propertiesStringLookup() {
    1070        return PropertiesStringLookup.INSTANCE;
    1071    }
    1072
    1073    /**
    1074     * Returns the ResourceBundleStringLookup singleton instance.
    1075     * <p>
    1076     * Looks up the value for a given key in the format "BundleName:BundleKey".
    1077     * </p>
    1078     * <p>
    1079     * For example: "com.domain.messages:MyKey".
    1080     * </p>
    1081     * <p>
    1082     * Using a {@link StringLookup} from the {@link StringLookupFactory}:
    1083     * </p>
    1084     *
    1085     * <pre>
    1086     * StringLookupFactory.INSTANCE.resourceBundleStringLookup().lookup("com.domain.messages:MyKey");
    1087     * </pre>
    1088     * <p>
    1089     * Using a {@link StringSubstitutor}:
    1090     * </p>
    1091     *
    1092     * <pre>
    1093     * StringSubstitutor.createInterpolator().replace("... ${resourceBundle:com.domain.messages:MyKey} ..."));
    1094     * </pre>
    1095     * <p>
    1096     * The above examples convert {@code "com.domain.messages:MyKey"} to the key value in the resource bundle at
    1097     * {@code "com.domain.messages"}.
    1098     * </p>
    1099     *
    1100     * @return The ResourceBundleStringLookup singleton instance.
    1101     */
    1102    public StringLookup resourceBundleStringLookup() {
    1103        return ResourceBundleStringLookup.INSTANCE;
    1104    }
    1105
    1106    /**
    1107     * Returns a ResourceBundleStringLookup instance for the given bundle name.
    1108     * <p>
    1109     * Looks up the value for a given key in the format "MyKey".
    1110     * </p>
    1111     * <p>
    1112     * For example: "MyKey".
    1113     * </p>
    1114     * <p>
    1115     * Using a {@link StringLookup} from the {@link StringLookupFactory}:
    1116     * </p>
    1117     *
    1118     * <pre>
    1119     * StringLookupFactory.INSTANCE.resourceBundleStringLookup("com.domain.messages").lookup("MyKey");
    1120     * </pre>
    1121     * <p>
    1122     * The above example converts {@code "MyKey"} to the key value in the resource bundle at
    1123     * {@code "com.domain.messages"}.
    1124     * </p>
    1125     *
    1126     * @param bundleName Only lookup in this bundle.
    1127     * @return a ResourceBundleStringLookup instance for the given bundle name.
    1128     * @since 1.5
    1129     */
    1130    public StringLookup resourceBundleStringLookup(final String bundleName) {
    1131        return new ResourceBundleStringLookup(bundleName);
    1132    }
    1133
    1134    /**
    1135     * Returns the ScriptStringLookup singleton instance. NOTE: This lookup is not included
    1136     * as a {@link #addDefaultStringLookups(Map) default lookup} unless explicitly enabled. See
    1137     * the class level documentation for details.
    1138     * <p>
    1139     * Looks up the value for the key in the format "ScriptEngineName:Script".
    1140     * </p>
    1141     * <p>
    1142     * For example: "javascript:3 + 4".
    1143     * </p>
    1144     * <p>
    1145     * Using a {@link StringLookup} from the {@link StringLookupFactory}:
    1146     * </p>
    1147     *
    1148     * <pre>
    1149     * StringLookupFactory.INSTANCE.scriptStringLookup().lookup("javascript:3 + 4");
    1150     * </pre>
    1151     * <p>
    1152     * When used through a {@link StringSubstitutor}, this lookup must either be added programmatically
    1153     * (as below) or enabled as a default lookup using the {@value #DEFAULT_STRING_LOOKUPS_PROPERTY} system property
    1154     * (see class documentation).
    1155     * </p>
    1156     *
    1157     * <pre>
    1158     * Map&lt;String, StringLookup&gt; lookupMap = new HashMap&lt;&gt;();
    1159     * lookupMap.put("script", StringLookupFactory.INSTANCE.scriptStringLookup());
    1160     *
    1161     * StringLookup variableResolver = StringLookupFactory.INSTANCE.interpolatorStringLookup(lookupMap, null, false);
    1162     *
    1163     * String value = new StringSubstitutor(variableResolver).replace("${script:javascript:3 + 4}");
    1164     * </pre>
    1165     * <p>
    1166     * The above examples convert {@code "javascript:3 + 4"} to {@code "7"}.
    1167     * </p>
    1168     *
    1169     * @return The ScriptStringLookup singleton instance.
    1170     * @since 1.5
    1171     */
    1172    public StringLookup scriptStringLookup() {
    1173        return ScriptStringLookup.INSTANCE;
    1174    }
    1175
    1176    /**
    1177     * Returns the SystemPropertyStringLookup singleton instance where the lookup key is a system property name.
    1178     *
    1179     * <p>
    1180     * Using a {@link StringLookup} from the {@link StringLookupFactory}:
    1181     * </p>
    1182     *
    1183     * <pre>
    1184     * StringLookupFactory.INSTANCE.systemPropertyStringLookup().lookup("os.name");
    1185     * </pre>
    1186     * <p>
    1187     * Using a {@link StringSubstitutor}:
    1188     * </p>
    1189     *
    1190     * <pre>
    1191     * StringSubstitutor.createInterpolator().replace("... ${sys:os.name} ..."));
    1192     * </pre>
    1193     * <p>
    1194     * The above examples convert {@code "os.name"} to the operating system name.
    1195     * </p>
    1196     *
    1197     * @return The SystemPropertyStringLookup singleton instance.
    1198     */
    1199    public StringLookup systemPropertyStringLookup() {
    1200        return StringLookupFactory.INSTANCE_SYSTEM_PROPERTIES;
    1201    }
    1202
    1203    /**
    1204     * Returns the UrlDecoderStringLookup singleton instance.
    1205     * <p>
    1206     * Decodes URL Strings using the UTF-8 encoding.
    1207     * </p>
    1208     * <p>
    1209     * For example: "Hello%20World%21" becomes "Hello World!".
    1210     * </p>
    1211     * <p>
    1212     * Using a {@link StringLookup} from the {@link StringLookupFactory}:
    1213     * </p>
    1214     *
    1215     * <pre>
    1216     * StringLookupFactory.INSTANCE.urlDecoderStringLookup().lookup("Hello%20World%21");
    1217     * </pre>
    1218     * <p>
    1219     * Using a {@link StringSubstitutor}:
    1220     * </p>
    1221     *
    1222     * <pre>
    1223     * StringSubstitutor.createInterpolator().replace("... ${urlDecoder:Hello%20World%21} ..."));
    1224     * </pre>
    1225     * <p>
    1226     * The above examples convert {@code "Hello%20World%21"} to {@code "Hello World!"}.
    1227     * </p>
    1228     *
    1229     * @return The UrlStringLookup singleton instance.
    1230     * @since 1.6
    1231     */
    1232    public StringLookup urlDecoderStringLookup() {
    1233        return UrlDecoderStringLookup.INSTANCE;
    1234    }
    1235
    1236    /**
    1237     * Returns the UrlDecoderStringLookup singleton instance.
    1238     * <p>
    1239     * Decodes URL Strings using the UTF-8 encoding.
    1240     * </p>
    1241     * <p>
    1242     * For example: "Hello World!" becomes "Hello+World%21".
    1243     * </p>
    1244     * <p>
    1245     * Using a {@link StringLookup} from the {@link StringLookupFactory}:
    1246     * </p>
    1247     *
    1248     * <pre>
    1249     * StringLookupFactory.INSTANCE.urlEncoderStringLookup().lookup("Hello World!");
    1250     * </pre>
    1251     * <p>
    1252     * Using a {@link StringSubstitutor}:
    1253     * </p>
    1254     *
    1255     * <pre>
    1256     * StringSubstitutor.createInterpolator().replace("... ${urlEncoder:Hello World!} ..."));
    1257     * </pre>
    1258     * <p>
    1259     * The above examples convert {@code "Hello World!"} to {@code "Hello%20World%21"}.
    1260     * </p>
    1261     *
    1262     * @return The UrlStringLookup singleton instance.
    1263     * @since 1.6
    1264     */
    1265    public StringLookup urlEncoderStringLookup() {
    1266        return UrlEncoderStringLookup.INSTANCE;
    1267    }
    1268
    1269    /**
    1270     * Returns the UrlStringLookup singleton instance. This lookup is not included
    1271     * as a {@link #addDefaultStringLookups(Map) default lookup} unless explicitly enabled. See
    1272     * the class level documentation for details.
    1273     * <p>
    1274     * Looks up the value for the key in the format "CharsetName:URL".
    1275     * </p>
    1276     * <p>
    1277     * For example, using the HTTP scheme: "UTF-8:http://www.google.com"
    1278     * </p>
    1279     * <p>
    1280     * For example, using the file scheme:
    1281     * "UTF-8:file:///C:/somehome/commons/commons-text/src/test/resources/document.properties"
    1282     * </p>
    1283     * <p>
    1284     * Using a {@link StringLookup} from the {@link StringLookupFactory}:
    1285     * </p>
    1286     *
    1287     * <pre>
    1288     * StringLookupFactory.INSTANCE.urlStringLookup().lookup("UTF-8:https://www.apache.org");
    1289     * </pre>
    1290     * <p>
    1291     * When used through a {@link StringSubstitutor}, this lookup must either be added programmatically
    1292     * (as below) or enabled as a default lookup using the {@value #DEFAULT_STRING_LOOKUPS_PROPERTY} system property
    1293     * (see class documentation).
    1294     * </p>
    1295     *
    1296     * <pre>
    1297     * Map&lt;String, StringLookup&gt; lookupMap = new HashMap&lt;&gt;();
    1298     * lookupMap.put("url", StringLookupFactory.INSTANCE.urlStringLookup());
    1299     *
    1300     * StringLookup variableResolver = StringLookupFactory.INSTANCE.interpolatorStringLookup(lookupMap, null, false);
    1301     *
    1302     * String value = new StringSubstitutor(variableResolver).replace("${url:UTF-8:https://www.apache.org}");
    1303     * </pre>
    1304     * <p>
    1305     * The above examples convert {@code "UTF-8:https://www.apache.org"} to the contents of that page.
    1306     * </p>
    1307     *
    1308     * @return The UrlStringLookup singleton instance.
    1309     * @since 1.5
    1310     */
    1311    public StringLookup urlStringLookup() {
    1312        return UrlStringLookup.INSTANCE;
    1313    }
    1314
    1315    /**
    1316     * Returns the XmlDecoderStringLookup singleton instance.
    1317     * <p>
    1318     * Decodes strings according to the XML 1.0 specification.
    1319     * </p>
    1320     * <p>
    1321     * For example: "&amp;lt;element&amp;gt;" becomes "&lt;element&gt;".
    1322     * </p>
    1323     * <p>
    1324     * Using a {@link StringLookup} from the {@link StringLookupFactory}:
    1325     * </p>
    1326     *
    1327     * <pre>
    1328     * StringLookupFactory.INSTANCE.xmlDecoderStringLookup().lookup("&amp;lt;element&amp;gt;");
    1329     * </pre>
    1330     * <p>
    1331     * Using a {@link StringSubstitutor}:
    1332     * </p>
    1333     *
    1334     * <pre>
    1335     * StringSubstitutor.createInterpolator().replace("... ${xmlDecoder:&amp;lt;element&amp;gt;} ..."));
    1336     * </pre>
    1337     * <p>
    1338     * The above examples convert {@code "&lt;element&gt;"} to {@code "<element>"}.
    1339     * </p>
    1340     *
    1341     * @return The XmlDecoderStringLookup singleton instance.
    1342     * @since 1.11.0
    1343     */
    1344    public StringLookup xmlDecoderStringLookup() {
    1345        return XmlDecoderStringLookup.INSTANCE;
    1346    }
    1347
    1348    /**
    1349     * Returns the XmlEncoderStringLookup singleton instance.
    1350     * <p>
    1351     * Encodes strings according to the XML 1.0 specification.
    1352     * </p>
    1353     * <p>
    1354     * For example: "&lt;element&gt;" becomes "&amp;lt;element&amp;gt;".
    1355     * </p>
    1356     * <p>
    1357     * Using a {@link StringLookup} from the {@link StringLookupFactory}:
    1358     * </p>
    1359     *
    1360     * <pre>
    1361     * StringLookupFactory.INSTANCE.xmlEncoderStringLookup().lookup("&lt;element&gt;");
    1362     * </pre>
    1363     * <p>
    1364     * Using a {@link StringSubstitutor}:
    1365     * </p>
    1366     *
    1367     * <pre>
    1368     * StringSubstitutor.createInterpolator().replace("... ${xmlEncoder:&lt;element&gt;} ..."));
    1369     * </pre>
    1370     * <p>
    1371     * The above examples convert {@code "<element>"} to {@code "&lt;element&gt;"}.
    1372     * </p>
    1373     *
    1374     * @return The XmlEncoderStringLookup singleton instance.
    1375     * @since 1.11.0
    1376     */
    1377    public StringLookup xmlEncoderStringLookup() {
    1378        return XmlEncoderStringLookup.INSTANCE;
    1379    }
    1380
    1381    /**
    1382     * Returns the XmlStringLookup singleton instance.
    1383     * <p>
    1384     * Looks up the value for the key in the format "DocumentPath:XPath".
    1385     * </p>
    1386     * <p>
    1387     * For example: "com/domain/document.xml:/path/to/node".
    1388     * </p>
    1389     * <p>
    1390     * Using a {@link StringLookup} from the {@link StringLookupFactory}:
    1391     * </p>
    1392     *
    1393     * <pre>
    1394     * StringLookupFactory.INSTANCE.xmlStringLookup().lookup("com/domain/document.xml:/path/to/node");
    1395     * </pre>
    1396     * <p>
    1397     * Using a {@link StringSubstitutor}:
    1398     * </p>
    1399     *
    1400     * <pre>
    1401     * StringSubstitutor.createInterpolator().replace("... ${xml:com/domain/document.xml:/path/to/node} ..."));
    1402     * </pre>
    1403     * <p>
    1404     * The above examples convert {@code "com/domain/document.xml:/path/to/node"} to the value of the XPath in the XML
    1405     * document.
    1406     * </p>
    1407     *
    1408     * @return The XmlStringLookup singleton instance.
    1409     * @since 1.5
    1410     */
    1411    public StringLookup xmlStringLookup() {
    1412        return XmlStringLookup.INSTANCE;
    1413    }
    1414
    1415    /**
    1416     * Returns the XmlStringLookup singleton instance.
    1417     * <p>
    1418     * Looks up the value for the key in the format "DocumentPath:XPath".
    1419     * </p>
    1420     * <p>
    1421     * For example: "com/domain/document.xml:/path/to/node".
    1422     * </p>
    1423     * <p>
    1424     * Using a {@link StringLookup} from the {@link StringLookupFactory}:
    1425     * </p>
    1426     *
    1427     * <pre>
    1428     * StringLookupFactory.INSTANCE.xmlStringLookup().lookup("com/domain/document.xml:/path/to/node");
    1429     * </pre>
    1430     * <p>
    1431     * Using a {@link StringSubstitutor}:
    1432     * </p>
    1433     *
    1434     * <pre>
    1435     * StringSubstitutor.createInterpolator().replace("... ${xml:com/domain/document.xml:/path/to/node} ..."));
    1436     * </pre>
    1437     * <p>
    1438     * The above examples convert {@code "com/domain/document.xml:/path/to/node"} to the value of the XPath in the XML
    1439     * document.
    1440     * </p>
    1441     *
    1442     * @param xPathFactoryFeatures XPathFactory features to set.
    1443     * @return The XmlStringLookup singleton instance.
    1444     * @see XPathFactory#setFeature(String, boolean)
    1445     * @since 1.11.0
    1446     */
    1447    public StringLookup xmlStringLookup(final Map<String, Boolean> xPathFactoryFeatures) {
    1448        return new XmlStringLookup(xPathFactoryFeatures);
    1449    }
    1450}