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