All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.tencent.polaris.factory.util.PropertyPlaceholderHelper Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2002-2018 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.tencent.polaris.factory.util;

import com.tencent.polaris.api.utils.StringUtils;
import com.tencent.polaris.logging.LoggerFactory;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import org.slf4j.Logger;

/**
 * copy from https://github.com/spring-projects/spring-framework/blob/main/spring-core/src/main/java/org/springframework/util/PropertyPlaceholderHelper.java
 *
 * Utility class for working with Strings that have placeholder values in them. A placeholder takes the form
 * {@code ${name}}. Using {@code PropertyPlaceholderHelper} these placeholders can be substituted for
 * user-supplied values. 

Values for substitution can be supplied using a {@link Properties} instance or * using a {@link PlaceholderResolver}. */ public class PropertyPlaceholderHelper { private static final Logger logger = LoggerFactory.getLogger(PropertyPlaceholderHelper.class); private static final Map wellKnownSimplePrefixes = new HashMap<>(4); static { wellKnownSimplePrefixes.put("}", "{"); wellKnownSimplePrefixes.put("]", "["); wellKnownSimplePrefixes.put(")", "("); } private final String placeholderPrefix; private final String placeholderSuffix; private final String simplePrefix; private final String valueSeparator; private final boolean ignoreUnresolvablePlaceholders; /** * Creates a new {@code PropertyPlaceholderHelper} that uses the supplied prefix and suffix. * Unresolvable placeholders are ignored. * * @param placeholderPrefix the prefix that denotes the start of a placeholder * @param placeholderSuffix the suffix that denotes the end of a placeholder */ public PropertyPlaceholderHelper(String placeholderPrefix, String placeholderSuffix) { this(placeholderPrefix, placeholderSuffix, null, true); } /** * Creates a new {@code PropertyPlaceholderHelper} that uses the supplied prefix and suffix. * * @param placeholderPrefix the prefix that denotes the start of a placeholder * @param placeholderSuffix the suffix that denotes the end of a placeholder * @param valueSeparator the separating character between the placeholder variable * and the associated default value, if any * @param ignoreUnresolvablePlaceholders indicates whether unresolvable placeholders should * be ignored ({@code true}) or cause an exception ({@code false}) */ public PropertyPlaceholderHelper(String placeholderPrefix, String placeholderSuffix, String valueSeparator, boolean ignoreUnresolvablePlaceholders) { this.placeholderPrefix = placeholderPrefix; this.placeholderSuffix = placeholderSuffix; String simplePrefixForSuffix = wellKnownSimplePrefixes.get(this.placeholderSuffix); if (simplePrefixForSuffix != null && this.placeholderPrefix.endsWith(simplePrefixForSuffix)) { this.simplePrefix = simplePrefixForSuffix; } else { this.simplePrefix = this.placeholderPrefix; } this.valueSeparator = valueSeparator; this.ignoreUnresolvablePlaceholders = ignoreUnresolvablePlaceholders; } /** * Replaces all placeholders of format {@code ${name}} with the corresponding * property from the supplied {@link Properties}. * * @param value the value containing the placeholders to be replaced * @param properties the {@code Properties} to use for replacement * @return the supplied value with placeholders replaced inline */ public String replacePlaceholders(String value, final Properties properties) { return replacePlaceholders(value, properties::getProperty); } /** * Replaces all placeholders of format {@code ${name}} with the value returned * from the supplied {@link PlaceholderResolver}. * * @param value the value containing the placeholders to be replaced * @param placeholderResolver the {@code PlaceholderResolver} to use for replacement * @return the supplied value with placeholders replaced inline */ public String replacePlaceholders(String value, PlaceholderResolver placeholderResolver) { return parseStringValue(value, placeholderResolver, null); } protected String parseStringValue( String value, PlaceholderResolver placeholderResolver, Set visitedPlaceholders) { int startIndex = value.indexOf(this.placeholderPrefix); if (startIndex == -1) { return value; } StringBuilder result = new StringBuilder(value); while (startIndex != -1) { int endIndex = findPlaceholderEndIndex(result, startIndex); if (endIndex != -1) { String placeholder = result.substring(startIndex + this.placeholderPrefix.length(), endIndex); String originalPlaceholder = placeholder; if (visitedPlaceholders == null) { visitedPlaceholders = new HashSet<>(4); } if (!visitedPlaceholders.add(originalPlaceholder)) { throw new IllegalArgumentException( "Circular placeholder reference '" + originalPlaceholder + "' in property definitions"); } // Recursive invocation, parsing placeholders contained in the placeholder key. placeholder = parseStringValue(placeholder, placeholderResolver, visitedPlaceholders); // Now obtain the value for the fully resolved key... String propVal = placeholderResolver.resolvePlaceholder(placeholder); if (propVal == null && this.valueSeparator != null) { int separatorIndex = placeholder.indexOf(this.valueSeparator); if (separatorIndex != -1) { String actualPlaceholder = placeholder.substring(0, separatorIndex); String defaultValue = placeholder.substring(separatorIndex + this.valueSeparator.length()); propVal = placeholderResolver.resolvePlaceholder(actualPlaceholder); if (propVal == null) { propVal = defaultValue; } } } if (propVal != null) { // Recursive invocation, parsing placeholders contained in the // previously resolved placeholder value. propVal = parseStringValue(propVal, placeholderResolver, visitedPlaceholders); result.replace(startIndex, endIndex + this.placeholderSuffix.length(), propVal); if (logger.isTraceEnabled()) { logger.trace("Resolved placeholder '" + placeholder + "'"); } startIndex = result.indexOf(this.placeholderPrefix, startIndex + propVal.length()); } else if (this.ignoreUnresolvablePlaceholders) { // Proceed with unprocessed value. startIndex = result.indexOf(this.placeholderPrefix, endIndex + this.placeholderSuffix.length()); } else { throw new IllegalArgumentException("Could not resolve placeholder '" + placeholder + "'" + " in value \"" + value + "\""); } visitedPlaceholders.remove(originalPlaceholder); } else { startIndex = -1; } } return result.toString(); } private int findPlaceholderEndIndex(CharSequence buf, int startIndex) { int index = startIndex + this.placeholderPrefix.length(); int withinNestedPlaceholder = 0; while (index < buf.length()) { if (StringUtils.substringMatch(buf, index, this.placeholderSuffix)) { if (withinNestedPlaceholder > 0) { withinNestedPlaceholder--; index = index + this.placeholderSuffix.length(); } else { return index; } } else if (StringUtils.substringMatch(buf, index, this.simplePrefix)) { withinNestedPlaceholder++; index = index + this.simplePrefix.length(); } else { index++; } } return -1; } /** * Strategy interface used to resolve replacement values for placeholders contained in Strings. */ @FunctionalInterface public interface PlaceholderResolver { /** * Resolve the supplied placeholder name to the replacement value. * * @param placeholderName the name of the placeholder to resolve * @return the replacement value, or {@code null} if no replacement is to be made */ String resolvePlaceholder(String placeholderName); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy