コード例 #1
0
        /// <summary>
        /// Creates a character handler to handle the "fraction of a second" specifier (f or F).
        /// </summary>
        internal static CharacterHandler <TResult, TBucket> CreateFractionHandler <TResult, TBucket>
            (int maxCount, NodaFunc <TResult, int> getter, NodaAction <TBucket, int> setter)
            where TBucket : ParseBucket <TResult>
        {
            return((pattern, builder) =>
            {
                char patternCharacter = pattern.Current;
                int count = pattern.GetRepeatCount(maxCount);
                builder.AddField(PatternFields.FractionalSeconds, pattern.Current);

                builder.AddParseAction((str, bucket) =>
                {
                    int fractionalSeconds;
                    // If the pattern is 'f', we need exactly "count" digits. Otherwise ('F') we need
                    // "up to count" digits.
                    if (!str.ParseFraction(count, maxCount, out fractionalSeconds, patternCharacter == 'f'))
                    {
                        return ParseResult <TResult> .MismatchedNumber(new string(patternCharacter, count));
                    }
                    // No need to validate the value - we've got an appropriate number of digits, so the range is guaranteed.
                    setter(bucket, fractionalSeconds);
                    return null;
                });
                if (patternCharacter == 'f')
                {
                    builder.AddFormatRightPad(count, maxCount, getter);
                }
                else
                {
                    builder.AddFormatRightPadTruncate(count, maxCount, getter);
                }
            });
        }
コード例 #2
0
        /// <summary>
        /// Adds parse and format actions for an "negative only" sign.
        /// </summary>
        /// <param name="signSetter">Action to take when to set the given sign within the bucket</param>
        /// <param name="nonNegativePredicate">Predicate to detect whether the value being formatted is non-negative</param>
        public void AddNegativeOnlySign(NodaAction <TBucket, bool> signSetter, NodaFunc <TResult, bool> nonNegativePredicate)
        {
            string negativeSign = formatInfo.NegativeSign;
            string positiveSign = formatInfo.PositiveSign;

            AddParseAction((str, bucket) =>
            {
                if (str.Match(negativeSign))
                {
                    signSetter(bucket, false);
                    return(null);
                }
                if (str.Match(positiveSign))
                {
                    return(ParseResult <TResult> .PositiveSignInvalid);
                }
                signSetter(bucket, true);
                return(null);
            });
            AddFormatAction((value, builder) =>
            {
                if (!nonNegativePredicate(value))
                {
                    builder.Append(negativeSign);
                }
            });
        }
コード例 #3
0
        /// <summary>
        /// Creates a character handler for the calendar specifier (c).
        /// </summary>
        internal static CharacterHandler <TResult, TBucket> CreateCalendarHandler <TResult, TBucket>
            (NodaFunc <TResult, CalendarSystem> getter, NodaAction <TBucket, CalendarSystem> setter)
            where TBucket : ParseBucket <TResult>
        {
            return((pattern, builder) =>
            {
                builder.AddField(PatternFields.Calendar, pattern.Current);

                builder.AddParseAction((cursor, bucket) =>
                {
                    // TODO(V2.0): (Breaking change, although undocumented.) Potentially make this case-sensitive
                    // as we're parsing IDs.
                    foreach (var id in CalendarSystem.Ids)
                    {
                        if (cursor.MatchCaseInsensitive(id, NodaFormatInfo.InvariantInfo.CompareInfo, true))
                        {
                            setter(bucket, CalendarSystem.ForId(id));
                            return null;
                        }
                    }
                    return ParseResult <TResult> .NoMatchingCalendarSystem;
                });
                builder.AddFormatAction((value, sb) => sb.Append(getter(value).Id));
            });
        }
コード例 #4
0
        /// <summary>
        /// Creates a character handler for the day specifier (d).
        /// </summary>
        internal static CharacterHandler <TResult, TBucket> CreateDayHandler <TResult, TBucket>
            (NodaFunc <TResult, int> dayOfMonthGetter, NodaFunc <TResult, int> dayOfWeekGetter,
            NodaAction <TBucket, int> dayOfMonthSetter, NodaAction <TBucket, int> dayOfWeekSetter)
            where TBucket : ParseBucket <TResult>
        {
            return((pattern, builder) =>
            {
                int count = pattern.GetRepeatCount(4);
                PatternFields field;
                switch (count)
                {
                case 1:
                case 2:
                    field = PatternFields.DayOfMonth;
                    // Handle real maximum value in the bucket
                    builder.AddParseValueAction(count, 2, pattern.Current, 1, 99, dayOfMonthSetter);
                    builder.AddFormatAction((value, sb) => FormatHelper.LeftPad(dayOfMonthGetter(value), count, sb));
                    break;

                case 3:
                case 4:
                    field = PatternFields.DayOfWeek;
                    var format = builder.FormatInfo;
                    IList <string> textValues = count == 3 ? format.ShortDayNames : format.LongDayNames;
                    builder.AddParseLongestTextAction(pattern.Current, dayOfWeekSetter, format.CompareInfo, textValues);
                    builder.AddFormatAction((value, sb) => sb.Append(textValues[dayOfWeekGetter(value)]));
                    break;

                default:
                    throw new InvalidOperationException("Invalid count!");
                }
                builder.AddField(field, pattern.Current);
            });
        }
コード例 #5
0
 internal T ExtractSingleValue <T>(NodaFunc <DateTimeZoneReader, T> readerFunction, IList <string> stringPool)
 {
     using (var stream = CreateStream())
     {
         return(readerFunction(new DateTimeZoneReader(stream, stringPool)));
     }
 }
コード例 #6
0
 internal SteppedPatternBuilder(NodaFormatInfo formatInfo, NodaFunc <TBucket> bucketProvider)
 {
     this.formatInfo     = formatInfo;
     formatActions       = new List <NodaAction <TResult, StringBuilder> >();
     parseActions        = new List <ParseAction>();
     this.bucketProvider = bucketProvider;
 }
コード例 #7
0
ファイル: Cache.cs プロジェクト: updateov/MyTestProjects
 internal Cache(int size, NodaFunc <TKey, TValue> valueFactory, IEqualityComparer <TKey> keyComparer)
 {
     this.size         = size;
     this.valueFactory = valueFactory;
     this.dictionary   = new Dictionary <TKey, TValue>(keyComparer);
     this.keyList      = new LinkedList <TKey>();
 }
コード例 #8
0
 public SteppedPattern(NodaAction <TResult, StringBuilder> formatActions,
                       ParseAction[] parseActions,
                       NodaFunc <TBucket> bucketProvider, PatternFields usedFields)
 {
     this.formatActions  = formatActions;
     this.parseActions   = parseActions;
     this.bucketProvider = bucketProvider;
     this.usedFields     = usedFields;
 }
コード例 #9
0
 private FixedFormatInfoPatternParser <T> EnsureFixedFormatInitialized <T>(ref FixedFormatInfoPatternParser <T> field,
                                                                           NodaFunc <IPatternParser <T> > patternParserFactory)
 {
     lock (fieldLock)
     {
         if (field == null)
         {
             field = new FixedFormatInfoPatternParser <T>(patternParserFactory(), this);
         }
         return(field);
     }
 }
コード例 #10
0
        private IPartialPattern <Offset> CreateGeneralPattern(NodaFormatInfo formatInfo)
        {
            var patterns = new List <IPartialPattern <Offset> >();

            foreach (char c in "flms")
            {
                patterns.Add(ParsePartialPattern(c.ToString(), formatInfo));
            }
            NodaFunc <Offset, IPartialPattern <Offset> > formatter = value => PickGeneralFormatter(value, patterns);

            return(new CompositePattern <Offset>(patterns, formatter));
        }
コード例 #11
0
        /// <summary>
        /// Creates a character handler for a dot (period) or comma, which have the same meaning.
        /// Formatting always uses a dot, but parsing will allow a comma instead, to conform with
        /// ISO-8601. This is *not* culture sensitive.
        /// </summary>
        internal static CharacterHandler <TResult, TBucket> CreateCommaDotHandler <TResult, TBucket>
            (int maxCount, NodaFunc <TResult, int> getter, NodaAction <TBucket, int> setter)
            where TBucket : ParseBucket <TResult>
        {
            return((pattern, builder) =>
            {
                // Note: Deliberately *not* using the decimal separator of the culture - see issue 21.

                // If the next part of the pattern is an F, then this decimal separator is effectively optional.
                // At parse time, we need to check whether we've matched the decimal separator. If we have, match the fractional
                // seconds part as normal. Otherwise, we continue on to the next parsing token.
                // At format time, we should always append the decimal separator, and then append using PadRightTruncate.
                if (pattern.PeekNext() == 'F')
                {
                    pattern.MoveNext();
                    int count = pattern.GetRepeatCount(maxCount);
                    builder.AddField(PatternFields.FractionalSeconds, pattern.Current);
                    builder.AddParseAction((valueCursor, bucket) =>
                    {
                        // If the next token isn't a dot or comma, we assume
                        // it's part of the next token in the pattern
                        if (!valueCursor.Match('.') && !valueCursor.Match(','))
                        {
                            return null;
                        }

                        // If there *was* a decimal separator, we should definitely have a number.
                        int fractionalSeconds;
                        // Last argument is false because we don't need *all* the digits to be present
                        if (!valueCursor.ParseFraction(count, maxCount, out fractionalSeconds, false))
                        {
                            return ParseResult <TResult> .MismatchedNumber(new string('F', count));
                        }
                        // No need to validate the value - we've got one to three digits, so the range 0-999 is guaranteed.
                        setter(bucket, fractionalSeconds);
                        return null;
                    });
                    builder.AddFormatAction((localTime, sb) => sb.Append('.'));
                    builder.AddFormatRightPadTruncate(count, maxCount, getter);
                }
                else
                {
                    builder.AddParseAction((str, bucket) => str.Match('.') || str.Match(',')
                                                            ? null
                                                            : ParseResult <TResult> .MismatchedCharacter(';'));
                    builder.AddFormatAction((value, sb) => sb.Append('.'));
                }
            });
        }
コード例 #12
0
        /// <summary>
        /// Creates a character handler for the year specifier (y).
        /// </summary>
        internal static CharacterHandler <TResult, TBucket> CreateYearHandler <TResult, TBucket>
            (NodaFunc <TResult, int> centuryGetter, NodaFunc <TResult, int> yearGetter, NodaAction <TBucket, int> setter)
            where TBucket : ParseBucket <TResult>
        {
            return((pattern, builder) =>
            {
                int count = pattern.GetRepeatCount(5);
                builder.AddField(PatternFields.Year, pattern.Current);
                switch (count)
                {
                case 1:
                case 2:
                    builder.AddParseValueAction(count, 2, 'y', -99, 99, setter);
                    builder.AddFormatAction((value, sb) => FormatHelper.LeftPad(centuryGetter(value), count, sb));
                    // Just remember that we've set this particular field. We can't set it twice as we've already got the Year flag set.
                    builder.AddField(PatternFields.YearTwoDigits, pattern.Current);
                    break;

                case 3:
                    // Maximum value will be determined later.
                    // Three or more digits (ick).
                    builder.AddParseValueAction(3, 5, 'y', -99999, 99999, setter);
                    builder.AddFormatAction((value, sb) => FormatHelper.LeftPad(yearGetter(value), 3, sb));
                    break;

                case 4:
                    // Left-pad to 4 digits when formatting. Parse either exactly 4 or up to 5 digits depending
                    // on the *next* character of the padding.
                    bool parseExactly4 = CheckIfNextCharacterMightBeDigit(pattern);
                    builder.AddParseValueAction(4, parseExactly4 ? 4 : 5, 'y', -99999, 99999, setter);
                    builder.AddFormatAction((value, sb) => FormatHelper.LeftPad(yearGetter(value), 4, sb));
                    break;

                case 5:
                    // Maximum value will be determined later.
                    // Note that the *exact* number of digits are required; not just "at least count".
                    builder.AddParseValueAction(count, count, 'y', -99999, 99999, setter);
                    builder.AddFormatAction((value, sb) => FormatHelper.LeftPad(yearGetter(value), 5, sb));
                    break;

                default:
                    throw new InvalidOperationException("Bug in Noda Time; invalid count for year went undetected.");
                }
            });
        }
コード例 #13
0
 /// <summary>
 /// Creates a character handler for the era specifier (g).
 /// </summary>
 internal static CharacterHandler <TResult, TBucket> CreateEraHandler <TResult, TBucket>
     (NodaFunc <TResult, Era> eraFromValue, NodaFunc <TBucket, LocalDatePatternParser.LocalDateParseBucket> dateBucketFromBucket)
     where TBucket : ParseBucket <TResult>
 {
     return((pattern, builder) =>
     {
         pattern.GetRepeatCount(2);
         builder.AddField(PatternFields.Era, pattern.Current);
         var formatInfo = builder.FormatInfo;
         // Note: currently the count is ignored. More work needed to determine whether abbreviated era names should be used for just "g".
         builder.AddParseAction((cursor, bucket) =>
         {
             var dateBucket = dateBucketFromBucket(bucket);
             return dateBucket.ParseEra <TResult>(formatInfo, cursor);
         });
         builder.AddFormatAction((value, sb) => sb.Append(formatInfo.GetEraPrimaryName(eraFromValue(value))));
     });
 }
コード例 #14
0
        /// <summary>
        /// Creates a character handler for the month-of-year specifier (M).
        /// </summary>
        internal static CharacterHandler <TResult, TBucket> CreateMonthOfYearHandler <TResult, TBucket>
            (NodaFunc <TResult, int> numberGetter, NodaAction <TBucket, int> textSetter, NodaAction <TBucket, int> numberSetter)
            where TBucket : ParseBucket <TResult>
        {
            return((pattern, builder) =>
            {
                int count = pattern.GetRepeatCount(4);
                PatternFields field;
                switch (count)
                {
                case 1:
                case 2:
                    field = PatternFields.MonthOfYearNumeric;
                    // Handle real maximum value in the bucket
                    builder.AddParseValueAction(count, 2, pattern.Current, 0, 99, numberSetter);
                    builder.AddFormatAction((value, sb) => FormatHelper.LeftPad(numberGetter(value), count, sb));
                    break;

                case 3:
                case 4:
                    field = PatternFields.MonthOfYearText;
                    var format = builder.FormatInfo;
                    IList <string> nonGenitiveTextValues = count == 3 ? format.ShortMonthNames : format.LongMonthNames;
                    IList <string> genitiveTextValues = count == 3 ? format.ShortMonthGenitiveNames : format.LongMonthGenitiveNames;
                    if (nonGenitiveTextValues == genitiveTextValues)
                    {
                        builder.AddParseLongestTextAction(pattern.Current, textSetter, format.CompareInfo, nonGenitiveTextValues);
                    }
                    else
                    {
                        builder.AddParseLongestTextAction(pattern.Current, textSetter, format.CompareInfo,
                                                          genitiveTextValues, nonGenitiveTextValues);
                    }

                    // Hack: see below
                    builder.AddFormatAction(new MonthFormatActionHolder <TResult, TBucket>(format, count, numberGetter).DummyMethod);
                    break;

                default:
                    throw new InvalidOperationException("Invalid count!");
                }
                builder.AddField(field, pattern.Current);
            });
        }
コード例 #15
0
        private static void HandleHalfAmPmDesignator <TResult, TBucket>
            (int count, string specifiedDesignator, int specifiedDesignatorValue, NodaFunc <TResult, int> hourOfDayGetter, NodaAction <TBucket, int> amPmSetter,
            SteppedPatternBuilder <TResult, TBucket> builder)
            where TBucket : ParseBucket <TResult>
        {
            CompareInfo compareInfo = builder.FormatInfo.CompareInfo;

            if (count == 1)
            {
                string abbreviation = specifiedDesignator.Substring(0, 1);
                builder.AddParseAction((str, bucket) =>
                {
                    int value = str.MatchCaseInsensitive(abbreviation, compareInfo, true) ? specifiedDesignatorValue : 1 - specifiedDesignatorValue;
                    amPmSetter(bucket, value);
                    return(null);
                });
                builder.AddFormatAction((value, sb) =>
                {
                    // Only append anything if it's the non-empty designator.
                    if (hourOfDayGetter(value) / 12 == specifiedDesignatorValue)
                    {
                        sb.Append(specifiedDesignator[0]);
                    }
                });
                return;
            }
            builder.AddParseAction((str, bucket) =>
            {
                int value = str.MatchCaseInsensitive(specifiedDesignator, compareInfo, true) ? specifiedDesignatorValue : 1 - specifiedDesignatorValue;
                amPmSetter(bucket, value);
                return(null);
            });
            builder.AddFormatAction((value, sb) =>
            {
                // Only append anything if it's the non-empty designator.
                if (hourOfDayGetter(value) / 12 == specifiedDesignatorValue)
                {
                    sb.Append(specifiedDesignator);
                }
            });
        }
コード例 #16
0
        /// <summary>
        /// Adds parse and format actions for a mandatory positive/negative sign.
        /// </summary>
        /// <param name="signSetter">Action to take when to set the given sign within the bucket</param>
        /// <param name="nonNegativePredicate">Predicate to detect whether the value being formatted is non-negative</param>
        public void AddRequiredSign(NodaAction <TBucket, bool> signSetter, NodaFunc <TResult, bool> nonNegativePredicate)
        {
            string negativeSign = formatInfo.NegativeSign;
            string positiveSign = formatInfo.PositiveSign;

            AddParseAction((str, bucket) =>
            {
                if (str.Match(negativeSign))
                {
                    signSetter(bucket, false);
                    return(null);
                }
                if (str.Match(positiveSign))
                {
                    signSetter(bucket, true);
                    return(null);
                }
                return(ParseResult <TResult> .MissingSign);
            });
            AddFormatAction((value, sb) => sb.Append(nonNegativePredicate(value) ? positiveSign : negativeSign));
        }
コード例 #17
0
 internal static ParseResult <T> ForException(NodaFunc <Exception> exceptionProvider)
 {
     return(new ParseResult <T>(exceptionProvider, false));
 }
コード例 #18
0
 /// <summary>
 /// Converts this result to a new target type, either by executing the given projection
 /// for a success result, or propagating the exception provider for failure.
 /// </summary>
 internal ParseResult <TTarget> Convert <TTarget>(NodaFunc <T, TTarget> projection)
 {
     return(Success ? ParseResult <TTarget> .ForValue(projection(Value))
         : new ParseResult <TTarget>(exceptionProvider, continueWithMultiple));
 }
コード例 #19
0
 internal MonthFormatActionHolder(NodaFormatInfo formatInfo, int count, NodaFunc <TResult, int> getter)
 {
     this.count      = count;
     this.formatInfo = formatInfo;
     this.getter     = getter;
 }
コード例 #20
0
 internal CompositePattern(IEnumerable <IPartialPattern <T> > parsePatterns, NodaFunc <T, IPartialPattern <T> > formatPatternPicker)
 {
     this.parsePatterns       = new List <IPartialPattern <T> >(parsePatterns);
     this.formatPatternPicker = formatPatternPicker;
 }
コード例 #21
0
 internal PatternBclSupport(string defaultFormatPattern, NodaFunc <NodaFormatInfo, FixedFormatInfoPatternParser <T> > patternParser)
 {
     this.patternParser        = patternParser;
     this.defaultFormatPattern = defaultFormatPattern;
 }
コード例 #22
0
 internal void AddFormatRightPadTruncate(int width, int scale, NodaFunc <TResult, int> selector)
 {
     AddFormatAction((value, sb) => FormatHelper.RightPadTruncate(selector(value), width, scale, formatInfo.DecimalSeparator, sb));
 }
コード例 #23
0
 internal void AddFormatRightPad(int width, int scale, NodaFunc <TResult, int> selector)
 {
     AddFormatAction((value, sb) => FormatHelper.RightPad(selector(value), width, scale, sb));
 }
コード例 #24
0
 internal void AddFormatLeftPad(int count, NodaFunc <TResult, int> selector)
 {
     AddFormatAction((value, sb) => FormatHelper.LeftPad(selector(value), count, sb));
 }
コード例 #25
0
 private static ParseResult <T> ForInvalidValue(NodaFunc <Exception> exceptionProvider)
 {
     return(new ParseResult <T>(exceptionProvider, true));
 }
コード例 #26
0
        internal static CharacterHandler <TResult, TBucket> CreateAmPmHandler <TResult, TBucket>
            (NodaFunc <TResult, int> hourOfDayGetter, NodaAction <TBucket, int> amPmSetter)
            where TBucket : ParseBucket <TResult>
        {
            return((pattern, builder) =>
            {
                int count = pattern.GetRepeatCount(2);
                builder.AddField(PatternFields.AmPm, pattern.Current);

                string amDesignator = builder.FormatInfo.AMDesignator;
                string pmDesignator = builder.FormatInfo.PMDesignator;

                // If we don't have an AM or PM designator, we're nearly done. Set the AM/PM designator
                // to the special value of 2, meaning "take it from the template".
                if (amDesignator == "" && pmDesignator == "")
                {
                    builder.AddParseAction((str, bucket) =>
                    {
                        amPmSetter(bucket, 2);
                        return null;
                    });
                    return;
                }
                // Odd scenario (but present in af-ZA for .NET 2) - exactly one of the AM/PM designator is valid.
                // Delegate to a separate method to keep this clearer...
                if (amDesignator == "" || pmDesignator == "")
                {
                    int specifiedDesignatorValue = amDesignator == "" ? 1 : 0;
                    string specifiedDesignator = specifiedDesignatorValue == 1 ? pmDesignator : amDesignator;
                    HandleHalfAmPmDesignator(count, specifiedDesignator, specifiedDesignatorValue, hourOfDayGetter, amPmSetter, builder);
                    return;
                }
                CompareInfo compareInfo = builder.FormatInfo.CompareInfo;
                // Single character designator
                if (count == 1)
                {
                    // It's not entirely clear whether this is the right thing to do... there's no nice
                    // way of providing a single-character case-insensitive match.
                    string amFirst = amDesignator.Substring(0, 1);
                    string pmFirst = pmDesignator.Substring(0, 1);
                    builder.AddParseAction((str, bucket) =>
                    {
                        if (str.MatchCaseInsensitive(amFirst, compareInfo, true))
                        {
                            amPmSetter(bucket, 0);
                            return null;
                        }
                        if (str.MatchCaseInsensitive(pmFirst, compareInfo, true))
                        {
                            amPmSetter(bucket, 1);
                            return null;
                        }
                        return ParseResult <TResult> .MissingAmPmDesignator;
                    });
                    builder.AddFormatAction((value, sb) => sb.Append(hourOfDayGetter(value) > 11 ? pmDesignator[0] : amDesignator[0]));
                    return;
                }
                // Full designator
                builder.AddParseAction((str, bucket) =>
                {
                    // Could use the "match longest" approach, but with only two it feels a bit silly to build a list...
                    bool pmLongerThanAm = pmDesignator.Length > amDesignator.Length;
                    string longerDesignator = pmLongerThanAm ? pmDesignator : amDesignator;
                    string shorterDesignator = pmLongerThanAm ? amDesignator : pmDesignator;
                    int longerValue = pmLongerThanAm ? 1 : 0;
                    if (str.MatchCaseInsensitive(longerDesignator, compareInfo, true))
                    {
                        amPmSetter(bucket, longerValue);
                        return null;
                    }
                    if (str.MatchCaseInsensitive(shorterDesignator, compareInfo, true))
                    {
                        amPmSetter(bucket, 1 - longerValue);
                        return null;
                    }
                    return ParseResult <TResult> .MissingAmPmDesignator;
                });
                builder.AddFormatAction((value, sb) => sb.Append(hourOfDayGetter(value) > 11 ? pmDesignator : amDesignator));
            });
        }
コード例 #27
0
 /// <summary>
 /// Adds a character which must be matched exactly when parsing, and appended directly when formatting.
 /// </summary>
 internal void AddLiteral(char expectedChar, NodaFunc <char, ParseResult <TResult> > failureSelector)
 {
     AddParseAction((str, bucket) => str.Match(expectedChar) ? null : failureSelector(expectedChar));
     AddFormatAction((value, builder) => builder.Append(expectedChar));
 }
コード例 #28
0
 private ParseResult(NodaFunc <Exception> exceptionProvider, bool continueWithMultiple)
 {
     this.exceptionProvider    = exceptionProvider;
     this.continueWithMultiple = continueWithMultiple;
 }
コード例 #29
0
 /// <summary>
 /// Returns a handler for a zero-padded purely-numeric field specifier, such as "seconds", "minutes", "24-hour", "12-hour" etc.
 /// </summary>
 /// <param name="maxCount">Maximum permissable count (usually two)</param>
 /// <param name="field">Field to remember that we've seen</param>
 /// <param name="minValue">Minimum valid value for the field (inclusive)</param>
 /// <param name="maxValue">Maximum value value for the field (inclusive)</param>
 /// <param name="getter">Delegate to retrieve the field value when formatting</param>
 /// <param name="setter">Delegate to set the field value into a bucket when parsing</param>
 /// <returns>The pattern parsing failure, or null on success.</returns>
 internal static CharacterHandler <TResult, TBucket> HandlePaddedField(int maxCount, PatternFields field, int minValue, int maxValue, NodaFunc <TResult, int> getter,
                                                                       NodaAction <TBucket, int> setter)
 {
     return((pattern, builder) =>
     {
         int count = pattern.GetRepeatCount(maxCount);
         builder.AddField(field, pattern.Current);
         builder.AddParseValueAction(count, maxCount, pattern.Current, minValue, maxValue, setter);
         builder.AddFormatLeftPad(count, getter);
     });
 }