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.net.InetAddress; 021import java.nio.charset.StandardCharsets; 022import java.nio.file.Path; 023import java.util.Base64; 024import java.util.Collections; 025import java.util.HashMap; 026import java.util.Locale; 027import java.util.Map; 028import java.util.Properties; 029import java.util.function.BiFunction; 030import java.util.function.Function; 031import java.util.function.Supplier; 032 033import javax.xml.xpath.XPathFactory; 034 035import org.apache.commons.text.StringSubstitutor; 036 037/** 038 * Create instances of string lookups or access singleton string lookups implemented in this package. 039 * <p> 040 * The "classic" look up is {@link #mapStringLookup(Map)}. 041 * </p> 042 * <p> 043 * The methods for variable interpolation (A.K.A. variable substitution) are: 044 * </p> 045 * <ul> 046 * <li>{@link #interpolatorStringLookup()}.</li> 047 * <li>{@link #interpolatorStringLookup(Map)}.</li> 048 * <li>{@link #interpolatorStringLookup(StringLookup)}.</li> 049 * <li>{@link #interpolatorStringLookup(Map, StringLookup, boolean)}.</li> 050 * </ul> 051 * <p> 052 * Unless explicitly requested otherwise, a set of default lookups are included for convenience with these variable interpolation methods. These defaults are 053 * listed in the table below. However, the exact lookups included can be configured through the use of the {@value #DEFAULT_STRING_LOOKUPS_PROPERTY} system 054 * 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 055 * {@link DefaultStringLookup} enum. For example, setting this system property to {@code "BASE64_ENCODER,ENVIRONMENT"} will only include the 056 * {@link DefaultStringLookup#BASE64_ENCODER BASE64_ENCODER} and {@link DefaultStringLookup#ENVIRONMENT ENVIRONMENT} lookups. Setting the property to the empty 057 * string will cause no defaults to be configured. Note that not all lookups defined here and in {@link DefaultStringLookup} are included by default. 058 * Specifically, lookups that can execute code (e.g., {@link DefaultStringLookup#SCRIPT SCRIPT}) and those that can result in contact with remote servers (e.g., 059 * {@link DefaultStringLookup#URL URL} and {@link DefaultStringLookup#DNS DNS}) are not included by default. The current set of default lookups can be accessed 060 * directly with {@link #addDefaultStringLookups(Map)}. 061 * </p> 062 * <table> 063 * <caption>Default String Lookups</caption> 064 * <tr> 065 * <th>Key</th> 066 * <th>Interface</th> 067 * <th>Factory Method</th> 068 * <th>Since</th> 069 * </tr> 070 * <tr> 071 * <td>{@value #KEY_BASE64_DECODER}</td> 072 * <td>{@link StringLookup}</td> 073 * <td>{@link #base64DecoderStringLookup()}</td> 074 * <td>1.6</td> 075 * </tr> 076 * <tr> 077 * <td>{@value #KEY_BASE64_ENCODER}</td> 078 * <td>{@link StringLookup}</td> 079 * <td>{@link #base64EncoderStringLookup()}</td> 080 * <td>1.6</td> 081 * </tr> 082 * <tr> 083 * <td>{@value #KEY_CONST}</td> 084 * <td>{@link StringLookup}</td> 085 * <td>{@link #constantStringLookup()}</td> 086 * <td>1.5</td> 087 * </tr> 088 * <tr> 089 * <td>{@value #KEY_DATE}</td> 090 * <td>{@link StringLookup}</td> 091 * <td>{@link #dateStringLookup()}</td> 092 * <td>1.5</td> 093 * </tr> 094 * <tr> 095 * <td>{@value #KEY_ENV}</td> 096 * <td>{@link StringLookup}</td> 097 * <td>{@link #environmentVariableStringLookup()}</td> 098 * <td>1.3</td> 099 * </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 */ 225public 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(StringLookupFactory.DEFAULT_STRING_LOOKUPS_PROPERTY) 349 ? parseStringLookups(props.getProperty(StringLookupFactory.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 StringLookupFactory.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 StringLookupFactory.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 StringLookupFactory.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 StringLookupFactory.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 StringLookupFactory.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 StringLookupFactory.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}