| /* |
| * 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.felix.sigil.config; |
| |
| import java.util.Properties; |
| import java.util.regex.Matcher; |
| import java.util.regex.Pattern; |
| |
| public class BldUtil |
| { |
| /** |
| * expands property references embedded in strings. Each occurrence of ${name} is replaced with the value of |
| * p.getProperty("name"); If the property is not set, then the original reference, is returned as follows "?<name>". |
| * |
| * Strings to be expanded should not contain $ or }, except to indicate expansions. |
| * |
| * Value is expanded recursively and so can contain further ${name} references. Also supports shell-expansions |
| * ${name:-value}, ${name:=value} and ${name:+value}. |
| * |
| * <pre> |
| * ${parameter} |
| * The value of parameter is substituted. |
| * ${parameter:-word} |
| * Use Default Values. If parameter is null, the expansion of word |
| * is substituted. Otherwise, the value of parameter is substituted. |
| * ${parameter:=word} |
| * Assign Default Values. If parameter is null, the expansion of |
| * word is assigned to parameter. The value of parameter is then |
| * substituted. |
| * ${parameter:+word} |
| * Use Alternate Value. If parameter is null, nothing is |
| * substituted, otherwise the expansion of word is substituted. |
| * ${parameter:?word} |
| * Raise Error. If parameter is null, a RuntimeException is thown, |
| * with word as the message. |
| * </pre> |
| */ |
| public static String expand(String s, Properties p) |
| { |
| // regex to match property references e.g. ${name} |
| // TODO this is very simplistic, so strings to be expanded should not |
| // contain $ or }, except where substitution is expected. |
| // Update: propRef regex now allows substitutions to contain $, |
| // e.g. where a Windows ${user.name} is $Admin or similar. |
| final Pattern propRef = Pattern.compile("\\$\\{(((\\$[^\\{\\}])|[^\\$\\}])+\\$?)\\}"); |
| final Pattern backslash = Pattern.compile("\\\\"); |
| final Pattern dollar = Pattern.compile("\\$"); |
| |
| if (s == null) |
| { |
| return null; |
| } |
| |
| if (s.indexOf("${") == -1) |
| { // shortcut if no expansions |
| return s; |
| } |
| |
| for (int i = 0; i < 20; i++) |
| { // avoids self-referencing expansions |
| // System.out.println("XXX expand[" + i + "] = [" + s + "]"); |
| Matcher matcher = propRef.matcher(s); |
| |
| if (!matcher.find()) |
| { |
| // replace unmatched items |
| s = s.replaceAll("\\Q??[\\E", "\\${"); |
| s = s.replaceAll("\\Q??]\\E", "}"); |
| // debug("expanded: " + s); |
| if (s.indexOf("${") != -1) |
| { |
| throw new RuntimeException("Can't expand: " + s); |
| } |
| return s; |
| } |
| |
| String key = matcher.group(1); |
| String[] keydef = key.split(":[=+-?@]", 2); |
| String replace; |
| |
| if (keydef.length != 2) |
| { |
| replace = key.length() == 0 ? null : p.getProperty(key); |
| } |
| else |
| { |
| replace = keydef[0].length() == 0 ? null : p.getProperty(keydef[0]); |
| |
| if (replace != null |
| && (replace.length() == 0 || replace.indexOf("${") != -1)) |
| { |
| // don't want unexpanded replacement, as it may stop ${...:-default} |
| replace = null; |
| } |
| |
| if (key.indexOf(":+") != -1) |
| { |
| replace = ((replace == null) ? "" : keydef[1]); |
| } |
| else if (replace == null) |
| { |
| replace = keydef[1]; |
| |
| if (key.indexOf(":?") != -1) |
| { |
| String msg = "${" + keydef[0] + ":?" + keydef[1] |
| + "} property not set"; |
| throw new RuntimeException(msg); |
| } |
| |
| if (key.indexOf(":=") != -1) |
| { |
| p.setProperty(keydef[0], keydef[1]); |
| } |
| } |
| } |
| |
| if (replace == null) |
| { |
| // TODO: this is a hack to avoid looping on unmatched references |
| // should really leave unchanged and process rest of string. |
| // We use "]" as delimiter to avoid non-matched "}" |
| // terminating potential _propRef match |
| replace = "??[" + key + "??]"; |
| } |
| |
| // Excerpt from replaceAll() javadoc: |
| // |
| // Note that backslashes (\) and dollar signs ($) in the replacement |
| // string may cause the results to be different than if it were |
| // being |
| // treated as a literal replacement string. Dollar signs may be |
| // treated |
| // as references to captured subsequences, and backslashes are used |
| // to |
| // escape literal characters in the replacement string. |
| // escape any \ or $ in replacement string |
| replace = backslash.matcher(replace).replaceAll("\\\\\\\\"); |
| replace = dollar.matcher(replace).replaceAll("\\\\\\$"); |
| |
| s = s.replaceAll("\\Q${" + key + "}\\E", replace); |
| } |
| |
| throw new RuntimeException("expand: loop expanding: " + s); |
| } |
| |
| public static String expand(String s) |
| { |
| return expand(s, BldProperties.global()); |
| } |
| } |