/// <summary>
 /// Tests if <paramref name="number"/> exists in <paramref name="group"/>.
 /// </summary>
 /// <param name="number"></param>
 /// <param name="group"></param>
 /// <returns></returns>
 public static bool InGroup(this IPluralNumber number, params IPluralNumber[] group)
 {
     foreach (var value in group)
     {
         if (number.Equals(value))
         {
             return(true);
         }
     }
     return(false);
 }
示例#2
0
 /// <summary>
 /// Evaluate <paramref name="number"/> against all rules in the scope stating from the last added.
 /// </summary>
 /// <param name="subset">RuleSet, Culture and Category must have non-null value. "" is valid. Case must be null and optional must be -1.</param>
 /// <param name="number">(optional) numeric and text representation of numberic value</param>
 /// <returns>Matching cases, first ones are optional, the last one is always mandatory (and only mandatory). Or null if evaluate failed.</returns>
 public                     IPluralRule[] Evaluate(PluralRuleInfo subset, IPluralNumber number)
 {
     for (int i = stack.Count - 1; i >= 0; i--)
     {
         if (stack[i] is IPluralRulesEvaluatable eval)
         {
             IPluralRule[] result = eval.Evaluate(subset, number);
             if (result != null)
             {
                 return(result);
             }
         }
     }
     // Could not evaluate.
     return(null);
 }
 /// <summary>
 /// Try to resolve ruleset and evaluate number.
 /// </summary>
 /// <param name="subset"></param>
 /// <param name="number"></param>
 /// <returns></returns>
 /// <exception cref="Exception">On wrapped error</exception>
 public IPluralRule[] Evaluate(PluralRuleInfo subset, IPluralNumber number)
 {
     if (subset.RuleSet != null)
     {
         ResultLine line = GetRules(subset.RuleSet);
         if (line.Error != null)
         {
             throw new Exception(line.Error.Message, line.Error);
         }
         if (line.Rules is IPluralRulesEvaluatable eval)
         {
             // Set RuleSet to null
             return(eval.Evaluate(subset.ChangeRuleSet(null), number));
         }
     }
     return(null);
 }
 /// <summary>
 /// Compare to zero.
 /// </summary>
 /// <param name="number"></param>
 /// <returns></returns>
 public override bool Evaluate(IPluralNumber number)
 => PluralNumberComparer.Default.Equals(number, _one);
 /// <summary>
 /// Compare to zero.
 /// </summary>
 /// <param name="number"></param>
 /// <returns></returns>
 public override bool Evaluate(IPluralNumber number)
 => number != null && number.Sign == 0;
 /// <summary>
 /// Evaluate number to the rule.
 /// </summary>
 /// <param name="number"></param>
 /// <returns></returns>
 public virtual bool Evaluate(IPluralNumber number)
 => false;
 /// <summary>
 /// Evaluate <paramref name="number"/> against <see cref="Rule"/>.
 /// </summary>
 /// <param name="number"></param>
 /// <returns></returns>
 public override bool Evaluate(IPluralNumber number)
 => Rule == null ? true : new PluralRuleExpressionEvaluator(number).EvaluateBoolean(Rule);
 /// <summary>
 /// Always true
 /// </summary>
 /// <param name="number"></param>
 /// <returns></returns>
 public override bool Evaluate(IPluralNumber number)
 => true;
 /// <summary>
 /// Compare to null.
 /// </summary>
 /// <param name="number"></param>
 /// <returns></returns>
 public override bool Evaluate(IPluralNumber number)
 => number == null || (number.I_Digits == 0 && number.F_Digits == 0 && number.E_Digits == 0 && number.Sign == 0);
示例#10
0
 /// <summary>
 /// Evaluate cases
 /// </summary>
 /// <param name="subset">filter</param>
 /// <param name="number"></param>
 /// <returns>matching cases. First ones are optional, last one is non-optional. Or null if none matched.</returns>
 public IPluralRule[] Evaluate(PluralRuleInfo subset, IPluralNumber number)
 => Query(subset) is IPluralRulesEvaluatable eval?eval.Evaluate(subset, number) : null;
 /// <summary>
 /// Create plural number evaluator
 /// </summary>
 /// <param name="number"></param>
 /// <exception cref="ArgumentNullException">null</exception>
 public PluralRuleExpressionEvaluator(IPluralNumber number)
 {
     Number = number ?? throw new ArgumentNullException(nameof(number));
 }
        /// <summary>
        /// Add placeholder and cases that apply to that placeholder.
        /// </summary>
        /// <param name="placeholder"></param>
        /// <param name="placeholderValue"></param>
        /// <param name="pluralRules"></param>
        /// <param name="culture"></param>
        public void AddPlaceholder(IPlaceholder placeholder, IPluralNumber placeholderValue, IPluralRules pluralRules, string culture)
        {
            IExpression e = placeholder?.Expression;

            if (e == null)
            {
                return;
            }

            // Query cases
            PluralRuleInfo query = new PluralRuleInfo(null, placeholder.PluralCategory, culture, null, -1);

            IPluralRule[] cases = pluralRules.Evaluate(query, placeholderValue);
            if (cases == null || cases.Length == 0)
            {
                return;
            }
            int optionalCount = cases.Length == 1 ? 0 : cases.Length - 1;
            int requiredCount = cases.Length > 0 ? 1 : 0;

            // Scan arguments
            StructList4 <int> argIndices = new StructList4 <int>();

            GetArguments(e, ref argIndices);

            // Add and unify cases
            for (int ix = 0; ix < argIndices.Count; ix++)
            {
                int argIx = argIndices[ix];

                // Find if argument already exists
                int prevIx = -1;
                for (int j = 0; j < arguments.Count; j++)
                {
                    if (arguments[j].ArgumentIndex == argIx)
                    {
                        prevIx = j; break;
                    }
                }

                if (prevIx < 0)
                {
                    // Add argument
                    Entry entry = new Entry {
                        Cases = cases, ArgumentIndex = argIx, OptionalCases = optionalCount, RequiredCases = requiredCount
                    };
                    arguments.Add(entry);
                    this.Count  *= (1 + cases.Length);
                    this.count1 *= cases.Length;
                }
                else
                {
                    // Previous entry
                    Entry entry = arguments[prevIx];
                    // Unify entries
                    StructList8 <IPluralRule> optionalCases = new StructList8 <IPluralRule>(IPluralRuleComparer.Default);
                    StructList8 <IPluralRule> requiredCases = new StructList8 <IPluralRule>(IPluralRuleComparer.Default);
                    foreach (var c in entry.Cases)
                    {
                        if (c.Info.Optional == 1)
                        {
                            optionalCases.AddIfNew(c);
                        }
                        else if (c.Info.Optional == 0)
                        {
                            requiredCases.AddIfNew(c);
                        }
                    }
                    foreach (var c in cases)
                    {
                        if (c.Info.Optional == 1)
                        {
                            optionalCases.AddIfNew(c);
                        }
                        else if (c.Info.Optional == 0)
                        {
                            requiredCases.AddIfNew(c);
                        }
                    }
                    StructList8 <IPluralRule> allCases = new StructList8 <IPluralRule>(IPluralRuleComparer.Default);
                    for (int i = 0; i < optionalCases.Count; i++)
                    {
                        allCases.Add(optionalCases[i]);
                    }
                    for (int i = 0; i < requiredCases.Count; i++)
                    {
                        allCases.Add(requiredCases[i]);
                    }

                    // Create new entry
                    Entry newEntry = new Entry {
                        Cases = allCases.ToArray(), ArgumentIndex = argIx, OptionalCases = optionalCases.Count, RequiredCases = requiredCases.Count
                    };

                    // Update
                    arguments[prevIx] = newEntry;

                    this.Count  /= (1 + entry.Cases.Length);
                    this.Count  *= (1 + newEntry.Cases.Length);
                    this.count1 /= entry.Cases.Length;
                    this.count1 *= newEntry.Cases.Length;
                }
            }
        }
        /// <summary>
        /// Resolve <paramref name="key"/> into <see cref="IString"/>, but without applying format arguments.
        ///
        /// If the <see cref="IString"/> contains plural categories, then matches into the applicable plurality case.
        /// </summary>
        /// <param name="key"></param>
        /// <returns>format string</returns>
        public IString ResolveFormatString(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 StatusString(null, features.Status));
            }

            // 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(new StatusString(null, features.Status));
            }

            // 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(new StatusString(null, features.Status));
            }

            // Plural Rules
            if (value.HasPluralRules())
            {
                if (features.PluralRules != null)
                {
                    // 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);

                    // 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 ?? "");
                    }

                    // 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;
                        }
                        // Parse value
                        IString value_for_plurality = line_for_plurality_arguments.GetString(Resolvers);
                        // 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(new StatusString(null, features.Status));
                        }
                        // Return with match
                        features.Status.UpPlurality(LineStatus.PluralityOkMatched);
                        // Update status codes
                        features.Status.Up(value_for_plurality.Status);
                        // Return values
                        value = value_for_plurality;
                        line  = line_for_plurality_arguments;
                        break;
                    }
                }
                else
                {
                    // Plural rules were not found
                    features.Status.Up(LineStatus.PluralityErrorRulesNotFound);
                }
            }
            else
            {
                // Plurality feature was not used.
                features.Status.UpPlurality(LineStatus.PluralityOkNotUsed);
            }

            return(value);
        }
        /// <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);
            }
        }