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