/// <summary> /// Gets a formatted and localized error message of a <see cref="JsonErrorInfoParameter"/>. /// </summary> /// <param name="parameter"> /// The <see cref="JsonErrorInfoParameter"/> to localize. /// </param> /// <param name="localizer"> /// The <see cref="Localizer"/> to use for generating a display value. /// </param> /// <returns> /// The localized display value. /// </returns> /// <exception cref="ArgumentNullException"> /// <paramref name="parameter"/> and/or <paramref name="localizer"/> are null. /// </exception> public static string GetLocalizedDisplayValue(JsonErrorInfoParameter parameter, Localizer localizer) { if (parameter == null) { throw new ArgumentNullException(nameof(parameter)); } if (localizer == null) { throw new ArgumentNullException(nameof(localizer)); } switch (parameter) { case JsonErrorInfoParameter <char> charParameter: char c = charParameter.Value; return(StringLiteral.CharacterMustBeEscaped(c) ? $"'{StringLiteral.EscapedCharacterString(c)}'" : $"'{c}'"); case JsonErrorInfoParameter <string> stringParameter: return(stringParameter.Value == null ? localizer.Localize(NullString) : $"\"{stringParameter.Value}\""); default: return(parameter.UntypedValue == null ? localizer.Localize(NullString) : localizer.Localize(UntypedObjectString, parameter.UntypedValue.ToString())); } }
internal void AppendString(string value) { outputBuilder.Append(StringLiteral.QuoteCharacter); if (value != null) { // Save on Append() operations by appending escape-char-less substrings // that are as large as possible. int firstNonEscapedCharPosition = 0; for (int i = 0; i < value.Length; i++) { char c = value[i]; if (StringLiteral.CharacterMustBeEscaped(c)) { // Non-empty substring between this character and the last? if (firstNonEscapedCharPosition < i) { outputBuilder.Append( value, firstNonEscapedCharPosition, i - firstNonEscapedCharPosition); } // Append the escape sequence. outputBuilder.Append(StringLiteral.EscapedCharacterString(c)); firstNonEscapedCharPosition = i + 1; } } if (firstNonEscapedCharPosition < value.Length) { outputBuilder.Append( value, firstNonEscapedCharPosition, value.Length - firstNonEscapedCharPosition); } } outputBuilder.Append(StringLiteral.QuoteCharacter); }
private IEnumerable <IGreenJsonSymbol> _TokenizeAll() { // This tokenizer uses labels with goto to switch between modes of tokenization. int length = Json.Length; int currentIndex = SymbolStartIndex; StringBuilder valueBuilder = new StringBuilder(); inWhitespace: while (currentIndex < length) { char c = Json[currentIndex]; int symbolClass = GetSymbolClass(c); // Possibly yield a text element, or choose a different tokenization mode if the symbol class changed. if (symbolClass != symbolClassWhitespace) { if (SymbolStartIndex < currentIndex) { yield return(GreenJsonWhitespaceSyntax.Create(currentIndex - SymbolStartIndex)); SymbolStartIndex = currentIndex; } if (symbolClass == symbolClassSymbol) { switch (c) { case JsonSpecialCharacter.CurlyOpenCharacter: yield return(GreenJsonCurlyOpenSyntax.Value); break; case JsonSpecialCharacter.CurlyCloseCharacter: yield return(GreenJsonCurlyCloseSyntax.Value); break; case JsonSpecialCharacter.SquareBracketOpenCharacter: yield return(GreenJsonSquareBracketOpenSyntax.Value); break; case JsonSpecialCharacter.SquareBracketCloseCharacter: yield return(GreenJsonSquareBracketCloseSyntax.Value); break; case JsonSpecialCharacter.ColonCharacter: yield return(GreenJsonColonSyntax.Value); break; case JsonSpecialCharacter.CommaCharacter: yield return(GreenJsonCommaSyntax.Value); break; case StringLiteral.QuoteCharacter: goto inString; case JsonSpecialCharacter.CommentStartFirstCharacter: // Look ahead 1 character to see if this is the start of a comment. // In all other cases, treat as an unexpected symbol. if (currentIndex + 1 < length) { char secondChar = Json[currentIndex + 1]; if (secondChar == JsonSpecialCharacter.SingleLineCommentStartSecondCharacter) { goto inSingleLineComment; } else if (secondChar == JsonSpecialCharacter.MultiLineCommentStartSecondCharacter) { goto inMultiLineComment; } } goto default; default: Report(JsonParseErrors.UnexpectedSymbol(c, currentIndex)); yield return(GreenJsonUnknownSymbolSyntax.Value); break; } // Increment to indicate the current character has been yielded. SymbolStartIndex++; } else { goto inValue; } } currentIndex++; } if (SymbolStartIndex < currentIndex) { yield return(GreenJsonWhitespaceSyntax.Create(currentIndex - SymbolStartIndex)); } yield break; inValue: // Eat the first symbol character, but leave SymbolStartIndex unchanged. currentIndex++; while (currentIndex < length) { char c = Json[currentIndex]; int symbolClass = GetSymbolClass(c); // Possibly yield a text element, or choose a different tokenization mode if the symbol class changed. if (symbolClass != symbolClassValueChar) { yield return(CreateValue(currentIndex)); SymbolStartIndex = currentIndex; if (symbolClass == symbolClassSymbol) { switch (c) { case JsonSpecialCharacter.CurlyOpenCharacter: yield return(GreenJsonCurlyOpenSyntax.Value); break; case JsonSpecialCharacter.CurlyCloseCharacter: yield return(GreenJsonCurlyCloseSyntax.Value); break; case JsonSpecialCharacter.SquareBracketOpenCharacter: yield return(GreenJsonSquareBracketOpenSyntax.Value); break; case JsonSpecialCharacter.SquareBracketCloseCharacter: yield return(GreenJsonSquareBracketCloseSyntax.Value); break; case JsonSpecialCharacter.ColonCharacter: yield return(GreenJsonColonSyntax.Value); break; case JsonSpecialCharacter.CommaCharacter: yield return(GreenJsonCommaSyntax.Value); break; case StringLiteral.QuoteCharacter: goto inString; case JsonSpecialCharacter.CommentStartFirstCharacter: // Look ahead 1 character to see if this is the start of a comment. // In all other cases, treat as an unexpected symbol. if (currentIndex + 1 < length) { char secondChar = Json[currentIndex + 1]; if (secondChar == JsonSpecialCharacter.SingleLineCommentStartSecondCharacter) { goto inSingleLineComment; } else if (secondChar == JsonSpecialCharacter.MultiLineCommentStartSecondCharacter) { goto inMultiLineComment; } } goto default; default: Report(JsonParseErrors.UnexpectedSymbol(c, currentIndex)); yield return(GreenJsonUnknownSymbolSyntax.Value); break; } // Increment to indicate the current character has been yielded. SymbolStartIndex++; } currentIndex++; goto inWhitespace; } currentIndex++; } if (SymbolStartIndex < currentIndex) { yield return(CreateValue(currentIndex)); } yield break; inString: // Detect errors. bool hasStringErrors = false; // Eat " character, but leave SymbolStartIndex unchanged. currentIndex++; // Prepare for use. valueBuilder.Clear(); while (currentIndex < length) { char c = Json[currentIndex]; switch (c) { case StringLiteral.QuoteCharacter: // Closing quote character. currentIndex++; if (hasStringErrors) { yield return(new GreenJsonErrorStringSyntax(currentIndex - SymbolStartIndex)); } else { yield return(new GreenJsonStringLiteralSyntax(valueBuilder.ToString(), currentIndex - SymbolStartIndex)); } SymbolStartIndex = currentIndex; goto inWhitespace; case StringLiteral.EscapeCharacter: // Escape sequence. // Look ahead one character. int escapeSequenceStart = currentIndex; currentIndex++; if (currentIndex < length) { char escapedChar = Json[currentIndex]; switch (escapedChar) { case StringLiteral.QuoteCharacter: case StringLiteral.EscapeCharacter: case '/': // Weird one, but it's in the specification. valueBuilder.Append(escapedChar); break; case 'b': valueBuilder.Append('\b'); break; case 'f': valueBuilder.Append('\f'); break; case 'n': valueBuilder.Append('\n'); break; case 'r': valueBuilder.Append('\r'); break; case 't': valueBuilder.Append('\t'); break; case 'v': valueBuilder.Append('\v'); break; case 'u': bool validUnicodeSequence = true; int unicodeValue = 0; // Expect exactly 4 hex characters. const int expectedHexLength = 4; for (int i = 0; i < expectedHexLength; i++) { currentIndex++; if (currentIndex < length) { // 1 hex character = 4 bits. unicodeValue <<= 4; char hexChar = Json[currentIndex]; if ('0' <= hexChar && hexChar <= '9') { unicodeValue = unicodeValue + hexChar - '0'; } else if ('a' <= hexChar && hexChar <= 'f') { const int aValue = 'a' - 10; unicodeValue = unicodeValue + hexChar - aValue; } else if ('A' <= hexChar && hexChar <= 'F') { const int aValue = 'A' - 10; unicodeValue = unicodeValue + hexChar - aValue; } else { currentIndex--; validUnicodeSequence = false; break; } } else { currentIndex--; validUnicodeSequence = false; break; } } if (validUnicodeSequence) { valueBuilder.Append(Convert.ToChar(unicodeValue)); } else { hasStringErrors = true; int escapeSequenceLength = currentIndex - escapeSequenceStart + 1; Report(JsonParseErrors.UnrecognizedEscapeSequence( Json.Substring(escapeSequenceStart, escapeSequenceLength), escapeSequenceStart, escapeSequenceLength)); } break; default: hasStringErrors = true; Report(JsonParseErrors.UnrecognizedEscapeSequence( Json.Substring(escapeSequenceStart, 2), escapeSequenceStart, 2)); break; } } break; default: if (StringLiteral.CharacterMustBeEscaped(c)) { // Generate user friendly representation of the illegal character in error message. hasStringErrors = true; Report(JsonParseErrors.IllegalControlCharacterInString(c, currentIndex)); } else { valueBuilder.Append(c); } break; } currentIndex++; } // Use length rather than currentIndex; currentIndex is bigger after a '\'. int unterminatedStringLength = length - SymbolStartIndex; Report(JsonParseErrors.UnterminatedString(SymbolStartIndex, unterminatedStringLength)); yield return(new GreenJsonErrorStringSyntax(unterminatedStringLength)); yield break; inSingleLineComment: // Eat both / characters, but leave SymbolStartIndex unchanged. currentIndex += 2; while (currentIndex < length) { char c = Json[currentIndex]; switch (c) { case '\r': // Can already eat this whitespace character. currentIndex++; // Look ahead to see if the next character is a linefeed. // Otherwise, the '\r' just becomes part of the comment. if (currentIndex < length) { char secondChar = Json[currentIndex]; if (secondChar == '\n') { yield return(GreenJsonCommentSyntax.Create(currentIndex - 1 - SymbolStartIndex)); // Eat the second whitespace character. SymbolStartIndex = currentIndex - 1; currentIndex++; goto inWhitespace; } } break; case '\n': yield return(GreenJsonCommentSyntax.Create(currentIndex - SymbolStartIndex)); // Eat the '\n'. SymbolStartIndex = currentIndex; currentIndex++; goto inWhitespace; } currentIndex++; } yield return(GreenJsonCommentSyntax.Create(currentIndex - SymbolStartIndex)); yield break; inMultiLineComment: // Eat /* characters, but leave SymbolStartIndex unchanged. currentIndex += 2; while (currentIndex < length) { if (Json[currentIndex] == '*') { // Look ahead to see if the next character is a slash. if (currentIndex + 1 < length) { char secondChar = Json[currentIndex + 1]; if (secondChar == '/') { // Increment so the closing '*/' is regarded as part of the comment. currentIndex += 2; yield return(GreenJsonCommentSyntax.Create(currentIndex - SymbolStartIndex)); SymbolStartIndex = currentIndex; goto inWhitespace; } } } currentIndex++; } int unterminatedCommentLength = length - SymbolStartIndex; Report(JsonParseErrors.UnterminatedMultiLineComment(SymbolStartIndex, unterminatedCommentLength)); yield return(new GreenJsonUnterminatedMultiLineCommentSyntax(unterminatedCommentLength)); }