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