Пример #1
0
        /// <summary>
        /// Creates an automaton for validating the correctness of argument placeholders for a given argument.
        /// </summary>
        /// <param name="argToValidateIndex">The index of the argument to validate.</param>
        /// <param name="argNames">The names of all arguments.</param>
        /// <returns>The created automaton.</returns>
        private static StringAutomaton GetArgumentValidatingAutomaton(int argToValidateIndex, IReadOnlyList <string> argNames)
        {
            Debug.Assert(
                argNames != null && argNames.All(name => !string.IsNullOrEmpty(name)),
                "A valid array of argument names must be provided.");
            Debug.Assert(
                argToValidateIndex >= 0 && argToValidateIndex < argNames.Count,
                "The provided argument index must be a valid index in the argument name array.");

            string          argListKey = ArgumentListToDictionaryKey(argToValidateIndex, argNames);
            StringAutomaton result;

            if (ArgsToValidatingAutomaton.TryGetValue(argListKey, out result))
            {
                return(result);
            }

            // Accepts placeholder for the current argument
            StringAutomaton checkBracesForCurrentArg = StringAutomaton.ConstantOn(1.0, "{" + argNames[argToValidateIndex] + "}");

            StringAutomaton checkBracesForOtherArgs = DisallowBracesAutomaton.Clone();

            if (argNames.Count > 1)
            {
                // Skips placeholders for every argument except the current one
                StringAutomaton skipOtherArgs = StringAutomaton.ConstantOnElement(1.0, '{')
                                                .Append(StringAutomaton.ConstantOn(1.0, argNames.Where((arg, index) => index != argToValidateIndex)))
                                                .Append(StringAutomaton.ConstantOnElement(1.0, '}'));

                // Accepts placeholders for arguments other than current, with arbitrary intermediate text
                checkBracesForOtherArgs = checkBracesForOtherArgs.Append(skipOtherArgs);
                checkBracesForOtherArgs = StringAutomaton.Repeat(checkBracesForOtherArgs, minTimes: 0)
                                          .Append(DisallowBracesAutomaton);
            }

            // Checks the placeholder for the current argument, then skips placeholders for other arguments
            StringAutomaton validateArgumentThenOtherArguments = checkBracesForCurrentArg.Append(checkBracesForOtherArgs);

            if (!RequirePlaceholderForEveryArgument)
            {
                // Make this block optional
                validateArgumentThenOtherArguments = StringAutomaton.Sum(
                    validateArgumentThenOtherArguments,
                    StringAutomaton.ConstantOn(1.0, string.Empty));
            }

            // Accepts placeholders for arguments other then current, then for the current argument, then again other placeholders
            result = checkBracesForOtherArgs.Append(validateArgumentThenOtherArguments);

            result = result.TryDeterminize();
            ArgsToValidatingAutomaton[argListKey] = result;

            return(result);
        }
Пример #2
0
        /// <summary>
        /// An implementation of <see cref="FormatAverageConditional(StringDistribution, IReadOnlyList{StringDistribution}, IReadOnlyList{string})"/>
        /// specialized for some cases for performance reasons.
        /// </summary>
        /// <param name="str">The message from <c>str</c>.</param>
        /// <param name="allowedArgs">The message from <c>args</c>, truncated to allowed values and converted to automata.</param>
        /// <param name="argNames">The names of the arguments.</param>
        /// <param name="resultDist">The computed result.</param>
        /// <returns>
        /// <see langword="true"/> if there is an optimized implementation available for the provided parameters,
        /// and <paramref name="resultDist"/> has been computed using it.
        /// <see langword="false"/> otherwise.
        /// </returns>
        /// <remarks>
        /// Supports the case of point mass <paramref name="str"/> and <paramref name="allowedArgs"/>,
        /// where each of the arguments is present in <paramref name="str"/> at most once and the occurrences
        /// are non-overlapping.
        /// </remarks>
        private static bool TryOptimizedFormatAverageConditionalImpl(
            StringDistribution str, IReadOnlyList <StringAutomaton> allowedArgs, IReadOnlyList <string> argNames, out StringDistribution resultDist)
        {
            resultDist = null;

            string[] allowedArgPoints = Util.ArrayInit(allowedArgs.Count, i => allowedArgs[i].TryComputePoint());
            if (!str.IsPointMass || !allowedArgPoints.All(argPoint => argPoint != null && SubstringOccurrencesCount(str.Point, argPoint) <= 1))
            {
                // Fall back to the general case
                return(false);
            }

            // Obtain arguments present in 'str' (ordered by position)
            var argPositions =
                allowedArgPoints.Select((arg, argIndex) => Tuple.Create(argIndex, str.Point.IndexOf(arg, StringComparison.Ordinal)))
                .Where(t => t.Item2 != -1)
                .OrderBy(t => t.Item2)
                .ToList();

            if (RequirePlaceholderForEveryArgument && argPositions.Count != allowedArgs.Count)
            {
                // Some argument is not in 'str'
                resultDist = StringDistribution.Zero();
                return(true);
            }

            StringAutomaton result            = StringAutomaton.ConstantOn(1.0, string.Empty);
            int             curArgumentIndex  = -1;
            int             curArgumentPos    = -1;
            int             curArgumentLength = 1;

            for (int i = 0; i < argPositions.Count; ++i)
            {
                int prevArgumentIndex  = curArgumentIndex;
                int prevArgumentPos    = curArgumentPos;
                int prevArgumentLength = curArgumentLength;
                curArgumentIndex  = argPositions[i].Item1;
                curArgumentPos    = argPositions[i].Item2;
                curArgumentLength = allowedArgPoints[curArgumentIndex].Length;

                if (prevArgumentIndex != -1 && curArgumentPos < prevArgumentPos + prevArgumentLength)
                {
                    // It's easier to fall back to the general case in case of overlapping arguments
                    return(false);
                }

                // Append the contents of 'str' preceeding the current argument
                result = result.Append(str.Point.Substring(prevArgumentPos + prevArgumentLength, curArgumentPos - prevArgumentPos - prevArgumentLength));

                // The format may have included either the text ot the placeholder
                string argName = "{" + argNames[curArgumentIndex] + "}";
                if (RequirePlaceholderForEveryArgument)
                {
                    result = result.Append(StringAutomaton.ConstantOn(1.0, argName));
                }
                else
                {
                    result = result.Append(StringAutomaton.ConstantOn(1.0, argName, allowedArgPoints[curArgumentIndex]));
                }
            }

            // Append the rest of 'str'
            result = result.Append(str.Point.Substring(curArgumentPos + curArgumentLength, str.Point.Length - curArgumentPos - curArgumentLength));

            resultDist = StringDistribution.FromWeightFunction(result);
            return(true);
        }