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}