content: package com.ctrip.framework.apollo.spring.property;
  
  import com.google.common.base.Strings;
  import com.google.common.collect.Sets;
  import java.util.Set;
  import java.util.Stack;
  import org.springframework.util.StringUtils;
  
  /**
   * Extract keys from placeholder, e.g.
   * <ul>
   * <li>${some.key} => "some.key"</li>
   * <li>${some.key:${some.other.key:100}} => "some.key", "some.other.key"</li>
   * <li>${${some.key}} => "some.key"</li>
   * <li>${${some.key:other.key}} => "some.key"</li>
   * <li>${${some.key}:${another.key}} => "some.key", "another.key"</li>
   * <li>#{new java.text.SimpleDateFormat('${some.key}').parse('${another.key}')} => "some.key", "another.key"</li>
   * </ul>
   */
  public class PlaceholderHelper {
  
    private static final String PLACEHOLDER_PREFIX = "${";
    private static final String PLACEHOLDER_SUFFIX = "}";
    private static final String VALUE_SEPARATOR = ":";
    private static final String SIMPLE_PLACEHOLDER_PREFIX = "{";
    private static final String EXPRESSION_PREFIX = "#{";
    private static final String EXPRESSION_SUFFIX = "}";
  
    public Set<String> extractPlaceholderKeys(String propertyString) {
      Set<String> placeholderKeys = Sets.newHashSet();
  
      if (!isNormalizedPlaceholder(propertyString) && !isExpressionWithPlaceholder(propertyString)) {
        return placeholderKeys;
      }
  
      Stack<String> stack = new Stack<>();
      stack.push(propertyString);
  
      while (!stack.isEmpty()) {
        String strVal = stack.pop();
        int startIndex = strVal.indexOf(PLACEHOLDER_PREFIX);
        if (startIndex == -1) {
          placeholderKeys.add(strVal);
          continue;
        }
        int endIndex = findPlaceholderEndIndex(strVal, startIndex);
        if (endIndex == -1) {
          // invalid placeholder?
          continue;
        }
  
        String placeholderCandidate = strVal.substring(startIndex + PLACEHOLDER_PREFIX.length(), endIndex);
  
        // ${some.key:other.key}
        if (placeholderCandidate.startsWith(PLACEHOLDER_PREFIX)) {
          stack.push(placeholderCandidate);
        } else {
          // some.key:${some.other.key:100}
          int separatorIndex = placeholderCandidate.indexOf(VALUE_SEPARATOR);
  
          if (separatorIndex == -1) {
            stack.push(placeholderCandidate);
          } else {
            stack.push(placeholderCandidate.substring(0, separatorIndex));
            String defaultValuePart =
                normalizeToPlaceholder(placeholderCandidate.substring(separatorIndex + VALUE_SEPARATOR.length()));
            if (!Strings.isNullOrEmpty(defaultValuePart)) {
              stack.push(defaultValuePart);
            }
          }
        }
  
        // has remaining part, e.g. ${a}.${b}
        if (endIndex + PLACEHOLDER_SUFFIX.length() < strVal.length() - 1) {
          String remainingPart = normalizeToPlaceholder(strVal.substring(endIndex + PLACEHOLDER_SUFFIX.length()));
          if (!Strings.isNullOrEmpty(remainingPart)) {
            stack.push(remainingPart);
          }
        }
      }
  
      return placeholderKeys;
    }
  
    private boolean isNormalizedPlaceholder(String propertyString) {
      return propertyString.startsWith(PLACEHOLDER_PREFIX) && propertyString.endsWith(PLACEHOLDER_SUFFIX);
    }
  
    private boolean isExpressionWithPlaceholder(String propertyString) {
      return propertyString.startsWith(EXPRESSION_PREFIX) && propertyString.endsWith(EXPRESSION_SUFFIX)
          && propertyString.contains(PLACEHOLDER_PREFIX);
    }
  
    private String normalizeToPlaceholder(String strVal) {
      int startIndex = strVal.indexOf(PLACEHOLDER_PREFIX);
      if (startIndex == -1) {
        return null;
      }
      int endIndex = strVal.lastIndexOf(PLACEHOLDER_SUFFIX);
      if (endIndex == -1) {
        return null;
      }
  
      return strVal.substring(startIndex, endIndex + PLACEHOLDER_SUFFIX.length());
    }
  
    private int findPlaceholderEndIndex(CharSequence buf, int startIndex) {
      int index = startIndex + PLACEHOLDER_PREFIX.length();
      int withinNestedPlaceholder = 0;
      while (index < buf.length()) {
        if (StringUtils.substringMatch(buf, index, PLACEHOLDER_SUFFIX)) {
          if (withinNestedPlaceholder > 0) {
            withinNestedPlaceholder--;
            index = index + PLACEHOLDER_SUFFIX.length();
          } else {
            return index;
          }
        } else if (StringUtils.substringMatch(buf, index, SIMPLE_PLACEHOLDER_PREFIX)) {
          withinNestedPlaceholder++;
          index = index + SIMPLE_PLACEHOLDER_PREFIX.length();
        } else {
          index++;
        }
      }
      return -1;
    }
  }
  