Пример #1
0
        /// <summary>
        /// Processes a list of format tokens into a string
        /// </summary>
        /// <param name="tokens">List of tokens to turn into a string</param>
        /// <param name="replacements">An <see cref="IDictionary"/> with keys and values to inject into the formatted result</param>
        /// <param name="missingKeyBehaviour">The behaviour to use when the format string contains a parameter that is not present in the lookup dictionary</param>
        /// <param name="fallbackReplacementValue">When the <see cref="MissingKeyBehaviour.ReplaceWithFallback"/> is specified, this string is used as a fallback replacement value when the parameter is present in the lookup dictionary.</param>
        /// <returns>The processed result of joining the tokens with the replacement dictionary.</returns>
        public static string ProcessTokens(
            IEnumerable <FormatToken> tokens,
            Func <string, ReplacementResult> handler,
            MissingKeyBehaviour missingKeyBehaviour,
            object fallbackReplacementValue,
            int outputLengthHint)
        {
            // create a StringBuilder to hold the resultant output string
            // use the input hint as the initial size
            StringBuilder resultBuilder = new StringBuilder(outputLengthHint);

            foreach (FormatToken thisToken in tokens)
            {
                if (thisToken.TokenType == TokenType.Text)
                {
                    // token is a text token
                    // add the token to the result string builder
                    resultBuilder.Append(thisToken.SourceString, thisToken.StartIndex, thisToken.Length);
                }
                else if (thisToken.TokenType == TokenType.Parameter)
                {
                    // token is a parameter token
                    // perform parameter logic now.

                    // append the replacement for this parameter
                    ReplacementResult replacementResult = handler(thisToken.Value);

                    if (replacementResult.Success)
                    {
                        // the key exists, add the replacement value
                        // this does nothing if replacement value is null
                        resultBuilder.Append(replacementResult.Value);
                    }
                    else
                    {
                        // the key does not exist, handle this using the missing key behaviour specified.
                        switch (missingKeyBehaviour)
                        {
                        case MissingKeyBehaviour.ThrowException:
                            // the key was not found as a possible replacement, throw exception
                            throw new KeyNotFoundException($"The parameter \"{thisToken.Value}\" was not present in the lookup dictionary");

                        case MissingKeyBehaviour.ReplaceWithFallback:
                            resultBuilder.Append(fallbackReplacementValue);
                            break;

                        case MissingKeyBehaviour.Ignore:
                            // the replacement value is the input key as a parameter.
                            // use source string and start/length directly with append rather than
                            // parameter.ParameterKey to avoid allocating an extra string
                            resultBuilder.Append(thisToken.SourceString, thisToken.StartIndex, thisToken.Length);
                            break;
                        }
                    }
                }
            }

            // return the resultant string
            return(resultBuilder.ToString());
        }
Пример #2
0
        /// <summary>
        /// Processes a list of format tokens into a string
        /// </summary>
        /// <param name="tokens">List of tokens to turn into a string</param>
        /// <param name="replacements">An <see cref="IDictionary"/> with keys and values to inject into the formatted result</param>
        /// <param name="missingKeyBehaviour">The behaviour to use when the format string contains a parameter that is not present in the lookup dictionary</param>
        /// <param name="fallbackReplacementValue">When the <see cref="MissingKeyBehaviour.ReplaceWithFallback"/> is specified, this string is used as a fallback replacement value when the parameter is present in the lookup dictionary.</param>
        /// <returns>The processed result of joining the tokens with the replacement dictionary.</returns>
        public static FormattableString ProcessTokensIntoFormattableString(
            IEnumerable <FormatToken> tokens,
            Func <string, ReplacementResult> handler,
            MissingKeyBehaviour missingKeyBehaviour,
            object fallbackReplacementValue,
            int outputLengthHint)
        {
            List <object> replacementParams = new List <object>();

            // create a StringBuilder to hold the resultant output string
            // use the input hint as the initial size
            StringBuilder resultBuilder = new StringBuilder(outputLengthHint);

            // this is the index of the current placeholder in the composite format string
            int placeholderIndex = 0;

            foreach (FormatToken thisToken in tokens)
            {
                if (thisToken.TokenType == TokenType.Text)
                {
                    // token is a text token.
                    // add the token to the result string builder.
                    // because this text is going into a standard composite format string,
                    // any instaces of { or } must be escaped with {{ and }}
                    resultBuilder.AppendWithEscapedBrackets(thisToken.SourceString, thisToken.StartIndex, thisToken.Length);
                }
                else if (thisToken.TokenType == TokenType.Parameter)
                {
                    // token is a parameter token
                    // perform parameter logic now.
                    var    tokenKey     = thisToken.Value;
                    string format       = null;
                    var    separatorIdx = tokenKey.IndexOf(":", StringComparison.Ordinal);
                    if (separatorIdx > -1)
                    {
                        tokenKey = thisToken.Value.Substring(0, separatorIdx);
                        format   = thisToken.Value.Substring(separatorIdx + 1);
                    }

                    // append the replacement for this parameter
                    ReplacementResult replacementResult = handler(tokenKey);

                    string IndexAndFormat()
                    {
                        if (string.IsNullOrWhiteSpace(format))
                        {
                            return("{" + placeholderIndex + "}");
                        }

                        return("{" + placeholderIndex + ":" + format + "}");
                    }

                    // append the replacement for this parameter
                    if (replacementResult.Success)
                    {
                        // Instead of appending the replacement value directly as before,
                        // append the next placeholder with the current placeholder index.
                        // Add the actual replacement format item into the replacement values.
                        resultBuilder.Append(IndexAndFormat());
                        placeholderIndex++;
                        replacementParams.Add(replacementResult.Value);
                    }
                    else
                    {
                        // the key does not exist, handle this using the missing key behaviour specified.
                        switch (missingKeyBehaviour)
                        {
                        case MissingKeyBehaviour.ThrowException:
                            // the key was not found as a possible replacement, throw exception
                            throw new KeyNotFoundException($"The parameter \"{thisToken.Value}\" was not present in the lookup dictionary");

                        case MissingKeyBehaviour.ReplaceWithFallback:
                            // Instead of appending the replacement value directly as before,
                            // append the next placeholder with the current placeholder index.
                            // Add the actual replacement format item into the replacement values.
                            resultBuilder.Append(IndexAndFormat());
                            placeholderIndex++;
                            replacementParams.Add(fallbackReplacementValue);
                            break;

                        case MissingKeyBehaviour.Ignore:
                            resultBuilder.AppendWithEscapedBrackets(thisToken.SourceString, thisToken.StartIndex, thisToken.Length);
                            break;
                        }
                    }
                }
            }

            // return the resultant string
            return(FormattableStringFactory.Create(resultBuilder.ToString(), replacementParams.ToArray()));
        }