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