/// <summary> /// Parses the arguments. /// </summary> /// <param name="request"> /// The request. /// </param> /// <returns> /// The <see cref="ParsedArguments" />. /// </returns> protected internal ParsedArguments ParseArguments(FormatterRequest request) { int index; var extensions = this.ParseExtensions(request, out index); var keyedBlocks = this.ParseKeyedBlocks(request, index); return new ParsedArguments(keyedBlocks, extensions); }
/// <summary> /// Parses the arguments. /// </summary> /// <param name="request"> /// The request. /// </param> /// <returns> /// The <see cref="ParsedArguments" />. /// </returns> protected internal ParsedArguments ParseArguments(FormatterRequest request) { int index; var extensions = this.ParseExtensions(request, out index); var keyedBlocks = this.ParseKeyedBlocks(request, index); return(new ParsedArguments(keyedBlocks, extensions)); }
/// <summary> /// Parses the extensions. /// </summary> /// <param name="request">The request.</param> /// <param name="index">The index.</param> /// <returns>The formatter extensions.</returns> protected internal IEnumerable <FormatterExtension> ParseExtensions(FormatterRequest request, out int index) { var result = new List <FormatterExtension>(); int length = request.FormatterArguments.Length; index = 0; var extension = new StringBuilder(); var value = new StringBuilder(); const char Colon = ':'; bool foundExtension = false; for (int i = 0; i < length; i++) { var c = request.FormatterArguments[i]; // Whitespace is tolerated at the beginning. bool isWhiteSpace = char.IsWhiteSpace(c); if (isWhiteSpace) { // We've reached the end if (value.Length > 0) { foundExtension = false; result.Add(new FormatterExtension(extension.ToString(), value.ToString())); extension.Clear(); value.Clear(); index = i; continue; } if (extension.Length > 0) { // It's not an extension, so we're done looking. break; } continue; } if (c == Colon) { foundExtension = true; continue; } if (foundExtension) { value.Append(c); continue; } extension.Append(c); } return(result); }
/// <summary> /// Parses the extensions. /// </summary> /// <param name="request">The request.</param> /// <param name="index">The index.</param> /// <returns>The formatter extensions.</returns> protected internal IEnumerable<FormatterExtension> ParseExtensions(FormatterRequest request, out int index) { var result = new List<FormatterExtension>(); int length = request.FormatterArguments.Length; index = 0; var extension = new StringBuilder(); var value = new StringBuilder(); const char Colon = ':'; bool foundExtension = false; for (int i = 0; i < length; i++) { var c = request.FormatterArguments[i]; // Whitespace is tolerated at the beginning. bool isWhiteSpace = char.IsWhiteSpace(c); if (isWhiteSpace) { // We've reached the end if (value.Length > 0) { foundExtension = false; result.Add(new FormatterExtension(extension.ToString(), value.ToString())); extension.Clear(); value.Clear(); index = i; continue; } if (extension.Length > 0) { // It's not an extension, so we're done looking. break; } continue; } if (c == Colon) { foundExtension = true; continue; } if (foundExtension) { value.Append(c); continue; } extension.Append(c); } return result; }
/// <summary> /// Builds the message. /// </summary> /// <param name="request"> /// The request. /// </param> /// <returns> /// The <see cref="string" />. /// </returns> private static string BuildMessage(FormatterRequest request) { return string.Format( "Format '{0}' could not be resolved.\r\n" + "Line {1}, position {2}\r\n" + "Source literal: '{3}'", request.FormatterName, request.SourceLiteral.SourceLineNumber, request.SourceLiteral.SourceColumnNumber, request.SourceLiteral.InnerText); }
/// <summary> /// Gets the formatter to use. If none was found, throws an exception. /// </summary> /// <param name="request"> /// The request. /// </param> /// <returns> /// The <see cref="IFormatter" />. /// </returns> /// <exception cref="FormatterNotFoundException"> /// Thrown when the formatter was not found. /// </exception> public IFormatter GetFormatter(FormatterRequest request) { var formatter = this.FirstOrDefault(x => x.CanFormat(request)); if (formatter == null) { throw new FormatterNotFoundException(request); } return(formatter); }
public void Format(string formatterArgs, string keyToUse, string expectedBlock) { var subject = new SelectFormatter(); var messageFormatterMock = new Mock<IMessageFormatter>(); messageFormatterMock.Setup(x => x.FormatMessage(It.IsAny<string>(), It.IsAny<Dictionary<string, object>>())) .Returns((string input, Dictionary<string, object> a) => input); var req = new FormatterRequest( new Literal(1, 1, 1, 1, new StringBuilder()), "gender", "select", formatterArgs); var args = new Dictionary<string, object> { { "gender", keyToUse } }; var result = subject.Format("en", req, args, messageFormatterMock.Object); Assert.Equal(expectedBlock, result); }
public void Pluralize(double n, string expected) { var subject = new PluralFormatter(); var args = new Dictionary<string, object> { { "test", n } }; var arguments = new ParsedArguments( new[] { new KeyedBlock("zero", "nothing"), new KeyedBlock("one", "just one"), new KeyedBlock("other", "wow") }, new FormatterExtension[0]); var request = new FormatterRequest(new Literal(1, 1, 1, 1, new StringBuilder()), "test", "plural", null); var actual = subject.Pluralize("en", arguments, Convert.ToDouble(args[request.Variable]), 0); Assert.Equal(expected, actual); }
public void GetFormatter() { var subject = new FormatterLibrary(); var mock1 = new Mock<IFormatter>(); var mock2 = new Mock<IFormatter>(); var req = new FormatterRequest(new Literal(1, 1, 1, 1, new StringBuilder()), "test", "dawg", null); subject.Add(mock1.Object); subject.Add(mock2.Object); Assert.Throws<FormatterNotFoundException>(() => subject.GetFormatter(req)); mock2.Setup(x => x.CanFormat(req)).Returns(true); var actual = subject.GetFormatter(req); Assert.Same(mock2.Object, actual); mock1.Setup(x => x.CanFormat(req)).Returns(true); actual = subject.GetFormatter(req); Assert.Same(mock1.Object, actual); }
/// <summary> /// Initializes a new instance of the <see cref="FormatterNotFoundException" /> class. /// </summary> /// <param name="request"> /// The request. /// </param> public FormatterNotFoundException(FormatterRequest request) : base(BuildMessage(request)) { }
public void ParseKeyedBlocks(string args, string[] keys, string[] values) { var subject = new BaseFormatterImpl(); var req = new FormatterRequest(new Literal(1, 1, 1, 1, new StringBuilder()), null, null, args); // Warm-up subject.ParseKeyedBlocks(req, 0); Benchmark.Start("Parsing keyed blocks..", this.outputHelper); for (int i = 0; i < 10000; i++) { subject.ParseKeyedBlocks(req, 0); } Benchmark.End(this.outputHelper); var actual = subject.ParseKeyedBlocks(req, 0); Assert.Equal(keys.Length, actual.Count()); this.outputHelper.WriteLine("Input: " + args); this.outputHelper.WriteLine("-----"); for (int index = 0; index < actual.ToArray().Length; index++) { var keyedBlock = actual.ToArray()[index]; var expectedKey = keys[index]; var expectedValue = values[index]; Assert.Equal(expectedKey, keyedBlock.Key); Assert.Equal(expectedValue, keyedBlock.BlockText); this.outputHelper.WriteLine("Key: " + keyedBlock.Key); this.outputHelper.WriteLine("Block: " + keyedBlock.BlockText); } }
/// <summary> /// Parses the keyed blocks. /// </summary> /// <param name="request"> /// The request. /// </param> /// <param name="startIndex"> /// The start index. /// </param> /// <returns> /// The keyed blocks. /// </returns> protected internal IEnumerable <KeyedBlock> ParseKeyedBlocks(FormatterRequest request, int startIndex) { const char OpenBrace = '{'; const char CloseBrace = '}'; const char EscapingChar = '\''; var result = new List <KeyedBlock>(); var key = new StringBuilder(); var block = new StringBuilder(); var braceBalance = 0; var foundWhitespaceAfterKey = false; var insideEscapeSequence = false; if (request.FormatterArguments == null) { return(Enumerable.Empty <KeyedBlock>()); } for (int i = startIndex; i < request.FormatterArguments.Length; i++) { var c = request.FormatterArguments[i]; var isWhitespace = char.IsWhiteSpace(c); if (c == EscapingChar) { if (braceBalance == 0) { throw new MalformedLiteralException( "Expected a key, but found start of a escape sequence.", 0, 0, request.FormatterArguments); } if (i == request.FormatterArguments.Length - 1) { if (!insideEscapeSequence) { block.Append(EscapingChar); } // The last char can't open a new escape sequence, it can only close one if (insideEscapeSequence) { insideEscapeSequence = false; } continue; } var nextChar = request.FormatterArguments[i + 1]; if (nextChar == EscapingChar) { block.Append(EscapingChar); block.Append(EscapingChar); ++i; continue; } if (insideEscapeSequence) { block.Append(EscapingChar); insideEscapeSequence = false; continue; } if (nextChar == '{' || nextChar == '}' || nextChar == '#') { block.Append(EscapingChar); block.Append(nextChar); insideEscapeSequence = true; ++i; continue; } block.Append(EscapingChar); continue; } if (insideEscapeSequence) { block.Append(c); continue; } if (c == OpenBrace) { if (key.Length == 0) { throw new MalformedLiteralException( "Expected a key, but found start of a new block.", 0, 0, request.FormatterArguments); } braceBalance++; if (braceBalance > 1) { block.Append(c); } continue; } if (c == CloseBrace) { if (key.Length == 0) { throw new MalformedLiteralException( "Expected a key, but found end of a block.", 0, 0, request.FormatterArguments); } if (braceBalance == 0) { throw new MalformedLiteralException( "Found end of a block, but no block has been started, or the" + " block has already been closed. " + "This could indicate an unescaped brace somewhere.", 0, 0, request.FormatterArguments); } braceBalance--; if (braceBalance == 0) { result.Add(new KeyedBlock(key.ToString(), block.ToString())); block.Clear(); key.Clear(); foundWhitespaceAfterKey = false; continue; } if (braceBalance < 0) { throw new MalformedLiteralException( "Expected '{', but found '}' - essentially this means there are more close braces than there are open braces.", 0, 0, request.FormatterArguments); } } // If we are inside a block, append to the block buffer if (braceBalance > 0) { block.Append(c); continue; } // Else, we are buffering our key if (isWhitespace == false) { if (foundWhitespaceAfterKey) { throw new MalformedLiteralException( "Any whitespace after a key should be followed by the beginning of a block.", 0, 0, request.FormatterArguments); } key.Append(c); } else if (key.Length > 0) { foundWhitespaceAfterKey = true; } } if (insideEscapeSequence) { throw new MalformedLiteralException( "There is an unclosed escape sequence.", 0, 0, request.FormatterArguments); } if (braceBalance > 0) { throw new MalformedLiteralException( "There are more open braces than there are close braces.", 0, 0, request.FormatterArguments); } return(result); }
/// <summary> /// Creates the request. /// </summary> /// <returns> /// The <see cref="FormatterRequest" />. /// </returns> private static FormatterRequest CreateRequest() { var req = new FormatterRequest(new Literal(1, 10, 1, 1, new StringBuilder()), "test", null, null); return req; }
public void ParseArguments( string args, string[] extensionKeys, string[] extensionValues, string[] keys, string[] blocks) { var subject = new BaseFormatterImpl(); var req = new FormatterRequest(new Literal(1, 1, 1, 1, new StringBuilder()), null, null, args); var actual = subject.ParseArguments(req); Assert.Equal(extensionKeys.Length, actual.Extensions.Count()); Assert.Equal(keys.Length, actual.KeyedBlocks.Count()); for (int i = 0; i < actual.Extensions.ToArray().Length; i++) { var extension = actual.Extensions.ToArray()[i]; Assert.Equal(extensionKeys[i], extension.Extension); Assert.Equal(extensionValues[i], extension.Value); } for (int i = 0; i < actual.KeyedBlocks.ToArray().Length; i++) { var block = actual.KeyedBlocks.ToArray()[i]; Assert.Equal(keys[i], block.Key); Assert.Equal(blocks[i], block.BlockText); } }
public void ParseArguments_invalid(string args) { var subject = new BaseFormatterImpl(); var req = new FormatterRequest(new Literal(1, 1, 1, 1, new StringBuilder()), null, null, args); var ex = Assert.Throws<MalformedLiteralException>(() => subject.ParseArguments(req)); this.outputHelper.WriteLine(ex.Message); }
public void ParseExtensions(string args, string extension, string value, int expectedIndex) { var subject = new BaseFormatterImpl(); int index; var req = new FormatterRequest(new Literal(1, 1, 1, 1, new StringBuilder()), null, null, args); // Warmup subject.ParseExtensions(req, out index); Benchmark.Start("Parsing extensions a few times (warmed up)", this.outputHelper); for (int i = 0; i < 1000; i++) { subject.ParseExtensions(req, out index); } Benchmark.End(this.outputHelper); var actual = subject.ParseExtensions(req, out index); Assert.NotEmpty(actual); var first = actual.First(); Assert.Equal(extension, first.Extension); Assert.Equal(value, first.Value); Assert.Equal(expectedIndex, index); }
public void ParseExtensions_multiple() { var subject = new BaseFormatterImpl(); int index; var args = " offset:2 code:js "; var expectedIndex = 17; var req = new FormatterRequest(new Literal(1, 1, 1, 1, new StringBuilder()), null, null, args); var actual = subject.ParseExtensions(req, out index); Assert.NotEmpty(actual); var result = actual.First(); Assert.Equal("offset", result.Extension); Assert.Equal("2", result.Value); result = actual.ElementAt(1); Assert.Equal("code", result.Extension); Assert.Equal("js", result.Value); Assert.Equal(expectedIndex, index); }
/// <summary> /// Parses the keyed blocks. /// </summary> /// <param name="request"> /// The request. /// </param> /// <param name="startIndex"> /// The start index. /// </param> /// <returns> /// The keyed blocks. /// </returns> protected internal IEnumerable<KeyedBlock> ParseKeyedBlocks(FormatterRequest request, int startIndex) { const char OpenBrace = '{'; const char CloseBrace = '}'; const char EscapingChar = '\\'; var result = new List<KeyedBlock>(); var key = new StringBuilder(); var block = new StringBuilder(); var braceBalance = 0; var foundWhitespaceAfterKey = false; for (int i = startIndex; i < request.FormatterArguments.Length; i++) { var c = request.FormatterArguments[i]; var isWhitespace = char.IsWhiteSpace(c); if (c == OpenBrace) { if (key.Length == 0) { throw new MalformedLiteralException( "Expected a key, but found start of a new block.", 0, 0, request.FormatterArguments); } if (i != 0 && request.FormatterArguments[i - 1] == EscapingChar) { block.Append(c); continue; } braceBalance++; if (braceBalance > 1) { block.Append(c); } continue; } if (c == CloseBrace) { if (key.Length == 0) { throw new MalformedLiteralException( "Expected a key, but found end of a block.", 0, 0, request.FormatterArguments); } if (i != 0 && request.FormatterArguments[i - 1] == EscapingChar) { block.Append(c); continue; } if (braceBalance == 0) { throw new MalformedLiteralException( "Found end of a block, but no block has been started, or the" + " block has already been closed. " + "This could indicate an unescaped brace somewhere.", 0, 0, request.FormatterArguments); } braceBalance--; if (braceBalance == 0) { result.Add(new KeyedBlock(key.ToString(), block.ToString())); block.Clear(); key.Clear(); foundWhitespaceAfterKey = false; continue; } if (braceBalance < 0) { throw new MalformedLiteralException( "Expected '{', but found '}' - essentially this means there are more close braces than there are open braces.", 0, 0, request.FormatterArguments); } } // If we are inside a block, append to the block buffer if (braceBalance > 0) { block.Append(c); continue; } // Else, we are buffering our key if (isWhitespace == false) { if (foundWhitespaceAfterKey) { throw new MalformedLiteralException( "Any whitespace after a key should be followed by the beginning of a block.", 0, 0, request.FormatterArguments); } key.Append(c); } else if (key.Length > 0) { foundWhitespaceAfterKey = true; } } if (braceBalance > 0) { throw new MalformedLiteralException( "There are more open braces than there are close braces.", 0, 0, request.FormatterArguments); } return result; }