| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF 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. |
| */ |
| package org.apache.sling.launchpad.base.shared; |
| |
| import java.util.HashMap; |
| import java.util.Map; |
| |
| /** |
| * The <code>Util</code> class provides general shared utilities. |
| */ |
| public final class Util { |
| |
| // no instantiate |
| private Util() {} |
| |
| // ---------- Property file variable substition support -------------------- |
| |
| /** |
| * The starting delimiter of variable names (value is "${"). |
| */ |
| private static final String DELIM_START = "${"; |
| |
| /** |
| * The ending delimiter of variable names (value is "}"). |
| */ |
| private static final String DELIM_STOP = "}"; |
| |
| /** |
| * This method performs property variable substitution on the specified |
| * value. If the specified value contains the syntax |
| * <tt>${<prop-name>}</tt>, where <tt><prop-name></tt> |
| * refers to either a configuration property or a system property, then the |
| * corresponding property value is substituted for the variable placeholder. |
| * Multiple variable placeholders may exist in the specified value as well |
| * as nested variable placeholders, which are substituted from inner most to |
| * outer most. Configuration properties override system properties. |
| * |
| * @param val The string on which to perform property substitution. |
| * @param currentKey The key of the property being evaluated used to detect |
| * cycles. |
| * @param cycleMap Map of variable references used to detect nested cycles. |
| * @param configProps Set of configuration properties. |
| * @return The value of the specified string after system property |
| * substitution. |
| * @throws IllegalArgumentException If there was a syntax error in the |
| * property placeholder syntax or a recursive variable |
| * reference. |
| */ |
| public static String substVars(String val, String currentKey, |
| Map<String, String> cycleMap, Map<String, String> configProps) |
| throws IllegalArgumentException { |
| |
| ///////////////////////////////////////////////////////////////// |
| // This version copied from org.apache.felix.framework.util.Util, Rev. 1762242 |
| ///////////////////////////////////////////////////////////////// |
| |
| // If there is currently no cycle map, then create |
| // one for detecting cycles for this invocation. |
| if (cycleMap == null) { |
| cycleMap = new HashMap<>(); |
| } |
| |
| // Put the current key in the cycle map. |
| cycleMap.put(currentKey, currentKey); |
| |
| // Assume we have a value that is something like: |
| // "leading ${foo.${bar}} middle ${baz} trailing" |
| |
| // Find the first ending '}' variable delimiter, which |
| // will correspond to the first deepest nested variable |
| // placeholder. |
| int stopDelim = -1; |
| int startDelim = -1; |
| |
| do { |
| stopDelim = val.indexOf(DELIM_STOP, stopDelim + 1); |
| // If there is no stopping delimiter, then just return |
| // the value since there is no variable declared. |
| if (stopDelim < 0) { |
| return val; |
| } |
| // Try to find the matching start delimiter by |
| // looping until we find a start delimiter that is |
| // greater than the stop delimiter we have found. |
| startDelim = val.indexOf(DELIM_START); |
| // If there is no starting delimiter, then just return |
| // the value since there is no variable declared. |
| if (startDelim < 0) { |
| return val; |
| } |
| while (stopDelim >= 0) { |
| int idx = val.indexOf(DELIM_START, startDelim + DELIM_START.length()); |
| if ((idx < 0) || (idx > stopDelim)) { |
| break; |
| } else if (idx < stopDelim) { |
| startDelim = idx; |
| } |
| } |
| } while ((startDelim > stopDelim) && (stopDelim >= 0)); |
| |
| // At this point, we have found a variable placeholder so |
| // we must perform a variable substitution on it. |
| // Using the start and stop delimiter indices, extract |
| // the first, deepest nested variable placeholder. |
| String variable = |
| val.substring(startDelim + DELIM_START.length(), stopDelim); |
| |
| // Verify that this is not a recursive variable reference. |
| if (cycleMap.get(variable) != null) { |
| throw new IllegalArgumentException( |
| "recursive variable reference: " + variable); |
| } |
| |
| // Get the value of the deepest nested variable placeholder. |
| // Try to configuration properties first. |
| String substValue = (configProps != null) |
| ? configProps.get(variable) |
| : null; |
| if (substValue == null) { |
| // Ignore unknown property values. |
| substValue = System.getProperty(variable, ""); |
| } |
| |
| // Remove the found variable from the cycle map, since |
| // it may appear more than once in the value and we don't |
| // want such situations to appear as a recursive reference. |
| cycleMap.remove(variable); |
| |
| // Append the leading characters, the substituted value of |
| // the variable, and the trailing characters to get the new |
| // value. |
| val = val.substring(0, startDelim) |
| + substValue |
| + val.substring(stopDelim + DELIM_STOP.length(), val.length()); |
| |
| // Now perform substitution again, since there could still |
| // be substitutions to make. |
| val = substVars(val, currentKey, cycleMap, configProps); |
| |
| // Return the value. |
| return val; |
| } |
| |
| } |