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