private static String doSubstVars()

in common/src/main/java/org/mvndaemon/mvnd/common/InterpolationHelper.java [113:243]


    private static String doSubstVars(
            String val,
            String currentKey,
            Map<String, String> cycleMap,
            Map<String, String> configProps,
            SubstitutionCallback callback,
            boolean substituteFromConfig,
            boolean defaultsToEmptyString)
            throws IllegalArgumentException {
        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 startDelim;
        int stopDelim = -1;
        do {
            stopDelim = val.indexOf(DELIM_STOP, stopDelim + 1);
            while (stopDelim > 0 && val.charAt(stopDelim - 1) == ESCAPE_CHAR) {
                stopDelim = val.indexOf(DELIM_STOP, stopDelim + 1);
            }

            // Find the matching starting "${" variable delimiter
            // by looping until we find a start delimiter that is
            // greater than the stop delimiter we have found.
            startDelim = val.indexOf(DELIM_START);
            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 >= 0 && stopDelim >= 0 && stopDelim < startDelim + DELIM_START.length());

        // If we do not have a start or stop delimiter, then just
        // return the existing value.
        if ((startDelim < 0) || (stopDelim < 0)) {
            cycleMap.remove(currentKey);
            return val;
        }

        // 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);
        String org = variable;

        // Strip expansion modifiers
        int idx1 = variable.lastIndexOf(":-");
        int idx2 = variable.lastIndexOf(":+");
        int idx = idx1 >= 0 && idx2 >= 0 ? Math.min(idx1, idx2) : idx1 >= 0 ? idx1 : idx2;
        String op = null;
        if (idx >= 0 && idx < variable.length()) {
            op = variable.substring(idx);
            variable = variable.substring(0, idx);
        }

        // Verify that this is not a recursive variable reference.
        if (cycleMap.get(variable) != null) {
            throw new IllegalArgumentException("recursive variable reference: " + variable);
        }

        String substValue = null;
        // Get the value of the deepest nested variable placeholder.
        // Try to configuration properties first.
        if (substituteFromConfig && configProps != null) {
            substValue = configProps.get(variable);
        }
        if (substValue == null) {
            if (variable.length() > 0) {
                if (callback != null) {
                    substValue = callback.getValue(variable);
                }
            }
        }

        if (op != null) {
            if (op.startsWith(":-")) {
                if (substValue == null || substValue.length() == 0) {
                    substValue = op.substring(":-".length());
                }
            } else if (op.startsWith(":+")) {
                if (substValue != null && substValue.length() != 0) {
                    substValue = op.substring(":+".length());
                }
            } else {
                throw new IllegalArgumentException("Bad substitution: ${" + org + "}");
            }
        }

        if (substValue == null) {
            if (defaultsToEmptyString) {
                substValue = "";
            } else {
                // alters the original token to avoid infinite recursion
                // altered tokens are reverted in substVarsPreserveUnresolved()
                substValue = MARKER + "{" + 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());

        // Now perform substitution again, since there could still
        // be substitutions to make.
        val = doSubstVars(
                val, currentKey, cycleMap, configProps, callback, substituteFromConfig, defaultsToEmptyString);

        cycleMap.remove(currentKey);

        // Return the value.
        return val;
    }