StringLookupFactory.java

  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. package org.apache.commons.text.lookup;

  18. import java.net.InetAddress;
  19. import java.nio.charset.StandardCharsets;
  20. import java.nio.file.Path;
  21. import java.util.Base64;
  22. import java.util.Collections;
  23. import java.util.HashMap;
  24. import java.util.Locale;
  25. import java.util.Map;
  26. import java.util.Properties;
  27. import java.util.function.BiFunction;
  28. import java.util.function.Function;
  29. import java.util.function.Supplier;

  30. import javax.xml.xpath.XPathFactory;

  31. import org.apache.commons.text.StringSubstitutor;

  32. /**
  33.  * Create instances of string lookups or access singleton string lookups implemented in this package.
  34.  * <p>
  35.  * The "classic" look up is {@link #mapStringLookup(Map)}.
  36.  * </p>
  37.  * <p>
  38.  * The methods for variable interpolation (A.K.A. variable substitution) are:
  39.  * </p>
  40.  * <ul>
  41.  * <li>{@link #interpolatorStringLookup()}.</li>
  42.  * <li>{@link #interpolatorStringLookup(Map)}.</li>
  43.  * <li>{@link #interpolatorStringLookup(StringLookup)}.</li>
  44.  * <li>{@link #interpolatorStringLookup(Map, StringLookup, boolean)}.</li>
  45.  * </ul>
  46.  * <p>
  47.  * Unless explicitly requested otherwise, a set of default lookups are included for convenience with these variable interpolation methods. These defaults are
  48.  * listed in the table below. However, the exact lookups included can be configured through the use of the {@value #DEFAULT_STRING_LOOKUPS_PROPERTY} system
  49.  * 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
  50.  * {@link DefaultStringLookup} enum. For example, setting this system property to {@code "BASE64_ENCODER,ENVIRONMENT"} will only include the
  51.  * {@link DefaultStringLookup#BASE64_ENCODER BASE64_ENCODER} and {@link DefaultStringLookup#ENVIRONMENT ENVIRONMENT} lookups. Setting the property to the empty
  52.  * string will cause no defaults to be configured. Note that not all lookups defined here and in {@link DefaultStringLookup} are included by default.
  53.  * Specifically, lookups that can execute code (e.g., {@link DefaultStringLookup#SCRIPT SCRIPT}) and those that can result in contact with remote servers (e.g.,
  54.  * {@link DefaultStringLookup#URL URL} and {@link DefaultStringLookup#DNS DNS}) are not included by default. The current set of default lookups can be accessed
  55.  * directly with {@link #addDefaultStringLookups(Map)}.
  56.  * </p>
  57.  * <table>
  58.  * <caption>Default String Lookups</caption>
  59.  * <tr>
  60.  * <th>Key</th>
  61.  * <th>Interface</th>
  62.  * <th>Factory Method</th>
  63.  * <th>Since</th>
  64.  * </tr>
  65.  * <tr>
  66.  * <td>{@value #KEY_BASE64_DECODER}</td>
  67.  * <td>{@link StringLookup}</td>
  68.  * <td>{@link #base64DecoderStringLookup()}</td>
  69.  * <td>1.6</td>
  70.  * </tr>
  71.  * <tr>
  72.  * <td>{@value #KEY_BASE64_ENCODER}</td>
  73.  * <td>{@link StringLookup}</td>
  74.  * <td>{@link #base64EncoderStringLookup()}</td>
  75.  * <td>1.6</td>
  76.  * </tr>
  77.  * <tr>
  78.  * <td>{@value #KEY_CONST}</td>
  79.  * <td>{@link StringLookup}</td>
  80.  * <td>{@link #constantStringLookup()}</td>
  81.  * <td>1.5</td>
  82.  * </tr>
  83.  * <tr>
  84.  * <td>{@value #KEY_DATE}</td>
  85.  * <td>{@link StringLookup}</td>
  86.  * <td>{@link #dateStringLookup()}</td>
  87.  * <td>1.5</td>
  88.  * </tr>
  89.  * <tr>
  90.  * <td>{@value #KEY_ENV}</td>
  91.  * <td>{@link StringLookup}</td>
  92.  * <td>{@link #environmentVariableStringLookup()}</td>
  93.  * <td>1.3</td>
  94.  * </tr>
  95.  * <tr>
  96.  * <td>{@value #KEY_FILE}</td>
  97.  * <td>{@link StringLookup}</td>
  98.  * <td>{@link #fileStringLookup(Path...)}</td>
  99.  * <td>1.5</td>
  100.  * </tr>
  101.  * <tr>
  102.  * <td>{@value #KEY_JAVA}</td>
  103.  * <td>{@link StringLookup}</td>
  104.  * <td>{@link #javaPlatformStringLookup()}</td>
  105.  * <td>1.5</td>
  106.  * </tr>
  107.  * <tr>
  108.  * <td>{@value #KEY_LOCALHOST}</td>
  109.  * <td>{@link StringLookup}</td>
  110.  * <td>{@link #localHostStringLookup()}</td>
  111.  * <td>1.3</td>
  112.  * </tr>
  113.  * <tr>
  114.  * <td>{@value #KEY_LOOPBACK_ADDRESS}</td>
  115.  * <td>{@link StringLookup}</td>
  116.  * <td>{@link #loopbackAddressStringLookup()}</td>
  117.  * <td>1.13.0</td>
  118.  * </tr>
  119.  * <tr>
  120.  * <td>{@value #KEY_PROPERTIES}</td>
  121.  * <td>{@link StringLookup}</td>
  122.  * <td>{@link #propertiesStringLookup(Path...)}</td>
  123.  * <td>1.5</td>
  124.  * </tr>
  125.  * <tr>
  126.  * <td>{@value #KEY_RESOURCE_BUNDLE}</td>
  127.  * <td>{@link StringLookup}</td>
  128.  * <td>{@link #resourceBundleStringLookup()}</td>
  129.  * <td>1.6</td>
  130.  * </tr>
  131.  * <tr>
  132.  * <td>{@value #KEY_SYS}</td>
  133.  * <td>{@link StringLookup}</td>
  134.  * <td>{@link #systemPropertyStringLookup()}</td>
  135.  * <td>1.3</td>
  136.  * </tr>
  137.  * <tr>
  138.  * <td>{@value #KEY_URL_DECODER}</td>
  139.  * <td>{@link StringLookup}</td>
  140.  * <td>{@link #urlDecoderStringLookup()}</td>
  141.  * <td>1.5</td>
  142.  * </tr>
  143.  * <tr>
  144.  * <td>{@value #KEY_URL_ENCODER}</td>
  145.  * <td>{@link StringLookup}</td>
  146.  * <td>{@link #urlEncoderStringLookup()}</td>
  147.  * <td>1.5</td>
  148.  * </tr>
  149.  * <tr>
  150.  * <td>{@value #KEY_XML}</td>
  151.  * <td>{@link StringLookup}</td>
  152.  * <td>{@link #xmlStringLookup(Map, Path...)}</td>
  153.  * <td>1.5</td>
  154.  * </tr>
  155.  * <tr>
  156.  * <td>{@value #KEY_XML_DECODER}</td>
  157.  * <td>{@link StringLookup}</td>
  158.  * <td>{@link #xmlDecoderStringLookup()}</td>
  159.  * <td>1.11.0</td>
  160.  * </tr>
  161.  * <tr>
  162.  * <td>{@value #KEY_XML_ENCODER}</td>
  163.  * <td>{@link StringLookup}</td>
  164.  * <td>{@link #xmlEncoderStringLookup()}</td>
  165.  * <td>1.11.0</td>
  166.  * </tr>
  167.  * </table>
  168.  *
  169.  * <table>
  170.  * <caption>Additional String Lookups (not included by default)</caption>
  171.  * <tr>
  172.  * <th>Key</th>
  173.  * <th>Interface</th>
  174.  * <th>Factory Method</th>
  175.  * <th>Since</th>
  176.  * </tr>
  177.  * <tr>
  178.  * <td>{@value #KEY_DNS}</td>
  179.  * <td>{@link StringLookup}</td>
  180.  * <td>{@link #dnsStringLookup()}</td>
  181.  * <td>1.8</td>
  182.  * </tr>
  183.  * <tr>
  184.  * <td>{@value #KEY_URL}</td>
  185.  * <td>{@link StringLookup}</td>
  186.  * <td>{@link #urlStringLookup()}</td>
  187.  * <td>1.5</td>
  188.  * </tr>
  189.  * <tr>
  190.  * <td>{@value #KEY_SCRIPT}</td>
  191.  * <td>{@link StringLookup}</td>
  192.  * <td>{@link #scriptStringLookup()}</td>
  193.  * <td>1.5</td>
  194.  * </tr>
  195.  * </table>
  196.  *
  197.  * <p>
  198.  * This class also provides functional lookups used as building blocks for other lookups.
  199.  * <table>
  200.  * <caption>Functional String Lookups</caption>
  201.  * <tr>
  202.  * <th>Interface</th>
  203.  * <th>Factory Method</th>
  204.  * <th>Since</th>
  205.  * </tr>
  206.  * <tr>
  207.  * <td>{@link BiStringLookup}</td>
  208.  * <td>{@link #biFunctionStringLookup(BiFunction)}</td>
  209.  * <td>1.9</td>
  210.  * </tr>
  211.  * <tr>
  212.  * <td>{@link StringLookup}</td>
  213.  * <td>{@link #functionStringLookup(Function)}</td>
  214.  * <td>1.9</td>
  215.  * </tr>
  216.  * </table>
  217.  *
  218.  * @since 1.3
  219.  */
  220. public final class StringLookupFactory {

  221.     /**
  222.      * Builds instance of {@link StringLookupFactory}.
  223.      *
  224.      * @since 1.12.0
  225.      */
  226.     public static final class Builder implements Supplier<StringLookupFactory> {

  227.         /**
  228.          * Fences.
  229.          */
  230.         private Path[] fences;

  231.         /**
  232.          * Creates a new instance.
  233.          */
  234.         public Builder() {
  235.             // empty
  236.         }


  237.         @Override
  238.         public StringLookupFactory get() {
  239.             return new StringLookupFactory(fences);
  240.         }

  241.         /**
  242.          * Sets Path resolution fences.
  243.          * <p>
  244.          * Path Fences apply to the file, property, and XML string lookups.
  245.          * </p>
  246.          *
  247.          * @param fences Path resolution fences.
  248.          * @return {@code this} instance.
  249.          */
  250.         public Builder setFences(final Path... fences) {
  251.             this.fences = fences;
  252.             return this;
  253.         }

  254.     }

  255.     /**
  256.      * Internal class used to construct the default {@link StringLookup} map used by {@link StringLookupFactory#addDefaultStringLookups(Map)}.
  257.      */
  258.     static final class DefaultStringLookupsHolder {

  259.         /** Singleton instance, initialized with the system properties. */
  260.         static final DefaultStringLookupsHolder INSTANCE = new DefaultStringLookupsHolder(System.getProperties());

  261.         /**
  262.          * 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
  263.          * {@link #toKey(String)} method.
  264.          *
  265.          * @param lookup lookup to add
  266.          * @param map    map to add to
  267.          */
  268.         private static void addLookup(final DefaultStringLookup lookup, final Map<String, StringLookup> map) {
  269.             map.put(toKey(lookup.getKey()), lookup.getStringLookup());
  270.             if (DefaultStringLookup.BASE64_DECODER.equals(lookup)) {
  271.                 // "base64" is deprecated in favor of KEY_BASE64_DECODER.
  272.                 map.put(toKey("base64"), lookup.getStringLookup());
  273.             }
  274.         }

  275.         /**
  276.          * Creates the lookup map used when the user has requested no customization.
  277.          *
  278.          * @return default lookup map
  279.          */
  280.         private static Map<String, StringLookup> createDefaultStringLookups() {
  281.             final Map<String, StringLookup> lookupMap = new HashMap<>();
  282.             addLookup(DefaultStringLookup.BASE64_DECODER, lookupMap);
  283.             addLookup(DefaultStringLookup.BASE64_ENCODER, lookupMap);
  284.             addLookup(DefaultStringLookup.CONST, lookupMap);
  285.             addLookup(DefaultStringLookup.DATE, lookupMap);
  286.             addLookup(DefaultStringLookup.ENVIRONMENT, lookupMap);
  287.             addLookup(DefaultStringLookup.FILE, lookupMap);
  288.             addLookup(DefaultStringLookup.JAVA, lookupMap);
  289.             addLookup(DefaultStringLookup.LOCAL_HOST, lookupMap);
  290.             addLookup(DefaultStringLookup.LOOPBACK_ADDRESS, lookupMap);
  291.             addLookup(DefaultStringLookup.PROPERTIES, lookupMap);
  292.             addLookup(DefaultStringLookup.RESOURCE_BUNDLE, lookupMap);
  293.             addLookup(DefaultStringLookup.SYSTEM_PROPERTIES, lookupMap);
  294.             addLookup(DefaultStringLookup.URL_DECODER, lookupMap);
  295.             addLookup(DefaultStringLookup.URL_ENCODER, lookupMap);
  296.             addLookup(DefaultStringLookup.XML, lookupMap);
  297.             addLookup(DefaultStringLookup.XML_DECODER, lookupMap);
  298.             addLookup(DefaultStringLookup.XML_ENCODER, lookupMap);
  299.             return lookupMap;
  300.         }

  301.         /**
  302.          * Constructs a lookup map by parsing the given string. The string is expected to contain comma or space-separated names of values from the
  303.          * {@link DefaultStringLookup} enum. If the given string is null or empty, an empty map is returned.
  304.          *
  305.          * @param str string to parse; may be null or empty
  306.          * @return lookup map parsed from the given string
  307.          */
  308.         private static Map<String, StringLookup> parseStringLookups(final String str) {
  309.             final Map<String, StringLookup> lookupMap = new HashMap<>();
  310.             try {
  311.                 for (final String lookupName : str.split("[\\s,]+")) {
  312.                     if (!lookupName.isEmpty()) {
  313.                         addLookup(DefaultStringLookup.valueOf(lookupName.toUpperCase()), lookupMap);
  314.                     }
  315.                 }
  316.             } catch (final IllegalArgumentException exc) {
  317.                 throw new IllegalArgumentException("Invalid default string lookups definition: " + str, exc);
  318.             }
  319.             return lookupMap;
  320.         }

  321.         /** Default string lookup map. */
  322.         private final Map<String, StringLookup> defaultStringLookups;

  323.         /**
  324.          * Constructs a new instance initialized with the given properties.
  325.          *
  326.          * @param props initialization properties
  327.          */
  328.         DefaultStringLookupsHolder(final Properties props) {
  329.             final Map<String, StringLookup> lookups = props.containsKey(StringLookupFactory.DEFAULT_STRING_LOOKUPS_PROPERTY)
  330.                     ? parseStringLookups(props.getProperty(StringLookupFactory.DEFAULT_STRING_LOOKUPS_PROPERTY))
  331.                     : createDefaultStringLookups();
  332.             defaultStringLookups = Collections.unmodifiableMap(lookups);
  333.         }

  334.         /**
  335.          * Gets the default string lookups map.
  336.          *
  337.          * @return default string lookups map
  338.          */
  339.         Map<String, StringLookup> getDefaultStringLookups() {
  340.             return defaultStringLookups;
  341.         }
  342.     }

  343.     /**
  344.      * Name of the system property used to determine the string lookups added by the {@link #addDefaultStringLookups(Map)} method. Use of this property is only
  345.      * required in cases where the set of default lookups must be modified. (See the class documentation for details.)
  346.      *
  347.      * @since 1.10.0
  348.      */
  349.     public static final String DEFAULT_STRING_LOOKUPS_PROPERTY = "org.apache.commons.text.lookup.StringLookupFactory.defaultStringLookups";

  350.     /**
  351.      * Defines the singleton for this class.
  352.      */
  353.     public static final StringLookupFactory INSTANCE = new StringLookupFactory();

  354.     /**
  355.      * Decodes Base64 Strings.
  356.      * <p>
  357.      * Using a {@link StringLookup} from the {@link StringLookupFactory}:
  358.      * </p>
  359.      *
  360.      * <pre>
  361.      * StringLookupFactory.INSTANCE.base64DecoderStringLookup().lookup("SGVsbG9Xb3JsZCE=");
  362.      * </pre>
  363.      * <p>
  364.      * Using a {@link StringSubstitutor}:
  365.      * </p>
  366.      *
  367.      * <pre>
  368.      * StringSubstitutor.createInterpolator().replace("... ${base64Decoder:SGVsbG9Xb3JsZCE=} ..."));
  369.      * </pre>
  370.      * <p>
  371.      * The above examples convert {@code "SGVsbG9Xb3JsZCE="} to {@code "HelloWorld!"}.
  372.      * </p>
  373.      */
  374.     static final FunctionStringLookup<String> INSTANCE_BASE64_DECODER = FunctionStringLookup
  375.             .on(key -> new String(Base64.getDecoder().decode(key), StandardCharsets.ISO_8859_1));

  376.     /**
  377.      * Encodes Base64 Strings.
  378.      * <p>
  379.      * Using a {@link StringLookup} from the {@link StringLookupFactory}:
  380.      * </p>
  381.      *
  382.      * <pre>
  383.      * StringLookupFactory.INSTANCE.base64EncoderStringLookup().lookup("HelloWorld!");
  384.      * </pre>
  385.      * <p>
  386.      * Using a {@link StringSubstitutor}:
  387.      * </p>
  388.      *
  389.      * <pre>
  390.      * StringSubstitutor.createInterpolator().replace("... ${base64Encoder:HelloWorld!} ..."));
  391.      * </pre>
  392.      * <p>
  393.      * The above examples convert {@code "HelloWorld!"} to {@code "SGVsbG9Xb3JsZCE="}.
  394.      * </p>
  395.      * Defines the singleton for this class.
  396.      */
  397.     static final FunctionStringLookup<String> INSTANCE_BASE64_ENCODER = FunctionStringLookup
  398.             .on(key -> Base64.getEncoder().encodeToString(key.getBytes(StandardCharsets.ISO_8859_1)));

  399.     /**
  400.      * Looks up keys from environment variables.
  401.      * <p>
  402.      * Using a {@link StringLookup} from the {@link StringLookupFactory}:
  403.      * </p>
  404.      *
  405.      * <pre>
  406.      * StringLookupFactory.INSTANCE.environmentVariableStringLookup().lookup("USER");
  407.      * </pre>
  408.      * <p>
  409.      * Using a {@link StringSubstitutor}:
  410.      * </p>
  411.      *
  412.      * <pre>
  413.      * StringSubstitutor.createInterpolator().replace("... ${env:USER} ..."));
  414.      * </pre>
  415.      * <p>
  416.      * 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.
  417.      * </p>
  418.      */
  419.     static final FunctionStringLookup<String> INSTANCE_ENVIRONMENT_VARIABLES = FunctionStringLookup.on(System::getenv);

  420.     /**
  421.      * Defines the FunctionStringLookup singleton that always returns null.
  422.      */
  423.     static final FunctionStringLookup<String> INSTANCE_NULL = FunctionStringLookup.on(key -> null);

  424.     /**
  425.      * Defines the FunctionStringLookup singleton for looking up system properties.
  426.      */
  427.     static final FunctionStringLookup<String> INSTANCE_SYSTEM_PROPERTIES = FunctionStringLookup.on(System::getProperty);

  428.     /**
  429.      * Default lookup key for interpolation {@value #KEY_BASE64_DECODER}.
  430.      *
  431.      * @since 1.6
  432.      */
  433.     public static final String KEY_BASE64_DECODER = "base64Decoder";

  434.     /**
  435.      * Default lookup key for interpolation {@value #KEY_BASE64_ENCODER}.
  436.      *
  437.      * @since 1.6
  438.      */
  439.     public static final String KEY_BASE64_ENCODER = "base64Encoder";

  440.     /**
  441.      * Default lookup key for interpolation {@value #KEY_CONST}.
  442.      *
  443.      * @since 1.6
  444.      */
  445.     public static final String KEY_CONST = "const";

  446.     /**
  447.      * Default lookup key for interpolation {@value #KEY_DATE}.
  448.      *
  449.      * @since 1.6
  450.      */
  451.     public static final String KEY_DATE = "date";

  452.     /**
  453.      * Default lookup key for interpolation {@value #KEY_DNS}.
  454.      *
  455.      * @since 1.8
  456.      */
  457.     public static final String KEY_DNS = "dns";

  458.     /**
  459.      * Default lookup key for interpolation {@value #KEY_ENV}.
  460.      *
  461.      * @since 1.6
  462.      */
  463.     public static final String KEY_ENV = "env";

  464.     /**
  465.      * Default lookup key for interpolation {@value #KEY_FILE}.
  466.      *
  467.      * @since 1.6
  468.      */
  469.     public static final String KEY_FILE = "file";

  470.     /**
  471.      * Default lookup key for interpolation {@value #KEY_JAVA}.
  472.      *
  473.      * @since 1.6
  474.      */
  475.     public static final String KEY_JAVA = "java";

  476.     /**
  477.      * Default lookup key for interpolation {@value #KEY_LOCALHOST}.
  478.      *
  479.      * @since 1.6
  480.      */
  481.     public static final String KEY_LOCALHOST = "localhost";

  482.     /**
  483.      * Default lookup key for interpolation {@value #KEY_LOOPBACK_ADDRESS}.
  484.      *
  485.      * @since 1.13.0
  486.      */
  487.     public static final String KEY_LOOPBACK_ADDRESS = "loobackAddress";

  488.     /**
  489.      * Default lookup key for interpolation {@value #KEY_PROPERTIES}.
  490.      *
  491.      * @since 1.6
  492.      */
  493.     public static final String KEY_PROPERTIES = "properties";

  494.     /**
  495.      * Default lookup key for interpolation {@value #KEY_RESOURCE_BUNDLE}.
  496.      *
  497.      * @since 1.6
  498.      */
  499.     public static final String KEY_RESOURCE_BUNDLE = "resourceBundle";

  500.     /**
  501.      * Default lookup key for interpolation {@value #KEY_SCRIPT}.
  502.      *
  503.      * @since 1.6
  504.      */
  505.     public static final String KEY_SCRIPT = "script";

  506.     /**
  507.      * Default lookup key for interpolation {@value #KEY_SYS}.
  508.      *
  509.      * @since 1.6
  510.      */
  511.     public static final String KEY_SYS = "sys";

  512.     /**
  513.      * Default lookup key for interpolation {@value #KEY_URL}.
  514.      *
  515.      * @since 1.6
  516.      */
  517.     public static final String KEY_URL = "url";

  518.     /**
  519.      * Default lookup key for interpolation {@value #KEY_URL_DECODER}.
  520.      *
  521.      * @since 1.6
  522.      */
  523.     public static final String KEY_URL_DECODER = "urlDecoder";

  524.     /**
  525.      * Default lookup key for interpolation {@value #KEY_URL_ENCODER}.
  526.      *
  527.      * @since 1.6
  528.      */
  529.     public static final String KEY_URL_ENCODER = "urlEncoder";

  530.     /**
  531.      * Default lookup key for interpolation {@value #KEY_XML}.
  532.      *
  533.      * @since 1.6
  534.      */
  535.     public static final String KEY_XML = "xml";

  536.     /**
  537.      * Default lookup key for interpolation {@value #KEY_XML_DECODER}.
  538.      *
  539.      * @since 1.11.0
  540.      */
  541.     public static final String KEY_XML_DECODER = "xmlDecoder";

  542.     /**
  543.      * Default lookup key for interpolation {@value #KEY_XML_ENCODER}.
  544.      *
  545.      * @since 1.11.0
  546.      */
  547.     public static final String KEY_XML_ENCODER = "xmlEncoder";

  548.     /**
  549.      * Constructs a new {@link Builder}.
  550.      *
  551.      * @return a new {@link Builder}
  552.      * @since 1.12.0
  553.      */
  554.     public static Builder builder() {
  555.         return new Builder();
  556.     }

  557.     /**
  558.      * Clears any static resources.
  559.      *
  560.      * @since 1.5
  561.      */
  562.     public static void clear() {
  563.         ConstantStringLookup.clear();
  564.     }

  565.     /**
  566.      * Gets a string suitable for use as a key in the string lookup map.
  567.      *
  568.      * @param key string to convert to a string lookup map key
  569.      * @return string lookup map key
  570.      */
  571.     static String toKey(final String key) {
  572.         return key.toLowerCase(Locale.ROOT);
  573.     }

  574.     /**
  575.      * Returns the given map if the input is non-null or an empty immutable map if the input is null.
  576.      *
  577.      * @param <K> the class of the map keys
  578.      * @param <V> the class of the map values
  579.      * @param map The map to test
  580.      * @return the given map if the input is non-null or an empty immutable map if the input is null.
  581.      */
  582.     static <K, V> Map<K, V> toMap(final Map<K, V> map) {
  583.         return map == null ? Collections.emptyMap() : map;
  584.     }

  585.     /**
  586.      * Fences.
  587.      */
  588.     private final Path[] fences;

  589.     /**
  590.      * Constructs a new instance.
  591.      */
  592.     private StringLookupFactory() {
  593.         this(null);
  594.     }

  595.     /**
  596.      * Constructs a new instance.
  597.      */
  598.     private StringLookupFactory(final Path[] fences) {
  599.         this.fences = fences;
  600.     }

  601.     /**
  602.      * 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
  603.      * during string interpolation. The defaults may be configured using the {@value #DEFAULT_STRING_LOOKUPS_PROPERTY} system property. See the class
  604.      * documentation for details and a list of lookups.
  605.      *
  606.      * @param stringLookupMap the map of string lookups to edit.
  607.      * @since 1.5
  608.      */
  609.     public void addDefaultStringLookups(final Map<String, StringLookup> stringLookupMap) {
  610.         if (stringLookupMap != null) {
  611.             stringLookupMap.putAll(DefaultStringLookupsHolder.INSTANCE.getDefaultStringLookups());
  612.         }
  613.     }

  614.     /**
  615.      * Returns the Base64DecoderStringLookup singleton instance to decode Base64 strings.
  616.      * <p>
  617.      * Using a {@link StringLookup} from the {@link StringLookupFactory}:
  618.      * </p>
  619.      *
  620.      * <pre>
  621.      * StringLookupFactory.INSTANCE.base64DecoderStringLookup().lookup("SGVsbG9Xb3JsZCE=");
  622.      * </pre>
  623.      * <p>
  624.      * Using a {@link StringSubstitutor}:
  625.      * </p>
  626.      *
  627.      * <pre>
  628.      * StringSubstitutor.createInterpolator().replace("... ${base64Decoder:SGVsbG9Xb3JsZCE=} ..."));
  629.      * </pre>
  630.      * <p>
  631.      * The above examples convert {@code "SGVsbG9Xb3JsZCE="} to {@code "HelloWorld!"}.
  632.      * </p>
  633.      *
  634.      * @return The Base64DecoderStringLookup singleton instance.
  635.      * @since 1.5
  636.      */
  637.     public StringLookup base64DecoderStringLookup() {
  638.         return StringLookupFactory.INSTANCE_BASE64_DECODER;
  639.     }

  640.     /**
  641.      * Returns the Base64EncoderStringLookup singleton instance to encode strings to Base64.
  642.      * <p>
  643.      * Using a {@link StringLookup} from the {@link StringLookupFactory}:
  644.      * </p>
  645.      *
  646.      * <pre>
  647.      * StringLookupFactory.INSTANCE.base64EncoderStringLookup().lookup("HelloWorld!");
  648.      * </pre>
  649.      * <p>
  650.      * Using a {@link StringSubstitutor}:
  651.      * </p>
  652.      *
  653.      * <pre>
  654.      * StringSubstitutor.createInterpolator().replace("... ${base64Encoder:HelloWorld!} ..."));
  655.      * </pre>
  656.      * <p>
  657.      * The above examples convert {@code } to {@code "SGVsbG9Xb3JsZCE="}.
  658.      * </p>
  659.      *
  660.      * @return The Base64EncoderStringLookup singleton instance.
  661.      * @since 1.6
  662.      */
  663.     public StringLookup base64EncoderStringLookup() {
  664.         return StringLookupFactory.INSTANCE_BASE64_ENCODER;
  665.     }

  666.     /**
  667.      * Returns the Base64DecoderStringLookup singleton instance to decode Base64 strings.
  668.      * <p>
  669.      * Using a {@link StringLookup} from the {@link StringLookupFactory}:
  670.      * </p>
  671.      *
  672.      * <pre>
  673.      * StringLookupFactory.INSTANCE.base64DecoderStringLookup().lookup("SGVsbG9Xb3JsZCE=");
  674.      * </pre>
  675.      * <p>
  676.      * Using a {@link StringSubstitutor}:
  677.      * </p>
  678.      *
  679.      * <pre>
  680.      * StringSubstitutor.createInterpolator().replace("... ${base64Decoder:SGVsbG9Xb3JsZCE=} ..."));
  681.      * </pre>
  682.      * <p>
  683.      * The above examples convert {@code "SGVsbG9Xb3JsZCE="} to {@code "HelloWorld!"}.
  684.      * </p>
  685.      *
  686.      * @return The Base64DecoderStringLookup singleton instance.
  687.      * @since 1.5
  688.      * @deprecated Use {@link #base64DecoderStringLookup()}.
  689.      */
  690.     @Deprecated
  691.     public StringLookup base64StringLookup() {
  692.         return StringLookupFactory.INSTANCE_BASE64_DECODER;
  693.     }

  694.     /**
  695.      * Returns a new function-based lookup where the request for a lookup is answered by applying the function with a lookup key.
  696.      *
  697.      * @param <R>        the function return type.
  698.      * @param <U>        the function's second parameter type.
  699.      * @param biFunction the function.
  700.      * @return a new MapStringLookup.
  701.      * @since 1.9
  702.      */
  703.     public <R, U> BiStringLookup<U> biFunctionStringLookup(final BiFunction<String, U, R> biFunction) {
  704.         return BiFunctionStringLookup.on(biFunction);
  705.     }

  706.     /**
  707.      * Returns the ConstantStringLookup singleton instance to look up the value of a fully-qualified static final value.
  708.      * <p>
  709.      * 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
  710.      * 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
  711.      * 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
  712.      * value is obtained using reflection.
  713.      * </p>
  714.      * <p>
  715.      * 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
  716.      * clients concurrently.
  717.      * </p>
  718.      * <p>
  719.      * Using a {@link StringLookup} from the {@link StringLookupFactory}:
  720.      * </p>
  721.      *
  722.      * <pre>
  723.      * StringLookupFactory.INSTANCE.constantStringLookup().lookup("java.awt.event.KeyEvent.VK_ESCAPE");
  724.      * </pre>
  725.      * <p>
  726.      * Using a {@link StringSubstitutor}:
  727.      * </p>
  728.      *
  729.      * <pre>
  730.      * StringSubstitutor.createInterpolator().replace("... ${const:java.awt.event.KeyEvent.VK_ESCAPE} ..."));
  731.      * </pre>
  732.      * <p>
  733.      * The above examples convert {@code java.awt.event.KeyEvent.VK_ESCAPE} to {@code "27"}.
  734.      * </p>
  735.      *
  736.      * @return The ConstantStringLookup singleton instance.
  737.      * @since 1.5
  738.      */
  739.     public StringLookup constantStringLookup() {
  740.         return ConstantStringLookup.INSTANCE;
  741.     }

  742.     /**
  743.      * Returns the DateStringLookup singleton instance to format the current date with the format given in the key in a format compatible with
  744.      * {@link java.text.SimpleDateFormat}.
  745.      * <p>
  746.      * Using a {@link StringLookup} from the {@link StringLookupFactory}:
  747.      * </p>
  748.      *
  749.      * <pre>
  750.      * StringLookupFactory.INSTANCE.dateStringLookup().lookup("yyyy-MM-dd");
  751.      * </pre>
  752.      * <p>
  753.      * Using a {@link StringSubstitutor}:
  754.      * </p>
  755.      *
  756.      * <pre>
  757.      * StringSubstitutor.createInterpolator().replace("... ${date:yyyy-MM-dd} ..."));
  758.      * </pre>
  759.      * <p>
  760.      * The above examples convert {@code "yyyy-MM-dd"} to todays's date, for example, {@code "2019-08-04"}.
  761.      * </p>
  762.      *
  763.      * @return The DateStringLookup singleton instance.
  764.      */
  765.     public StringLookup dateStringLookup() {
  766.         return DateStringLookup.INSTANCE;
  767.     }

  768.     /**
  769.      * Returns the DnsStringLookup singleton instance where the lookup key is one of:
  770.      * <ul>
  771.      * <li><strong>name</strong>: for the local host name, for example {@code EXAMPLE} but also {@code EXAMPLE.apache.org}.</li>
  772.      * <li><strong>canonical-name</strong>: for the local canonical host name, for example {@code EXAMPLE.apache.org}.</li>
  773.      * <li><strong>address</strong>: for the local host address, for example {@code 192.168.56.1}.</li>
  774.      * </ul>
  775.      *
  776.      * <p>
  777.      * Using a {@link StringLookup} from the {@link StringLookupFactory}:
  778.      * </p>
  779.      *
  780.      * <pre>
  781.      * StringLookupFactory.INSTANCE.dnsStringLookup().lookup("address|apache.org");
  782.      * </pre>
  783.      * <p>
  784.      * When used through a {@link StringSubstitutor}, this lookup must either be added programmatically (as below) or enabled as a default lookup using the
  785.      * {@value #DEFAULT_STRING_LOOKUPS_PROPERTY} system property (see class documentation).
  786.      * </p>
  787.      *
  788.      * <pre>
  789.      * Map&lt;String, StringLookup&gt; lookupMap = new HashMap&lt;&gt;();
  790.      * lookupMap.put("dns", StringLookupFactory.INSTANCE.dnsStringLookup());
  791.      *
  792.      * StringLookup variableResolver = StringLookupFactory.INSTANCE.interpolatorStringLookup(lookupMap, null, false);
  793.      *
  794.      * new StringSubstitutor(variableResolver).replace("... ${dns:address|apache.org} ...");
  795.      * </pre>
  796.      * <p>
  797.      * The above examples convert {@code "address|apache.org"} to the IP address of {@code apache.org}.
  798.      * </p>
  799.      *
  800.      * @return the DnsStringLookup singleton instance.
  801.      * @since 1.8
  802.      */
  803.     public StringLookup dnsStringLookup() {
  804.         return DnsStringLookup.INSTANCE;
  805.     }

  806.     /**
  807.      * Returns the EnvironmentVariableStringLookup singleton instance where the lookup key is an environment variable name.
  808.      * <p>
  809.      * Using a {@link StringLookup} from the {@link StringLookupFactory}:
  810.      * </p>
  811.      *
  812.      * <pre>
  813.      * StringLookupFactory.INSTANCE.environmentVariableStringLookup().lookup("USER");
  814.      * </pre>
  815.      * <p>
  816.      * Using a {@link StringSubstitutor}:
  817.      * </p>
  818.      *
  819.      * <pre>
  820.      * StringSubstitutor.createInterpolator().replace("... ${env:USER} ..."));
  821.      * </pre>
  822.      * <p>
  823.      * 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.
  824.      * </p>
  825.      *
  826.      * @return The EnvironmentVariableStringLookup singleton instance.
  827.      */
  828.     public StringLookup environmentVariableStringLookup() {
  829.         return StringLookupFactory.INSTANCE_ENVIRONMENT_VARIABLES;
  830.     }

  831.     /**
  832.      * Returns a file StringLookup instance.
  833.      * <p>
  834.      * If this factory was built using {@link Builder#setFences(Path...)}, then the string lookup is fenced and will throw an {@link IllegalArgumentException}
  835.      * if a lookup causes causes a path to resolve outside of these fences. Otherwise, the result is unfenced to preserved behavior from previous versions.
  836.      * </p>
  837.      * <em>Using a fenced StringLookup</em>
  838.      * <p>
  839.      * To use a fenced {@link StringLookup}, use {@link StringLookupFactory#builder()}:
  840.      * </p>
  841.      *
  842.      * <pre>
  843.      * // Make the fence the current directory
  844.      * StringLookupFactory factory = StringLookupFactory.builder().setFences(Paths.get("")).get();
  845.      * factory.fileStringLookup().lookup("UTF-8:com/domain/document.txt");
  846.      *
  847.      * // throws IllegalArgumentException
  848.      * factory.fileStringLookup().lookup("UTF-8:/rootdir/foo/document.txt");
  849.      *
  850.      * // throws IllegalArgumentException
  851.      * factory.fileStringLookup().lookup("UTF-8:../document.txt");
  852.      * </pre>
  853.      *
  854.      * <em>Using an unfenced StringLookup</em>
  855.      * <p>
  856.      * To use an unfenced {@link StringLookup}, use {@link StringLookupFactory#INSTANCE}:
  857.      * </p>
  858.      *
  859.      * <pre>
  860.      * StringLookupFactory.INSTANCE.fileStringLookup().lookup("UTF-8:com/domain/document.properties");
  861.      * </pre>
  862.      *
  863.      * <em>Using a StringLookup with StringSubstitutor</em>
  864.      * <p>
  865.      * To build a fenced StringSubstitutor, use:
  866.      * </p>
  867.      *
  868.      * <pre>
  869.      * // Make the fence the current directory
  870.      * final StringLookupFactory factory = StringLookupFactory.builder().setFences(Paths.get("")).get();
  871.      * final StringSubstitutor stringSubstitutor = new StringSubstitutor(factory.interpolatorStringLookup());
  872.      * stringSubstitutor.replace("... ${file:UTF-8:com/domain/document.txt} ..."));
  873.      *
  874.      * // throws IllegalArgumentException
  875.      * stringSubstitutor.replace("... ${file:UTF-8:/rootdir/foo/document.txt} ..."));
  876.      * </pre>
  877.      * <p>
  878.      * Using an unfenced {@link StringSubstitutor}:
  879.      * </p>
  880.      *
  881.      * <pre>
  882.      * StringSubstitutor.createInterpolator().replace("... ${file:UTF-8:com/domain/document.txt} ..."));
  883.      * </pre>
  884.      * <p>
  885.      * The above examples convert {@code "UTF-8:com/domain/document.txt"} to the contents of the file.
  886.      * </p>
  887.      *
  888.      * @return a file StringLookup instance.
  889.      * @since 1.5
  890.      */
  891.     public StringLookup fileStringLookup() {
  892.         return fences != null ? fileStringLookup(fences) : FileStringLookup.INSTANCE;
  893.     }

  894.     /**
  895.      * Returns a fenced file StringLookup instance.
  896.      * <p>
  897.      * To use a {@link StringLookup} fenced by the current directory, use:
  898.      * </p>
  899.      *
  900.      * <pre>
  901.      * StringLookupFactory.INSTANCE.fileStringLookup(Paths.get("")).lookup("UTF-8:com/domain/document.txt");
  902.      *
  903.      * // throws IllegalArgumentException
  904.      * StringLookupFactory.INSTANCE.fileStringLookup(Paths.get("")).lookup("UTF-8:/rootdir/foo/document.txt");
  905.      *
  906.      * // throws IllegalArgumentException
  907.      * StringLookupFactory.INSTANCE.fileStringLookup(Paths.get("")).lookup("UTF-8:../com/domain/document.txt");
  908.      * </pre>
  909.      * <p>
  910.      * The above example converts {@code "UTF-8:com/domain/document.txt"} to the contents of the file.
  911.      * </p>
  912.      * <p>
  913.      * {@link StringSubstitutor} methods like {@link StringSubstitutor#replace(String)} will throw a {@link IllegalArgumentException} when a file doesn't
  914.      * resolves in a fence.
  915.      * </p>
  916.      *
  917.      * @param fences The fences guarding Path resolution.
  918.      * @return a file StringLookup instance.
  919.      * @since 1.12.0
  920.      */
  921.     public StringLookup fileStringLookup(final Path... fences) {
  922.         return new FileStringLookup(fences);
  923.     }

  924.     /**
  925.      * Returns a new function-based lookup where the request for a lookup is answered by applying the function with a lookup key.
  926.      *
  927.      * @param <R>      the function return type.
  928.      * @param function the function.
  929.      * @return a new MapStringLookup.
  930.      * @since 1.9
  931.      */
  932.     public <R> StringLookup functionStringLookup(final Function<String, R> function) {
  933.         return FunctionStringLookup.on(function);
  934.     }

  935.     /**
  936.      * Returns a {@link InterpolatorStringLookup} containing the configured {@link #addDefaultStringLookups(Map) default lookups}. See the class documentation
  937.      * for details on how these defaults are configured.
  938.      * <p>
  939.      * Using a {@link StringLookup} from the {@link StringLookupFactory}:
  940.      * </p>
  941.      *
  942.      * <pre>
  943.      * StringLookupFactory.INSTANCE.interpolatorStringLookup().lookup("${sys:os.name}, ${env:USER}");
  944.      * </pre>
  945.      * <p>
  946.      * Using a {@link StringSubstitutor}:
  947.      * </p>
  948.      *
  949.      * <pre>
  950.      * StringSubstitutor.createInterpolator().replace("... ${sys:os.name}, ${env:USER} ..."));
  951.      * </pre>
  952.      * <p>
  953.      * The above examples convert {@code "${sys:os.name}, ${env:USER}"} to the OS name and Linux user name.
  954.      * </p>
  955.      *
  956.      * @return the default {@link InterpolatorStringLookup}.
  957.      */
  958.     public StringLookup interpolatorStringLookup() {
  959.         return InterpolatorStringLookup.INSTANCE;
  960.     }

  961.     /**
  962.      * Returns a new InterpolatorStringLookup. If {@code addDefaultLookups} is {@code true}, the configured {@link #addDefaultStringLookups(Map) default
  963.      * lookups} are included in addition to the ones provided in {@code stringLookupMap}. (See the class documentation for details on how default lookups are
  964.      * configured.)
  965.      *
  966.      * @param stringLookupMap     the map of string lookups.
  967.      * @param defaultStringLookup the default string lookup; this lookup is used when a variable cannot be resolved using the lookups in {@code stringLookupMap}
  968.      *                            or the configured default lookups (if enabled)
  969.      * @param addDefaultLookups   whether to use default lookups as described above.
  970.      * @return a new InterpolatorStringLookup.
  971.      * @since 1.4
  972.      */
  973.     public StringLookup interpolatorStringLookup(final Map<String, StringLookup> stringLookupMap, final StringLookup defaultStringLookup,
  974.             final boolean addDefaultLookups) {
  975.         return new InterpolatorStringLookup(stringLookupMap, defaultStringLookup, addDefaultLookups);
  976.     }

  977.     /**
  978.      * Returns a new InterpolatorStringLookup using the given key-value pairs and the configured {@link #addDefaultStringLookups(Map) default lookups} to
  979.      * resolve variables. (See the class documentation for details on how default lookups are configured.)
  980.      *
  981.      * @param <V> the value type the default string lookup's map.
  982.      * @param map the default map for string lookups.
  983.      * @return a new InterpolatorStringLookup.
  984.      */
  985.     public <V> StringLookup interpolatorStringLookup(final Map<String, V> map) {
  986.         return new InterpolatorStringLookup(map);
  987.     }

  988.     /**
  989.      * Returns a new InterpolatorStringLookup using the given lookup and the configured {@link #addDefaultStringLookups(Map) default lookups} to resolve
  990.      * variables. (See the class documentation for details on how default lookups are configured.)
  991.      *
  992.      * @param defaultStringLookup the default string lookup.
  993.      * @return a new InterpolatorStringLookup.
  994.      */
  995.     public StringLookup interpolatorStringLookup(final StringLookup defaultStringLookup) {
  996.         return new InterpolatorStringLookup(defaultStringLookup);
  997.     }

  998.     /**
  999.      * Returns the JavaPlatformStringLookup singleton instance. Looks up keys related to Java: Java version, JRE version, VM version, and so on.
  1000.      * <p>
  1001.      * The lookup keys with examples are:
  1002.      * </p>
  1003.      * <ul>
  1004.      * <li><strong>version</strong>: "Java version 1.8.0_181"</li>
  1005.      * <li><strong>runtime</strong>: "Java(TM) SE Runtime Environment (build 1.8.0_181-b13) from Oracle Corporation"</li>
  1006.      * <li><strong>vm</strong>: "Java HotSpot(TM) 64-Bit Server VM (build 25.181-b13, mixed mode)"</li>
  1007.      * <li><strong>os</strong>: "Windows 10 10.0, architecture: amd64-64"</li>
  1008.      * <li><strong>hardware</strong>: "processors: 4, architecture: amd64-64, instruction sets: amd64"</li>
  1009.      * <li><strong>locale</strong>: "default locale: en_US, platform encoding: iso-8859-1"</li>
  1010.      * </ul>
  1011.      *
  1012.      * <p>
  1013.      * Using a {@link StringLookup} from the {@link StringLookupFactory}:
  1014.      * </p>
  1015.      *
  1016.      * <pre>
  1017.      * StringLookupFactory.INSTANCE.javaPlatformStringLookup().lookup("version");
  1018.      * </pre>
  1019.      * <p>
  1020.      * Using a {@link StringSubstitutor}:
  1021.      * </p>
  1022.      *
  1023.      * <pre>
  1024.      * StringSubstitutor.createInterpolator().replace("... ${java:version} ..."));
  1025.      * </pre>
  1026.      * <p>
  1027.      * The above examples convert {@code "version"} to the current VM version, for example, {@code "Java version 1.8.0_181"}.
  1028.      * </p>
  1029.      *
  1030.      * @return The JavaPlatformStringLookup singleton instance.
  1031.      */
  1032.     public StringLookup javaPlatformStringLookup() {
  1033.         return JavaPlatformStringLookup.INSTANCE;
  1034.     }

  1035.     /**
  1036.      * Returns the InetAddressStringLookup instance where the lookup key for {@link InetAddress#getLocalHost()} is one of:
  1037.      * <ul>
  1038.      * <li><strong>name</strong>: for the local host name, for example {@code EXAMPLE}.</li>
  1039.      * <li><strong>canonical-name</strong>: for the local canonical host name, for example {@code EXAMPLE.apache.org}.</li>
  1040.      * <li><strong>address</strong>: for the local host address, for example {@code 192.168.56.1}.</li>
  1041.      * </ul>
  1042.      *
  1043.      * <p>
  1044.      * Using a {@link StringLookup} from the {@link StringLookupFactory}:
  1045.      * </p>
  1046.      *
  1047.      * <pre>
  1048.      * StringLookupFactory.INSTANCE.localHostStringLookup().lookup("canonical-name");
  1049.      * </pre>
  1050.      * <p>
  1051.      * Using a {@link StringSubstitutor}:
  1052.      * </p>
  1053.      *
  1054.      * <pre>
  1055.      * StringSubstitutor.createInterpolator().replace("... ${localhost:canonical-name} ..."));
  1056.      * </pre>
  1057.      * <p>
  1058.      * The above examples convert {@code "canonical-name"} to the current host name, for example, {@code "EXAMPLE.apache.org"}.
  1059.      * </p>
  1060.      *
  1061.      * @return The InetAddressStringLookup singleton instance.
  1062.      */
  1063.     public StringLookup localHostStringLookup() {
  1064.         return InetAddressStringLookup.LOCAL_HOST;
  1065.     }

  1066.     /**
  1067.      * Returns the InetAddressStringLookup instance where the lookup key for {@link InetAddress#getLoopbackAddress()} is one of:
  1068.      * <ul>
  1069.      * <li><strong>name</strong>: for the local host name, for example {@code EXAMPLE}.</li>
  1070.      * <li><strong>canonical-name</strong>: for the local canonical host name, for example {@code EXAMPLE.apache.org}.</li>
  1071.      * <li><strong>address</strong>: for the local host address, for example {@code 192.168.56.1}.</li>
  1072.      * </ul>
  1073.      *
  1074.      * <p>
  1075.      * Using a {@link StringLookup} from the {@link StringLookupFactory}:
  1076.      * </p>
  1077.      *
  1078.      * <pre>
  1079.      * StringLookupFactory.INSTANCE.loopbackAddressStringLookup().lookup("canonical-name");
  1080.      * </pre>
  1081.      * <p>
  1082.      * Using a {@link StringSubstitutor}:
  1083.      * </p>
  1084.      *
  1085.      * <pre>
  1086.      * StringSubstitutor.createInterpolator().replace("... ${loopbackAddress:canonical-name} ..."));
  1087.      * </pre>
  1088.      * <p>
  1089.      * The above examples convert {@code "canonical-name"} to the current host name, for example, {@code "EXAMPLE.apache.org"}.
  1090.      * </p>
  1091.      *
  1092.      * @return The InetAddressStringLookup singleton instance.
  1093.      */
  1094.     public StringLookup loopbackAddressStringLookup() {
  1095.         return InetAddressStringLookup.LOOPACK_ADDRESS;
  1096.     }

  1097.     /**
  1098.      * Returns a new map-based lookup where the request for a lookup is answered with the value for that key.
  1099.      *
  1100.      * @param <V> the map value type.
  1101.      * @param map the map.
  1102.      * @return a new MapStringLookup.
  1103.      */
  1104.     public <V> StringLookup mapStringLookup(final Map<String, V> map) {
  1105.         return FunctionStringLookup.on(map);
  1106.     }

  1107.     /**
  1108.      * Returns the NullStringLookup singleton instance which always returns null.
  1109.      *
  1110.      * @return The NullStringLookup singleton instance.
  1111.      */
  1112.     public StringLookup nullStringLookup() {
  1113.         return StringLookupFactory.INSTANCE_NULL;
  1114.     }

  1115.     /**
  1116.      * Returns a Properties StringLookup instance.
  1117.      * <p>
  1118.      * If this factory was built using {@link Builder#setFences(Path...)}, then the string lookup is fenced and will throw an {@link IllegalArgumentException}
  1119.      * if a lookup causes causes a path to resolve outside of these fences. Otherwise, the result is unfenced to preserved behavior from previous versions.
  1120.      * </p>
  1121.      * <p>
  1122.      * We looks up a value for the key in the format "DocumentPath::MyKey".
  1123.      * </p>
  1124.      * <p>
  1125.      * Note the use of "::" instead of ":" to allow for "C:" drive letters in paths.
  1126.      * </p>
  1127.      * <p>
  1128.      * For example: "com/domain/document.properties::MyKey".
  1129.      * </p>
  1130.      * <em>Using a fenced StringLookup</em>
  1131.      * <p>
  1132.      * To use a fenced {@link StringLookup}, use {@link StringLookupFactory#builder()}:
  1133.      * </p>
  1134.      *
  1135.      * <pre>
  1136.      * // Make the fence the current directory
  1137.      * StringLookupFactory factory = StringLookupFactory.builder().setFences(Paths.get("")).get();
  1138.      * factory.propertiesStringLookup().lookup("com/domain/document.properties::MyKey");
  1139.      *
  1140.      * // throws IllegalArgumentException
  1141.      * factory.propertiesStringLookup().lookup("/com/domain/document.properties::MyKey");
  1142.      *
  1143.      * // throws IllegalArgumentException
  1144.      * factory.propertiesStringLookup().lookup("../com/domain/document.properties::MyKey");
  1145.      * </pre>
  1146.      *
  1147.      * <em>Using an unfenced StringLookup</em>
  1148.      * <p>
  1149.      * To use an unfenced {@link StringLookup}, use {@link StringLookupFactory#INSTANCE}:
  1150.      * </p>
  1151.      *
  1152.      * <pre>
  1153.      * StringLookupFactory.INSTANCE.propertiesStringLookup().lookup("com/domain/document.properties::MyKey");
  1154.      * </pre>
  1155.      *
  1156.      * <em>Using a StringLookup with StringSubstitutor</em>
  1157.      * <p>
  1158.      * To build a fenced StringSubstitutor, use:
  1159.      * </p>
  1160.      *
  1161.      * <pre>
  1162.      * // Make the fence the current directory
  1163.      * final StringLookupFactory factory = StringLookupFactory.builder().setFences(Paths.get("")).get();
  1164.      * final StringSubstitutor stringSubstitutor = new StringSubstitutor(factory.interpolatorStringLookup());
  1165.      * stringSubstitutor.replace("... ${properties:com/domain/document.properties::MyKey} ..."));
  1166.      *
  1167.      * // throws IllegalArgumentException
  1168.      * stringSubstitutor.replace("... ${properties:/rootdir/foo/document.properties::MyKey} ..."));
  1169.      * </pre>
  1170.      * <p>
  1171.      * Using an unfenced {@link StringSubstitutor}:
  1172.      * </p>
  1173.      *
  1174.      * <pre>
  1175.      * StringSubstitutor.createInterpolator().replace("... ${properties:com/domain/document.properties::MyKey} ..."));
  1176.      * </pre>
  1177.      * <p>
  1178.      * The above examples convert {@code "com/domain/document.properties::MyKey"} to the key value in the properties file at the path
  1179.      * "com/domain/document.properties".
  1180.      * </p>
  1181.      *
  1182.      * @return a Properties StringLookup instance.
  1183.      * @since 1.5
  1184.      */
  1185.     public StringLookup propertiesStringLookup() {
  1186.         return fences != null ? propertiesStringLookup(fences) : PropertiesStringLookup.INSTANCE;
  1187.     }

  1188.     /**
  1189.      * Returns a fenced Properties StringLookup instance.
  1190.      * <p>
  1191.      * Looks up the value for the key in the format "DocumentPath::MyKey":.
  1192.      * </p>
  1193.      * <p>
  1194.      * Note the use of "::" instead of ":" to allow for "C:" drive letters in paths.
  1195.      * </p>
  1196.      * <p>
  1197.      * For example: "com/domain/document.properties::MyKey".
  1198.      * </p>
  1199.      * <p>
  1200.      * To use a {@link StringLookup} fenced by the current directory, use:
  1201.      * </p>
  1202.      *
  1203.      * <pre>
  1204.      * StringLookupFactory.INSTANCE.fileStringLookup(Paths.get("")).lookup("com/domain/document.properties::MyKey");
  1205.      *
  1206.      * // throws IllegalArgumentException
  1207.      * StringLookupFactory.INSTANCE.fileStringLookup(Paths.get("")).lookup("com/domain/document.properties::MyKey");
  1208.      *
  1209.      * // throws IllegalArgumentException
  1210.      * StringLookupFactory.INSTANCE.fileStringLookup(Paths.get("")).lookup("com/domain/document.properties::MyKey");
  1211.      * </pre>
  1212.      * <p>
  1213.      * The above example converts {@code "com/domain/document.properties::MyKey"} to the key value in the properties file at the path
  1214.      * "com/domain/document.properties".
  1215.      * </p>
  1216.      * <p>
  1217.      * {@link StringSubstitutor} methods like {@link StringSubstitutor#replace(String)} will throw a {@link IllegalArgumentException} when a file doesn't
  1218.      * resolves in a fence.
  1219.      * </p>
  1220.      *
  1221.      * @param fences The fences guarding Path resolution.
  1222.      * @return a Properties StringLookup instance.
  1223.      * @since 1.12.0
  1224.      */
  1225.     public StringLookup propertiesStringLookup(final Path... fences) {
  1226.         return new PropertiesStringLookup(fences);
  1227.     }

  1228.     /**
  1229.      * Returns the ResourceBundleStringLookup singleton instance.
  1230.      * <p>
  1231.      * Looks up the value for a given key in the format "BundleName:BundleKey".
  1232.      * </p>
  1233.      * <p>
  1234.      * For example: "com.domain.messages:MyKey".
  1235.      * </p>
  1236.      * <p>
  1237.      * Using a {@link StringLookup} from the {@link StringLookupFactory}:
  1238.      * </p>
  1239.      *
  1240.      * <pre>
  1241.      * StringLookupFactory.INSTANCE.resourceBundleStringLookup().lookup("com.domain.messages:MyKey");
  1242.      * </pre>
  1243.      * <p>
  1244.      * Using a {@link StringSubstitutor}:
  1245.      * </p>
  1246.      *
  1247.      * <pre>
  1248.      * StringSubstitutor.createInterpolator().replace("... ${resourceBundle:com.domain.messages:MyKey} ..."));
  1249.      * </pre>
  1250.      * <p>
  1251.      * The above examples convert {@code "com.domain.messages:MyKey"} to the key value in the resource bundle at {@code "com.domain.messages"}.
  1252.      * </p>
  1253.      *
  1254.      * @return The ResourceBundleStringLookup singleton instance.
  1255.      */
  1256.     public StringLookup resourceBundleStringLookup() {
  1257.         return ResourceBundleStringLookup.INSTANCE;
  1258.     }

  1259.     /**
  1260.      * Returns a ResourceBundleStringLookup instance for the given bundle name.
  1261.      * <p>
  1262.      * Looks up the value for a given key in the format "MyKey".
  1263.      * </p>
  1264.      * <p>
  1265.      * For example: "MyKey".
  1266.      * </p>
  1267.      * <p>
  1268.      * Using a {@link StringLookup} from the {@link StringLookupFactory}:
  1269.      * </p>
  1270.      *
  1271.      * <pre>
  1272.      * StringLookupFactory.INSTANCE.resourceBundleStringLookup("com.domain.messages").lookup("MyKey");
  1273.      * </pre>
  1274.      * <p>
  1275.      * The above example converts {@code "MyKey"} to the key value in the resource bundle at {@code "com.domain.messages"}.
  1276.      * </p>
  1277.      *
  1278.      * @param bundleName Only lookup in this bundle.
  1279.      * @return a ResourceBundleStringLookup instance for the given bundle name.
  1280.      * @since 1.5
  1281.      */
  1282.     public StringLookup resourceBundleStringLookup(final String bundleName) {
  1283.         return new ResourceBundleStringLookup(bundleName);
  1284.     }

  1285.     /**
  1286.      * Returns the ScriptStringLookup singleton instance. NOTE: This lookup is not included as a {@link #addDefaultStringLookups(Map) default lookup} unless
  1287.      * explicitly enabled. See the class level documentation for details.
  1288.      * <p>
  1289.      * Looks up the value for the key in the format "ScriptEngineName:Script".
  1290.      * </p>
  1291.      * <p>
  1292.      * For example: "javascript:3 + 4".
  1293.      * </p>
  1294.      * <p>
  1295.      * Using a {@link StringLookup} from the {@link StringLookupFactory}:
  1296.      * </p>
  1297.      *
  1298.      * <pre>
  1299.      * StringLookupFactory.INSTANCE.scriptStringLookup().lookup("javascript:3 + 4");
  1300.      * </pre>
  1301.      * <p>
  1302.      * When used through a {@link StringSubstitutor}, this lookup must either be added programmatically (as below) or enabled as a default lookup using the
  1303.      * {@value #DEFAULT_STRING_LOOKUPS_PROPERTY} system property (see class documentation).
  1304.      * </p>
  1305.      *
  1306.      * <pre>
  1307.      * Map&lt;String, StringLookup&gt; lookupMap = new HashMap&lt;&gt;();
  1308.      * lookupMap.put("script", StringLookupFactory.INSTANCE.scriptStringLookup());
  1309.      *
  1310.      * StringLookup variableResolver = StringLookupFactory.INSTANCE.interpolatorStringLookup(lookupMap, null, false);
  1311.      *
  1312.      * String value = new StringSubstitutor(variableResolver).replace("${script:javascript:3 + 4}");
  1313.      * </pre>
  1314.      * <p>
  1315.      * The above examples convert {@code "javascript:3 + 4"} to {@code "7"}.
  1316.      * </p>
  1317.      *
  1318.      * @return The ScriptStringLookup singleton instance.
  1319.      * @since 1.5
  1320.      */
  1321.     public StringLookup scriptStringLookup() {
  1322.         return ScriptStringLookup.INSTANCE;
  1323.     }

  1324.     /**
  1325.      * Returns the SystemPropertyStringLookup singleton instance where the lookup key is a system property name.
  1326.      *
  1327.      * <p>
  1328.      * Using a {@link StringLookup} from the {@link StringLookupFactory}:
  1329.      * </p>
  1330.      *
  1331.      * <pre>
  1332.      * StringLookupFactory.INSTANCE.systemPropertyStringLookup().lookup("os.name");
  1333.      * </pre>
  1334.      * <p>
  1335.      * Using a {@link StringSubstitutor}:
  1336.      * </p>
  1337.      *
  1338.      * <pre>
  1339.      * StringSubstitutor.createInterpolator().replace("... ${sys:os.name} ..."));
  1340.      * </pre>
  1341.      * <p>
  1342.      * The above examples convert {@code "os.name"} to the operating system name.
  1343.      * </p>
  1344.      *
  1345.      * @return The SystemPropertyStringLookup singleton instance.
  1346.      */
  1347.     public StringLookup systemPropertyStringLookup() {
  1348.         return StringLookupFactory.INSTANCE_SYSTEM_PROPERTIES;
  1349.     }

  1350.     /**
  1351.      * Returns the UrlDecoderStringLookup singleton instance.
  1352.      * <p>
  1353.      * Decodes URL Strings using the UTF-8 encoding.
  1354.      * </p>
  1355.      * <p>
  1356.      * For example: "Hello%20World%21" becomes "Hello World!".
  1357.      * </p>
  1358.      * <p>
  1359.      * Using a {@link StringLookup} from the {@link StringLookupFactory}:
  1360.      * </p>
  1361.      *
  1362.      * <pre>
  1363.      * StringLookupFactory.INSTANCE.urlDecoderStringLookup().lookup("Hello%20World%21");
  1364.      * </pre>
  1365.      * <p>
  1366.      * Using a {@link StringSubstitutor}:
  1367.      * </p>
  1368.      *
  1369.      * <pre>
  1370.      * StringSubstitutor.createInterpolator().replace("... ${urlDecoder:Hello%20World%21} ..."));
  1371.      * </pre>
  1372.      * <p>
  1373.      * The above examples convert {@code "Hello%20World%21"} to {@code "Hello World!"}.
  1374.      * </p>
  1375.      *
  1376.      * @return The UrlStringLookup singleton instance.
  1377.      * @since 1.6
  1378.      */
  1379.     public StringLookup urlDecoderStringLookup() {
  1380.         return UrlDecoderStringLookup.INSTANCE;
  1381.     }

  1382.     /**
  1383.      * Returns the UrlDecoderStringLookup singleton instance.
  1384.      * <p>
  1385.      * Decodes URL Strings using the UTF-8 encoding.
  1386.      * </p>
  1387.      * <p>
  1388.      * For example: "Hello World!" becomes "Hello+World%21".
  1389.      * </p>
  1390.      * <p>
  1391.      * Using a {@link StringLookup} from the {@link StringLookupFactory}:
  1392.      * </p>
  1393.      *
  1394.      * <pre>
  1395.      * StringLookupFactory.INSTANCE.urlEncoderStringLookup().lookup("Hello World!");
  1396.      * </pre>
  1397.      * <p>
  1398.      * Using a {@link StringSubstitutor}:
  1399.      * </p>
  1400.      *
  1401.      * <pre>
  1402.      * StringSubstitutor.createInterpolator().replace("... ${urlEncoder:Hello World!} ..."));
  1403.      * </pre>
  1404.      * <p>
  1405.      * The above examples convert {@code "Hello World!"} to {@code "Hello%20World%21"}.
  1406.      * </p>
  1407.      *
  1408.      * @return The UrlStringLookup singleton instance.
  1409.      * @since 1.6
  1410.      */
  1411.     public StringLookup urlEncoderStringLookup() {
  1412.         return UrlEncoderStringLookup.INSTANCE;
  1413.     }

  1414.     /**
  1415.      * Returns the UrlStringLookup singleton instance. This lookup is not included as a {@link #addDefaultStringLookups(Map) default lookup} unless explicitly
  1416.      * enabled. See the class level documentation for details.
  1417.      * <p>
  1418.      * Looks up the value for the key in the format "CharsetName:URL".
  1419.      * </p>
  1420.      * <p>
  1421.      * For example, using the HTTP scheme: "UTF-8:http://www.google.com"
  1422.      * </p>
  1423.      * <p>
  1424.      * For example, using the file scheme: "UTF-8:file:///C:/somehome/commons/commons-text/src/test/resources/document.properties"
  1425.      * </p>
  1426.      * <p>
  1427.      * Using a {@link StringLookup} from the {@link StringLookupFactory}:
  1428.      * </p>
  1429.      *
  1430.      * <pre>
  1431.      * StringLookupFactory.INSTANCE.urlStringLookup().lookup("UTF-8:https://www.apache.org");
  1432.      * </pre>
  1433.      * <p>
  1434.      * When used through a {@link StringSubstitutor}, this lookup must either be added programmatically (as below) or enabled as a default lookup using the
  1435.      * {@value #DEFAULT_STRING_LOOKUPS_PROPERTY} system property (see class documentation).
  1436.      * </p>
  1437.      *
  1438.      * <pre>
  1439.      * Map&lt;String, StringLookup&gt; lookupMap = new HashMap&lt;&gt;();
  1440.      * lookupMap.put("url", StringLookupFactory.INSTANCE.urlStringLookup());
  1441.      *
  1442.      * StringLookup variableResolver = StringLookupFactory.INSTANCE.interpolatorStringLookup(lookupMap, null, false);
  1443.      *
  1444.      * String value = new StringSubstitutor(variableResolver).replace("${url:UTF-8:https://www.apache.org}");
  1445.      * </pre>
  1446.      * <p>
  1447.      * The above examples convert {@code "UTF-8:https://www.apache.org"} to the contents of that page.
  1448.      * </p>
  1449.      *
  1450.      * @return The UrlStringLookup singleton instance.
  1451.      * @since 1.5
  1452.      */
  1453.     public StringLookup urlStringLookup() {
  1454.         return UrlStringLookup.INSTANCE;
  1455.     }

  1456.     /**
  1457.      * Returns the XmlDecoderStringLookup singleton instance.
  1458.      * <p>
  1459.      * Decodes strings according to the XML 1.0 specification.
  1460.      * </p>
  1461.      * <p>
  1462.      * For example: "&amp;lt;element&amp;gt;" becomes "&lt;element&gt;".
  1463.      * </p>
  1464.      * <p>
  1465.      * Using a {@link StringLookup} from the {@link StringLookupFactory}:
  1466.      * </p>
  1467.      *
  1468.      * <pre>
  1469.      * StringLookupFactory.INSTANCE.xmlDecoderStringLookup().lookup("&amp;lt;element&amp;gt;");
  1470.      * </pre>
  1471.      * <p>
  1472.      * Using a {@link StringSubstitutor}:
  1473.      * </p>
  1474.      *
  1475.      * <pre>
  1476.      * StringSubstitutor.createInterpolator().replace("... ${xmlDecoder:&amp;lt;element&amp;gt;} ..."));
  1477.      * </pre>
  1478.      * <p>
  1479.      * The above examples convert {@code "&lt;element&gt;"} to {@code "<element>"}.
  1480.      * </p>
  1481.      *
  1482.      * @return The XmlDecoderStringLookup singleton instance.
  1483.      * @since 1.11.0
  1484.      */
  1485.     public StringLookup xmlDecoderStringLookup() {
  1486.         return XmlDecoderStringLookup.INSTANCE;
  1487.     }

  1488.     /**
  1489.      * Returns the XmlEncoderStringLookup singleton instance.
  1490.      * <p>
  1491.      * Encodes strings according to the XML 1.0 specification.
  1492.      * </p>
  1493.      * <p>
  1494.      * For example: "&lt;element&gt;" becomes "&amp;lt;element&amp;gt;".
  1495.      * </p>
  1496.      * <p>
  1497.      * Using a {@link StringLookup} from the {@link StringLookupFactory}:
  1498.      * </p>
  1499.      *
  1500.      * <pre>
  1501.      * StringLookupFactory.INSTANCE.xmlEncoderStringLookup().lookup("&lt;element&gt;");
  1502.      * </pre>
  1503.      * <p>
  1504.      * Using a {@link StringSubstitutor}:
  1505.      * </p>
  1506.      *
  1507.      * <pre>
  1508.      * StringSubstitutor.createInterpolator().replace("... ${xmlEncoder:&lt;element&gt;} ..."));
  1509.      * </pre>
  1510.      * <p>
  1511.      * The above examples convert {@code "<element>"} to {@code "&lt;element&gt;"}.
  1512.      * </p>
  1513.      *
  1514.      * @return The XmlEncoderStringLookup singleton instance.
  1515.      * @since 1.11.0
  1516.      */
  1517.     public StringLookup xmlEncoderStringLookup() {
  1518.         return XmlEncoderStringLookup.INSTANCE;
  1519.     }

  1520.     /**
  1521.      * Returns an XML StringLookup instance.
  1522.      * <p>
  1523.      * If this factory was built using {@link Builder#setFences(Path...)}, then the string lookup is fenced and will throw an {@link IllegalArgumentException}
  1524.      * if a lookup causes causes a path to resolve outside of these fences. Otherwise, the result is unfenced to preserved behavior from previous versions.
  1525.      * </p>
  1526.      * <p>
  1527.      * We look up the value for the key in the format "DocumentPath:XPath".
  1528.      * </p>
  1529.      * <p>
  1530.      * For example: "com/domain/document.xml:/path/to/node".
  1531.      * </p>
  1532.      * <p>
  1533.      * Using a {@link StringLookup} from the {@link StringLookupFactory}:
  1534.      * </p>
  1535.      *
  1536.      * <pre>
  1537.      * StringLookupFactory.INSTANCE.xmlStringLookup().lookup("com/domain/document.xml:/path/to/node");
  1538.      * </pre>
  1539.      * <p>
  1540.      * Using a {@link StringSubstitutor}:
  1541.      * </p>
  1542.      *
  1543.      * <pre>
  1544.      * StringSubstitutor.createInterpolator().replace("... ${xml:com/domain/document.xml:/path/to/node} ..."));
  1545.      * </pre>
  1546.      * <p>
  1547.      * The above examples convert {@code "com/domain/document.xml:/path/to/node"} to the value of the XPath in the XML document.
  1548.      * </p>
  1549.      *
  1550.      * @return An XML StringLookup instance.
  1551.      * @since 1.5
  1552.      */
  1553.     public StringLookup xmlStringLookup() {
  1554.         return fences != null ? xmlStringLookup(XmlStringLookup.DEFAULT_FEATURES, fences) : XmlStringLookup.INSTANCE;
  1555.     }

  1556.     /**
  1557.      * Returns an XML StringLookup instance.
  1558.      * <p>
  1559.      * If this factory was built using {@link Builder#setFences(Path...)}, then the string lookup is fenced and will throw an {@link IllegalArgumentException}
  1560.      * if a lookup causes causes a path to resolve outside of these fences. Otherwise, the result is unfenced to preserved behavior from previous versions.
  1561.      * </p>
  1562.      * <p>
  1563.      * We look up the value for the key in the format "DocumentPath:XPath".
  1564.      * </p>
  1565.      * <p>
  1566.      * For example: "com/domain/document.xml:/path/to/node".
  1567.      * </p>
  1568.      * <p>
  1569.      * Using a {@link StringLookup} from the {@link StringLookupFactory}:
  1570.      * </p>
  1571.      *
  1572.      * <pre>
  1573.      * StringLookupFactory.INSTANCE.xmlStringLookup().lookup("com/domain/document.xml:/path/to/node");
  1574.      * </pre>
  1575.      * <p>
  1576.      * Using a {@link StringSubstitutor}:
  1577.      * </p>
  1578.      *
  1579.      * <pre>
  1580.      * StringSubstitutor.createInterpolator().replace("... ${xml:com/domain/document.xml:/path/to/node} ..."));
  1581.      * </pre>
  1582.      * <p>
  1583.      * The above examples convert {@code "com/domain/document.xml:/path/to/node"} to the value of the XPath in the XML document.
  1584.      * </p>
  1585.      *
  1586.      * @param xPathFactoryFeatures XPathFactory features to set.
  1587.      * @return An XML StringLookup instance.
  1588.      * @see XPathFactory#setFeature(String, boolean)
  1589.      * @since 1.11.0
  1590.      */
  1591.     public StringLookup xmlStringLookup(final Map<String, Boolean> xPathFactoryFeatures) {
  1592.         return xmlStringLookup(xPathFactoryFeatures, fences);
  1593.     }

  1594.     /**
  1595.      * Returns a fenced XML StringLookup instance.
  1596.      * <p>
  1597.      * If this factory was built using {@link Builder#setFences(Path...)}, then the string lookup is fenced and will throw an {@link IllegalArgumentException}
  1598.      * if a lookup causes causes a path to resolve outside of these fences. Otherwise, the result is unfenced to preserved behavior from previous versions.
  1599.      * </p>
  1600.      * <p>
  1601.      * We look up the value for the key in the format "DocumentPath:XPath".
  1602.      * </p>
  1603.      * <p>
  1604.      * For example: "com/domain/document.xml:/path/to/node".
  1605.      * </p>
  1606.      * <p>
  1607.      * Using a {@link StringLookup} from the {@link StringLookupFactory} fenced by the current directory ({@code Paths.get("")}):
  1608.      * </p>
  1609.      *
  1610.      * <pre>
  1611.      * StringLookupFactory.INSTANCE.xmlStringLookup(map, Pathe.get("")).lookup("com/domain/document.xml:/path/to/node");
  1612.      * </pre>
  1613.      * <p>
  1614.      * To use a {@link StringLookup} fenced by the current directory, use:
  1615.      * </p>
  1616.      *
  1617.      * <pre>
  1618.      * StringLookupFactory.INSTANCE.xmlStringLookup(Paths.get("")).lookup("com/domain/document.xml:/path/to/node");
  1619.      *
  1620.      * // throws IllegalArgumentException
  1621.      * StringLookupFactory.INSTANCE.xmlStringLookup(Paths.get("")).lookup("/rootdir/foo/document.xml:/path/to/node");
  1622.      *
  1623.      * // throws IllegalArgumentException
  1624.      * StringLookupFactory.INSTANCE.xmlStringLookup(Paths.get("")).lookup("../com/domain/document.xml:/path/to/node");
  1625.      * </pre>
  1626.      * <p>
  1627.      * The above examples convert {@code "com/domain/document.xml:/path/to/node"} to the value of the XPath in the XML document.
  1628.      * </p>
  1629.      * <p>
  1630.      * {@link StringSubstitutor} methods like {@link StringSubstitutor#replace(String)} will throw a {@link IllegalArgumentException} when a file doesn't
  1631.      * resolves in a fence.
  1632.      * </p>
  1633.      *
  1634.      * @param xPathFactoryFeatures XPathFactory features to set.
  1635.      * @param fences               The fences guarding Path resolution.
  1636.      * @return An XML StringLookup instance.
  1637.      * @since 1.12.0
  1638.      */
  1639.     public StringLookup xmlStringLookup(final Map<String, Boolean> xPathFactoryFeatures, final Path... fences) {
  1640.         return new XmlStringLookup(xPathFactoryFeatures, fences);
  1641.     }
  1642. }