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