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

org.opensearch.common.settings.PropertyPlaceholder Maven / Gradle / Ivy

There is a newer version: 2.18.0
Show newest version
/*
 * SPDX-License-Identifier: Apache-2.0
 *
 * The OpenSearch Contributors require contributions made to
 * this file be licensed under the Apache-2.0 license or a
 * compatible open source license.
 */

/*
 * Licensed to Elasticsearch under one or more contributor
 * license agreements. See the NOTICE file distributed with
 * this work for additional information regarding copyright
 * ownership. Elasticsearch licenses this file to you 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
 *
 *     http://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.
 */

/*
 * Modifications Copyright OpenSearch Contributors. See
 * GitHub history for details.
 */

package org.opensearch.common.settings;

import org.opensearch.core.common.Strings;

import java.util.HashSet;
import java.util.Objects;
import java.util.Properties;
import java.util.Set;

/**
 * Utility class for working with Strings that have placeholder values in them. A placeholder takes the form
 * {@code ${name}}. Using {@code PropertyPlaceholder} 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}. * * @opensearch.internal */ class PropertyPlaceholder { private final String placeholderPrefix; private final String placeholderSuffix; private final boolean ignoreUnresolvablePlaceholders; /** * Creates a new 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 ignoreUnresolvablePlaceholders indicates whether unresolvable placeholders should be ignored * (true) or cause an exception (false). */ PropertyPlaceholder(String placeholderPrefix, String placeholderSuffix, boolean ignoreUnresolvablePlaceholders) { this.placeholderPrefix = Objects.requireNonNull(placeholderPrefix); this.placeholderSuffix = Objects.requireNonNull(placeholderSuffix); this.ignoreUnresolvablePlaceholders = ignoreUnresolvablePlaceholders; } /** * Replaces all placeholders of format ${name} with the value returned from the supplied {@link * PlaceholderResolver}. * * @param value the value containing the placeholders to be replaced. * @param placeholderResolver the PlaceholderResolver to use for replacement. * @return the supplied value with placeholders replaced inline. * @throws NullPointerException if value is null */ String replacePlaceholders(String value, PlaceholderResolver placeholderResolver) { Objects.requireNonNull(value); return parseStringValue(value, placeholderResolver, new HashSet<>()); } private String parseStringValue(String strVal, PlaceholderResolver placeholderResolver, Set visitedPlaceholders) { StringBuilder buf = new StringBuilder(strVal); int startIndex = strVal.indexOf(this.placeholderPrefix); while (startIndex != -1) { int endIndex = findPlaceholderEndIndex(buf, startIndex); if (endIndex != -1) { String placeholder = buf.substring(startIndex + this.placeholderPrefix.length(), endIndex); if (!visitedPlaceholders.add(placeholder)) { throw new IllegalArgumentException("Circular placeholder reference '" + placeholder + "' 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... int defaultValueIdx = placeholder.indexOf(':'); String defaultValue = null; if (defaultValueIdx != -1) { defaultValue = placeholder.substring(defaultValueIdx + 1); placeholder = placeholder.substring(0, defaultValueIdx); } String propVal = placeholderResolver.resolvePlaceholder(placeholder); if (propVal == null) { propVal = defaultValue; } if (propVal == null && placeholderResolver.shouldIgnoreMissing(placeholder)) { if (placeholderResolver.shouldRemoveMissingPlaceholder(placeholder)) { propVal = ""; } else { return strVal; } } if (propVal != null) { // Recursive invocation, parsing placeholders contained in the // previously resolved placeholder value. propVal = parseStringValue(propVal, placeholderResolver, visitedPlaceholders); buf.replace(startIndex, endIndex + this.placeholderSuffix.length(), propVal); startIndex = buf.indexOf(this.placeholderPrefix, startIndex + propVal.length()); } else if (this.ignoreUnresolvablePlaceholders) { // Proceed with unprocessed value. startIndex = buf.indexOf(this.placeholderPrefix, endIndex + this.placeholderSuffix.length()); } else { throw new IllegalArgumentException("Could not resolve placeholder '" + placeholder + "'"); } visitedPlaceholders.remove(placeholder); } else { startIndex = -1; } } return buf.toString(); } private int findPlaceholderEndIndex(CharSequence buf, int startIndex) { int index = startIndex + this.placeholderPrefix.length(); int withinNestedPlaceholder = 0; while (index < buf.length()) { if (Strings.substringMatch(buf, index, this.placeholderSuffix)) { if (withinNestedPlaceholder > 0) { withinNestedPlaceholder--; index = index + this.placeholderSuffix.length(); } else { return index; } } else if (Strings.substringMatch(buf, index, this.placeholderPrefix)) { withinNestedPlaceholder++; index = index + this.placeholderPrefix.length(); } else { index++; } } return -1; } /** * Strategy interface used to resolve replacement values for placeholders contained in Strings. * * @see PropertyPlaceholder * * @opensearch.internal */ interface PlaceholderResolver { /** * Resolves the supplied placeholder name into the replacement value. * * @param placeholderName the name of the placeholder to resolve. * @return the replacement value or null if no replacement is to be made. */ String resolvePlaceholder(String placeholderName); boolean shouldIgnoreMissing(String placeholderName); /** * Allows for special handling for ignored missing placeholders that may be resolved elsewhere * * @param placeholderName the name of the placeholder to resolve. * @return true if the placeholder should be replaced with a empty string */ boolean shouldRemoveMissingPlaceholder(String placeholderName); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy