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.net.InetAddress;
21  import java.nio.charset.StandardCharsets;
22  import java.nio.file.Path;
23  import java.util.Base64;
24  import java.util.Collections;
25  import java.util.HashMap;
26  import java.util.Locale;
27  import java.util.Map;
28  import java.util.Properties;
29  import java.util.function.BiFunction;
30  import java.util.function.Function;
31  import java.util.function.Supplier;
32  
33  import javax.xml.xpath.XPathFactory;
34  
35  import org.apache.commons.text.StringSubstitutor;
36  
37  /**
38   * Create instances of string lookups or access singleton string lookups implemented in this package.
39   * <p>
40   * The "classic" look up is {@link #mapStringLookup(Map)}.
41   * </p>
42   * <p>
43   * The methods for variable interpolation (A.K.A. variable substitution) are:
44   * </p>
45   * <ul>
46   * <li>{@link #interpolatorStringLookup()}.</li>
47   * <li>{@link #interpolatorStringLookup(Map)}.</li>
48   * <li>{@link #interpolatorStringLookup(StringLookup)}.</li>
49   * <li>{@link #interpolatorStringLookup(Map, StringLookup, boolean)}.</li>
50   * </ul>
51   * <p>
52   * Unless explicitly requested otherwise, a set of default lookups are included for convenience with these variable interpolation methods. These defaults are
53   * listed in the table below. However, the exact lookups included can be configured through the use of the {@value #DEFAULT_STRING_LOOKUPS_PROPERTY} system
54   * property. If present, this system property will be parsed as a comma-separated list of lookup names, with the names being those defined by the
55   * {@link DefaultStringLookup} enum. For example, setting this system property to {@code "BASE64_ENCODER,ENVIRONMENT"} will only include the
56   * {@link DefaultStringLookup#BASE64_ENCODER BASE64_ENCODER} and {@link DefaultStringLookup#ENVIRONMENT ENVIRONMENT} lookups. Setting the property to the empty
57   * string will cause no defaults to be configured. 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 that can result in contact with remote servers (e.g.,
59   * {@link DefaultStringLookup#URL URL} and {@link DefaultStringLookup#DNS DNS}) are not included by default. The current set of default lookups can be accessed
60   * directly with {@link #addDefaultStringLookups(Map)}.
61   * </p>
62   * <table>
63   * <caption>Default String Lookups</caption>
64   * <tr>
65   * <th>Key</th>
66   * <th>Interface</th>
67   * <th>Factory Method</th>
68   * <th>Since</th>
69   * </tr>
70   * <tr>
71   * <td>{@value #KEY_BASE64_DECODER}</td>
72   * <td>{@link StringLookup}</td>
73   * <td>{@link #base64DecoderStringLookup()}</td>
74   * <td>1.6</td>
75   * </tr>
76   * <tr>
77   * <td>{@value #KEY_BASE64_ENCODER}</td>
78   * <td>{@link StringLookup}</td>
79   * <td>{@link #base64EncoderStringLookup()}</td>
80   * <td>1.6</td>
81   * </tr>
82   * <tr>
83   * <td>{@value #KEY_CONST}</td>
84   * <td>{@link StringLookup}</td>
85   * <td>{@link #constantStringLookup()}</td>
86   * <td>1.5</td>
87   * </tr>
88   * <tr>
89   * <td>{@value #KEY_DATE}</td>
90   * <td>{@link StringLookup}</td>
91   * <td>{@link #dateStringLookup()}</td>
92   * <td>1.5</td>
93   * </tr>
94   * <tr>
95   * <td>{@value #KEY_ENV}</td>
96   * <td>{@link StringLookup}</td>
97   * <td>{@link #environmentVariableStringLookup()}</td>
98   * <td>1.3</td>
99   * </tr>
100  * <tr>
101  * <td>{@value #KEY_FILE}</td>
102  * <td>{@link StringLookup}</td>
103  * <td>{@link #fileStringLookup(Path...)}</td>
104  * <td>1.5</td>
105  * </tr>
106  * <tr>
107  * <td>{@value #KEY_JAVA}</td>
108  * <td>{@link StringLookup}</td>
109  * <td>{@link #javaPlatformStringLookup()}</td>
110  * <td>1.5</td>
111  * </tr>
112  * <tr>
113  * <td>{@value #KEY_LOCALHOST}</td>
114  * <td>{@link StringLookup}</td>
115  * <td>{@link #localHostStringLookup()}</td>
116  * <td>1.3</td>
117  * </tr>
118  * <tr>
119  * <td>{@value #KEY_LOOPBACK_ADDRESS}</td>
120  * <td>{@link StringLookup}</td>
121  * <td>{@link #loopbackAddressStringLookup()}</td>
122  * <td>1.13.0</td>
123  * </tr>
124  * <tr>
125  * <td>{@value #KEY_PROPERTIES}</td>
126  * <td>{@link StringLookup}</td>
127  * <td>{@link #propertiesStringLookup(Path...)}</td>
128  * <td>1.5</td>
129  * </tr>
130  * <tr>
131  * <td>{@value #KEY_RESOURCE_BUNDLE}</td>
132  * <td>{@link StringLookup}</td>
133  * <td>{@link #resourceBundleStringLookup()}</td>
134  * <td>1.6</td>
135  * </tr>
136  * <tr>
137  * <td>{@value #KEY_SYS}</td>
138  * <td>{@link StringLookup}</td>
139  * <td>{@link #systemPropertyStringLookup()}</td>
140  * <td>1.3</td>
141  * </tr>
142  * <tr>
143  * <td>{@value #KEY_URL_DECODER}</td>
144  * <td>{@link StringLookup}</td>
145  * <td>{@link #urlDecoderStringLookup()}</td>
146  * <td>1.5</td>
147  * </tr>
148  * <tr>
149  * <td>{@value #KEY_URL_ENCODER}</td>
150  * <td>{@link StringLookup}</td>
151  * <td>{@link #urlEncoderStringLookup()}</td>
152  * <td>1.5</td>
153  * </tr>
154  * <tr>
155  * <td>{@value #KEY_XML}</td>
156  * <td>{@link StringLookup}</td>
157  * <td>{@link #xmlStringLookup(Map, Path...)}</td>
158  * <td>1.5</td>
159  * </tr>
160  * <tr>
161  * <td>{@value #KEY_XML_DECODER}</td>
162  * <td>{@link StringLookup}</td>
163  * <td>{@link #xmlDecoderStringLookup()}</td>
164  * <td>1.11.0</td>
165  * </tr>
166  * <tr>
167  * <td>{@value #KEY_XML_ENCODER}</td>
168  * <td>{@link StringLookup}</td>
169  * <td>{@link #xmlEncoderStringLookup()}</td>
170  * <td>1.11.0</td>
171  * </tr>
172  * </table>
173  *
174  * <table>
175  * <caption>Additional String Lookups (not included by default)</caption>
176  * <tr>
177  * <th>Key</th>
178  * <th>Interface</th>
179  * <th>Factory Method</th>
180  * <th>Since</th>
181  * </tr>
182  * <tr>
183  * <td>{@value #KEY_DNS}</td>
184  * <td>{@link StringLookup}</td>
185  * <td>{@link #dnsStringLookup()}</td>
186  * <td>1.8</td>
187  * </tr>
188  * <tr>
189  * <td>{@value #KEY_URL}</td>
190  * <td>{@link StringLookup}</td>
191  * <td>{@link #urlStringLookup()}</td>
192  * <td>1.5</td>
193  * </tr>
194  * <tr>
195  * <td>{@value #KEY_SCRIPT}</td>
196  * <td>{@link StringLookup}</td>
197  * <td>{@link #scriptStringLookup()}</td>
198  * <td>1.5</td>
199  * </tr>
200  * </table>
201  *
202  * <p>
203  * This class also provides functional lookups used as building blocks for other lookups.
204  * <table>
205  * <caption>Functional String Lookups</caption>
206  * <tr>
207  * <th>Interface</th>
208  * <th>Factory Method</th>
209  * <th>Since</th>
210  * </tr>
211  * <tr>
212  * <td>{@link BiStringLookup}</td>
213  * <td>{@link #biFunctionStringLookup(BiFunction)}</td>
214  * <td>1.9</td>
215  * </tr>
216  * <tr>
217  * <td>{@link StringLookup}</td>
218  * <td>{@link #functionStringLookup(Function)}</td>
219  * <td>1.9</td>
220  * </tr>
221  * </table>
222  *
223  * @since 1.3
224  */
225 public final class StringLookupFactory {
226 
227     /**
228      * Builds instance of {@link StringLookupFactory}.
229      *
230      * @since 1.12.0
231      */
232     public static final class Builder implements Supplier<StringLookupFactory> {
233 
234         /**
235          * Fences.
236          */
237         private Path[] fences;
238 
239         @Override
240         public StringLookupFactory get() {
241             return new StringLookupFactory(fences);
242         }
243 
244         /**
245          * Sets Path resolution fences.
246          * <p>
247          * Path Fences apply to the file, property, and XML string lookups.
248          * </p>
249          *
250          * @param fences Path resolution fences.
251          * @return {@code this} instance.
252          */
253         public Builder setFences(final Path... fences) {
254             this.fences = fences;
255             return this;
256         }
257 
258     }
259 
260     /**
261      * Internal class used to construct the default {@link StringLookup} map used by {@link StringLookupFactory#addDefaultStringLookups(Map)}.
262      */
263     static final class DefaultStringLookupsHolder {
264 
265         /** Singleton instance, initialized with the system properties. */
266         static final DefaultStringLookupsHolder INSTANCE = new DefaultStringLookupsHolder(System.getProperties());
267 
268         /**
269          * Adds the key and string lookup from {@code lookup} to {@code map}, also adding any additional key aliases if needed. Keys are normalized using the
270          * {@link #toKey(String)} method.
271          *
272          * @param lookup lookup to add
273          * @param map    map to add to
274          */
275         private static void addLookup(final DefaultStringLookup lookup, final Map<String, StringLookup> map) {
276             map.put(toKey(lookup.getKey()), lookup.getStringLookup());
277             if (DefaultStringLookup.BASE64_DECODER.equals(lookup)) {
278                 // "base64" is deprecated in favor of KEY_BASE64_DECODER.
279                 map.put(toKey("base64"), lookup.getStringLookup());
280             }
281         }
282 
283         /**
284          * Creates the lookup map used when the user has requested no customization.
285          *
286          * @return default lookup map
287          */
288         private static Map<String, StringLookup> createDefaultStringLookups() {
289             final Map<String, StringLookup> lookupMap = new HashMap<>();
290 
291             addLookup(DefaultStringLookup.BASE64_DECODER, lookupMap);
292             addLookup(DefaultStringLookup.BASE64_ENCODER, lookupMap);
293             addLookup(DefaultStringLookup.CONST, lookupMap);
294             addLookup(DefaultStringLookup.DATE, lookupMap);
295             addLookup(DefaultStringLookup.ENVIRONMENT, lookupMap);
296             addLookup(DefaultStringLookup.FILE, lookupMap);
297             addLookup(DefaultStringLookup.JAVA, lookupMap);
298             addLookup(DefaultStringLookup.LOCAL_HOST, lookupMap);
299             addLookup(DefaultStringLookup.LOCAL_HOST, lookupMap);
300             addLookup(DefaultStringLookup.PROPERTIES, lookupMap);
301             addLookup(DefaultStringLookup.RESOURCE_BUNDLE, lookupMap);
302             addLookup(DefaultStringLookup.SYSTEM_PROPERTIES, lookupMap);
303             addLookup(DefaultStringLookup.URL_DECODER, lookupMap);
304             addLookup(DefaultStringLookup.URL_ENCODER, lookupMap);
305             addLookup(DefaultStringLookup.XML, lookupMap);
306             addLookup(DefaultStringLookup.XML_DECODER, lookupMap);
307             addLookup(DefaultStringLookup.XML_ENCODER, lookupMap);
308 
309             return lookupMap;
310         }
311 
312         /**
313          * Constructs a lookup map by parsing the given string. The string is expected to contain comma or space-separated names of values from the
314          * {@link DefaultStringLookup} enum. If the given string is null or empty, an empty map is returned.
315          *
316          * @param str string to parse; may be null or empty
317          * @return lookup map parsed from the given string
318          */
319         private static Map<String, StringLookup> parseStringLookups(final String str) {
320             final Map<String, StringLookup> lookupMap = new HashMap<>();
321             try {
322                 for (final String lookupName : str.split("[\\s,]+")) {
323                     if (!lookupName.isEmpty()) {
324                         addLookup(DefaultStringLookup.valueOf(lookupName.toUpperCase()), lookupMap);
325                     }
326                 }
327             } catch (final IllegalArgumentException exc) {
328                 throw new IllegalArgumentException("Invalid default string lookups definition: " + str, exc);
329             }
330             return lookupMap;
331         }
332 
333         /** Default string lookup map. */
334         private final Map<String, StringLookup> defaultStringLookups;
335 
336         /**
337          * Constructs a new instance initialized with the given properties.
338          *
339          * @param props initialization properties
340          */
341         DefaultStringLookupsHolder(final Properties props) {
342             final Map<String, StringLookup> lookups = props.containsKey(StringLookupFactory.DEFAULT_STRING_LOOKUPS_PROPERTY)
343                     ? parseStringLookups(props.getProperty(StringLookupFactory.DEFAULT_STRING_LOOKUPS_PROPERTY))
344                     : createDefaultStringLookups();
345             defaultStringLookups = Collections.unmodifiableMap(lookups);
346         }
347 
348         /**
349          * Gets the default string lookups map.
350          *
351          * @return default string lookups map
352          */
353         Map<String, StringLookup> getDefaultStringLookups() {
354             return defaultStringLookups;
355         }
356     }
357 
358     /**
359      * Name of the system property used to determine the string lookups added by the {@link #addDefaultStringLookups(Map)} method. Use of this property is only
360      * required in cases where the set of default lookups must be modified. (See the class documentation for details.)
361      *
362      * @since 1.10.0
363      */
364     public static final String DEFAULT_STRING_LOOKUPS_PROPERTY = "org.apache.commons.text.lookup.StringLookupFactory.defaultStringLookups";
365 
366     /**
367      * Defines the singleton for this class.
368      */
369     public static final StringLookupFactory INSTANCE = new StringLookupFactory();
370 
371     /**
372      * Decodes Base64 Strings.
373      * <p>
374      * Using a {@link StringLookup} from the {@link StringLookupFactory}:
375      * </p>
376      *
377      * <pre>
378      * StringLookupFactory.INSTANCE.base64DecoderStringLookup().lookup("SGVsbG9Xb3JsZCE=");
379      * </pre>
380      * <p>
381      * Using a {@link StringSubstitutor}:
382      * </p>
383      *
384      * <pre>
385      * StringSubstitutor.createInterpolator().replace("... ${base64Decoder:SGVsbG9Xb3JsZCE=} ..."));
386      * </pre>
387      * <p>
388      * The above examples convert {@code "SGVsbG9Xb3JsZCE="} to {@code "HelloWorld!"}.
389      * </p>
390      */
391     static final FunctionStringLookup<String> INSTANCE_BASE64_DECODER = FunctionStringLookup
392             .on(key -> new String(Base64.getDecoder().decode(key), StandardCharsets.ISO_8859_1));
393 
394     /**
395      * Encodes Base64 Strings.
396      * <p>
397      * Using a {@link StringLookup} from the {@link StringLookupFactory}:
398      * </p>
399      *
400      * <pre>
401      * StringLookupFactory.INSTANCE.base64EncoderStringLookup().lookup("HelloWorld!");
402      * </pre>
403      * <p>
404      * Using a {@link StringSubstitutor}:
405      * </p>
406      *
407      * <pre>
408      * StringSubstitutor.createInterpolator().replace("... ${base64Encoder:HelloWorld!} ..."));
409      * </pre>
410      * <p>
411      * The above examples convert {@code "HelloWorld!"} to {@code "SGVsbG9Xb3JsZCE="}.
412      * </p>
413      * Defines the singleton for this class.
414      */
415     static final FunctionStringLookup<String> INSTANCE_BASE64_ENCODER = FunctionStringLookup
416             .on(key -> Base64.getEncoder().encodeToString(key.getBytes(StandardCharsets.ISO_8859_1)));
417 
418     /**
419      * Looks up keys from environment variables.
420      * <p>
421      * Using a {@link StringLookup} from the {@link StringLookupFactory}:
422      * </p>
423      *
424      * <pre>
425      * StringLookupFactory.INSTANCE.environmentVariableStringLookup().lookup("USER");
426      * </pre>
427      * <p>
428      * Using a {@link StringSubstitutor}:
429      * </p>
430      *
431      * <pre>
432      * StringSubstitutor.createInterpolator().replace("... ${env:USER} ..."));
433      * </pre>
434      * <p>
435      * The above examples convert (on Linux) {@code "USER"} to the current user name. On Windows 10, you would use {@code "USERNAME"} to the same effect.
436      * </p>
437      */
438     static final FunctionStringLookup<String> INSTANCE_ENVIRONMENT_VARIABLES = FunctionStringLookup.on(System::getenv);
439 
440     /**
441      * Defines the FunctionStringLookup singleton that always returns null.
442      */
443     static final FunctionStringLookup<String> INSTANCE_NULL = FunctionStringLookup.on(key -> null);
444 
445     /**
446      * Defines the FunctionStringLookup singleton for looking up system properties.
447      */
448     static final FunctionStringLookup<String> INSTANCE_SYSTEM_PROPERTIES = FunctionStringLookup.on(System::getProperty);
449 
450     /**
451      * Default lookup key for interpolation {@value #KEY_BASE64_DECODER}.
452      *
453      * @since 1.6
454      */
455     public static final String KEY_BASE64_DECODER = "base64Decoder";
456 
457     /**
458      * Default lookup key for interpolation {@value #KEY_BASE64_ENCODER}.
459      *
460      * @since 1.6
461      */
462     public static final String KEY_BASE64_ENCODER = "base64Encoder";
463 
464     /**
465      * Default lookup key for interpolation {@value #KEY_CONST}.
466      *
467      * @since 1.6
468      */
469     public static final String KEY_CONST = "const";
470 
471     /**
472      * Default lookup key for interpolation {@value #KEY_DATE}.
473      *
474      * @since 1.6
475      */
476     public static final String KEY_DATE = "date";
477 
478     /**
479      * Default lookup key for interpolation {@value #KEY_DNS}.
480      *
481      * @since 1.8
482      */
483     public static final String KEY_DNS = "dns";
484 
485     /**
486      * Default lookup key for interpolation {@value #KEY_ENV}.
487      *
488      * @since 1.6
489      */
490     public static final String KEY_ENV = "env";
491 
492     /**
493      * Default lookup key for interpolation {@value #KEY_FILE}.
494      *
495      * @since 1.6
496      */
497     public static final String KEY_FILE = "file";
498 
499     /**
500      * Default lookup key for interpolation {@value #KEY_JAVA}.
501      *
502      * @since 1.6
503      */
504     public static final String KEY_JAVA = "java";
505 
506     /**
507      * Default lookup key for interpolation {@value #KEY_LOCALHOST}.
508      *
509      * @since 1.6
510      */
511     public static final String KEY_LOCALHOST = "localhost";
512 
513     /**
514      * Default lookup key for interpolation {@value #KEY_LOOPBACK_ADDRESS}.
515      *
516      * @since 1.13.0
517      */
518     public static final String KEY_LOOPBACK_ADDRESS = "loobackAddress";
519 
520     /**
521      * Default lookup key for interpolation {@value #KEY_PROPERTIES}.
522      *
523      * @since 1.6
524      */
525     public static final String KEY_PROPERTIES = "properties";
526 
527     /**
528      * Default lookup key for interpolation {@value #KEY_RESOURCE_BUNDLE}.
529      *
530      * @since 1.6
531      */
532     public static final String KEY_RESOURCE_BUNDLE = "resourceBundle";
533 
534     /**
535      * Default lookup key for interpolation {@value #KEY_SCRIPT}.
536      *
537      * @since 1.6
538      */
539     public static final String KEY_SCRIPT = "script";
540 
541     /**
542      * Default lookup key for interpolation {@value #KEY_SYS}.
543      *
544      * @since 1.6
545      */
546     public static final String KEY_SYS = "sys";
547 
548     /**
549      * Default lookup key for interpolation {@value #KEY_URL}.
550      *
551      * @since 1.6
552      */
553     public static final String KEY_URL = "url";
554 
555     /**
556      * Default lookup key for interpolation {@value #KEY_URL_DECODER}.
557      *
558      * @since 1.6
559      */
560     public static final String KEY_URL_DECODER = "urlDecoder";
561 
562     /**
563      * Default lookup key for interpolation {@value #KEY_URL_ENCODER}.
564      *
565      * @since 1.6
566      */
567     public static final String KEY_URL_ENCODER = "urlEncoder";
568 
569     /**
570      * Default lookup key for interpolation {@value #KEY_XML}.
571      *
572      * @since 1.6
573      */
574     public static final String KEY_XML = "xml";
575 
576     /**
577      * Default lookup key for interpolation {@value #KEY_XML_DECODER}.
578      *
579      * @since 1.11.0
580      */
581     public static final String KEY_XML_DECODER = "xmlDecoder";
582 
583     /**
584      * Default lookup key for interpolation {@value #KEY_XML_ENCODER}.
585      *
586      * @since 1.11.0
587      */
588     public static final String KEY_XML_ENCODER = "xmlEncoder";
589 
590     /**
591      * Constructs a new {@link Builder}.
592      *
593      * @return a new {@link Builder}
594      * @since 1.12.0
595      */
596     public static Builder builder() {
597         return new Builder();
598     }
599 
600     /**
601      * Clears any static resources.
602      *
603      * @since 1.5
604      */
605     public static void clear() {
606         ConstantStringLookup.clear();
607     }
608 
609     /**
610      * Gets a string suitable for use as a key in the string lookup map.
611      *
612      * @param key string to convert to a string lookup map key
613      * @return string lookup map key
614      */
615     static String toKey(final String key) {
616         return key.toLowerCase(Locale.ROOT);
617     }
618 
619     /**
620      * Returns the given map if the input is non-null or an empty immutable map if the input is null.
621      *
622      * @param <K> the class of the map keys
623      * @param <V> the class of the map values
624      * @param map The map to test
625      * @return the given map if the input is non-null or an empty immutable map if the input is null.
626      */
627     static <K, V> Map<K, V> toMap(final Map<K, V> map) {
628         return map == null ? Collections.emptyMap() : map;
629     }
630 
631     /**
632      * Fences.
633      */
634     private final Path[] fences;
635 
636     /**
637      * Constructs a new instance.
638      */
639     private StringLookupFactory() {
640         this(null);
641     }
642 
643     /**
644      * Constructs a new instance.
645      */
646     private StringLookupFactory(final Path[] fences) {
647         this.fences = fences;
648     }
649 
650     /**
651      * Adds the default string lookups for this class to {@code stringLookupMap}. The default string lookups are a set of built-in lookups added for convenience
652      * during string interpolation. The defaults may be configured using the {@value #DEFAULT_STRING_LOOKUPS_PROPERTY} system property. See the class
653      * documentation for details and a list of lookups.
654      *
655      * @param stringLookupMap the map of string lookups to edit.
656      * @since 1.5
657      */
658     public void addDefaultStringLookups(final Map<String, StringLookup> stringLookupMap) {
659         if (stringLookupMap != null) {
660             stringLookupMap.putAll(DefaultStringLookupsHolder.INSTANCE.getDefaultStringLookups());
661         }
662     }
663 
664     /**
665      * Returns the Base64DecoderStringLookup singleton instance to decode Base64 strings.
666      * <p>
667      * Using a {@link StringLookup} from the {@link StringLookupFactory}:
668      * </p>
669      *
670      * <pre>
671      * StringLookupFactory.INSTANCE.base64DecoderStringLookup().lookup("SGVsbG9Xb3JsZCE=");
672      * </pre>
673      * <p>
674      * Using a {@link StringSubstitutor}:
675      * </p>
676      *
677      * <pre>
678      * StringSubstitutor.createInterpolator().replace("... ${base64Decoder:SGVsbG9Xb3JsZCE=} ..."));
679      * </pre>
680      * <p>
681      * The above examples convert {@code "SGVsbG9Xb3JsZCE="} to {@code "HelloWorld!"}.
682      * </p>
683      *
684      * @return The Base64DecoderStringLookup singleton instance.
685      * @since 1.5
686      */
687     public StringLookup base64DecoderStringLookup() {
688         return StringLookupFactory.INSTANCE_BASE64_DECODER;
689     }
690 
691     /**
692      * Returns the Base64EncoderStringLookup singleton instance to encode strings to Base64.
693      * <p>
694      * Using a {@link StringLookup} from the {@link StringLookupFactory}:
695      * </p>
696      *
697      * <pre>
698      * StringLookupFactory.INSTANCE.base64EncoderStringLookup().lookup("HelloWorld!");
699      * </pre>
700      * <p>
701      * Using a {@link StringSubstitutor}:
702      * </p>
703      *
704      * <pre>
705      * StringSubstitutor.createInterpolator().replace("... ${base64Encoder:HelloWorld!} ..."));
706      * </pre>
707      * <p>
708      * The above examples convert {@code } to {@code "SGVsbG9Xb3JsZCE="}.
709      * </p>
710      *
711      * @return The Base64EncoderStringLookup singleton instance.
712      * @since 1.6
713      */
714     public StringLookup base64EncoderStringLookup() {
715         return StringLookupFactory.INSTANCE_BASE64_ENCODER;
716     }
717 
718     /**
719      * Returns the Base64DecoderStringLookup singleton instance to decode Base64 strings.
720      * <p>
721      * Using a {@link StringLookup} from the {@link StringLookupFactory}:
722      * </p>
723      *
724      * <pre>
725      * StringLookupFactory.INSTANCE.base64DecoderStringLookup().lookup("SGVsbG9Xb3JsZCE=");
726      * </pre>
727      * <p>
728      * Using a {@link StringSubstitutor}:
729      * </p>
730      *
731      * <pre>
732      * StringSubstitutor.createInterpolator().replace("... ${base64Decoder:SGVsbG9Xb3JsZCE=} ..."));
733      * </pre>
734      * <p>
735      * The above examples convert {@code "SGVsbG9Xb3JsZCE="} to {@code "HelloWorld!"}.
736      * </p>
737      *
738      * @return The Base64DecoderStringLookup singleton instance.
739      * @since 1.5
740      * @deprecated Use {@link #base64DecoderStringLookup()}.
741      */
742     @Deprecated
743     public StringLookup base64StringLookup() {
744         return StringLookupFactory.INSTANCE_BASE64_DECODER;
745     }
746 
747     /**
748      * Returns a new function-based lookup where the request for a lookup is answered by applying the function with a lookup key.
749      *
750      * @param <R>        the function return type.
751      * @param <U>        the function's second parameter type.
752      * @param biFunction the function.
753      * @return a new MapStringLookup.
754      * @since 1.9
755      */
756     public <R, U> BiStringLookup<U> biFunctionStringLookup(final BiFunction<String, U, R> biFunction) {
757         return BiFunctionStringLookup.on(biFunction);
758     }
759 
760     /**
761      * Returns the ConstantStringLookup singleton instance to look up the value of a fully-qualified static final value.
762      * <p>
763      * Sometimes it is necessary in a configuration file to refer to a constant defined in a class. This can be done with this lookup implementation. Variable
764      * names must be in the format {@code apackage.AClass.AFIELD}. The {@code lookup(String)} method will split the passed in string at the last dot, separating
765      * the fully qualified class name and the name of the constant (i.e. <strong>static final</strong>) member field. Then the class is loaded and the field's
766      * value is obtained using reflection.
767      * </p>
768      * <p>
769      * Once retrieved values are cached for fast access. This class is thread-safe. It can be used as a standard (i.e. global) lookup object and serve multiple
770      * clients concurrently.
771      * </p>
772      * <p>
773      * Using a {@link StringLookup} from the {@link StringLookupFactory}:
774      * </p>
775      *
776      * <pre>
777      * StringLookupFactory.INSTANCE.constantStringLookup().lookup("java.awt.event.KeyEvent.VK_ESCAPE");
778      * </pre>
779      * <p>
780      * Using a {@link StringSubstitutor}:
781      * </p>
782      *
783      * <pre>
784      * StringSubstitutor.createInterpolator().replace("... ${const:java.awt.event.KeyEvent.VK_ESCAPE} ..."));
785      * </pre>
786      * <p>
787      * The above examples convert {@code java.awt.event.KeyEvent.VK_ESCAPE} to {@code "27"}.
788      * </p>
789      *
790      * @return The ConstantStringLookup singleton instance.
791      * @since 1.5
792      */
793     public StringLookup constantStringLookup() {
794         return ConstantStringLookup.INSTANCE;
795     }
796 
797     /**
798      * Returns the DateStringLookup singleton instance to format the current date with the format given in the key in a format compatible with
799      * {@link java.text.SimpleDateFormat}.
800      * <p>
801      * Using a {@link StringLookup} from the {@link StringLookupFactory}:
802      * </p>
803      *
804      * <pre>
805      * StringLookupFactory.INSTANCE.dateStringLookup().lookup("yyyy-MM-dd");
806      * </pre>
807      * <p>
808      * Using a {@link StringSubstitutor}:
809      * </p>
810      *
811      * <pre>
812      * StringSubstitutor.createInterpolator().replace("... ${date:yyyy-MM-dd} ..."));
813      * </pre>
814      * <p>
815      * The above examples convert {@code "yyyy-MM-dd"} to todays's date, for example, {@code "2019-08-04"}.
816      * </p>
817      *
818      * @return The DateStringLookup singleton instance.
819      */
820     public StringLookup dateStringLookup() {
821         return DateStringLookup.INSTANCE;
822     }
823 
824     /**
825      * Returns the DnsStringLookup singleton instance where the lookup key is one of:
826      * <ul>
827      * <li><strong>name</strong>: for the local host name, for example {@code EXAMPLE} but also {@code EXAMPLE.apache.org}.</li>
828      * <li><strong>canonical-name</strong>: for the local canonical host name, for example {@code EXAMPLE.apache.org}.</li>
829      * <li><strong>address</strong>: for the local host address, for example {@code 192.168.56.1}.</li>
830      * </ul>
831      *
832      * <p>
833      * Using a {@link StringLookup} from the {@link StringLookupFactory}:
834      * </p>
835      *
836      * <pre>
837      * StringLookupFactory.INSTANCE.dnsStringLookup().lookup("address|apache.org");
838      * </pre>
839      * <p>
840      * When used through a {@link StringSubstitutor}, this lookup must either be added programmatically (as below) or enabled as a default lookup using the
841      * {@value #DEFAULT_STRING_LOOKUPS_PROPERTY} system property (see class documentation).
842      * </p>
843      *
844      * <pre>
845      * Map&lt;String, StringLookup&gt; lookupMap = new HashMap&lt;&gt;();
846      * lookupMap.put("dns", StringLookupFactory.INSTANCE.dnsStringLookup());
847      *
848      * StringLookup variableResolver = StringLookupFactory.INSTANCE.interpolatorStringLookup(lookupMap, null, false);
849      *
850      * new StringSubstitutor(variableResolver).replace("... ${dns:address|apache.org} ...");
851      * </pre>
852      * <p>
853      * The above examples convert {@code "address|apache.org"} to the IP address of {@code apache.org}.
854      * </p>
855      *
856      * @return the DnsStringLookup singleton instance.
857      * @since 1.8
858      */
859     public StringLookup dnsStringLookup() {
860         return DnsStringLookup.INSTANCE;
861     }
862 
863     /**
864      * Returns the EnvironmentVariableStringLookup singleton instance where the lookup key is an environment variable name.
865      * <p>
866      * Using a {@link StringLookup} from the {@link StringLookupFactory}:
867      * </p>
868      *
869      * <pre>
870      * StringLookupFactory.INSTANCE.environmentVariableStringLookup().lookup("USER");
871      * </pre>
872      * <p>
873      * Using a {@link StringSubstitutor}:
874      * </p>
875      *
876      * <pre>
877      * StringSubstitutor.createInterpolator().replace("... ${env:USER} ..."));
878      * </pre>
879      * <p>
880      * The above examples convert (on Linux) {@code "USER"} to the current user name. On Windows 10, you would use {@code "USERNAME"} to the same effect.
881      * </p>
882      *
883      * @return The EnvironmentVariableStringLookup singleton instance.
884      */
885     public StringLookup environmentVariableStringLookup() {
886         return StringLookupFactory.INSTANCE_ENVIRONMENT_VARIABLES;
887     }
888 
889     /**
890      * Returns a file StringLookup instance.
891      * <p>
892      * If this factory was built using {@link Builder#setFences(Path...)}, then the string lookup is fenced and will throw an {@link IllegalArgumentException}
893      * if a lookup causes causes a path to resolve outside of these fences. Otherwise, the result is unfenced to preserved behavior from previous versions.
894      * </p>
895      * <em>Using a fenced StringLookup</em>
896      * <p>
897      * To use a fenced {@link StringLookup}, use {@link StringLookupFactory#builder()}:
898      * </p>
899      *
900      * <pre>
901      * // Make the fence the current directory
902      * StringLookupFactory factory = StringLookupFactory.builder().setFences(Paths.get("")).get();
903      * factory.fileStringLookup().lookup("UTF-8:com/domain/document.txt");
904      *
905      * // throws IllegalArgumentException
906      * factory.fileStringLookup().lookup("UTF-8:/rootdir/foo/document.txt");
907      *
908      * // throws IllegalArgumentException
909      * factory.fileStringLookup().lookup("UTF-8:../document.txt");
910      * </pre>
911      *
912      * <em>Using an unfenced StringLookup</em>
913      * <p>
914      * To use an unfenced {@link StringLookup}, use {@link StringLookupFactory#INSTANCE}:
915      * </p>
916      *
917      * <pre>
918      * StringLookupFactory.INSTANCE.fileStringLookup().lookup("UTF-8:com/domain/document.properties");
919      * </pre>
920      *
921      * <em>Using a StringLookup with StringSubstitutor</em>
922      * <p>
923      * To build a fenced StringSubstitutor, use:
924      * </p>
925      *
926      * <pre>
927      * // Make the fence the current directory
928      * final StringLookupFactory factory = StringLookupFactory.builder().setFences(Paths.get("")).get();
929      * final StringSubstitutor stringSubstitutor = new StringSubstitutor(factory.interpolatorStringLookup());
930      * stringSubstitutor.replace("... ${file:UTF-8:com/domain/document.txt} ..."));
931      *
932      * // throws IllegalArgumentException
933      * stringSubstitutor.replace("... ${file:UTF-8:/rootdir/foo/document.txt} ..."));
934      * </pre>
935      * <p>
936      * Using an unfenced {@link StringSubstitutor}:
937      * </p>
938      *
939      * <pre>
940      * StringSubstitutor.createInterpolator().replace("... ${file:UTF-8:com/domain/document.txt} ..."));
941      * </pre>
942      * <p>
943      * The above examples convert {@code "UTF-8:com/domain/document.txt"} to the contents of the file.
944      * </p>
945      *
946      * @return a file StringLookup instance.
947      * @since 1.5
948      */
949     public StringLookup fileStringLookup() {
950         return fences != null ? fileStringLookup(fences) : FileStringLookup.INSTANCE;
951     }
952 
953     /**
954      * Returns a fenced file StringLookup instance.
955      * <p>
956      * To use a {@link StringLookup} fenced by the current directory, use:
957      * </p>
958      *
959      * <pre>
960      * StringLookupFactory.INSTANCE.fileStringLookup(Paths.get("")).lookup("UTF-8:com/domain/document.txt");
961      *
962      * // throws IllegalArgumentException
963      * StringLookupFactory.INSTANCE.fileStringLookup(Paths.get("")).lookup("UTF-8:/rootdir/foo/document.txt");
964      *
965      * // throws IllegalArgumentException
966      * StringLookupFactory.INSTANCE.fileStringLookup(Paths.get("")).lookup("UTF-8:../com/domain/document.txt");
967      * </pre>
968      * <p>
969      * The above example converts {@code "UTF-8:com/domain/document.txt"} to the contents of the file.
970      * </p>
971      * <p>
972      * {@link StringSubstitutor} methods like {@link StringSubstitutor#replace(String)} will throw a {@link IllegalArgumentException} when a file doesn't
973      * resolves in a fence.
974      * </p>
975      *
976      * @param fences The fences guarding Path resolution.
977      * @return a file StringLookup instance.
978      * @since 1.12.0
979      */
980     public StringLookup fileStringLookup(final Path... fences) {
981         return new FileStringLookup(fences);
982     }
983 
984     /**
985      * Returns a new function-based lookup where the request for a lookup is answered by applying the function with a lookup key.
986      *
987      * @param <R>      the function return type.
988      * @param function the function.
989      * @return a new MapStringLookup.
990      * @since 1.9
991      */
992     public <R> StringLookup functionStringLookup(final Function<String, R> function) {
993         return FunctionStringLookup.on(function);
994     }
995 
996     /**
997      * Returns a {@link InterpolatorStringLookup} containing the configured {@link #addDefaultStringLookups(Map) default lookups}. See the class documentation
998      * for details on how these defaults are configured.
999      * <p>
1000      * Using a {@link StringLookup} from the {@link StringLookupFactory}:
1001      * </p>
1002      *
1003      * <pre>
1004      * StringLookupFactory.INSTANCE.interpolatorStringLookup().lookup("${sys:os.name}, ${env:USER}");
1005      * </pre>
1006      * <p>
1007      * Using a {@link StringSubstitutor}:
1008      * </p>
1009      *
1010      * <pre>
1011      * StringSubstitutor.createInterpolator().replace("... ${sys:os.name}, ${env:USER} ..."));
1012      * </pre>
1013      * <p>
1014      * The above examples convert {@code "${sys:os.name}, ${env:USER}"} to the OS name and Linux user name.
1015      * </p>
1016      *
1017      * @return the default {@link InterpolatorStringLookup}.
1018      */
1019     public StringLookup interpolatorStringLookup() {
1020         return InterpolatorStringLookup.INSTANCE;
1021     }
1022 
1023     /**
1024      * Returns a new InterpolatorStringLookup. If {@code addDefaultLookups} is {@code true}, the configured {@link #addDefaultStringLookups(Map) default
1025      * lookups} are included in addition to the ones provided in {@code stringLookupMap}. (See the class documentation for details on how default lookups are
1026      * configured.)
1027      *
1028      * @param stringLookupMap     the map of string lookups.
1029      * @param defaultStringLookup the default string lookup; this lookup is used when a variable cannot be resolved using the lookups in {@code stringLookupMap}
1030      *                            or the configured default lookups (if enabled)
1031      * @param addDefaultLookups   whether to use default lookups as described above.
1032      * @return a new InterpolatorStringLookup.
1033      * @since 1.4
1034      */
1035     public StringLookup interpolatorStringLookup(final Map<String, StringLookup> stringLookupMap, final StringLookup defaultStringLookup,
1036             final boolean addDefaultLookups) {
1037         return new InterpolatorStringLookup(stringLookupMap, defaultStringLookup, addDefaultLookups);
1038     }
1039 
1040     /**
1041      * Returns a new InterpolatorStringLookup using the given key-value pairs and the configured {@link #addDefaultStringLookups(Map) default lookups} to
1042      * resolve variables. (See the class documentation for details on how default lookups are configured.)
1043      *
1044      * @param <V> the value type the default string lookup's map.
1045      * @param map the default map for string lookups.
1046      * @return a new InterpolatorStringLookup.
1047      */
1048     public <V> StringLookup interpolatorStringLookup(final Map<String, V> map) {
1049         return new InterpolatorStringLookup(map);
1050     }
1051 
1052     /**
1053      * Returns a new InterpolatorStringLookup using the given lookup and the configured {@link #addDefaultStringLookups(Map) default lookups} to resolve
1054      * variables. (See the class documentation for details on how default lookups are configured.)
1055      *
1056      * @param defaultStringLookup the default string lookup.
1057      * @return a new InterpolatorStringLookup.
1058      */
1059     public StringLookup interpolatorStringLookup(final StringLookup defaultStringLookup) {
1060         return new InterpolatorStringLookup(defaultStringLookup);
1061     }
1062 
1063     /**
1064      * Returns the JavaPlatformStringLookup singleton instance. Looks up keys related to Java: Java version, JRE version, VM version, and so on.
1065      * <p>
1066      * The lookup keys with examples are:
1067      * </p>
1068      * <ul>
1069      * <li><strong>version</strong>: "Java version 1.8.0_181"</li>
1070      * <li><strong>runtime</strong>: "Java(TM) SE Runtime Environment (build 1.8.0_181-b13) from Oracle Corporation"</li>
1071      * <li><strong>vm</strong>: "Java HotSpot(TM) 64-Bit Server VM (build 25.181-b13, mixed mode)"</li>
1072      * <li><strong>os</strong>: "Windows 10 10.0, architecture: amd64-64"</li>
1073      * <li><strong>hardware</strong>: "processors: 4, architecture: amd64-64, instruction sets: amd64"</li>
1074      * <li><strong>locale</strong>: "default locale: en_US, platform encoding: iso-8859-1"</li>
1075      * </ul>
1076      *
1077      * <p>
1078      * Using a {@link StringLookup} from the {@link StringLookupFactory}:
1079      * </p>
1080      *
1081      * <pre>
1082      * StringLookupFactory.INSTANCE.javaPlatformStringLookup().lookup("version");
1083      * </pre>
1084      * <p>
1085      * Using a {@link StringSubstitutor}:
1086      * </p>
1087      *
1088      * <pre>
1089      * StringSubstitutor.createInterpolator().replace("... ${java:version} ..."));
1090      * </pre>
1091      * <p>
1092      * The above examples convert {@code "version"} to the current VM version, for example, {@code "Java version 1.8.0_181"}.
1093      * </p>
1094      *
1095      * @return The JavaPlatformStringLookup singleton instance.
1096      */
1097     public StringLookup javaPlatformStringLookup() {
1098         return JavaPlatformStringLookup.INSTANCE;
1099     }
1100 
1101     /**
1102      * Returns the InetAddressStringLookup instance where the lookup key for {@link InetAddress#getLocalHost()} is one of:
1103      * <ul>
1104      * <li><strong>name</strong>: for the local host name, for example {@code EXAMPLE}.</li>
1105      * <li><strong>canonical-name</strong>: for the local canonical host name, for example {@code EXAMPLE.apache.org}.</li>
1106      * <li><strong>address</strong>: for the local host address, for example {@code 192.168.56.1}.</li>
1107      * </ul>
1108      *
1109      * <p>
1110      * Using a {@link StringLookup} from the {@link StringLookupFactory}:
1111      * </p>
1112      *
1113      * <pre>
1114      * StringLookupFactory.INSTANCE.localHostStringLookup().lookup("canonical-name");
1115      * </pre>
1116      * <p>
1117      * Using a {@link StringSubstitutor}:
1118      * </p>
1119      *
1120      * <pre>
1121      * StringSubstitutor.createInterpolator().replace("... ${localhost:canonical-name} ..."));
1122      * </pre>
1123      * <p>
1124      * The above examples convert {@code "canonical-name"} to the current host name, for example, {@code "EXAMPLE.apache.org"}.
1125      * </p>
1126      *
1127      * @return The InetAddressStringLookup singleton instance.
1128      */
1129     public StringLookup localHostStringLookup() {
1130         return InetAddressStringLookup.LOCAL_HOST;
1131     }
1132 
1133     /**
1134      * Returns the InetAddressStringLookup instance where the lookup key for {@link InetAddress#getLoopbackAddress()} is one of:
1135      * <ul>
1136      * <li><strong>name</strong>: for the local host name, for example {@code EXAMPLE}.</li>
1137      * <li><strong>canonical-name</strong>: for the local canonical host name, for example {@code EXAMPLE.apache.org}.</li>
1138      * <li><strong>address</strong>: for the local host address, for example {@code 192.168.56.1}.</li>
1139      * </ul>
1140      *
1141      * <p>
1142      * Using a {@link StringLookup} from the {@link StringLookupFactory}:
1143      * </p>
1144      *
1145      * <pre>
1146      * StringLookupFactory.INSTANCE.loopbackAddressStringLookup().lookup("canonical-name");
1147      * </pre>
1148      * <p>
1149      * Using a {@link StringSubstitutor}:
1150      * </p>
1151      *
1152      * <pre>
1153      * StringSubstitutor.createInterpolator().replace("... ${loopbackAddress:canonical-name} ..."));
1154      * </pre>
1155      * <p>
1156      * The above examples convert {@code "canonical-name"} to the current host name, for example, {@code "EXAMPLE.apache.org"}.
1157      * </p>
1158      *
1159      * @return The InetAddressStringLookup singleton instance.
1160      */
1161     public StringLookup loopbackAddressStringLookup() {
1162         return InetAddressStringLookup.LOOPACK_ADDRESS;
1163     }
1164 
1165     /**
1166      * Returns a new map-based lookup where the request for a lookup is answered with the value for that key.
1167      *
1168      * @param <V> the map value type.
1169      * @param map the map.
1170      * @return a new MapStringLookup.
1171      */
1172     public <V> StringLookup mapStringLookup(final Map<String, V> map) {
1173         return FunctionStringLookup.on(map);
1174     }
1175 
1176     /**
1177      * Returns the NullStringLookup singleton instance which always returns null.
1178      *
1179      * @return The NullStringLookup singleton instance.
1180      */
1181     public StringLookup nullStringLookup() {
1182         return StringLookupFactory.INSTANCE_NULL;
1183     }
1184 
1185     /**
1186      * Returns a Properties StringLookup instance.
1187      * <p>
1188      * If this factory was built using {@link Builder#setFences(Path...)}, then the string lookup is fenced and will throw an {@link IllegalArgumentException}
1189      * if a lookup causes causes a path to resolve outside of these fences. Otherwise, the result is unfenced to preserved behavior from previous versions.
1190      * </p>
1191      * <p>
1192      * We looks up a value for the key in the format "DocumentPath::MyKey".
1193      * </p>
1194      * <p>
1195      * Note the use of "::" instead of ":" to allow for "C:" drive letters in paths.
1196      * </p>
1197      * <p>
1198      * For example: "com/domain/document.properties::MyKey".
1199      * </p>
1200      * <em>Using a fenced StringLookup</em>
1201      * <p>
1202      * To use a fenced {@link StringLookup}, use {@link StringLookupFactory#builder()}:
1203      * </p>
1204      *
1205      * <pre>
1206      * // Make the fence the current directory
1207      * StringLookupFactory factory = StringLookupFactory.builder().setFences(Paths.get("")).get();
1208      * factory.propertiesStringLookup().lookup("com/domain/document.properties::MyKey");
1209      *
1210      * // throws IllegalArgumentException
1211      * factory.propertiesStringLookup().lookup("/com/domain/document.properties::MyKey");
1212      *
1213      * // throws IllegalArgumentException
1214      * factory.propertiesStringLookup().lookup("../com/domain/document.properties::MyKey");
1215      * </pre>
1216      *
1217      * <em>Using an unfenced StringLookup</em>
1218      * <p>
1219      * To use an unfenced {@link StringLookup}, use {@link StringLookupFactory#INSTANCE}:
1220      * </p>
1221      *
1222      * <pre>
1223      * StringLookupFactory.INSTANCE.propertiesStringLookup().lookup("com/domain/document.properties::MyKey");
1224      * </pre>
1225      *
1226      * <em>Using a StringLookup with StringSubstitutor</em>
1227      * <p>
1228      * To build a fenced StringSubstitutor, use:
1229      * </p>
1230      *
1231      * <pre>
1232      * // Make the fence the current directory
1233      * final StringLookupFactory factory = StringLookupFactory.builder().setFences(Paths.get("")).get();
1234      * final StringSubstitutor stringSubstitutor = new StringSubstitutor(factory.interpolatorStringLookup());
1235      * stringSubstitutor.replace("... ${properties:com/domain/document.properties::MyKey} ..."));
1236      *
1237      * // throws IllegalArgumentException
1238      * stringSubstitutor.replace("... ${properties:/rootdir/foo/document.properties::MyKey} ..."));
1239      * </pre>
1240      * <p>
1241      * Using an unfenced {@link StringSubstitutor}:
1242      * </p>
1243      *
1244      * <pre>
1245      * StringSubstitutor.createInterpolator().replace("... ${properties:com/domain/document.properties::MyKey} ..."));
1246      * </pre>
1247      * <p>
1248      * The above examples convert {@code "com/domain/document.properties::MyKey"} to the key value in the properties file at the path
1249      * "com/domain/document.properties".
1250      * </p>
1251      *
1252      * @return a Properties StringLookup instance.
1253      * @since 1.5
1254      */
1255     public StringLookup propertiesStringLookup() {
1256         return fences != null ? propertiesStringLookup(fences) : PropertiesStringLookup.INSTANCE;
1257     }
1258 
1259     /**
1260      * Returns a fenced Properties StringLookup instance.
1261      * <p>
1262      * Looks up the value for the key in the format "DocumentPath::MyKey":.
1263      * </p>
1264      * <p>
1265      * Note the use of "::" instead of ":" to allow for "C:" drive letters in paths.
1266      * </p>
1267      * <p>
1268      * For example: "com/domain/document.properties::MyKey".
1269      * </p>
1270      * <p>
1271      * To use a {@link StringLookup} fenced by the current directory, use:
1272      * </p>
1273      *
1274      * <pre>
1275      * StringLookupFactory.INSTANCE.fileStringLookup(Paths.get("")).lookup("com/domain/document.properties::MyKey");
1276      *
1277      * // throws IllegalArgumentException
1278      * StringLookupFactory.INSTANCE.fileStringLookup(Paths.get("")).lookup("com/domain/document.properties::MyKey");
1279      *
1280      * // throws IllegalArgumentException
1281      * StringLookupFactory.INSTANCE.fileStringLookup(Paths.get("")).lookup("com/domain/document.properties::MyKey");
1282      * </pre>
1283      * <p>
1284      * The above example converts {@code "com/domain/document.properties::MyKey"} to the key value in the properties file at the path
1285      * "com/domain/document.properties".
1286      * </p>
1287      * <p>
1288      * {@link StringSubstitutor} methods like {@link StringSubstitutor#replace(String)} will throw a {@link IllegalArgumentException} when a file doesn't
1289      * resolves in a fence.
1290      * </p>
1291      *
1292      * @param fences The fences guarding Path resolution.
1293      * @return a Properties StringLookup instance.
1294      * @since 1.12.0
1295      */
1296     public StringLookup propertiesStringLookup(final Path... fences) {
1297         return new PropertiesStringLookup(fences);
1298     }
1299 
1300     /**
1301      * Returns the ResourceBundleStringLookup singleton instance.
1302      * <p>
1303      * Looks up the value for a given key in the format "BundleName:BundleKey".
1304      * </p>
1305      * <p>
1306      * For example: "com.domain.messages:MyKey".
1307      * </p>
1308      * <p>
1309      * Using a {@link StringLookup} from the {@link StringLookupFactory}:
1310      * </p>
1311      *
1312      * <pre>
1313      * StringLookupFactory.INSTANCE.resourceBundleStringLookup().lookup("com.domain.messages:MyKey");
1314      * </pre>
1315      * <p>
1316      * Using a {@link StringSubstitutor}:
1317      * </p>
1318      *
1319      * <pre>
1320      * StringSubstitutor.createInterpolator().replace("... ${resourceBundle:com.domain.messages:MyKey} ..."));
1321      * </pre>
1322      * <p>
1323      * The above examples convert {@code "com.domain.messages:MyKey"} to the key value in the resource bundle at {@code "com.domain.messages"}.
1324      * </p>
1325      *
1326      * @return The ResourceBundleStringLookup singleton instance.
1327      */
1328     public StringLookup resourceBundleStringLookup() {
1329         return ResourceBundleStringLookup.INSTANCE;
1330     }
1331 
1332     /**
1333      * Returns a ResourceBundleStringLookup instance for the given bundle name.
1334      * <p>
1335      * Looks up the value for a given key in the format "MyKey".
1336      * </p>
1337      * <p>
1338      * For example: "MyKey".
1339      * </p>
1340      * <p>
1341      * Using a {@link StringLookup} from the {@link StringLookupFactory}:
1342      * </p>
1343      *
1344      * <pre>
1345      * StringLookupFactory.INSTANCE.resourceBundleStringLookup("com.domain.messages").lookup("MyKey");
1346      * </pre>
1347      * <p>
1348      * The above example converts {@code "MyKey"} to the key value in the resource bundle at {@code "com.domain.messages"}.
1349      * </p>
1350      *
1351      * @param bundleName Only lookup in this bundle.
1352      * @return a ResourceBundleStringLookup instance for the given bundle name.
1353      * @since 1.5
1354      */
1355     public StringLookup resourceBundleStringLookup(final String bundleName) {
1356         return new ResourceBundleStringLookup(bundleName);
1357     }
1358 
1359     /**
1360      * Returns the ScriptStringLookup singleton instance. NOTE: This lookup is not included as a {@link #addDefaultStringLookups(Map) default lookup} unless
1361      * explicitly enabled. See the class level documentation for details.
1362      * <p>
1363      * Looks up the value for the key in the format "ScriptEngineName:Script".
1364      * </p>
1365      * <p>
1366      * For example: "javascript:3 + 4".
1367      * </p>
1368      * <p>
1369      * Using a {@link StringLookup} from the {@link StringLookupFactory}:
1370      * </p>
1371      *
1372      * <pre>
1373      * StringLookupFactory.INSTANCE.scriptStringLookup().lookup("javascript:3 + 4");
1374      * </pre>
1375      * <p>
1376      * When used through a {@link StringSubstitutor}, this lookup must either be added programmatically (as below) or enabled as a default lookup using the
1377      * {@value #DEFAULT_STRING_LOOKUPS_PROPERTY} system property (see class documentation).
1378      * </p>
1379      *
1380      * <pre>
1381      * Map&lt;String, StringLookup&gt; lookupMap = new HashMap&lt;&gt;();
1382      * lookupMap.put("script", StringLookupFactory.INSTANCE.scriptStringLookup());
1383      *
1384      * StringLookup variableResolver = StringLookupFactory.INSTANCE.interpolatorStringLookup(lookupMap, null, false);
1385      *
1386      * String value = new StringSubstitutor(variableResolver).replace("${script:javascript:3 + 4}");
1387      * </pre>
1388      * <p>
1389      * The above examples convert {@code "javascript:3 + 4"} to {@code "7"}.
1390      * </p>
1391      *
1392      * @return The ScriptStringLookup singleton instance.
1393      * @since 1.5
1394      */
1395     public StringLookup scriptStringLookup() {
1396         return ScriptStringLookup.INSTANCE;
1397     }
1398 
1399     /**
1400      * Returns the SystemPropertyStringLookup singleton instance where the lookup key is a system property name.
1401      *
1402      * <p>
1403      * Using a {@link StringLookup} from the {@link StringLookupFactory}:
1404      * </p>
1405      *
1406      * <pre>
1407      * StringLookupFactory.INSTANCE.systemPropertyStringLookup().lookup("os.name");
1408      * </pre>
1409      * <p>
1410      * Using a {@link StringSubstitutor}:
1411      * </p>
1412      *
1413      * <pre>
1414      * StringSubstitutor.createInterpolator().replace("... ${sys:os.name} ..."));
1415      * </pre>
1416      * <p>
1417      * The above examples convert {@code "os.name"} to the operating system name.
1418      * </p>
1419      *
1420      * @return The SystemPropertyStringLookup singleton instance.
1421      */
1422     public StringLookup systemPropertyStringLookup() {
1423         return StringLookupFactory.INSTANCE_SYSTEM_PROPERTIES;
1424     }
1425 
1426     /**
1427      * Returns the UrlDecoderStringLookup singleton instance.
1428      * <p>
1429      * Decodes URL Strings using the UTF-8 encoding.
1430      * </p>
1431      * <p>
1432      * For example: "Hello%20World%21" becomes "Hello World!".
1433      * </p>
1434      * <p>
1435      * Using a {@link StringLookup} from the {@link StringLookupFactory}:
1436      * </p>
1437      *
1438      * <pre>
1439      * StringLookupFactory.INSTANCE.urlDecoderStringLookup().lookup("Hello%20World%21");
1440      * </pre>
1441      * <p>
1442      * Using a {@link StringSubstitutor}:
1443      * </p>
1444      *
1445      * <pre>
1446      * StringSubstitutor.createInterpolator().replace("... ${urlDecoder:Hello%20World%21} ..."));
1447      * </pre>
1448      * <p>
1449      * The above examples convert {@code "Hello%20World%21"} to {@code "Hello World!"}.
1450      * </p>
1451      *
1452      * @return The UrlStringLookup singleton instance.
1453      * @since 1.6
1454      */
1455     public StringLookup urlDecoderStringLookup() {
1456         return UrlDecoderStringLookup.INSTANCE;
1457     }
1458 
1459     /**
1460      * Returns the UrlDecoderStringLookup singleton instance.
1461      * <p>
1462      * Decodes URL Strings using the UTF-8 encoding.
1463      * </p>
1464      * <p>
1465      * For example: "Hello World!" becomes "Hello+World%21".
1466      * </p>
1467      * <p>
1468      * Using a {@link StringLookup} from the {@link StringLookupFactory}:
1469      * </p>
1470      *
1471      * <pre>
1472      * StringLookupFactory.INSTANCE.urlEncoderStringLookup().lookup("Hello World!");
1473      * </pre>
1474      * <p>
1475      * Using a {@link StringSubstitutor}:
1476      * </p>
1477      *
1478      * <pre>
1479      * StringSubstitutor.createInterpolator().replace("... ${urlEncoder:Hello World!} ..."));
1480      * </pre>
1481      * <p>
1482      * The above examples convert {@code "Hello World!"} to {@code "Hello%20World%21"}.
1483      * </p>
1484      *
1485      * @return The UrlStringLookup singleton instance.
1486      * @since 1.6
1487      */
1488     public StringLookup urlEncoderStringLookup() {
1489         return UrlEncoderStringLookup.INSTANCE;
1490     }
1491 
1492     /**
1493      * Returns the UrlStringLookup singleton instance. This lookup is not included as a {@link #addDefaultStringLookups(Map) default lookup} unless explicitly
1494      * enabled. See the class level documentation for details.
1495      * <p>
1496      * Looks up the value for the key in the format "CharsetName:URL".
1497      * </p>
1498      * <p>
1499      * For example, using the HTTP scheme: "UTF-8:http://www.google.com"
1500      * </p>
1501      * <p>
1502      * For example, using the file scheme: "UTF-8:file:///C:/somehome/commons/commons-text/src/test/resources/document.properties"
1503      * </p>
1504      * <p>
1505      * Using a {@link StringLookup} from the {@link StringLookupFactory}:
1506      * </p>
1507      *
1508      * <pre>
1509      * StringLookupFactory.INSTANCE.urlStringLookup().lookup("UTF-8:https://www.apache.org");
1510      * </pre>
1511      * <p>
1512      * When used through a {@link StringSubstitutor}, this lookup must either be added programmatically (as below) or enabled as a default lookup using the
1513      * {@value #DEFAULT_STRING_LOOKUPS_PROPERTY} system property (see class documentation).
1514      * </p>
1515      *
1516      * <pre>
1517      * Map&lt;String, StringLookup&gt; lookupMap = new HashMap&lt;&gt;();
1518      * lookupMap.put("url", StringLookupFactory.INSTANCE.urlStringLookup());
1519      *
1520      * StringLookup variableResolver = StringLookupFactory.INSTANCE.interpolatorStringLookup(lookupMap, null, false);
1521      *
1522      * String value = new StringSubstitutor(variableResolver).replace("${url:UTF-8:https://www.apache.org}");
1523      * </pre>
1524      * <p>
1525      * The above examples convert {@code "UTF-8:https://www.apache.org"} to the contents of that page.
1526      * </p>
1527      *
1528      * @return The UrlStringLookup singleton instance.
1529      * @since 1.5
1530      */
1531     public StringLookup urlStringLookup() {
1532         return UrlStringLookup.INSTANCE;
1533     }
1534 
1535     /**
1536      * Returns the XmlDecoderStringLookup singleton instance.
1537      * <p>
1538      * Decodes strings according to the XML 1.0 specification.
1539      * </p>
1540      * <p>
1541      * For example: "&amp;lt;element&amp;gt;" becomes "&lt;element&gt;".
1542      * </p>
1543      * <p>
1544      * Using a {@link StringLookup} from the {@link StringLookupFactory}:
1545      * </p>
1546      *
1547      * <pre>
1548      * StringLookupFactory.INSTANCE.xmlDecoderStringLookup().lookup("&amp;lt;element&amp;gt;");
1549      * </pre>
1550      * <p>
1551      * Using a {@link StringSubstitutor}:
1552      * </p>
1553      *
1554      * <pre>
1555      * StringSubstitutor.createInterpolator().replace("... ${xmlDecoder:&amp;lt;element&amp;gt;} ..."));
1556      * </pre>
1557      * <p>
1558      * The above examples convert {@code "&lt;element&gt;"} to {@code "<element>"}.
1559      * </p>
1560      *
1561      * @return The XmlDecoderStringLookup singleton instance.
1562      * @since 1.11.0
1563      */
1564     public StringLookup xmlDecoderStringLookup() {
1565         return XmlDecoderStringLookup.INSTANCE;
1566     }
1567 
1568     /**
1569      * Returns the XmlEncoderStringLookup singleton instance.
1570      * <p>
1571      * Encodes strings according to the XML 1.0 specification.
1572      * </p>
1573      * <p>
1574      * For example: "&lt;element&gt;" becomes "&amp;lt;element&amp;gt;".
1575      * </p>
1576      * <p>
1577      * Using a {@link StringLookup} from the {@link StringLookupFactory}:
1578      * </p>
1579      *
1580      * <pre>
1581      * StringLookupFactory.INSTANCE.xmlEncoderStringLookup().lookup("&lt;element&gt;");
1582      * </pre>
1583      * <p>
1584      * Using a {@link StringSubstitutor}:
1585      * </p>
1586      *
1587      * <pre>
1588      * StringSubstitutor.createInterpolator().replace("... ${xmlEncoder:&lt;element&gt;} ..."));
1589      * </pre>
1590      * <p>
1591      * The above examples convert {@code "<element>"} to {@code "&lt;element&gt;"}.
1592      * </p>
1593      *
1594      * @return The XmlEncoderStringLookup singleton instance.
1595      * @since 1.11.0
1596      */
1597     public StringLookup xmlEncoderStringLookup() {
1598         return XmlEncoderStringLookup.INSTANCE;
1599     }
1600 
1601     /**
1602      * Returns an XML StringLookup instance.
1603      * <p>
1604      * If this factory was built using {@link Builder#setFences(Path...)}, then the string lookup is fenced and will throw an {@link IllegalArgumentException}
1605      * if a lookup causes causes a path to resolve outside of these fences. Otherwise, the result is unfenced to preserved behavior from previous versions.
1606      * </p>
1607      * <p>
1608      * We look up the value for the key in the format "DocumentPath:XPath".
1609      * </p>
1610      * <p>
1611      * For example: "com/domain/document.xml:/path/to/node".
1612      * </p>
1613      * <p>
1614      * Using a {@link StringLookup} from the {@link StringLookupFactory}:
1615      * </p>
1616      *
1617      * <pre>
1618      * StringLookupFactory.INSTANCE.xmlStringLookup().lookup("com/domain/document.xml:/path/to/node");
1619      * </pre>
1620      * <p>
1621      * Using a {@link StringSubstitutor}:
1622      * </p>
1623      *
1624      * <pre>
1625      * StringSubstitutor.createInterpolator().replace("... ${xml:com/domain/document.xml:/path/to/node} ..."));
1626      * </pre>
1627      * <p>
1628      * The above examples convert {@code "com/domain/document.xml:/path/to/node"} to the value of the XPath in the XML document.
1629      * </p>
1630      *
1631      * @return An XML StringLookup instance.
1632      * @since 1.5
1633      */
1634     public StringLookup xmlStringLookup() {
1635         return fences != null ? xmlStringLookup(XmlStringLookup.DEFAULT_FEATURES, fences) : XmlStringLookup.INSTANCE;
1636     }
1637 
1638     /**
1639      * Returns an XML StringLookup instance.
1640      * <p>
1641      * If this factory was built using {@link Builder#setFences(Path...)}, then the string lookup is fenced and will throw an {@link IllegalArgumentException}
1642      * if a lookup causes causes a path to resolve outside of these fences. Otherwise, the result is unfenced to preserved behavior from previous versions.
1643      * </p>
1644      * <p>
1645      * We look up the value for the key in the format "DocumentPath:XPath".
1646      * </p>
1647      * <p>
1648      * For example: "com/domain/document.xml:/path/to/node".
1649      * </p>
1650      * <p>
1651      * Using a {@link StringLookup} from the {@link StringLookupFactory}:
1652      * </p>
1653      *
1654      * <pre>
1655      * StringLookupFactory.INSTANCE.xmlStringLookup().lookup("com/domain/document.xml:/path/to/node");
1656      * </pre>
1657      * <p>
1658      * Using a {@link StringSubstitutor}:
1659      * </p>
1660      *
1661      * <pre>
1662      * StringSubstitutor.createInterpolator().replace("... ${xml:com/domain/document.xml:/path/to/node} ..."));
1663      * </pre>
1664      * <p>
1665      * The above examples convert {@code "com/domain/document.xml:/path/to/node"} to the value of the XPath in the XML document.
1666      * </p>
1667      *
1668      * @param xPathFactoryFeatures XPathFactory features to set.
1669      * @return An XML StringLookup instance.
1670      * @see XPathFactory#setFeature(String, boolean)
1671      * @since 1.11.0
1672      */
1673     public StringLookup xmlStringLookup(final Map<String, Boolean> xPathFactoryFeatures) {
1674         return xmlStringLookup(xPathFactoryFeatures, fences);
1675     }
1676 
1677     /**
1678      * Returns a fenced XML StringLookup instance.
1679      * <p>
1680      * If this factory was built using {@link Builder#setFences(Path...)}, then the string lookup is fenced and will throw an {@link IllegalArgumentException}
1681      * if a lookup causes causes a path to resolve outside of these fences. Otherwise, the result is unfenced to preserved behavior from previous versions.
1682      * </p>
1683      * <p>
1684      * We look up the value for the key in the format "DocumentPath:XPath".
1685      * </p>
1686      * <p>
1687      * For example: "com/domain/document.xml:/path/to/node".
1688      * </p>
1689      * <p>
1690      * Using a {@link StringLookup} from the {@link StringLookupFactory} fenced by the current directory ({@code Paths.get("")}):
1691      * </p>
1692      *
1693      * <pre>
1694      * StringLookupFactory.INSTANCE.xmlStringLookup(map, Pathe.get("")).lookup("com/domain/document.xml:/path/to/node");
1695      * </pre>
1696      * <p>
1697      * To use a {@link StringLookup} fenced by the current directory, use:
1698      * </p>
1699      *
1700      * <pre>
1701      * StringLookupFactory.INSTANCE.xmlStringLookup(Paths.get("")).lookup("com/domain/document.xml:/path/to/node");
1702      *
1703      * // throws IllegalArgumentException
1704      * StringLookupFactory.INSTANCE.xmlStringLookup(Paths.get("")).lookup("/rootdir/foo/document.xml:/path/to/node");
1705      *
1706      * // throws IllegalArgumentException
1707      * StringLookupFactory.INSTANCE.xmlStringLookup(Paths.get("")).lookup("../com/domain/document.xml:/path/to/node");
1708      * </pre>
1709      * <p>
1710      * The above examples convert {@code "com/domain/document.xml:/path/to/node"} to the value of the XPath in the XML document.
1711      * </p>
1712      * <p>
1713      * {@link StringSubstitutor} methods like {@link StringSubstitutor#replace(String)} will throw a {@link IllegalArgumentException} when a file doesn't
1714      * resolves in a fence.
1715      * </p>
1716      *
1717      * @param xPathFactoryFeatures XPathFactory features to set.
1718      * @param fences               The fences guarding Path resolution.
1719      * @return An XML StringLookup instance.
1720      * @since 1.12.0
1721      */
1722     public StringLookup xmlStringLookup(final Map<String, Boolean> xPathFactoryFeatures, final Path... fences) {
1723         return new XmlStringLookup(xPathFactoryFeatures, fences);
1724     }
1725 }