/// <summary>
            /// Parse string
            /// </summary>
            protected void Build()
            {
                StructList8 <IStringPart> parts = new StructList8 <IStringPart>();

                // Create parser
                CSharpFormat.CSharpParser parser = new CSharpFormat.CSharpParser(this);

                // Read parts
                while (parser.HasMore)
                {
                    IStringPart part = parser.ReadNext();
                    if (part != null)
                    {
                        parts.Add(part);
                    }
                }

                // Unify text parts
                for (int i = 1; i < parts.Count;)
                {
                    if (parts[i - 1] is TextPart left && parts[i] is TextPart right)
                    {
                        parts[i - 1] = TextPart.Unify(left, right);
                        parts.RemoveAt(i);
                    }
Esempio n. 2
0
            /// <summary>
            /// Read next character
            /// </summary>
            /// <returns>possible part</returns>
            public IStringPart ReadNext()
            {
                while (HasMore)
                {
                    // Move to next index
                    i++;
                    // Beyond end
                    if (i >= str.Length)
                    {
                        return(CompletePart(str.Length));
                    }
                    // End escape
                    if (escaped)
                    {
                        escaped = false; continue;
                    }
                    // Read char
                    char ch = str[i];
                    // Begin escape
                    if (ch == '\\')
                    {
                        escaped = true; continue;
                    }

                    // Open brace
                    if (ch == '{')
                    {
                        // Start argument
                        if (state == ParserState.Text)
                        {
                            // Complate previous part, and reset state
                            IStringPart part = CompletePart(i);
                            // Start argument
                            state = ParserState.ArgumentStart;
                            //
                            return(part);
                        }
                        else
                        {
                            // Already in argument format and got unexpected unescaped '{'
                            status = LineStatus.StringFormatErrorMalformed;
                            //
                            continue;
                        }
                    }

                    // Close brace
                    if (ch == '}')
                    {
                        // End argument
                        if (state != ParserState.Text)
                        {
                            // End argument
                            state = ParserState.ArgumentEnd;
                            // Complete previous part, and reset state
                            IStringPart part = CompletePart(i + 1);
                            //
                            return(part);
                        }
                        else
                        {
                            // In text state and got unexpected unescaped '}'
                            //status = LocalizationStatus.FormatErrorMalformed;
                            // Go on
                            continue;
                        }
                    }

                    // Nothing further for text part
                    if (state == ParserState.Text)
                    {
                        continue;
                    }

                    // At ArgumentStart, choose next state
                    if (state == ParserState.ArgumentStart)
                    {
                        // index char
                        if (ch >= '0' && ch <= '9')
                        {
                            if (indexStartIx < 0)
                            {
                                indexStartIx = i;
                            }
                            indexEndIx = i + 1;
                            state      = ParserState.Index;
                        }
                        else
                        // function char
                        {
                            if (pluralCategoryStartIx < 0)
                            {
                                pluralCategoryStartIx = i;
                            }
                            pluralCategoryEndIx = i + 1;
                            state = ParserState.PluralCategory;
                        }
                        continue;
                    }

                    // At PluralCategory state
                    if (state == ParserState.PluralCategory)
                    {
                        // Change to Index state
                        if (ch == ':')
                        {
                            state = ParserState.Index;
                        }
                        else
                        // Move indices
                        {
                            if (pluralCategoryStartIx < 0)
                            {
                                pluralCategoryStartIx = i;
                            }
                            pluralCategoryEndIx = i + 1;
                        }
                        continue;
                    }

                    // At Index state
                    if (state == ParserState.Index)
                    {
                        // Move indices
                        if (ch >= '0' && ch <= '9')
                        {
                            if (indexStartIx < 0)
                            {
                                indexStartIx = i;
                            }
                            indexEndIx = i + 1;
                            continue;
                        }
                        // Change to Alignment state
                        if (ch == ',')
                        {
                            state = ParserState.Alignment;
                            continue;
                        }
                        // Change to Format state
                        if (ch == ':')
                        {
                            state = ParserState.Format;
                            continue;
                        }
                        // Unexpected character
                        status = LineStatus.StringFormatErrorMalformed;
                        return(CompletePart(i));
                    }

                    // At Alignment state
                    if (state == ParserState.Alignment)
                    {
                        // Move indices
                        if ((ch >= '0' && ch <= '9') || (ch == '-'))
                        {
                            if (alignmentStartIx < 0)
                            {
                                alignmentStartIx = i;
                            }
                            alignmentEndIx = i + 1;
                            continue;
                        }
                        // Change to Format state
                        if (ch == ':')
                        {
                            state = ParserState.Format;
                            continue;
                        }
                        // Unexpected character
                        status = LineStatus.StringFormatErrorMalformed;
                        return(CompletePart(i));
                    }

                    // At Format state
                    if (state == ParserState.Format)
                    {
                        // Move indices
                        if (formatStartIx < 0)
                        {
                            formatStartIx = i;
                        }
                        formatEndIx = i + 1;
                        continue;
                    }
                }
                return(null);
            }
        /// <summary>
        /// Resolve <paramref name="key"/> into <see cref="LineString"/> with format arguments applied.
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public LineString ResolveString(ILine key)
        {
            // Extract parameters from line
            LineFeatures features = new LineFeatures {
                Resolvers = Resolvers
            };

            // Scan features
            try
            {
                features.ScanFeatures(key);
            }
            catch (Exception e)
            {
                features.Log(e);
                features.Status.UpResolve(LineStatus.ResolveFailedException);
                return(new LineString(key, e, features.Status));
            }

            try
            {
                // Resolve key to line
                CultureInfo culture = features.Culture;
                ILine       line    = ResolveKeyToLine(key, ref features, ref culture);

                // No line or value
                if (line == null || !features.HasValue)
                {
                    features.Status.UpResolve(LineStatus.ResolveFailedNoValue);
                    LineString str = new LineString(key, (Exception)null, features.Status);
                    features.Log(str);
                    return(str);
                }

                // Parse value
                IString value = features.EffectiveString;
                features.Status.Up(value.Status);

                // Value has error
                if (value.Parts == null || value.Status.Failed())
                {
                    LineString str = new LineString(key, (Exception)null, features.Status);
                    features.Log(str);
                    return(str);
                }

                // Evaluate expressions in placeholders into strings
                StructList12 <string> placeholder_values = new StructList12 <string>();
                CultureInfo           culture_for_format = features.Culture;
                if (culture_for_format == null && features.CulturePolicy != null)
                {
                    CultureInfo[] cultures = features.CulturePolicy.Cultures; if (cultures != null && cultures.Length > 0)
                    {
                        culture_for_format = cultures[0];
                    }
                }
                if (culture_for_format == null)
                {
                    culture_for_format = CultureInfo.InvariantCulture;
                }
                EvaluatePlaceholderValues(key, line, null, value.Placeholders, ref features, ref placeholder_values, culture_for_format);

                // Plural Rules
                if (value.HasPluralRules())
                {
                    if (features.PluralRules != null)
                    {
                        // Create permutation configuration
                        PluralCasePermutations permutations = new PluralCasePermutations(line);
                        for (int i = 0; i < value.Placeholders.Length; i++)
                        {
                            // Get placeholder
                            IPlaceholder placeholder = value.Placeholders[i];
                            // No plural category in this placeholder
                            if (placeholder.PluralCategory == null)
                            {
                                continue;
                            }
                            // Placeholder value after evaluation
                            string ph_value = placeholder_values[i];
                            // Placeholder evaluated value
                            IPluralNumber placeholderValue = ph_value == null ? DecimalNumber.Empty : new DecimalNumber.Text(ph_value?.ToString(), culture);
                            // Add placeholder to permutation configuration
                            permutations.AddPlaceholder(placeholder, placeholderValue, features.PluralRules, culture?.Name ?? "");
                        }

                        if (permutations.ArgumentCount <= MaxPluralArguments)
                        {
                            // Find first value that matches permutations
                            features.CulturePolicy = null;
                            features.String        = null;
                            features.StringText    = null;
                            for (int i = 0; i < permutations.Count - 1; i++)
                            {
                                // Create key with plurality cases
                                ILine key_with_plurality = permutations[i];
                                // Search line with the key
                                ILine line_for_plurality_arguments = ResolveKeyToLine(key_with_plurality, ref features, ref culture);
                                // Got no match
                                if (line_for_plurality_arguments == null)
                                {
                                    continue;
                                }
                                // Scan value
                                try
                                {
                                    features.ScanValueFeature(line_for_plurality_arguments);
                                }
                                catch (Exception e)
                                {
                                    features.Log(e);
                                    features.Status.Up(LineStatus.FailedUnknownReason);
                                    return(new LineString(key, e, features.Status));
                                }
                                // Parse value
                                IString value_for_plurality = features.EffectiveString;
                                // Add status from parsing the value
                                features.Status.Up(value_for_plurality.Status);
                                // Value has error
                                if (value_for_plurality.Parts == null || value_for_plurality.Status.Failed())
                                {
                                    LineString str = new LineString(key, (Exception)null, features.Status);
                                    features.Log(str);
                                    return(str);
                                }
                                // Return with match
                                features.Status.UpPlurality(LineStatus.PluralityOkMatched);
                                // Evaluate placeholders again
                                if (!EqualPlaceholders(value, value_for_plurality))
                                {
                                    placeholder_values.Clear(); EvaluatePlaceholderValues(key, line, line_for_plurality_arguments, value_for_plurality.Placeholders, ref features, ref placeholder_values, culture);
                                }
                                // Update status codes
                                features.Status.Up(value_for_plurality.Status);
                                // Return values
                                value = value_for_plurality;
                                line  = line_for_plurality_arguments;
                                break;
                            }
                        }
                        else
                        {
                            features.Status.UpPlaceholder(LineStatus.PluralityErrorMaxPluralArgumentsExceeded);
                        }
                    }
                    else
                    {
                        // Plural rules were not found
                        features.Status.Up(LineStatus.PluralityErrorRulesNotFound);
                    }
                }
                else
                {
                    // Plurality feature was not used.
                    features.Status.UpPlurality(LineStatus.PluralityOkNotUsed);
                }

                // Put string together
                string text = null;

                if (value == null || value.Parts == null)
                {
                    text = null;
                }

                // Only one part
                else if (value.Parts.Length == 1)
                {
                    if (value.Parts[0].Kind == StringPartKind.Text)
                    {
                        text = value.Parts[0].Text;
                        features.Status.UpStringFormat(LineStatus.StringFormatOkString);
                    }
                    else if (value.Parts[0].Kind == StringPartKind.Placeholder)
                    {
                        text = placeholder_values[0];
                        features.Status.UpStringFormat(LineStatus.StringFormatOkString);
                    }
                }
                // Compile multiple parts
                else
                {
                    // Calculate length
                    int length = 0;
                    for (int i = 0; i < value.Parts.Length; i++)
                    {
                        IStringPart part     = value.Parts[i];
                        string      partText = part.Kind switch { StringPartKind.Text => part.Text, StringPartKind.Placeholder => placeholder_values[((IPlaceholder)part).PlaceholderIndex], _ => null };
                        if (partText != null)
                        {
                            length += partText.Length;
                        }
                    }

                    // Copy characters
                    char[] arr = new char[length];
                    int    ix  = 0;
                    for (int i = 0; i < value.Parts.Length; i++)
                    {
                        IStringPart part = value.Parts[i];
                        string      str  = part.Kind switch { StringPartKind.Text => part.Text, StringPartKind.Placeholder => placeholder_values[((IPlaceholder)part).PlaceholderIndex], _ => null };
                        if (str != null)
                        {
                            str.CopyTo(0, arr, ix, str.Length); ix += str.Length;
                        }
                    }

                    // String
                    text = new string(arr);
                    features.Status.UpStringFormat(LineStatus.StringFormatOkString);
                }

                // Create result
                LineString result = new LineString(key, text, features.Status);

                // Log
                features.Log(result);

                // Return
                return(result);
            } catch (Exception e)
            {
                // Capture unexpected error
                features.Log(e);
                features.Status.UpResolve(LineStatus.ResolveFailedException);
                LineString lineString = new LineString(key, e, features.Status);
                features.Log(lineString);
                return(lineString);
            }
        }