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