private String substituteVars(String expr) {
		if (expr == null) {
			return null;
		}
		String eval = expr;
		for(int s = 0; s < MAX_SUBST; s++) {
			final int[] varBounds = findSubVariable(eval);
			if (varBounds[SUB_START_IDX] == -1) {
				return eval;
			}
			final String var = eval.substring(varBounds[SUB_START_IDX],
					varBounds[SUB_END_IDX]);
			String val = null;
			if (!restrictSystemProps) {
				try {
					if (var.startsWith("env.") && 4 < var.length()) {
						String v = var.substring(4);
						int i = 0;
						for (; i < v.length(); i++) {
							char c = v.charAt(i);
							if (c == ':' && i < v.length() - 1 && v.charAt(i + 1) == '-') {
								val = getenv(v.substring(0, i));
								if (val == null || val.length() == 0) {
									val = v.substring(i + 2);
								}
								break;
							} else if (c == '-') {
								val = getenv(v.substring(0, i));
								if (val == null) {
									val = v.substring(i + 1);
								}
								break;
							}
						}
						if (i == v.length()) {
							val = getenv(v);
						}
					} else {
						val = getProperty(var);
					}
				} catch (SecurityException se) {
					LOG.warn("Unexpected SecurityException in Configuration", se);
				}
			}
			if (val == null) {
				val = getRaw(var);
			}
			if (val == null) {
				return eval; // return literal ${var}: var is unbound
			}

			final int dollar = varBounds[SUB_START_IDX] - "${".length();
			final int afterRightBrace = varBounds[SUB_END_IDX] + "}".length();
			final String refVar = eval.substring(dollar, afterRightBrace);

			// detect self-referential values
			if (val.contains(refVar)) {
				return expr; // return original expression if there is a loop
			}

			// substitute
			eval = eval.substring(0, dollar)
					+ val
					+ eval.substring(afterRightBrace);
		}
		throw new IllegalStateException("Variable substitution depth too large: "
				+ MAX_SUBST + " " + expr);
	}
private String substituteVars ( String expr ) { if ( expr == null ) { return null ; } String eval = expr ; for ( int s = 0 ; s < MAX_SUBST ; s ++ ) { final int [ ] varBounds = findSubVariable ( eval ) ; if ( varBounds [ SUB_START_IDX ] == - 1 ) { return eval ; } final String var = eval . substring ( varBounds [ SUB_START_IDX ] , varBounds [ SUB_END_IDX ] ) ; String val = null ; if ( ! restrictSystemProps ) { try { if ( var . startsWith ( "env." ) && 4 < var . length ( ) ) { String v = var . substring ( 4 ) ; int i = 0 ; for ( ; i < v . length ( ) ; i ++ ) { char c = v . charAt ( i ) ; if ( c == ':' && i < v . length ( ) - 1 && v . charAt ( i + 1 ) == '-' ) { val = getenv ( v . substring ( 0 , i ) ) ; if ( val == null || val . length ( ) == 0 ) { val = v . substring ( i + 2 ) ; } break ; } else if ( c == '-' ) { val = getenv ( v . substring ( 0 , i ) ) ; if ( val == null ) { val = v . substring ( i + 1 ) ; } break ; } } if ( i == v . length ( ) ) { val = getenv ( v ) ; } } else { val = getProperty ( var ) ; } } catch ( SecurityException se ) { LOG . warn ( "Unexpected SecurityException in Configuration" , se ) ; } } if ( val == null ) { val = getRaw ( var ) ; } if ( val == null ) { return eval ; } final int dollar = varBounds [ SUB_START_IDX ] - "${" . length ( ) ; final int afterRightBrace = varBounds [ SUB_END_IDX ] + "}" . length ( ) ; final String refVar = eval . substring ( dollar , afterRightBrace ) ; if ( val . contains ( refVar ) ) { return expr ; } eval = eval . substring ( 0 , dollar ) + val + eval . substring ( afterRightBrace ) ; } throw new IllegalStateException ( "Variable substitution depth too large: " + MAX_SUBST + " " + expr ) ; }
Attempts to repeatedly expand the value {@code expr} by replacing the
left-most substring of the form "${var}" in the following precedence order
<ol>
<li>by the value of the environment variable "var" if defined</li>
<li>by the value of the Java system property "var" if defined</li>
<li>by the value of the configuration key "var" if defined</li>
</ol>

If var is unbounded the current state of expansion "prefix${var}suffix" is
returned.
<p>
This function also detects self-referential substitutions, i.e.
<pre>
{@code
foo.bar = ${foo.bar}
}
</pre>
If a cycle is detected then the original expr is returned. Loops
involving multiple substitutions are not detected.

@param expr the literal value of a config key
@return null if expr is null, otherwise the value resulting from expanding
expr using the algorithm above.
@throws IllegalArgumentException when more than
{@link Configuration#MAX_SUBST} replacements are required