class VariableReplacementHelper {

  public String replaceVariables(String value, Map<String, Map<String, String>> replacementsMap) throws AmbariException {
    if ((value != null) && (replacementsMap != null) && !replacementsMap.isEmpty()) {
      int count = 0; // Used to help prevent an infinite loop...
      boolean replacementPerformed;

      do {
        if (++count > 1000) {
          throw new AmbariException(String.format("Circular reference found while replacing variables in %s", value));
        }

        Matcher matcher = PATTERN_VARIABLE.matcher(value);
        StringBuffer sb = new StringBuffer();

        replacementPerformed = false;

        while (matcher.find()) {
          String type = matcher.group(1);
          String name = matcher.group(2);
          String function = matcher.group(3);

          Map<String, String> replacements;

          if ((name != null) && !name.isEmpty()) {
            if (type == null) {
              replacements = replacementsMap.get("");
            } else {
              replacements = replacementsMap.get(type);
            }

            if (replacements != null) {
              String replacement = replacements.get(name);

              if (replacement != null) {
                if (function != null) {
                  replacement = applyReplacementFunction(function, replacement, replacementsMap);
                }

                // Escape '$' and '\' so they don't cause any issues.
                matcher.appendReplacement(sb, replacement.replace("\\", "\\\\").replace("$", "\\$"));
                replacementPerformed = true;
              }
            }
          }
        }

        matcher.appendTail(sb);
        value = sb.toString();
      }
      while (replacementPerformed); // Process the string again to make sure new variables were not introduced
    }

    return value;
  }

}