001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache license, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the license for the specific language governing permissions and
015 * limitations under the license.
016 */
017
018package org.apache.commons.text.lookup;
019
020import java.net.InetAddress;
021import java.nio.charset.StandardCharsets;
022import java.nio.file.Path;
023import java.util.Base64;
024import java.util.Collections;
025import java.util.HashMap;
026import java.util.Locale;
027import java.util.Map;
028import java.util.Properties;
029import java.util.function.BiFunction;
030import java.util.function.Function;
031import java.util.function.Supplier;
032
033import javax.xml.xpath.XPathFactory;
034
035import org.apache.commons.text.StringSubstitutor;
036
037/**
038 * Create instances of string lookups or access singleton string lookups implemented in this package.
039 * <p>
040 * The "classic" look up is {@link #mapStringLookup(Map)}.
041 * </p>
042 * <p>
043 * The methods for variable interpolation (A.K.A. variable substitution) are:
044 * </p>
045 * <ul>
046 * <li>{@link #interpolatorStringLookup()}.</li>
047 * <li>{@link #interpolatorStringLookup(Map)}.</li>
048 * <li>{@link #interpolatorStringLookup(StringLookup)}.</li>
049 * <li>{@link #interpolatorStringLookup(Map, StringLookup, boolean)}.</li>
050 * </ul>
051 * <p>
052 * Unless explicitly requested otherwise, a set of default lookups are included for convenience with these variable interpolation methods. These defaults are
053 * listed in the table below. However, the exact lookups included can be configured through the use of the {@value #DEFAULT_STRING_LOOKUPS_PROPERTY} system
054 * 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
055 * {@link DefaultStringLookup} enum. For example, setting this system property to {@code "BASE64_ENCODER,ENVIRONMENT"} will only include the
056 * {@link DefaultStringLookup#BASE64_ENCODER BASE64_ENCODER} and {@link DefaultStringLookup#ENVIRONMENT ENVIRONMENT} lookups. Setting the property to the empty
057 * string will cause no defaults to be configured. Note that not all lookups defined here and in {@link DefaultStringLookup} are included by default.
058 * Specifically, lookups that can execute code (e.g., {@link DefaultStringLookup#SCRIPT SCRIPT}) and those that can result in contact with remote servers (e.g.,
059 * {@link DefaultStringLookup#URL URL} and {@link DefaultStringLookup#DNS DNS}) are not included by default. The current set of default lookups can be accessed
060 * directly with {@link #addDefaultStringLookups(Map)}.
061 * </p>
062 * <table>
063 * <caption>Default String Lookups</caption>
064 * <tr>
065 * <th>Key</th>
066 * <th>Interface</th>
067 * <th>Factory Method</th>
068 * <th>Since</th>
069 * </tr>
070 * <tr>
071 * <td>{@value #KEY_BASE64_DECODER}</td>
072 * <td>{@link StringLookup}</td>
073 * <td>{@link #base64DecoderStringLookup()}</td>
074 * <td>1.6</td>
075 * </tr>
076 * <tr>
077 * <td>{@value #KEY_BASE64_ENCODER}</td>
078 * <td>{@link StringLookup}</td>
079 * <td>{@link #base64EncoderStringLookup()}</td>
080 * <td>1.6</td>
081 * </tr>
082 * <tr>
083 * <td>{@value #KEY_CONST}</td>
084 * <td>{@link StringLookup}</td>
085 * <td>{@link #constantStringLookup()}</td>
086 * <td>1.5</td>
087 * </tr>
088 * <tr>
089 * <td>{@value #KEY_DATE}</td>
090 * <td>{@link StringLookup}</td>
091 * <td>{@link #dateStringLookup()}</td>
092 * <td>1.5</td>
093 * </tr>
094 * <tr>
095 * <td>{@value #KEY_ENV}</td>
096 * <td>{@link StringLookup}</td>
097 * <td>{@link #environmentVariableStringLookup()}</td>
098 * <td>1.3</td>
099 * </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 */
225public 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}