private int substitute()

in log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/StrSubstitutor.java [1015:1174]


    private int substitute(
            final LogEvent event,
            final StringBuilder buf,
            final int offset,
            final int length,
            List<String> priorVariables) {
        final StrMatcher prefixMatcher = getVariablePrefixMatcher();
        final StrMatcher suffixMatcher = getVariableSuffixMatcher();
        final char escape = getEscapeChar();
        final StrMatcher valueDelimiterMatcher = getValueDelimiterMatcher();
        final boolean substitutionInVariablesEnabled = isEnableSubstitutionInVariables();

        final boolean top = priorVariables == null;
        boolean altered = false;
        int lengthChange = 0;
        char[] chars = getChars(buf);
        int bufEnd = offset + length;
        int pos = offset;
        while (pos < bufEnd) {
            final int startMatchLen = prefixMatcher.isMatch(chars, pos, offset, bufEnd);
            if (startMatchLen == 0) {
                pos++;
            } else // found variable start marker
            if (pos > offset && chars[pos - 1] == escape) {
                // escaped
                buf.deleteCharAt(pos - 1);
                chars = getChars(buf);
                lengthChange--;
                altered = true;
                bufEnd--;
            } else {
                // find suffix
                final int startPos = pos;
                pos += startMatchLen;
                int endMatchLen = 0;
                int nestedVarCount = 0;
                while (pos < bufEnd) {
                    if (substitutionInVariablesEnabled
                            && (endMatchLen = prefixMatcher.isMatch(chars, pos, offset, bufEnd)) != 0) {
                        // found a nested variable start
                        nestedVarCount++;
                        pos += endMatchLen;
                        continue;
                    }

                    endMatchLen = suffixMatcher.isMatch(chars, pos, offset, bufEnd);
                    if (endMatchLen == 0) {
                        pos++;
                    } else {
                        // found variable end marker
                        if (nestedVarCount == 0) {
                            String varNameExpr =
                                    new String(chars, startPos + startMatchLen, pos - startPos - startMatchLen);
                            if (substitutionInVariablesEnabled) {
                                // initialize priorVariables if they're not already set
                                if (priorVariables == null) {
                                    priorVariables = new ArrayList<>();
                                }
                                final StringBuilder bufName = new StringBuilder(varNameExpr);
                                substitute(event, bufName, 0, bufName.length(), priorVariables);
                                varNameExpr = bufName.toString();
                            }
                            pos += endMatchLen;
                            final int endPos = pos;

                            String varName = varNameExpr;
                            String varDefaultValue = null;

                            if (valueDelimiterMatcher != null) {
                                final char[] varNameExprChars = varNameExpr.toCharArray();
                                int valueDelimiterMatchLen = 0;
                                for (int i = 0; i < varNameExprChars.length; i++) {
                                    // if there's any nested variable when nested variable substitution disabled, then
                                    // stop resolving name and default value.
                                    if (!substitutionInVariablesEnabled
                                            && prefixMatcher.isMatch(varNameExprChars, i, i, varNameExprChars.length)
                                                    != 0) {
                                        break;
                                    }
                                    if (valueEscapeDelimiterMatcher != null) {
                                        final int matchLen = valueEscapeDelimiterMatcher.isMatch(varNameExprChars, i);
                                        if (matchLen != 0) {
                                            final String varNamePrefix =
                                                    varNameExpr.substring(0, i) + Interpolator.PREFIX_SEPARATOR;
                                            varName = varNamePrefix + varNameExpr.substring(i + matchLen - 1);
                                            for (int j = i + matchLen; j < varNameExprChars.length; ++j) {
                                                if ((valueDelimiterMatchLen =
                                                                valueDelimiterMatcher.isMatch(varNameExprChars, j))
                                                        != 0) {
                                                    varName = varNamePrefix + varNameExpr.substring(i + matchLen, j);
                                                    varDefaultValue = varNameExpr.substring(j + valueDelimiterMatchLen);
                                                    break;
                                                }
                                            }
                                            break;
                                        } else if ((valueDelimiterMatchLen =
                                                        valueDelimiterMatcher.isMatch(varNameExprChars, i))
                                                != 0) {
                                            varName = varNameExpr.substring(0, i);
                                            varDefaultValue = varNameExpr.substring(i + valueDelimiterMatchLen);
                                            break;
                                        }
                                    } else if ((valueDelimiterMatchLen =
                                                    valueDelimiterMatcher.isMatch(varNameExprChars, i))
                                            != 0) {
                                        varName = varNameExpr.substring(0, i);
                                        varDefaultValue = varNameExpr.substring(i + valueDelimiterMatchLen);
                                        break;
                                    }
                                }
                            }

                            // on the first call initialize priorVariables
                            if (priorVariables == null) {
                                priorVariables = new ArrayList<>();
                                priorVariables.add(new String(chars, offset, length + lengthChange));
                            }

                            // handle cyclic substitution
                            final boolean isCyclic = isCyclicSubstitution(varName, priorVariables);

                            // resolve the variable
                            final LookupResult resolvedResult =
                                    isCyclic ? null : resolveVariable(event, varName, buf, startPos, endPos);
                            String varValue = resolvedResult == null ? null : resolvedResult.value();
                            if (varValue == null) {
                                varValue = varDefaultValue;
                            }
                            if (varValue != null) {
                                // recursive replace
                                final int varLen = varValue.length();
                                buf.replace(startPos, endPos, varValue);
                                altered = true;
                                int change = resolvedResult != null && resolvedResult.isLookupEvaluationAllowedInValue()
                                        ? substitute(event, buf, startPos, varLen, priorVariables)
                                        : 0;
                                change = change + (varLen - (endPos - startPos));
                                pos += change;
                                bufEnd += change;
                                lengthChange += change;
                                chars = getChars(buf); // in case buffer was altered
                            }

                            // remove variable from the cyclic stack
                            if (!isCyclic) {
                                priorVariables.remove(priorVariables.size() - 1);
                            }
                            break;
                        }
                        nestedVarCount--;
                        pos += endMatchLen;
                    }
                }
            }
        }
        if (top) {
            return altered ? 1 : 0;
        }
        return lengthChange;
    }