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