Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Interpolation Defaults #341

Merged
merged 2 commits into from Sep 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 1 addition & 2 deletions src/changes/changes.xml
Expand Up @@ -71,15 +71,14 @@ The <action> type attribute can be add,update,fix,remove.
<action issue="TEXT-209" type="fix" dev="ggregory" due-to="Arturo Bernal">Use Math.min() call instead of doing it manually. #335.</action>
<action type="fix" dev="ggregory" due-to="ValentijnvdBeek, Gary Gregory">TextStringBuilder: Throw OutOfMemoryError instead of NegativeArraySizeException.</action>
<action type="fix" dev="ggregory" due-to="ValentijnvdBeek, Gary Gregory">TextStringBuilder: Can't grow to sizes up to Integer.MAX_VALUE.</action>
<action type="fix" dev="mattjuntunen">Make default string lookups configurable via system property. Remove dns, url, and script lookups from defaults. If these lookups are required for use in StringSubstitutor.createInterpolator(), they must be enabled via system property. See StringLookupFactory for details.</action>
<!-- ADD -->
<action issue="TEXT-207" type="add" dev="mattjuntunen">Add DoubleFormat utility.</action>
<action issue="TEXT-190" type="add" dev="kinow" due-to="Benjamin Bing">Document negative limit for WordUtils abbreviate method</action>
<action issue="TEXT-188" type="add" dev="kinow" due-to="Jakob Vesterstrøm">Speed up LevenshteinDistance with threshold by exiting early</action>
<action issue="TEXT-185" type="add" dev="ggregory" due-to="Larry West, Gary Gregory">Release Notes page hasn't been updated for 1.9 release yet.</action>
<action type="add" dev="ggregory" due-to="Gary Gregory">Add StrBuilder.isNotEmpty().</action>
<!-- UPDATE -->
<action type="update" dev="kinow" due-to="Dependabot">Bump actions/cache from v2 to v3.0.8 #205, #217, #234, #339.</action>
<action type="update" dev="kinow" due-to="Dependabot, Gary Gregory">Bump actions/checkout from 1 to 3.0.2 #138, #146, #165, #183, #274, #279, #304.</action>
<action type="update" dev="kinow" due-to="Dependabot">Bump actions/setup-java from v1.4.0 to 3 #147, #156, #155, #172, #215, #314.</action>
<action type="update" dev="kinow" due-to="Dependabot">Bump github/codeql-action from 1 to 2 #319.</action>
<action type="update" dev="ggregory" due-to="Dependabot">Bump checkstyle from 8.34 to 9.3, #141, #168, #182, #188, #193, #201, #208, #211, #228, #235, #245, #253, #255, #262, #270, #280, #287, #299, #315, #321.</action>
Expand Down
94 changes: 84 additions & 10 deletions src/main/java/org/apache/commons/text/StringSubstitutor.java
Expand Up @@ -141,26 +141,28 @@
*
* <pre>
* final StringSubstitutor interpolator = StringSubstitutor.createInterpolator();
* interpolator.setEnableSubstitutionInVariables(true); // Allows for nested $'s.
* final String text = interpolator.replace("Base64 Decoder: ${base64Decoder:SGVsbG9Xb3JsZCE=}\n"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See my other comment below for this type of comment.

* final String text = interpolator.replace(
* "Base64 Decoder: ${base64Decoder:SGVsbG9Xb3JsZCE=}\n"
* + "Base64 Encoder: ${base64Encoder:HelloWorld!}\n"
* + "Java Constant: ${const:java.awt.event.KeyEvent.VK_ESCAPE}\n"
* + "Date: ${date:yyyy-MM-dd}\n" + "DNS: ${dns:address|apache.org}\n"
* + "Date: ${date:yyyy-MM-dd}\n"
* + "Environment Variable: ${env:USERNAME}\n"
* + "File Content: ${file:UTF-8:src/test/resources/document.properties}\n"
* + "Java: ${java:version}\n" + "Localhost: ${localhost:canonical-name}\n"
* + "Java: ${java:version}\n"
* + "Localhost: ${localhost:canonical-name}\n"
* + "Properties File: ${properties:src/test/resources/document.properties::mykey}\n"
* + "Resource Bundle: ${resourceBundle:org.apache.commons.text.example.testResourceBundleLookup:mykey}\n"
* + "Script: ${script:javascript:3 + 4}\n" + "System Property: ${sys:user.dir}\n"
* + "System Property: ${sys:user.dir}\n"
* + "URL Decoder: ${urlDecoder:Hello%20World%21}\n"
* + "URL Encoder: ${urlEncoder:Hello World!}\n"
* + "URL Content (HTTP): ${url:UTF-8:http://www.apache.org}\n"
* + "URL Content (HTTPS): ${url:UTF-8:https://www.apache.org}\n"
* + "URL Content (File): ${url:UTF-8:file:///${sys:user.dir}/src/test/resources/document.properties}\n"
* + "XML XPath: ${xml:src/test/resources/document.xml:/root/path/to/node}\n");
* </pre>
* <p>
* For documentation of each lookup, see {@link StringLookupFactory}.
* For documentation and a full list of available lookups, see {@link StringLookupFactory}.
* </p>
* <p><strong>NOTE:</strong> The list of lookups available by default in {@link #createInterpolator()} changed
* in version {@code 1.10.0}. See the {@link StringLookupFactory} documentation for details and an explanation
* on how to reproduce the previous functionality.
* </p>
*
* <h2>Using Recursive Variable Replacement</h2>
Expand Down Expand Up @@ -292,9 +294,81 @@ public String toString() {
*
* <pre>
* StringSubstitutor.createInterpolator().replace(
* "OS name: ${sys:os.name}, " + "3 + 4 = ${script:javascript:3 + 4}");
* "OS name: ${sys:os.name}, user: ${env:USER}");
* </pre>
*
* <p>The table below lists the lookups available by default in the returned instance. These
* may be modified through the use of the {@value StringLookupFactory#DEFAULT_STRING_LOOKUPS_PROPERTY}
* system property, as described in the {@link StringLookupFactory} documentation.</p>
*
* <p><strong>NOTE:</strong> The list of lookups available by default changed in version {@code 1.10.0}.
* Configuration via system property (as mentioned above) may be necessary to reproduce previous functionality.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"may be"? You MUST do something if you want the previous behavior. I would be more explicit and list the lookups removed from the default here (DNS, ...)

* </p>
*
* <table>
* <caption>Default Lookups</caption>
* <tr>
* <th>Key</th>
* <th>Lookup</th>
* </tr>
* <tr>
* <td>{@value StringLookupFactory#KEY_BASE64_DECODER}</td>
* <td>{@link StringLookupFactory#base64DecoderStringLookup()}</td>
* </tr>
* <tr>
* <td>{@value StringLookupFactory#KEY_BASE64_ENCODER}</td>
* <td>{@link StringLookupFactory#base64EncoderStringLookup()}</td>
* </tr>
* <tr>
* <td>{@value StringLookupFactory#KEY_CONST}</td>
* <td>{@link StringLookupFactory#constantStringLookup()}</td>
* </tr>
* <tr>
* <td>{@value StringLookupFactory#KEY_DATE}</td>
* <td>{@link StringLookupFactory#dateStringLookup()}</td>
* </tr>
* <tr>
* <td>{@value StringLookupFactory#KEY_ENV}</td>
* <td>{@link StringLookupFactory#environmentVariableStringLookup()}</td>
* </tr>
* <tr>
* <td>{@value StringLookupFactory#KEY_FILE}</td>
* <td>{@link StringLookupFactory#fileStringLookup()}</td>
* </tr>
* <tr>
* <td>{@value StringLookupFactory#KEY_JAVA}</td>
* <td>{@link StringLookupFactory#javaPlatformStringLookup()}</td>
* </tr>
* <tr>
* <td>{@value StringLookupFactory#KEY_LOCALHOST}</td>
* <td>{@link StringLookupFactory#localHostStringLookup()}</td>
* </tr>
* <tr>
* <td>{@value StringLookupFactory#KEY_PROPERTIES}</td>
* <td>{@link StringLookupFactory#propertiesStringLookup()}</td>
* </tr>
* <tr>
* <td>{@value StringLookupFactory#KEY_RESOURCE_BUNDLE}</td>
* <td>{@link StringLookupFactory#resourceBundleStringLookup()}</td>
* </tr>
* <tr>
* <td>{@value StringLookupFactory#KEY_SYS}</td>
* <td>{@link StringLookupFactory#systemPropertyStringLookup()}</td>
* </tr>
* <tr>
* <td>{@value StringLookupFactory#KEY_URL_DECODER}</td>
* <td>{@link StringLookupFactory#urlDecoderStringLookup()}</td>
* </tr>
* <tr>
* <td>{@value StringLookupFactory#KEY_URL_ENCODER}</td>
* <td>{@link StringLookupFactory#urlEncoderStringLookup()}</td>
* </tr>
* <tr>
* <td>{@value StringLookupFactory#KEY_XML}</td>
* <td>{@link StringLookupFactory#xmlStringLookup()}</td>
* </tr>
* </table>
*
* @return a new instance using the interpolator string lookup.
* @see StringLookupFactory#interpolatorStringLookup()
* @since 1.8
Expand Down
Expand Up @@ -16,13 +16,16 @@
*/
package org.apache.commons.text.lookup;

import java.util.stream.Stream;

/**
* An enumeration defining {@link StringLookup} objects available through {@link StringLookupFactory}.
* <p>
* This enum was adapted and expanded from Apache Commons Configuration 2.4.
* </p>
* <p><strong>NOTE:</strong> Starting in version 1.10.0, not all lookups defined in this class are
* included by default in the
* {@link StringLookupFactory#addDefaultStringLookups(java.util.Map) StringLookupFactory.addDefaultStringLookups}
* method. See the {@link StringLookupFactory} class documentation for details.
* </p>
*
* @see StringLookupFactory
* @see StringLookup
Expand All @@ -31,100 +34,125 @@
public enum DefaultStringLookup {

/**
* The lookup for Base64 decoding using the key {@value StringLookupFactory#KEY_BASE64_DECODER}.
* The lookup for Base64 decoding using the key {@code "base64Decoder"}.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wrong fix here and in subsequent comments, instead, use the FQCN, for example, replace:

{@value StringLookupFactory#KEY_BASE64_DECODER}

with

{@value org.apache.commons.text.lookup.StringLookupFactory#KEY_BASE64_DECODER}

* @see StringLookupFactory#KEY_BASE64_DECODER
* @see StringLookupFactory#base64DecoderStringLookup()
*/
BASE64_DECODER(StringLookupFactory.KEY_BASE64_DECODER, StringLookupFactory.INSTANCE.base64DecoderStringLookup()),

/**
* The lookup for Base64 decoding using the key {@value StringLookupFactory#KEY_BASE64_ENCODER}.
* The lookup for Base64 decoding using the key {@code "base64Encoder"}.
* @see StringLookupFactory#KEY_BASE64_ENCODER
* @see StringLookupFactory#base64EncoderStringLookup()
*/
BASE64_ENCODER(StringLookupFactory.KEY_BASE64_ENCODER, StringLookupFactory.INSTANCE.base64EncoderStringLookup()),

/**
* The lookup for constants using the key {@value StringLookupFactory#KEY_CONST}.
* The lookup for Java static class member constants using the key {@code "const"}.
* @see StringLookupFactory#KEY_CONST
* @see StringLookupFactory#constantStringLookup()
*/
CONST(StringLookupFactory.KEY_CONST, StringLookupFactory.INSTANCE.constantStringLookup()),

/**
* The lookup for dates using the key {@value StringLookupFactory#KEY_DATE}.
* The lookup for formatting the current date using the key {@code "date"}.
* @see StringLookupFactory#KEY_DATE
* @see StringLookupFactory#dateStringLookup()
*/
DATE(StringLookupFactory.KEY_DATE, StringLookupFactory.INSTANCE.dateStringLookup()),

/**
* The lookup for DNS using the key {@value StringLookupFactory#KEY_DNS}.
*
* The lookup for DNS using the key {@code "dns"}.
* @see StringLookupFactory#KEY_DNS
* @see StringLookupFactory#dnsStringLookup()
* @since 1.8
*/
DNS(StringLookupFactory.KEY_DNS, StringLookupFactory.INSTANCE.dnsStringLookup()),

/**
* The lookup for environment properties using the key {@value StringLookupFactory#KEY_ENV}.
* The lookup for environment properties using the key {@code "env"}.
* @see StringLookupFactory#KEY_ENV
* @see StringLookupFactory#environmentVariableStringLookup()
*/
ENVIRONMENT(StringLookupFactory.KEY_ENV, StringLookupFactory.INSTANCE.environmentVariableStringLookup()),

/**
* The lookup for files using the key {@value StringLookupFactory#KEY_FILE}.
* The lookup for files using the key {@code "file"}.
* @see StringLookupFactory#KEY_FILE
* @see StringLookupFactory#fileStringLookup()
*/
FILE(StringLookupFactory.KEY_FILE, StringLookupFactory.INSTANCE.fileStringLookup()),

/**
* The lookup for Java platform information using the key {@value StringLookupFactory#KEY_JAVA}.
* The lookup for Java platform information using the key {@code "java"}.
* @see StringLookupFactory#KEY_JAVA
* @see StringLookupFactory#javaPlatformStringLookup()
*/
JAVA(StringLookupFactory.KEY_JAVA, StringLookupFactory.INSTANCE.javaPlatformStringLookup()),

/**
* The lookup for localhost information using the key {@value StringLookupFactory#KEY_LOCALHOST}.
* The lookup for localhost information using the key {@code "localhost"}.
* @see StringLookupFactory#KEY_LOCALHOST
* @see StringLookupFactory#localHostStringLookup()
*/
LOCAL_HOST(StringLookupFactory.KEY_LOCALHOST, StringLookupFactory.INSTANCE.localHostStringLookup()),

/**
* The lookup for properties using the key {@value StringLookupFactory#KEY_PROPERTIES}.
* The lookup for properties using the key {@code "properties"}.
* @see StringLookupFactory#KEY_PROPERTIES
* @see StringLookupFactory#propertiesStringLookup()
*/
PROPERTIES(StringLookupFactory.KEY_PROPERTIES, StringLookupFactory.INSTANCE.propertiesStringLookup()),

/**
* The lookup for resource bundles using the key {@value StringLookupFactory#KEY_RESOURCE_BUNDLE}.
* The lookup for resource bundles using the key {@code "resourceBundle"}.
* @see StringLookupFactory#KEY_RESOURCE_BUNDLE
* @see StringLookupFactory#resourceBundleStringLookup()
*/
RESOURCE_BUNDLE(StringLookupFactory.KEY_RESOURCE_BUNDLE, StringLookupFactory.INSTANCE.resourceBundleStringLookup()),

/**
* The lookup for scripts using the key {@value StringLookupFactory#KEY_SCRIPT}.
* The lookup for scripts using the key {@code "script"}.
* @see StringLookupFactory#KEY_SCRIPT
* @see StringLookupFactory#scriptStringLookup()
*/
SCRIPT(StringLookupFactory.KEY_SCRIPT, StringLookupFactory.INSTANCE.scriptStringLookup()),

/**
* The lookup for system properties using the key {@value StringLookupFactory#KEY_SYS}.
* The lookup for system properties using the key {@code "sys"}.
* @see StringLookupFactory#KEY_SYS
* @see StringLookupFactory#systemPropertyStringLookup()
*/
SYSTEM_PROPERTIES(StringLookupFactory.KEY_SYS, StringLookupFactory.INSTANCE.systemPropertyStringLookup()),

/**
* The lookup for URLs using the key {@value StringLookupFactory#KEY_URL}.
* The lookup for URLs using the key {@code "url"}.
* @see StringLookupFactory#KEY_URL
* @see StringLookupFactory#urlStringLookup()
*/
URL(StringLookupFactory.KEY_URL, StringLookupFactory.INSTANCE.urlStringLookup()),

/**
* The lookup for URL decoding using the key {@value StringLookupFactory#KEY_URL_DECODER}.
* The lookup for URL decoding using the key {@code "urlDecoder"}.
* @see StringLookupFactory#KEY_URL_DECODER
* @see StringLookupFactory#urlDecoderStringLookup()
*/
URL_DECODER(StringLookupFactory.KEY_URL_DECODER, StringLookupFactory.INSTANCE.urlDecoderStringLookup()),

/**
* The lookup for URL decoding using the key {@value StringLookupFactory#KEY_URL_ENCODER}.
* The lookup for URL decoding using the key {@code "urlEncoder"}.
* @see StringLookupFactory#KEY_URL_ENCODER
* @see StringLookupFactory#urlEncoderStringLookup()
*/
URL_ENCODER(StringLookupFactory.KEY_URL_ENCODER, StringLookupFactory.INSTANCE.urlEncoderStringLookup()),

/**
* The lookup for URL decoding using the key {@value StringLookupFactory#KEY_XML}.
* The lookup for URL decoding using the key {@code "xml"}.
* @see StringLookupFactory#KEY_XML
* @see StringLookupFactory#xmlStringLookup()
*/
XML(StringLookupFactory.KEY_XML, StringLookupFactory.INSTANCE.xmlStringLookup());

/**
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is handy, why remote it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't believe it is appropriate to include a main method here. If the values are not easily accessible via the documentation, then that indicates a failure in the documentation and not a need for a one-off tool.

* Prints out to the console the mapping from enum keys to enum name.
* @param args ignored.
*/
public static void main(final String[] args) {
Stream.of(values()).forEach(e -> System.out.println(e.getKey() + "=" + e.name()));
}

/** The prefix under which the associated lookup object is registered. */
private final String key;

Expand Down
Expand Up @@ -18,7 +18,6 @@

import java.util.Collections;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;

Expand All @@ -40,10 +39,6 @@ class InterpolatorStringLookup extends AbstractStringLookup {
/** Constant for the prefix separator. */
private static final char PREFIX_SEPARATOR = ':';

static String toKey(final String key) {
return key.toLowerCase(Locale.ROOT);
}

/** The default string lookup. */
private final StringLookup defaultStringLookup;

Expand Down Expand Up @@ -72,7 +67,7 @@ static String toKey(final String key) {
this.defaultStringLookup = defaultStringLookup;
this.stringLookupMap = new HashMap<>(stringLookupMap.size());
for (final Entry<String, StringLookup> entry : stringLookupMap.entrySet()) {
this.stringLookupMap.put(toKey(entry.getKey()), entry.getValue());
this.stringLookupMap.put(StringLookupFactory.toKey(entry.getKey()), entry.getValue());
}
if (addDefaultLookups) {
StringLookupFactory.INSTANCE.addDefaultStringLookups(this.stringLookupMap);
Expand Down Expand Up @@ -127,7 +122,7 @@ public String lookup(String key) {

final int prefixPos = key.indexOf(PREFIX_SEPARATOR);
if (prefixPos >= 0) {
final String prefix = toKey(key.substring(0, prefixPos));
final String prefix = StringLookupFactory.toKey(key.substring(0, prefixPos));
final String name = key.substring(prefixPos + 1);
final StringLookup lookup = stringLookupMap.get(prefix);
String value = null;
Expand Down