Example #1
0
        // ICU4N specific - de-nested Node class

        // ICU4N specific - de-nested MessageNode class

        // ICU4N specific - de-nested MessageContentsNode class

        // ICU4N specific - de-nested TextNode class

        // ICU4N specific - de-nested ArgNode class

        // ICU4N specific - de-nested ComplexArgStyleNode class

        // ICU4N specific - de-nested VariantNode class

        private static MessageNode BuildMessageNode(MessagePattern pattern, int start, int limit)
        {
            int         prevPatternIndex = pattern.GetPart(start).Limit;
            MessageNode node             = new MessageNode();

            for (int i = start + 1; ; ++i)
            {
                MessagePatternPart part = pattern.GetPart(i);
                int patternIndex        = part.Index;
                if (prevPatternIndex < patternIndex)
                {
                    node.AddContentsNode(
                        new TextNode(pattern.PatternString.Substring(prevPatternIndex, patternIndex - prevPatternIndex)));     // ICU4N: Corrected 2nd arg
                }
                if (i == limit)
                {
                    break;
                }
                MessagePatternPartType partType = part.Type;
                if (partType == MessagePatternPartType.ArgStart)
                {
                    int argLimit = pattern.GetLimitPartIndex(i);
                    node.AddContentsNode(BuildArgNode(pattern, i, argLimit));
                    i    = argLimit;
                    part = pattern.GetPart(i);
                }
                else if (partType == MessagePatternPartType.ReplaceNumber)
                {
                    node.AddContentsNode(MessageContentsNode.CreateReplaceNumberNode());
                    // else: ignore SKIP_SYNTAX and INSERT_CHAR parts.
                }
                prevPatternIndex = part.Limit;
            }
            return(node.Freeze());
        }
Example #2
0
        /// <summary>
        /// Finds the <see cref="SelectFormat"/> sub-message for the given <paramref name="keyword"/>, or the "other" sub-message.
        /// </summary>
        /// <param name="pattern">A <see cref="MessagePattern"/>.</param>
        /// <param name="partIndex">The index of the first <see cref="SelectFormat"/> argument style part.</param>
        /// <param name="keyword">A keyword to be matched to one of the <see cref="SelectFormat"/> argument's keywords.</param>
        /// <returns>The sub-message start part index.</returns>
        internal static int FindSubMessage(MessagePattern pattern, int partIndex, string keyword)
        {
            int count    = pattern.CountParts();
            int msgStart = 0;

            // Iterate over (ARG_SELECTOR, message) pairs until ARG_LIMIT or end of select-only pattern.
            do
            {
                MessagePatternPart     part = pattern.GetPart(partIndex++);
                MessagePatternPartType type = part.Type;
                if (type == MessagePatternPartType.ArgLimit)
                {
                    break;
                }
                Debug.Assert(type == MessagePatternPartType.ArgSelector);
                // part is an ARG_SELECTOR followed by a message
                if (pattern.PartSubstringMatches(part, keyword))
                {
                    // keyword matches
                    return(partIndex);
                }
                else if (msgStart == 0 && pattern.PartSubstringMatches(part, "other"))
                {
                    msgStart = partIndex;
                }
                partIndex = pattern.GetLimitPartIndex(partIndex);
            } while (++partIndex < count);
            return(msgStart);
        }
Example #3
0
        /// <summary>
        /// Selects the phrase for the given <paramref name="keyword"/>.
        /// </summary>
        /// <param name="keyword">A phrase selection keyword.</param>
        /// <returns>The string containing the formatted select message.</returns>
        /// <exception cref="ArgumentException">When the given keyword is not a "pattern identifier".</exception>
        /// <stable>ICU 4.4</stable>
        public string Format(string keyword)
        {
            //Check for the validity of the keyword
            if (!PatternProps.IsIdentifier(keyword))
            {
                throw new ArgumentException("Invalid formatting argument.");
            }
            // If no pattern was applied, throw an exception
            if (msgPattern == null || msgPattern.CountParts() == 0)
            {
                throw new InvalidOperationException("Invalid format error.");
            }

            // Get the appropriate sub-message.
            int msgStart = FindSubMessage(msgPattern, 0, keyword);

            if (!msgPattern.JdkAposMode)
            {
                int msgLimit = msgPattern.GetLimitPartIndex(msgStart);
                return(msgPattern.PatternString.Substring(msgPattern.GetPart(msgStart).Limit,
                                                          msgPattern.GetPatternIndex(msgLimit)));
            }
            // JDK compatibility mode: Remove SKIP_SYNTAX.
            StringBuilder result    = null;
            int           prevIndex = msgPattern.GetPart(msgStart).Limit;

            for (int i = msgStart; ;)
            {
                MessagePatternPart     part = msgPattern.GetPart(++i);
                MessagePatternPartType type = part.Type;
                int index = part.Index;
                if (type == MessagePatternPartType.MsgLimit)
                {
                    if (result == null)
                    {
                        return(pattern.Substring(prevIndex, index - prevIndex)); // ICU4N: Corrected 2nd arg
                    }
                    else
                    {
                        return(result.Append(pattern, prevIndex, index).ToString());
                    }
                }
                else if (type == MessagePatternPartType.SkipSyntax)
                {
                    if (result == null)
                    {
                        result = new StringBuilder();
                    }
                    result.Append(pattern, prevIndex, index);
                    prevIndex = part.Limit;
                }
                else if (type == MessagePatternPartType.ArgStart)
                {
                    if (result == null)
                    {
                        result = new StringBuilder();
                    }
                    result.Append(pattern, prevIndex, index);
                    prevIndex = index;
                    i         = msgPattern.GetLimitPartIndex(i);
                    index     = msgPattern.GetPart(i).Limit;
                    MessagePattern.AppendReducedApostrophes(pattern, prevIndex, index, result);
                    prevIndex = index;
                }
            }
        }
Example #4
0
        private string Format(/*Number*/ object numberObject, double number)
        {
            // If no pattern was applied, return the formatted number.
            if (msgPattern == null || msgPattern.CountParts() == 0)
            {
                return(numberFormat.Format(numberObject));
            }

            // Get the appropriate sub-message.
            // Select it based on the formatted number-offset.
            double numberMinusOffset = number - offset;
            string numberString;

            if (offset == 0)
            {
                numberString = numberFormat.Format(numberObject);  // could be BigDecimal etc.
            }
            else
            {
                numberString = numberFormat.Format(numberMinusOffset);
            }
#pragma warning disable 612, 618
            IFixedDecimal dec;
            // ICU4N TODO:
            //if (numberFormat is DecimalFormat)
            //{
            //    dec = ((DecimalFormat)numberFormat).GetFixedDecimal(numberMinusOffset);
            //}
            //else
            //{
            dec = new FixedDecimal(numberMinusOffset);
#pragma warning restore 612, 618
            //}
            int partIndex = FindSubMessage(msgPattern, 0, pluralRulesWrapper, dec, number);
            // Replace syntactic # signs in the top level of this sub-message
            // (not in nested arguments) with the formatted number-offset.
            StringBuilder result    = null;
            int           prevIndex = msgPattern.GetPart(partIndex).Limit;
            for (; ;)
            {
                MessagePatternPart     part = msgPattern.GetPart(++partIndex);
                MessagePatternPartType type = part.Type;
                int index = part.Index;
                if (type == MessagePatternPartType.MsgLimit)
                {
                    if (result == null)
                    {
                        return(pattern.Substring(prevIndex, index - prevIndex)); // ICU4N: Corrected 2nd arg
                    }
                    else
                    {
                        return(result.Append(pattern, prevIndex, index).ToString());
                    }
                }
                else if (type == MessagePatternPartType.ReplaceNumber ||
                         // JDK compatibility mode: Remove SKIP_SYNTAX.
                         (type == MessagePatternPartType.SkipSyntax && msgPattern.JdkAposMode))
                {
                    if (result == null)
                    {
                        result = new StringBuilder();
                    }
                    result.Append(pattern, prevIndex, index);
                    if (type == MessagePatternPartType.ReplaceNumber)
                    {
                        result.Append(numberString);
                    }
                    prevIndex = part.Limit;
                }
                else if (type == MessagePatternPartType.ArgStart)
                {
                    if (result == null)
                    {
                        result = new StringBuilder();
                    }
                    result.Append(pattern, prevIndex, index);
                    prevIndex = index;
                    partIndex = msgPattern.GetLimitPartIndex(partIndex);
                    index     = msgPattern.GetPart(partIndex).Limit;
                    MessagePattern.AppendReducedApostrophes(pattern, prevIndex, index, result);
                    prevIndex = index;
                }
            }
        }
Example #5
0
        /// <summary>
        /// Finds the <see cref="PluralFormat"/> sub-message for the given number, or the "other" sub-message.
        /// </summary>
        /// <param name="pattern">A <see cref="MessagePattern"/>.</param>
        /// <param name="partIndex">the index of the first <see cref="PluralFormat"/> argument style part.</param>
        /// <param name="selector">the <see cref="IPluralSelector"/> for mapping the number (minus offset) to a keyword.</param>
        /// <param name="context">worker object for the selector.</param>
        /// <param name="number">a number to be matched to one of the <see cref="PluralFormat"/> argument's explicit values,
        /// or mapped via the <see cref="IPluralSelector"/>.</param>
        /// <returns>the sub-message start part index.</returns>
        internal static int FindSubMessage(
            MessagePattern pattern, int partIndex,
            IPluralSelector selector, object context, double number)
        {
            int                count = pattern.CountParts();
            double             offset;
            MessagePatternPart part = pattern.GetPart(partIndex);

            if (part.Type.HasNumericValue())
            {
                offset = pattern.GetNumericValue(part);
                ++partIndex;
            }
            else
            {
                offset = 0;
            }
            // The keyword is null until we need to match against a non-explicit, not-"other" value.
            // Then we get the keyword from the selector.
            // (In other words, we never call the selector if we match against an explicit value,
            // or if the only non-explicit keyword is "other".)
            string keyword = null;
            // When we find a match, we set msgStart>0 and also set this boolean to true
            // to avoid matching the keyword again (duplicates are allowed)
            // while we continue to look for an explicit-value match.
            bool haveKeywordMatch = false;
            // msgStart is 0 until we find any appropriate sub-message.
            // We remember the first "other" sub-message if we have not seen any
            // appropriate sub-message before.
            // We remember the first matching-keyword sub-message if we have not seen
            // one of those before.
            // (The parser allows [does not check for] duplicate keywords.
            // We just have to make sure to take the first one.)
            // We avoid matching the keyword twice by also setting haveKeywordMatch=true
            // at the first keyword match.
            // We keep going until we find an explicit-value match or reach the end of the plural style.
            int msgStart = 0;

            // Iterate over (ARG_SELECTOR [ARG_INT|ARG_DOUBLE] message) tuples
            // until ARG_LIMIT or end of plural-only pattern.
            do
            {
                part = pattern.GetPart(partIndex++);
                MessagePatternPartType type = part.Type;
                if (type == MessagePatternPartType.ArgLimit)
                {
                    break;
                }
                Debug.Assert(type == MessagePatternPartType.ArgSelector);
                // part is an ARG_SELECTOR followed by an optional explicit value, and then a message
                if (pattern.GetPartType(partIndex).HasNumericValue())
                {
                    // explicit value like "=2"
                    part = pattern.GetPart(partIndex++);
                    if (number == pattern.GetNumericValue(part))
                    {
                        // matches explicit value
                        return(partIndex);
                    }
                }
                else if (!haveKeywordMatch)
                {
                    // plural keyword like "few" or "other"
                    // Compare "other" first and call the selector if this is not "other".
                    if (pattern.PartSubstringMatches(part, "other"))
                    {
                        if (msgStart == 0)
                        {
                            msgStart = partIndex;
                            if (keyword != null && keyword.Equals("other"))
                            {
                                // This is the first "other" sub-message,
                                // and the selected keyword is also "other".
                                // Do not match "other" again.
                                haveKeywordMatch = true;
                            }
                        }
                    }
                    else
                    {
                        if (keyword == null)
                        {
                            keyword = selector.Select(context, number - offset);
                            if (msgStart != 0 && keyword.Equals("other"))
                            {
                                // We have already seen an "other" sub-message.
                                // Do not match "other" again.
                                haveKeywordMatch = true;
                                // Skip keyword matching but do getLimitPartIndex().
                            }
                        }
                        if (!haveKeywordMatch && pattern.PartSubstringMatches(part, keyword))
                        {
                            // keyword matches
                            msgStart = partIndex;
                            // Do not match this keyword again.
                            haveKeywordMatch = true;
                        }
                    }
                }
                partIndex = pattern.GetLimitPartIndex(partIndex);
            } while (++partIndex < count);
            return(msgStart);
        }