View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements. See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache license, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License. You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the license for the specific language governing permissions and
15   * limitations under the license.
16   */
17  
18  package org.apache.commons.text.lookup;
19  
20  import java.nio.charset.StandardCharsets;
21  import java.util.Base64;
22  import java.util.Collections;
23  import java.util.HashMap;
24  import java.util.Locale;
25  import java.util.Map;
26  import java.util.Properties;
27  import java.util.function.BiFunction;
28  import java.util.function.Function;
29  
30  import javax.xml.xpath.XPathFactory;
31  
32  import org.apache.commons.text.StringSubstitutor;
33  
34  /**
35   * Create instances of string lookups or access singleton string lookups implemented in this package.
36   * <p>
37   * The "classic" look up is {@link #mapStringLookup(Map)}.
38   * </p>
39   * <p>
40   * The methods for variable interpolation (A.K.A. variable substitution) are:
41   * </p>
42   * <ul>
43   * <li>{@link #interpolatorStringLookup()}.</li>
44   * <li>{@link #interpolatorStringLookup(Map)}.</li>
45   * <li>{@link #interpolatorStringLookup(StringLookup)}.</li>
46   * <li>{@link #interpolatorStringLookup(Map, StringLookup, boolean)}.</li>
47   * </ul>
48   * <p>
49   * Unless explicitly requested otherwise, a set of default lookups are included for convenience with these
50   * variable interpolation methods. These defaults are listed in the table below. However, the exact lookups
51   * included can be configured through the use of the {@value #DEFAULT_STRING_LOOKUPS_PROPERTY} system property.
52   * If present, this system property will be parsed as a comma-separated list of lookup names, with the names
53   * being those defined by the {@link DefaultStringLookup} enum. For example, setting this system property to
54   * {@code "BASE64_ENCODER,ENVIRONMENT"} will only include the
55   * {@link DefaultStringLookup#BASE64_ENCODER BASE64_ENCODER} and {@link DefaultStringLookup#ENVIRONMENT ENVIRONMENT}
56   * lookups. Setting the property to the empty string will cause no defaults to be configured.
57   * Note that not all lookups defined here and in {@link DefaultStringLookup} are included by default.
58   * Specifically, lookups that can execute code (e.g., {@link DefaultStringLookup#SCRIPT SCRIPT}) and those
59   * that can result in contact with remote servers (e.g., {@link DefaultStringLookup#URL URL} and
60   * {@link DefaultStringLookup#DNS DNS}) are not included by default. The current set of default lookups can
61   * be accessed directly with {@link #addDefaultStringLookups(Map)}.
62   * </p>
63   * <table>
64   * <caption>Default String Lookups</caption>
65   * <tr>
66   * <th>Key</th>
67   * <th>Interface</th>
68   * <th>Factory Method</th>
69   * <th>Since</th>
70   * </tr>
71   * <tr>
72   * <td>{@value #KEY_BASE64_DECODER}</td>
73   * <td>{@link StringLookup}</td>
74   * <td>{@link #base64DecoderStringLookup()}</td>
75   * <td>1.6</td>
76   * </tr>
77   * <tr>
78   * <td>{@value #KEY_BASE64_ENCODER}</td>
79   * <td>{@link StringLookup}</td>
80   * <td>{@link #base64EncoderStringLookup()}</td>
81   * <td>1.6</td>
82   * </tr>
83   * <tr>
84   * <td>{@value #KEY_CONST}</td>
85   * <td>{@link StringLookup}</td>
86   * <td>{@link #constantStringLookup()}</td>
87   * <td>1.5</td>
88   * </tr>
89   * <tr>
90   * <td>{@value #KEY_DATE}</td>
91   * <td>{@link StringLookup}</td>
92   * <td>{@link #dateStringLookup()}</td>
93   * <td>1.5</td>
94   * </tr>
95   * <tr>
96   * <td>{@value #KEY_ENV}</td>
97   * <td>{@link StringLookup}</td>
98   * <td>{@link #environmentVariableStringLookup()}</td>
99   * <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  */
220 public 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 }