/// <summary> /// Performs common parsing operations: start with a parse action to move the /// value cursor onto the first character, then call a character handler for each /// character in the pattern to build up the steps. If any handler fails, /// that failure is returned - otherwise the return value is null. /// </summary> internal void ParseCustomPattern(string patternText, Dictionary <char, CharacterHandler <TResult, TBucket> > characterHandlers) { var patternCursor = new PatternCursor(patternText); // Now iterate over the pattern. while (patternCursor.MoveNext()) { CharacterHandler <TResult, TBucket> handler; if (characterHandlers.TryGetValue(patternCursor.Current, out handler)) { handler(patternCursor, this); } else { char current = patternCursor.Current; if ((current >= 'A' && current <= 'Z') || (current >= 'a' && current <= 'z') || current == PatternCursor.EmbeddedPatternStart || current == PatternCursor.EmbeddedPatternEnd) { throw new InvalidPatternException(TextErrorMessages.UnquotedLiteral, current); } AddLiteral(patternCursor.Current, ParseResult <TResult> .MismatchedCharacter); } } }
public void GetRepeatCount_Valid(string text, int expectedCount) { var cursor = new PatternCursor(text); Assert.IsTrue(cursor.MoveNext()); int actual = cursor.GetRepeatCount(10); Assert.AreEqual(expectedCount, actual); ValidateCurrentCharacter(cursor, expectedCount - 1, 'a'); }
public void TestGetQuotedString() { var cursor = new PatternCursor("'abc'"); Assert.AreEqual('\'', GetNextCharacter(cursor)); string actual = cursor.GetQuotedString('\''); Assert.AreEqual("abc", actual); Assert.IsFalse(cursor.MoveNext()); }
public void TestGetQuotedString_handlesDoubleQuote() { var cursor = new PatternCursor("\"abc\""); char openQuote = GetNextCharacter(cursor); string actual = cursor.GetQuotedString(openQuote, ref failure); Assert.AreEqual("abc", actual); Assert.IsFalse(cursor.MoveNext()); }
public void TestGetQuotedString_HandlesEscapedCloseQuote() { var cursor = new PatternCursor("'ab\\'c'"); char openQuote = GetNextCharacter(cursor); string actual = cursor.GetQuotedString(openQuote); Assert.AreEqual("ab'c", actual); Assert.IsFalse(cursor.MoveNext()); }
public void TestGetQuotedString_HandlesOtherQuote() { var cursor = new PatternCursor("[abc]"); GetNextCharacter(cursor); string actual = cursor.GetQuotedString(']'); Assert.AreEqual("abc", actual); Assert.IsFalse(cursor.MoveNext()); }
public void GetQuotedString_Valid(string pattern, string expected) { var cursor = new PatternCursor(pattern); Assert.AreEqual('\'', GetNextCharacter(cursor)); string actual = cursor.GetQuotedString('\''); Assert.AreEqual(expected, actual); Assert.IsFalse(cursor.MoveNext()); }
public void TestGetQuotedString_Empty() { var cursor = new PatternCursor("''"); char openQuote = GetNextCharacter(cursor); string actual = cursor.GetQuotedString(openQuote); Assert.AreEqual(string.Empty, actual); Assert.IsFalse(cursor.MoveNext()); }
internal static void HandleBackslash(PatternCursor pattern, SteppedPatternBuilder <TResult, TBucket> builder) { if (!pattern.MoveNext()) { throw new InvalidPatternException(TextErrorMessages.EscapeAtEndOfString); } builder.AddLiteral(pattern.Current, ParseResult <TResult> .EscapedCharacterMismatch); }
/// <summary> /// Performs common parsing operations: start with a parse action to move the /// value cursor onto the first character, then call a character handler for each /// character in the pattern to build up the steps. If any handler fails, /// that failure is returned - otherwise the return value is null. /// </summary> internal void ParseCustomPattern(string patternText, Dictionary <char, CharacterHandler <TResult, TBucket> > characterHandlers) { var patternCursor = new PatternCursor(patternText); // Now iterate over the pattern. while (patternCursor.MoveNext()) { CharacterHandler <TResult, TBucket> handler; if (characterHandlers.TryGetValue(patternCursor.Current, out handler)) { handler(patternCursor, this); } else { AddLiteral(patternCursor.Current, ParseResult <TResult> .MismatchedCharacter); } } }
public void TestGetEmbeddedPattern_QuotedCloseCharacter() { var cursor = new PatternCursor("x<oops'>'"); cursor.MoveNext(); Assert.Throws<InvalidPatternException>(() => cursor.GetEmbeddedPattern('<', '>')); }
public void TestGetQuotedString_simple() { var cursor = new PatternCursor("'abc'"); char openQuote = GetNextCharacter(cursor); string actual = cursor.GetQuotedString(openQuote, ref failure); AssertNoFailure(); Assert.AreEqual("abc", actual); Assert.IsFalse(cursor.MoveNext()); }
/// <summary> /// Returns true if the next character in the pattern might represent a digit from another value (e.g. a different /// field). Returns false otherwise, e.g. if we've reached the end of the pattern, or the next character is a literal /// non-digit. /// </summary> private static bool CheckIfNextCharacterMightBeDigit(PatternCursor pattern) { int originalIndex = pattern.Index; try { if (!pattern.MoveNext()) { return(false); } char next = pattern.Current; // If we've got an unescaped letter, assume it could be a field. // If we've got an unescaped digit, it's a no-brainer. if ((next >= '0' && next <= '9') || (next >= 'a' && next <= 'z') || (next >= 'A' && next <= 'Z')) { return(true); } // A % is tricky - could be any number of things. Act conservatively. if (next == '%') { return(true); } // Quoting: find the unquoted text, and see whether it starts with a non-digit. if (next == '\'' || next == '\"') { // If this throws, catch it and let it get thrown later, in the right context. try { string quoted = pattern.GetQuotedString(next); // Empty quotes - could be trying to disguise a digit afterwards... if (quoted.Length == 0) { return(true); } char firstQuoted = quoted[0]; // Check if the quoted string starts with a digit, basically. return(firstQuoted >= '0' && firstQuoted <= '9'); } catch (InvalidPatternException) { // Doesn't really matter... return(true); } } if (next == '\\') { if (!pattern.MoveNext()) { return(true); // Doesn't really matter; we'll throw an exception soon anyway. } char quoted = pattern.Current; return(quoted >= '0' && quoted <= '9'); } // Could be a date/time separator, but otherwise it's just something that we'll include // as a literal and won't be a digit. return(false); } finally { pattern.Move(originalIndex); } }
public void TestGetEmbeddedPattern_WrongOpenCharacter() { var cursor = new PatternCursor("x(oops)"); cursor.MoveNext(); Assert.Throws<InvalidPatternException>(() => cursor.GetEmbeddedPattern('<', '>')); }
public void TestGetEmbeddedPattern_Valid_WithEscaping() { var cursor = new PatternCursor(@"x<HH:\Tmm>y"); cursor.MoveNext(); string embedded = cursor.GetEmbeddedPattern('<', '>'); Assert.AreEqual(@"HH:\Tmm", embedded); Assert.AreEqual('>', cursor.Current); }
public void TestGetEmbeddedPattern_Valid() { var cursor = new PatternCursor("x<HH:mm>y"); cursor.MoveNext(); string embedded = cursor.GetEmbeddedPattern('<', '>'); Assert.AreEqual("HH:mm", embedded); ValidateCurrentCharacter(cursor, 7, '>'); }
public void TestGetRepeatCount_Three() { var cursor = new PatternCursor("aaa"); Assert.IsTrue(cursor.MoveNext()); int actual = cursor.GetRepeatCount(10); Assert.AreEqual(3, actual); ValidateCurrentCharacter(cursor, 2, 'a'); }
public void TestGetRepeatCount_ExceedsMax() { var cursor = new PatternCursor("aaa"); Assert.IsTrue(cursor.MoveNext()); Assert.Throws<InvalidPatternException>(() => cursor.GetRepeatCount(2)); }
public void GetEmbeddedPattern_Valid(string pattern, string expectedEmbedded) { var cursor = new PatternCursor(pattern); cursor.MoveNext(); string embedded = cursor.GetEmbeddedPattern(); Assert.AreEqual(expectedEmbedded, embedded); ValidateCurrentCharacter(cursor, expectedEmbedded.Length + 2, '>'); }
/// <summary> /// Handles date, time and date/time embedded patterns. /// </summary> internal void AddEmbeddedLocalPartial( PatternCursor pattern, Func <TBucket, LocalDatePatternParser.LocalDateParseBucket> dateBucketExtractor, Func <TBucket, LocalTimePatternParser.LocalTimeParseBucket> timeBucketExtractor, Func <TResult, LocalDate> dateExtractor, Func <TResult, LocalTime> timeExtractor, // null if date/time embedded patterns are invalid Func <TResult, LocalDateTime>?dateTimeExtractor) { // This will be d (date-only), t (time-only), or < (date and time) // If it's anything else, we'll see the problem when we try to get the pattern. var patternType = pattern.PeekNext(); if (patternType == 'd' || patternType == 't') { pattern.MoveNext(); } string embeddedPatternText = pattern.GetEmbeddedPattern(); switch (patternType) { case '<': { var sampleBucket = CreateSampleBucket(); var templateTime = timeBucketExtractor(sampleBucket).TemplateValue; var templateDate = dateBucketExtractor(sampleBucket).TemplateValue; if (dateTimeExtractor is null) { throw new InvalidPatternException(TextErrorMessages.InvalidEmbeddedPatternType); } AddField(PatternFields.EmbeddedDate, 'l'); AddField(PatternFields.EmbeddedTime, 'l'); AddEmbeddedPattern( LocalDateTimePattern.Create(embeddedPatternText, FormatInfo, templateDate + templateTime).UnderlyingPattern, (bucket, value) => { var dateBucket = dateBucketExtractor(bucket); var timeBucket = timeBucketExtractor(bucket); dateBucket.Calendar = value.Calendar; dateBucket.Year = value.Year; dateBucket.MonthOfYearNumeric = value.Month; dateBucket.DayOfMonth = value.Day; timeBucket.Hours24 = value.Hour; timeBucket.Minutes = value.Minute; timeBucket.Seconds = value.Second; timeBucket.FractionalSeconds = value.NanosecondOfSecond; }, dateTimeExtractor); break; } case 'd': AddEmbeddedDatePattern('l', embeddedPatternText, dateBucketExtractor, dateExtractor); break; case 't': AddEmbeddedTimePattern('l', embeddedPatternText, timeBucketExtractor, timeExtractor); break; default: throw new InvalidOperationException("Bug in Noda Time: embedded pattern type wasn't date, time, or date+time"); } }
public void GetEmbeddedPattern_Invalid(string text) { var cursor = new PatternCursor(text); cursor.MoveNext(); Assert.Throws<InvalidPatternException>(() => cursor.GetEmbeddedPattern()); }