Browse files

Created a stream like facility to read and parse JSON text.

  • Loading branch information...
1 parent 76894fa commit 3e3a0a5614103e99db84335a1dc756e055a09f8e @dragan dragan committed Aug 18, 2010
View
972 src/SineSignal.Ottoman.Specs/Serialization/JsonReaderSpecs/ReadingArraySpecs.cs
@@ -0,0 +1,972 @@
+using System;
+
+using NSubstitute;
+using NUnit.Framework;
+using SineSignal.Ottoman.Specs.Framework;
+
+using SineSignal.Ottoman.Serialization;
+
+namespace SineSignal.Ottoman.Specs.Serialization.JsonReaderSpecs
+{
+ public class ReadingArraySpecs
+ {
+ public class When_reading_a_json_string_that_contains_an_array_with_an_empty_string : ConcernFor<JsonReader>
+ {
+ private string input;
+
+ protected override void Given()
+ {
+ input = "[ \"\" ]";
+ }
+
+ public override JsonReader CreateSystemUnderTest()
+ {
+ return new JsonReader(input);
+ }
+
+ public class And_when_calling_read_twice : When_reading_a_json_string_that_contains_an_array_with_an_empty_string
+ {
+ protected override void When()
+ {
+ Sut.Read();
+ Sut.Read();
+ Sut.Close();
+ }
+
+ [Test]
+ public void Should_set_current_token_value_to_an_empty_string()
+ {
+ Assert.That(Sut.CurrentTokenValue, Is.EqualTo(String.Empty));
+ }
+ }
+ }
+
+ public class When_reading_a_json_string_that_contains_an_array_with_two_booleans : ConcernFor<JsonReader>
+ {
+ private string input;
+
+ protected override void Given()
+ {
+ input = "[ true, false ]";
+ }
+
+ public override JsonReader CreateSystemUnderTest()
+ {
+ return new JsonReader(input);
+ }
+
+ public class And_when_calling_read_once : When_reading_a_json_string_that_contains_an_array_with_two_booleans
+ {
+ protected override void When()
+ {
+ Sut.Read();
+ }
+
+ [Test]
+ public void Should_set_current_token_to_array_start()
+ {
+ Assert.That(Sut.CurrentToken, Is.EqualTo(JsonToken.ArrayStart));
+ }
+ }
+
+ public class And_when_calling_read_twice : When_reading_a_json_string_that_contains_an_array_with_two_booleans
+ {
+ protected override void When()
+ {
+ Sut.Read();
+ Sut.Read();
+ }
+
+ [Test]
+ public void Should_set_current_token_to_json_boolean()
+ {
+ Assert.That(Sut.CurrentToken, Is.EqualTo(JsonToken.Boolean));
+ }
+
+ [Test]
+ public void Should_set_current_token_value_to_true()
+ {
+ Assert.That(Sut.CurrentTokenValue, Is.True);
+ }
+ }
+
+ public class And_when_calling_read_three_times : When_reading_a_json_string_that_contains_an_array_with_two_booleans
+ {
+ protected override void When()
+ {
+ for (int index = 1; index <= 3; index++)
+ {
+ Sut.Read();
+ }
+ }
+
+ [Test]
+ public void Should_set_current_token_to_json_boolean()
+ {
+ Assert.That(Sut.CurrentToken, Is.EqualTo(JsonToken.Boolean));
+ }
+
+ [Test]
+ public void Should_set_current_token_value_to_false()
+ {
+ Assert.That(Sut.CurrentTokenValue, Is.False);
+ }
+ }
+
+ public class And_when_calling_read_four_times : When_reading_a_json_string_that_contains_an_array_with_two_booleans
+ {
+ protected override void When()
+ {
+ for (int index = 1; index <= 4; index++)
+ {
+ Sut.Read();
+ }
+ }
+
+ [Test]
+ public void Should_set_current_token_to_array_end()
+ {
+ Assert.That(Sut.CurrentToken, Is.EqualTo(JsonToken.ArrayEnd));
+ }
+ }
+
+ public class And_when_calling_read_five_times : When_reading_a_json_string_that_contains_an_array_with_two_booleans
+ {
+ protected override void When()
+ {
+ for (int index = 1; index <= 5; index++)
+ {
+ Sut.Read();
+ }
+ }
+
+ [Test]
+ public void Should_set_end_of_json_to_true()
+ {
+ Assert.That(Sut.EndOfJson, Is.True);
+ }
+ }
+ }
+
+ public class When_reading_a_json_string_that_contains_an_array_containing_four_strings : ConcernFor<JsonReader>
+ {
+ private string input;
+ private string stringValue3;
+ private string stringValue4;
+
+ protected override void Given()
+ {
+ input = "[ \"One\", \"Two\", \"abc 123 \\n\\f\\b\\t\\r \\\" \\\\ \\u263a \\u25CF\", \"\\\"Hello\\\" \\'world\\'\" ]";
+ stringValue3 = "abc 123 \n\f\b\t\r \" \\ \u263a \u25cf";
+ stringValue4 = "\"Hello\" 'world'";
+ }
+
+ public override JsonReader CreateSystemUnderTest()
+ {
+ return new JsonReader(input);
+ }
+
+ public class And_when_calling_read_once : When_reading_a_json_string_that_contains_an_array_containing_four_strings
+ {
+ protected override void When()
+ {
+ Sut.Read();
+ }
+
+ [Test]
+ public void Should_set_current_token_to_array_start()
+ {
+ Assert.That(Sut.CurrentToken, Is.EqualTo(JsonToken.ArrayStart));
+ }
+ }
+
+ public class And_when_calling_read_twice : When_reading_a_json_string_that_contains_an_array_containing_four_strings
+ {
+ protected override void When()
+ {
+ Sut.Read();
+ Sut.Read();
+ }
+
+ [Test]
+ public void Should_set_current_token_to_json_string()
+ {
+ Assert.That(Sut.CurrentToken, Is.EqualTo(JsonToken.String));
+ }
+
+ [Test]
+ public void Should_set_current_token_value_to_One()
+ {
+ Assert.That(Sut.CurrentTokenValue, Is.EqualTo("One"));
+ }
+ }
+
+ public class And_when_calling_read_three_times : When_reading_a_json_string_that_contains_an_array_containing_four_strings
+ {
+ protected override void When()
+ {
+ for (int index = 1; index <= 3; index++)
+ {
+ Sut.Read();
+ }
+ }
+
+ [Test]
+ public void Should_set_current_token_to_json_string()
+ {
+ Assert.That(Sut.CurrentToken, Is.EqualTo(JsonToken.String));
+ }
+
+ [Test]
+ public void Should_set_current_token_value_to_Two()
+ {
+ Assert.That(Sut.CurrentTokenValue, Is.EqualTo("Two"));
+ }
+ }
+
+ public class And_when_calling_read_four_times : When_reading_a_json_string_that_contains_an_array_containing_four_strings
+ {
+ protected override void When()
+ {
+ for (int index = 1; index <= 4; index++)
+ {
+ Sut.Read();
+ }
+ }
+
+ [Test]
+ public void Should_set_current_token_to_json_string()
+ {
+ Assert.That(Sut.CurrentToken, Is.EqualTo(JsonToken.String));
+ }
+
+ [Test]
+ public void Should_set_current_token_value_to_stringValue3()
+ {
+ Assert.That(Sut.CurrentTokenValue, Is.EqualTo(stringValue3));
+ }
+ }
+
+ public class And_when_calling_read_five_times : When_reading_a_json_string_that_contains_an_array_containing_four_strings
+ {
+ protected override void When()
+ {
+ for (int index = 1; index <= 5; index++)
+ {
+ Sut.Read();
+ }
+ }
+
+ [Test]
+ public void Should_set_current_token_to_json_string()
+ {
+ Assert.That(Sut.CurrentToken, Is.EqualTo(JsonToken.String));
+ }
+
+ [Test]
+ public void Should_set_current_token_value_to_stringValue4()
+ {
+ Assert.That(Sut.CurrentTokenValue, Is.EqualTo(stringValue4));
+ }
+ }
+
+ public class And_when_calling_read_six_times : When_reading_a_json_string_that_contains_an_array_containing_four_strings
+ {
+ protected override void When()
+ {
+ for (int index = 1; index <= 6; index++)
+ {
+ Sut.Read();
+ }
+ }
+
+ [Test]
+ public void Should_set_current_token_to_array_end()
+ {
+ Assert.That(Sut.CurrentToken, Is.EqualTo(JsonToken.ArrayEnd));
+ }
+ }
+
+ public class And_when_calling_read_seven_times : When_reading_a_json_string_that_contains_an_array_containing_four_strings
+ {
+ protected override void When()
+ {
+ for (int index = 1; index <= 7; index++)
+ {
+ Sut.Read();
+ }
+ }
+
+ [Test]
+ public void Should_set_end_of_json_to_true()
+ {
+ Assert.That(Sut.EndOfJson, Is.True);
+ }
+ }
+ }
+
+ public class When_reading_a_json_string_that_contains_an_array_of_ints : ConcernFor<JsonReader>
+ {
+ private string input;
+
+ protected override void Given()
+ {
+ input = @"[ -10, -5, -0, 0, 5, 10 ]";
+ }
+
+ public override JsonReader CreateSystemUnderTest()
+ {
+ return new JsonReader(input);
+ }
+
+ public class And_when_calling_read_twice : When_reading_a_json_string_that_contains_an_array_of_ints
+ {
+ protected override void When()
+ {
+ Sut.Read();
+ Sut.Read();
+ }
+
+ [Test]
+ public void Should_set_current_token_value_to_negative_10()
+ {
+ Assert.That((int)Sut.CurrentTokenValue, Is.EqualTo(-10));
+ }
+ }
+
+ public class And_when_calling_read_three_times : When_reading_a_json_string_that_contains_an_array_of_ints
+ {
+ protected override void When()
+ {
+ for (int index = 1; index <= 3; index++)
+ {
+ Sut.Read();
+ }
+ }
+
+ [Test]
+ public void Should_set_current_token_value_to_negative_5()
+ {
+ Assert.That((int)Sut.CurrentTokenValue, Is.EqualTo(-5));
+ }
+ }
+
+ public class And_when_calling_read_four_times : When_reading_a_json_string_that_contains_an_array_of_ints
+ {
+ protected override void When()
+ {
+ for (int index = 1; index <= 4; index++)
+ {
+ Sut.Read();
+ }
+ }
+
+ [Test]
+ public void Should_set_current_token_value_to_0()
+ {
+ Assert.That((int)Sut.CurrentTokenValue, Is.EqualTo(0));
+ }
+ }
+
+ public class And_when_calling_read_five_times : When_reading_a_json_string_that_contains_an_array_of_ints
+ {
+ protected override void When()
+ {
+ for (int index = 1; index <= 5; index++)
+ {
+ Sut.Read();
+ }
+ }
+
+ [Test]
+ public void Should_set_current_token_value_to_0()
+ {
+ Assert.That((int)Sut.CurrentTokenValue, Is.EqualTo(0));
+ }
+ }
+
+ public class And_when_calling_read_six_times : When_reading_a_json_string_that_contains_an_array_of_ints
+ {
+ protected override void When()
+ {
+ for (int index = 1; index <= 6; index++)
+ {
+ Sut.Read();
+ }
+ }
+
+ [Test]
+ public void Should_set_current_token_value_to_5()
+ {
+ Assert.That((int)Sut.CurrentTokenValue, Is.EqualTo(5));
+ }
+ }
+
+ public class And_when_calling_read_seven_times : When_reading_a_json_string_that_contains_an_array_of_ints
+ {
+ protected override void When()
+ {
+ for (int index = 1; index <= 7; index++)
+ {
+ Sut.Read();
+ }
+ }
+
+ [Test]
+ public void Should_set_current_token_value_to_10()
+ {
+ Assert.That((int)Sut.CurrentTokenValue, Is.EqualTo(10));
+ }
+ }
+ }
+
+ public class When_reading_a_json_string_that_contains_an_array_of_doubles : ConcernFor<JsonReader>
+ {
+ private string input;
+
+ protected override void Given()
+ {
+ input = @"[ -125.000009, 10.0, -10.0, 0.0, -0.0, 3.1415926536, 5e-4, 233e+5, 0.6e2, 2E-5 ]";
+ }
+
+ public override JsonReader CreateSystemUnderTest()
+ {
+ return new JsonReader(input);
+ }
+
+ public class And_when_calling_read_twice : When_reading_a_json_string_that_contains_an_array_of_doubles
+ {
+ protected override void When()
+ {
+ Sut.Read();
+ Sut.Read();
+ }
+
+ [Test]
+ public void Should_set_current_token_value_to_negative_125_dot_000009()
+ {
+ Assert.That((double)Sut.CurrentTokenValue, Is.EqualTo(-125.000009));
+ }
+ }
+
+ public class And_when_calling_read_three_times : When_reading_a_json_string_that_contains_an_array_of_doubles
+ {
+ protected override void When()
+ {
+ for (int index = 1; index <= 3; index++)
+ {
+ Sut.Read();
+ }
+ }
+
+ [Test]
+ public void Should_set_current_token_value_to_10_dot_0()
+ {
+ Assert.That((double)Sut.CurrentTokenValue, Is.EqualTo(10.0));
+ }
+ }
+
+ public class And_when_calling_read_four_times : When_reading_a_json_string_that_contains_an_array_of_doubles
+ {
+ protected override void When()
+ {
+ for (int index = 1; index <= 4; index++)
+ {
+ Sut.Read();
+ }
+ }
+
+ [Test]
+ public void Should_set_current_token_value_to_negative_10_dot_0()
+ {
+ Assert.That((double)Sut.CurrentTokenValue, Is.EqualTo(-10.0));
+ }
+ }
+
+ public class And_when_calling_read_five_times : When_reading_a_json_string_that_contains_an_array_of_doubles
+ {
+ protected override void When()
+ {
+ for (int index = 1; index <= 5; index++)
+ {
+ Sut.Read();
+ }
+ }
+
+ [Test]
+ public void Should_set_current_token_value_to_0_dot_0()
+ {
+ Assert.That((double)Sut.CurrentTokenValue, Is.EqualTo(0.0));
+ }
+ }
+
+ public class And_when_calling_read_six_times : When_reading_a_json_string_that_contains_an_array_of_doubles
+ {
+ protected override void When()
+ {
+ for (int index = 1; index <= 6; index++)
+ {
+ Sut.Read();
+ }
+ }
+
+ [Test]
+ public void Should_set_current_token_value_to_0_dot_0()
+ {
+ Assert.That((double)Sut.CurrentTokenValue, Is.EqualTo(0.0));
+ }
+ }
+
+ public class And_when_calling_read_seven_times : When_reading_a_json_string_that_contains_an_array_of_doubles
+ {
+ protected override void When()
+ {
+ for (int index = 1; index <= 7; index++)
+ {
+ Sut.Read();
+ }
+ }
+
+ [Test]
+ public void Should_set_current_token_value_to_3_dot_1415926536()
+ {
+ Assert.That((double)Sut.CurrentTokenValue, Is.EqualTo(3.1415926536));
+ }
+ }
+
+ public class And_when_calling_read_eight_times : When_reading_a_json_string_that_contains_an_array_of_doubles
+ {
+ protected override void When()
+ {
+ for (int index = 1; index <= 8; index++)
+ {
+ Sut.Read();
+ }
+ }
+
+ [Test]
+ public void Should_set_current_token_value_to_0_dot_0005()
+ {
+ Assert.That((double)Sut.CurrentTokenValue, Is.EqualTo(0.0005));
+ }
+ }
+
+ public class And_when_calling_read_nine_times : When_reading_a_json_string_that_contains_an_array_of_doubles
+ {
+ protected override void When()
+ {
+ for (int index = 1; index <= 9; index++)
+ {
+ Sut.Read();
+ }
+ }
+
+ [Test]
+ public void Should_set_current_token_value_to_23300000_dot_0()
+ {
+ Assert.That((double)Sut.CurrentTokenValue, Is.EqualTo(23300000.0));
+ }
+ }
+
+ public class And_when_calling_read_ten_times : When_reading_a_json_string_that_contains_an_array_of_doubles
+ {
+ protected override void When()
+ {
+ for (int index = 1; index <= 10; index++)
+ {
+ Sut.Read();
+ }
+ }
+
+ [Test]
+ public void Should_set_current_token_value_to_60_dot_0()
+ {
+ Assert.That((double)Sut.CurrentTokenValue, Is.EqualTo(60.0));
+ }
+ }
+
+ public class And_when_calling_read_eleven_times : When_reading_a_json_string_that_contains_an_array_of_doubles
+ {
+ protected override void When()
+ {
+ for (int index = 1; index <= 11; index++)
+ {
+ Sut.Read();
+ }
+ }
+
+ [Test]
+ public void Should_set_current_token_value_to_0_dot_00002()
+ {
+ Assert.That((double)Sut.CurrentTokenValue, Is.EqualTo(0.00002));
+ }
+ }
+ }
+
+ public class When_reading_a_json_string_that_contains_an_error_in_the_escape_sequence : ConcernFor<JsonReader>
+ {
+ private string input;
+ private JsonException expectedException;
+
+ protected override void Given()
+ {
+ input = "[ \"Hello World \\ufffg \" ]";
+ }
+
+ public override JsonReader CreateSystemUnderTest()
+ {
+ return new JsonReader(input);
+ }
+
+ protected override void When()
+ {
+ try
+ {
+ while (Sut.Read());
+ }
+ catch (JsonException e)
+ {
+ expectedException = e;
+ }
+ }
+
+ [Test]
+ public void Should_throw_a_json_exception()
+ {
+ Assert.That(expectedException, Is.TypeOf(typeof(JsonException)));
+ }
+
+ [Test]
+ public void Should_set_message_to_Invalid_character_g_in_input_string()
+ {
+ Assert.That(expectedException.Message, Is.EqualTo("Invalid character 'g' in input string"));
+ }
+ }
+
+ public class When_reading_a_json_string_that_contains_an_invalid_real_number : ConcernFor<JsonReader>
+ {
+ private string input;
+ private JsonException expectedException;
+
+ protected override void Given()
+ {
+ input = "[ 0.e5 ]";
+ }
+
+ public override JsonReader CreateSystemUnderTest()
+ {
+ return new JsonReader(input);
+ }
+
+ protected override void When()
+ {
+ try
+ {
+ while (Sut.Read());
+ }
+ catch (JsonException e)
+ {
+ expectedException = e;
+ }
+ }
+
+ [Test]
+ public void Should_throw_a_json_exception()
+ {
+ Assert.That(expectedException, Is.TypeOf(typeof(JsonException)));
+ }
+
+ [Test]
+ public void Should_set_message_to_Invalid_character_e_in_input_string()
+ {
+ Assert.That(expectedException.Message, Is.EqualTo("Invalid character 'e' in input string"));
+ }
+ }
+
+ public class When_reading_a_json_string_that_contains_an_invalid_boolean_value : ConcernFor<JsonReader>
+ {
+ private string input;
+ private JsonException expectedException;
+
+ protected override void Given()
+ {
+ input = "[ TRUE ]";
+ }
+
+ public override JsonReader CreateSystemUnderTest()
+ {
+ return new JsonReader(input);
+ }
+
+ protected override void When()
+ {
+ try
+ {
+ while (Sut.Read());
+ }
+ catch (JsonException e)
+ {
+ expectedException = e;
+ }
+ }
+
+ [Test]
+ public void Should_throw_a_json_exception()
+ {
+ Assert.That(expectedException, Is.TypeOf(typeof(JsonException)));
+ }
+
+ [Test]
+ public void Should_set_message_to_Invalid_character_T_in_input_string()
+ {
+ Assert.That(expectedException.Message, Is.EqualTo("Invalid character 'T' in input string"));
+ }
+ }
+
+ public class When_reading_a_json_string_that_contains_an_array_with_the_wrong_closing_token : ConcernFor<JsonReader>
+ {
+ private string input;
+ private JsonException expectedException;
+
+ protected override void Given()
+ {
+ input = "[ 1, 2, 3 }";
+ }
+
+ public override JsonReader CreateSystemUnderTest()
+ {
+ return new JsonReader(input);
+ }
+
+ protected override void When()
+ {
+ try
+ {
+ while (Sut.Read());
+ }
+ catch (JsonException e)
+ {
+ expectedException = e;
+ }
+ }
+
+ [Test]
+ public void Should_throw_a_json_exception()
+ {
+ Assert.That(expectedException, Is.TypeOf(typeof(JsonException)));
+ }
+
+ [Test]
+ public void Should_set_message_to_Invalid_token_125_in_input_string()
+ {
+ Assert.That(expectedException.Message, Is.EqualTo("Invalid token '125' in input string"));
+ }
+ }
+
+ public class When_reading_a_json_string_that_contains_an_array_with_no_closing_token : ConcernFor<JsonReader>
+ {
+ private string input;
+ private JsonException expectedException;
+
+ protected override void Given()
+ {
+ input = "[ \"name1\" ";
+ }
+
+ public override JsonReader CreateSystemUnderTest()
+ {
+ return new JsonReader(input);
+ }
+
+ protected override void When()
+ {
+ try
+ {
+ while (Sut.Read());
+ }
+ catch (JsonException e)
+ {
+ expectedException = e;
+ }
+ }
+
+ [Test]
+ public void Should_throw_a_json_exception()
+ {
+ Assert.That(expectedException, Is.TypeOf(typeof(JsonException)));
+ }
+
+ [Test]
+ public void Should_set_message_to_Input_does_not_evaluate_to_proper_JSON_text()
+ {
+ Assert.That(expectedException.Message, Is.EqualTo("Input doesn't evaluate to proper JSON text"));
+ }
+ }
+
+ public class When_reading_a_json_string_that_contains_no_array_or_object_tokens : ConcernFor<JsonReader>
+ {
+ private string input;
+ private JsonException expectedException;
+
+ protected override void Given()
+ {
+ input = " true ";
+ }
+
+ public override JsonReader CreateSystemUnderTest()
+ {
+ return new JsonReader(input);
+ }
+
+ protected override void When()
+ {
+ try
+ {
+ while (Sut.Read());
+ }
+ catch (JsonException e)
+ {
+ expectedException = e;
+ }
+ }
+
+ [Test]
+ public void Should_throw_a_json_exception()
+ {
+ Assert.That(expectedException, Is.TypeOf(typeof(JsonException)));
+ }
+
+ [Test]
+ public void Should_set_message_to_Invalid_token_True_in_input_string()
+ {
+ Assert.That(expectedException.Message, Is.EqualTo("Invalid token 'True' in input string"));
+ }
+ }
+
+ public class When_reading_a_json_string_with_nested_arrays : ConcernFor<JsonReader>
+ {
+ private string input;
+ private int arrayCount;
+
+ protected override void Given()
+ {
+ input = "[ [ [ [ [ 1, 2, 3 ] ] ] ] ]";
+ arrayCount = 0;
+ }
+
+ public override JsonReader CreateSystemUnderTest()
+ {
+ return new JsonReader(input);
+ }
+
+ protected override void When()
+ {
+ while (Sut.Read())
+ {
+ if (Sut.CurrentToken == JsonToken.ArrayStart)
+ {
+ arrayCount++;
+ }
+ }
+ }
+
+ [Test]
+ public void Should_count_five_arrays()
+ {
+ Assert.That(arrayCount, Is.EqualTo(5));
+ }
+ }
+
+ public class When_reading_a_json_string_that_contains_an_array_with_comments : ConcernFor<JsonReader>
+ {
+ private string input;
+ private JsonException expectedException;
+
+ protected override void Given()
+ {
+ input = @"
+ [
+ // This is a comment
+ 1,
+ 2,
+ 3
+ ]";
+ }
+
+ public override JsonReader CreateSystemUnderTest()
+ {
+ return new JsonReader(input);
+ }
+
+ public class And_when_comments_are_not_allowed : When_reading_a_json_string_that_contains_an_array_with_comments
+ {
+ protected override void When()
+ {
+ try
+ {
+ while (Sut.Read());
+ }
+ catch (JsonException e)
+ {
+ expectedException = e;
+ }
+ }
+
+ [Test]
+ public void Should_throw_a_json_exception()
+ {
+ Assert.That(expectedException, Is.TypeOf(typeof(JsonException)));
+ }
+
+ [Test]
+ public void Should_set_message_to_Invalid_character_forward_slash_in_input_string()
+ {
+ Assert.That(expectedException.Message, Is.EqualTo("Invalid character '/' in input string"));
+ }
+ }
+ }
+
+ public class When_reading_a_json_string_that_contains_an_array_with_a_string_in_single_quotes : ConcernFor<JsonReader>
+ {
+ private string input;
+ private JsonException expectedException;
+
+ protected override void Given()
+ {
+ input = "[ 'Single quotes' ]";
+ }
+
+ public override JsonReader CreateSystemUnderTest()
+ {
+ return new JsonReader(input);
+ }
+
+ public class And_when_single_quotes_are_not_allowed : When_reading_a_json_string_that_contains_an_array_with_a_string_in_single_quotes
+ {
+ protected override void When()
+ {
+ try
+ {
+ while (Sut.Read());
+ }
+ catch (JsonException e)
+ {
+ expectedException = e;
+ }
+ }
+
+ [Test]
+ public void Should_throw_a_json_exception()
+ {
+ Assert.That(expectedException, Is.TypeOf(typeof(JsonException)));
+ }
+
+ [Test]
+ public void Should_set_message_to_Invalid_character_single_quote_in_input_string()
+ {
+ Assert.That(expectedException.Message, Is.EqualTo("Invalid character ''' in input string"));
+ }
+ }
+ }
+ }
+}
View
407 src/SineSignal.Ottoman.Specs/Serialization/JsonReaderSpecs/ReadingObjectsSpecs.cs
@@ -0,0 +1,407 @@
+using System;
+
+using NSubstitute;
+using NUnit.Framework;
+using SineSignal.Ottoman.Specs.Framework;
+
+using SineSignal.Ottoman.Serialization;
+
+namespace SineSignal.Ottoman.Specs.Serialization.JsonReaderSpecs
+{
+ public class ReadingObjectsSpecs
+ {
+ public class When_reading_a_json_string_that_contains_an_object_with_a_single_member : ConcernFor<JsonReader>
+ {
+ private string input;
+
+ protected override void Given()
+ {
+ input = "{\"name1\":true}";
+ }
+
+ public override JsonReader CreateSystemUnderTest()
+ {
+ return new JsonReader(input);
+ }
+
+ public class And_when_calling_read_once : When_reading_a_json_string_that_contains_an_object_with_a_single_member
+ {
+ protected override void When()
+ {
+ Sut.Read();
+ }
+
+ [Test]
+ public void Should_set_current_token_to_object_start()
+ {
+ Assert.That(Sut.CurrentToken, Is.EqualTo(JsonToken.ObjectStart));
+ }
+ }
+
+ public class And_when_calling_read_twice : When_reading_a_json_string_that_contains_an_object_with_a_single_member
+ {
+ protected override void When()
+ {
+ Sut.Read();
+ Sut.Read();
+ }
+
+ [Test]
+ public void Should_set_current_token_to_member_name()
+ {
+ Assert.That(Sut.CurrentToken, Is.EqualTo(JsonToken.MemberName));
+ }
+
+ [Test]
+ public void Should_set_current_token_value_to_name1()
+ {
+ Assert.That(Sut.CurrentTokenValue, Is.EqualTo("name1"));
+ }
+ }
+
+ public class And_when_calling_read_three_times : When_reading_a_json_string_that_contains_an_object_with_a_single_member
+ {
+ protected override void When()
+ {
+ for (int index = 1; index <= 3; index++)
+ {
+ Sut.Read();
+ }
+ }
+
+ [Test]
+ public void Should_set_current_token_to_json_boolean()
+ {
+ Assert.That(Sut.CurrentToken, Is.EqualTo(JsonToken.Boolean));
+ }
+
+ [Test]
+ public void Should_set_current_token_value_to_true()
+ {
+ Assert.That(Sut.CurrentTokenValue, Is.True);
+ }
+ }
+
+ public class And_when_calling_read_four_times : When_reading_a_json_string_that_contains_an_object_with_a_single_member
+ {
+ protected override void When()
+ {
+ for (int index = 1; index <= 4; index++)
+ {
+ Sut.Read();
+ }
+ }
+
+ [Test]
+ public void Should_set_current_token_to_object_end()
+ {
+ Assert.That(Sut.CurrentToken, Is.EqualTo(JsonToken.ObjectEnd));
+ }
+ }
+
+ public class And_when_calling_read_five_times : When_reading_a_json_string_that_contains_an_object_with_a_single_member
+ {
+ protected override void When()
+ {
+ for (int index = 1; index <= 5; index++)
+ {
+ Sut.Read();
+ }
+ }
+
+ [Test]
+ public void Should_set_end_of_json_to_true()
+ {
+ Assert.That(Sut.EndOfJson, Is.True);
+ }
+ }
+ }
+
+ public class When_reading_a_json_string_that_contains_an_object_with_two_members_and_comments : ConcernFor<JsonReader>
+ {
+ private string input;
+
+ protected override void Given()
+ {
+ input = @"
+ {
+ // This is a single-line comment
+ ""name1"" : ""One"",
+
+ /**
+ * This is a multi-line another comment
+ **/
+ ""name2"": ""Two""
+ }";
+ }
+
+ public override JsonReader CreateSystemUnderTest()
+ {
+ var jsonReader = new JsonReader(input);
+ jsonReader.AllowComments = true;
+ return jsonReader;
+ }
+
+ public class And_when_calling_read_twice : When_reading_a_json_string_that_contains_an_object_with_two_members_and_comments
+ {
+ protected override void When()
+ {
+ Sut.Read();
+ Sut.Read();
+ }
+
+ [Test]
+ public void Should_set_current_token_to_member_name()
+ {
+ Assert.That(Sut.CurrentToken, Is.EqualTo(JsonToken.MemberName));
+ }
+
+ [Test]
+ public void Should_set_current_token_value_to_name1()
+ {
+ Assert.That(Sut.CurrentTokenValue, Is.EqualTo("name1"));
+ }
+ }
+
+ public class And_when_calling_read_three_times : When_reading_a_json_string_that_contains_an_object_with_two_members_and_comments
+ {
+ protected override void When()
+ {
+ for (int index = 1; index <= 3; index++)
+ {
+ Sut.Read();
+ }
+ }
+
+ [Test]
+ public void Should_set_current_token_to_json_string()
+ {
+ Assert.That(Sut.CurrentToken, Is.EqualTo(JsonToken.String));
+ }
+
+ [Test]
+ public void Should_set_current_token_value_to_One()
+ {
+ Assert.That(Sut.CurrentTokenValue, Is.EqualTo("One"));
+ }
+ }
+
+ public class And_when_calling_read_four_times : When_reading_a_json_string_that_contains_an_object_with_two_members_and_comments
+ {
+ protected override void When()
+ {
+ for (int index = 1; index <= 4; index++)
+ {
+ Sut.Read();
+ }
+ }
+
+ [Test]
+ public void Should_set_current_token_to_member_name()
+ {
+ Assert.That(Sut.CurrentToken, Is.EqualTo(JsonToken.MemberName));
+ }
+
+ [Test]
+ public void Should_set_current_token_value_to_name2()
+ {
+ Assert.That(Sut.CurrentTokenValue, Is.EqualTo("name2"));
+ }
+ }
+
+ public class And_when_calling_read_five_times : When_reading_a_json_string_that_contains_an_object_with_two_members_and_comments
+ {
+ protected override void When()
+ {
+ for (int index = 1; index <= 5; index++)
+ {
+ Sut.Read();
+ }
+ }
+
+ [Test]
+ public void Should_set_current_token_to_json_string()
+ {
+ Assert.That(Sut.CurrentToken, Is.EqualTo(JsonToken.String));
+ }
+
+ [Test]
+ public void Should_set_current_token_value_to_Two()
+ {
+ Assert.That(Sut.CurrentTokenValue, Is.EqualTo("Two"));
+ }
+ }
+
+ public class And_when_calling_read_seven_times : When_reading_a_json_string_that_contains_an_object_with_two_members_and_comments
+ {
+ protected override void When()
+ {
+ for (int index = 1; index <= 7; index++)
+ {
+ Sut.Read();
+ }
+ }
+
+ [Test]
+ public void Should_set_end_of_json_to_true()
+ {
+ Assert.That(Sut.EndOfJson, Is.True);
+ }
+ }
+ }
+
+ public class When_reading_a_json_string_with_nested_objects : ConcernFor<JsonReader>
+ {
+ private string input;
+ private int objectCount;
+
+ protected override void Given()
+ {
+ input = "{ \"name1\" : { \"name2\": { \"name3\": true } } }";
+ objectCount = 0;
+ }
+
+ public override JsonReader CreateSystemUnderTest()
+ {
+ return new JsonReader(input);
+ }
+
+ protected override void When()
+ {
+ while (Sut.Read())
+ {
+ if (Sut.CurrentToken == JsonToken.ObjectStart)
+ {
+ objectCount++;
+ }
+ }
+ }
+
+ [Test]
+ public void Should_count_three_objects()
+ {
+ Assert.That(objectCount, Is.EqualTo(3));
+ }
+ }
+
+ public class When_reading_a_json_string_that_contains_an_object_with_the_wrong_closing_token : ConcernFor<JsonReader>
+ {
+ private string input;
+ private JsonException expectedException;
+
+ protected override void Given()
+ {
+ input = "{ \"name1\" : [ \"name2\", \"name3\", \"name4\" ] ]";
+ }
+
+ public override JsonReader CreateSystemUnderTest()
+ {
+ return new JsonReader(input);
+ }
+
+ protected override void When()
+ {
+ try
+ {
+ while (Sut.Read());
+ }
+ catch (JsonException e)
+ {
+ expectedException = e;
+ }
+ }
+
+ [Test]
+ public void Should_throw_a_json_exception()
+ {
+ Assert.That(expectedException, Is.TypeOf(typeof(JsonException)));
+ }
+
+ [Test]
+ public void Should_set_message_to_Invalid_token_93_in_input_string()
+ {
+ Assert.That(expectedException.Message, Is.EqualTo("Invalid token '93' in input string"));
+ }
+ }
+
+ public class When_reading_a_json_string_that_contains_an_object_with_no_closing_token : ConcernFor<JsonReader>
+ {
+ private string input;
+ private JsonException expectedException;
+
+ protected override void Given()
+ {
+ input = "{ \"name1\" : true ";
+ }
+
+ public override JsonReader CreateSystemUnderTest()
+ {
+ return new JsonReader(input);
+ }
+
+ protected override void When()
+ {
+ try
+ {
+ while (Sut.Read());
+ }
+ catch (JsonException e)
+ {
+ expectedException = e;
+ }
+ }
+
+ [Test]
+ public void Should_throw_a_json_exception()
+ {
+ Assert.That(expectedException, Is.TypeOf(typeof(JsonException)));
+ }
+
+ [Test]
+ public void Should_set_message_to_Input_does_not_evaluate_to_proper_JSON_text()
+ {
+ Assert.That(expectedException.Message, Is.EqualTo("Input doesn't evaluate to proper JSON text"));
+ }
+ }
+
+ public class When_reading_a_json_string_that_contains_an_object_without_a_member_name : ConcernFor<JsonReader>
+ {
+ private string input;
+ private JsonException expectedException;
+
+ protected override void Given()
+ {
+ input = "{ {\"name1\": true} }";
+ }
+
+ public override JsonReader CreateSystemUnderTest()
+ {
+ return new JsonReader(input);
+ }
+
+ protected override void When()
+ {
+ try
+ {
+ while (Sut.Read());
+ }
+ catch (JsonException e)
+ {
+ expectedException = e;
+ }
+ }
+
+ [Test]
+ public void Should_throw_a_json_exception()
+ {
+ Assert.That(expectedException, Is.TypeOf(typeof(JsonException)));
+ }
+
+ [Test]
+ public void Should_set_message_to_Invalid_token_123_in_input_string()
+ {
+ Assert.That(expectedException.Message, Is.EqualTo("Invalid token '123' in input string"));
+ }
+ }
+ }
+}
View
3 src/SineSignal.Ottoman.Specs/SineSignal.Ottoman.Specs.csproj
@@ -40,6 +40,8 @@
<Compile Include="CouchDocumentSpecs.cs" />
<Compile Include="Http\RestClientSpecs.cs" />
<Compile Include="Serialization\JsonWriterSpecs.cs" />
+ <Compile Include="Serialization\JsonReaderSpecs\ReadingObjectsSpecs.cs" />
+ <Compile Include="Serialization\JsonReaderSpecs\ReadingArraySpecs.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\SineSignal.Ottoman\SineSignal.Ottoman.csproj">
@@ -65,5 +67,6 @@
<Folder Include="Framework\" />
<Folder Include="Http\" />
<Folder Include="Serialization\" />
+ <Folder Include="Serialization\JsonReaderSpecs\" />
</ItemGroup>
</Project>
View
30 src/SineSignal.Ottoman/Serialization/JsonException.cs
@@ -0,0 +1,30 @@
+using System;
+
+namespace SineSignal.Ottoman.Serialization
+{
+ public class JsonException : Exception
+ {
+ public JsonException() : base()
+ {
+ }
+
+ internal JsonException(int c) :
+ base(String.Format("Invalid character '{0}' in input string", (char)c))
+ {
+ }
+
+ internal JsonException(JsonParserToken token, Exception innerException) :
+ base (String.Format("Invalid token '{0}' in input string", token), innerException)
+ {
+ }
+
+ public JsonException(string message) : base (message)
+ {
+ }
+
+ public JsonException(string message, Exception innerException) :
+ base (message, innerException)
+ {
+ }
+ }
+}
View
919 src/SineSignal.Ottoman/Serialization/JsonParser.cs
@@ -0,0 +1,919 @@
+using System;
+using System.IO;
+using System.Text;
+
+namespace SineSignal.Ottoman.Serialization
+{
+ // This is loosely based off of LitJson's Lexer. I modified the naming convention and also updated
+ // it a little also to take advantage of newer language features. It will do for now, until I have
+ // more time to revisit and finish the task below.
+ // TODO: Refactor using the State pattern(http://www.dofactory.com/Patterns/PatternState.aspx). It will make this a lot more maintainable and testable.
+ internal sealed class JsonParser
+ {
+ private static int[] stateReturnTable;
+ private static Func<StateContext, bool>[] stateHandlerTable;
+
+ private StateContext Context { get; set; }
+ private int InputBuffer { get; set; }
+ private int InputCharacter { get; set; }
+ private TextReader Reader { get; set; }
+ private int State { get; set; }
+ private StringBuilder StringBuffer { get; set; }
+ private int UnicodeCharacter { get; set; }
+
+ public bool AllowComments { get; set; }
+ public bool AllowSingleQuotedStrings { get; set; }
+
+ public bool EndOfInput { get; private set; }
+ public int Token { get; private set; }
+ public string StringValue { get; private set; }
+
+ static JsonParser()
+ {
+ PopulateStateTables();
+ }
+
+ public JsonParser(TextReader reader)
+ {
+ InputBuffer = 0;
+ StringBuffer = new StringBuilder(128);
+ State = 1;
+
+ AllowComments = false;
+ AllowSingleQuotedStrings = false;
+ EndOfInput = false;
+
+ this.Reader = reader;
+
+ Context = new StateContext();
+ Context.Parser = this;
+ }
+
+ public bool NextToken()
+ {
+ Func<StateContext, bool> stateHandler;
+ Context.Return = false;
+
+ while (true)
+ {
+ stateHandler = stateHandlerTable[State - 1];
+
+ if (!stateHandler(Context))
+ throw new JsonException(InputCharacter);
+
+ if (EndOfInput)
+ return false;
+
+ if (Context.Return)
+ {
+ StringValue = StringBuffer.ToString();
+ StringBuffer.Remove(0, StringBuffer.Length);
+ Token = stateReturnTable[State - 1];
+
+ if (Token == (int)JsonParserToken.Char)
+ Token = InputCharacter;
+
+ State = Context.NextState;
+
+ return true;
+ }
+
+ State = Context.NextState;
+ }
+ }
+
+ private bool GetCharacter()
+ {
+ if ((InputCharacter = NextCharacter()) != -1)
+ return true;
+
+ EndOfInput = true;
+ return false;
+ }
+
+ private int NextCharacter()
+ {
+ if (InputBuffer != 0)
+ {
+ int tmp = InputBuffer;
+ InputBuffer = 0;
+
+ return tmp;
+ }
+
+ return Reader.Read();
+ }
+
+ private void PreviousCharacter()
+ {
+ InputBuffer = InputCharacter;
+ }
+
+ private static void PopulateStateTables()
+ {
+ stateHandlerTable = new Func<StateContext, bool>[28] {
+ State1,
+ State2,
+ State3,
+ State4,
+ State5,
+ State6,
+ State7,
+ State8,
+ State9,
+ State10,
+ State11,
+ State12,
+ State13,
+ State14,
+ State15,
+ State16,
+ State17,
+ State18,
+ State19,
+ State20,
+ State21,
+ State22,
+ State23,
+ State24,
+ State25,
+ State26,
+ State27,
+ State28
+ };
+
+ stateReturnTable = new int[28] {
+ (int)JsonParserToken.Char,
+ 0,
+ (int)JsonParserToken.Number,
+ (int)JsonParserToken.Number,
+ 0,
+ (int)JsonParserToken.Number,
+ 0,
+ (int)JsonParserToken.Number,
+ 0,
+ 0,
+ (int)JsonParserToken.True,
+ 0,
+ 0,
+ 0,
+ (int)JsonParserToken.False,
+ 0,
+ 0,
+ (int)JsonParserToken.Null,
+ (int)JsonParserToken.CharSeq,
+ (int)JsonParserToken.Char,
+ 0,
+ 0,
+ (int)JsonParserToken.CharSeq,
+ (int)JsonParserToken.Char,
+ 0,
+ 0,
+ 0,
+ 0
+ };
+ }
+
+ private static bool State1(StateContext context)
+ {
+ while (context.Parser.GetCharacter())
+ {
+ if (context.Parser.InputCharacter == ' ' ||
+ context.Parser.InputCharacter >= '\t' && context.Parser.InputCharacter <= '\r')
+ continue;
+
+ if (context.Parser.InputCharacter >= '1' && context.Parser.InputCharacter <= '9')
+ {
+ context.Parser.StringBuffer.Append((char)context.Parser.InputCharacter);
+ context.NextState = 3;
+ return true;
+ }
+
+ switch (context.Parser.InputCharacter)
+ {
+ case '"':
+ context.NextState = 19;
+ context.Return = true;
+ return true;
+
+ case ',':
+ case ':':
+ case '[':
+ case ']':
+ case '{':
+ case '}':
+ context.NextState = 1;
+ context.Return = true;
+ return true;
+
+ case '-':
+ context.Parser.StringBuffer.Append((char)context.Parser.InputCharacter);
+ context.NextState = 2;
+ return true;
+
+ case '0':
+ context.Parser.StringBuffer.Append((char)context.Parser.InputCharacter);
+ context.NextState = 4;
+ return true;
+
+ case 'f':
+ context.NextState = 12;
+ return true;
+
+ case 'n':
+ context.NextState = 16;
+ return true;
+
+ case 't':
+ context.NextState = 9;
+ return true;
+
+ case '\'':
+ if (!context.Parser.AllowSingleQuotedStrings)
+ return false;
+
+ context.Parser.InputCharacter = '"';
+ context.NextState = 23;
+ context.Return = true;
+ return true;
+
+ case '/':
+ if (!context.Parser.AllowComments)
+ return false;
+
+ context.NextState = 25;
+ return true;
+
+ default:
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private static bool State2(StateContext context)
+ {
+ context.Parser.GetCharacter();
+
+ if (context.Parser.InputCharacter >= '1' && context.Parser.InputCharacter<= '9')
+ {
+ context.Parser.StringBuffer.Append((char)context.Parser.InputCharacter);
+ context.NextState = 3;
+ return true;
+ }
+
+ switch (context.Parser.InputCharacter)
+ {
+ case '0':
+ context.Parser.StringBuffer.Append((char)context.Parser.InputCharacter);
+ context.NextState = 4;
+ return true;
+
+ default:
+ return false;
+ }
+ }
+
+ private static bool State3(StateContext context)
+ {
+ while (context.Parser.GetCharacter())
+ {
+ if (context.Parser.InputCharacter >= '0' && context.Parser.InputCharacter <= '9')
+ {
+ context.Parser.StringBuffer.Append((char)context.Parser.InputCharacter);
+ continue;
+ }
+
+ if (context.Parser.InputCharacter == ' ' ||
+ context.Parser.InputCharacter >= '\t' && context.Parser.InputCharacter <= '\r')
+ {
+ context.Return = true;
+ context.NextState = 1;
+ return true;
+ }
+
+ switch (context.Parser.InputCharacter)
+ {
+ case ',':
+ case ']':
+ case '}':
+ context.Parser.PreviousCharacter();
+ context.Return = true;
+ context.NextState = 1;
+ return true;
+
+ case '.':
+ context.Parser.StringBuffer.Append((char)context.Parser.InputCharacter);
+ context.NextState = 5;
+ return true;
+
+ case 'e':
+ case 'E':
+ context.Parser.StringBuffer.Append((char)context.Parser.InputCharacter);
+ context.NextState = 7;
+ return true;
+
+ default:
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private static bool State4(StateContext context)
+ {
+ context.Parser.GetCharacter();
+
+ if (context.Parser.InputCharacter == ' ' ||
+ context.Parser.InputCharacter >= '\t' && context.Parser.InputCharacter <= '\r')
+ {
+ context.Return = true;
+ context.NextState = 1;
+ return true;
+ }
+
+ switch (context.Parser.InputCharacter)
+ {
+ case ',':
+ case ']':
+ case '}':
+ context.Parser.PreviousCharacter();
+ context.Return = true;
+ context.NextState = 1;
+ return true;
+
+ case '.':
+ context.Parser.StringBuffer.Append((char)context.Parser.InputCharacter);
+ context.NextState = 5;
+ return true;
+
+ case 'e':
+ case 'E':
+ context.Parser.StringBuffer.Append((char)context.Parser.InputCharacter);
+ context.NextState = 7;
+ return true;
+
+ default:
+ return false;
+ }
+ }
+
+ private static bool State5(StateContext context)
+ {
+ context.Parser.GetCharacter();
+
+ if (context.Parser.InputCharacter >= '0' && context.Parser.InputCharacter <= '9')
+ {
+ context.Parser.StringBuffer.Append((char)context.Parser.InputCharacter);
+ context.NextState = 6;
+ return true;
+ }
+
+ return false;
+ }
+
+ private static bool State6(StateContext context)
+ {
+ while (context.Parser.GetCharacter())
+ {
+ if (context.Parser.InputCharacter >= '0' && context.Parser.InputCharacter <= '9')
+ {
+ context.Parser.StringBuffer.Append((char)context.Parser.InputCharacter);
+ continue;
+ }
+
+ if (context.Parser.InputCharacter == ' ' ||
+ context.Parser.InputCharacter >= '\t' && context.Parser.InputCharacter <= '\r')
+ {
+ context.Return = true;
+ context.NextState = 1;
+ return true;
+ }
+
+ switch (context.Parser.InputCharacter)
+ {
+ case ',':
+ case ']':
+ case '}':
+ context.Parser.PreviousCharacter();
+ context.Return = true;
+ context.NextState = 1;
+ return true;
+
+ case 'e':
+ case 'E':
+ context.Parser.StringBuffer.Append((char)context.Parser.InputCharacter);
+ context.NextState = 7;
+ return true;
+
+ default:
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private static bool State7(StateContext context)
+ {
+ context.Parser.GetCharacter();
+
+ if (context.Parser.InputCharacter >= '0' && context.Parser.InputCharacter<= '9')
+ {
+ context.Parser.StringBuffer.Append((char)context.Parser.InputCharacter);
+ context.NextState = 8;
+ return true;
+ }
+
+ switch (context.Parser.InputCharacter)
+ {
+ case '+':
+ case '-':
+ context.Parser.StringBuffer.Append((char)context.Parser.InputCharacter);
+ context.NextState = 8;
+ return true;
+
+ default:
+ return false;
+ }
+ }
+
+ private static bool State8(StateContext context)
+ {
+ while (context.Parser.GetCharacter())
+ {
+ if (context.Parser.InputCharacter >= '0' && context.Parser.InputCharacter<= '9')
+ {
+ context.Parser.StringBuffer.Append((char)context.Parser.InputCharacter);
+ continue;
+ }
+
+ if (context.Parser.InputCharacter == ' ' ||
+ context.Parser.InputCharacter >= '\t' && context.Parser.InputCharacter<= '\r')
+ {
+ context.Return = true;
+ context.NextState = 1;
+ return true;
+ }
+
+ switch (context.Parser.InputCharacter)
+ {
+ case ',':
+ case ']':
+ case '}':
+ context.Parser.PreviousCharacter();
+ context.Return = true;
+ context.NextState = 1;
+ return true;
+
+ default:
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private static bool State9(StateContext context)
+ {
+ context.Parser.GetCharacter();
+
+ switch (context.Parser.InputCharacter)
+ {
+ case 'r':
+ context.NextState = 10;
+ return true;
+
+ default:
+ return false;
+ }
+ }
+
+ private static bool State10(StateContext context)
+ {
+ context.Parser.GetCharacter();
+
+ switch (context.Parser.InputCharacter)
+ {
+ case 'u':
+ context.NextState = 11;
+ return true;
+
+ default:
+ return false;
+ }
+ }
+
+ private static bool State11(StateContext context)
+ {
+ context.Parser.GetCharacter();
+
+ switch (context.Parser.InputCharacter)
+ {
+ case 'e':
+ context.Return = true;
+ context.NextState = 1;
+ return true;
+
+ default:
+ return false;
+ }
+ }
+
+ private static bool State12(StateContext context)
+ {
+ context.Parser.GetCharacter();
+
+ switch (context.Parser.InputCharacter)
+ {
+ case 'a':
+ context.NextState = 13;
+ return true;
+
+ default:
+ return false;
+ }
+ }
+
+ private static bool State13(StateContext context)
+ {
+ context.Parser.GetCharacter();
+
+ switch (context.Parser.InputCharacter)
+ {
+ case 'l':
+ context.NextState = 14;
+ return true;
+
+ default:
+ return false;
+ }
+ }
+
+ private static bool State14(StateContext context)
+ {
+ context.Parser.GetCharacter();
+
+ switch (context.Parser.InputCharacter)
+ {
+ case 's':
+ context.NextState = 15;
+ return true;
+
+ default:
+ return false;
+ }
+ }
+
+ private static bool State15(StateContext context)
+ {
+ context.Parser.GetCharacter();
+
+ switch (context.Parser.InputCharacter)
+ {
+ case 'e':
+ context.Return = true;
+ context.NextState = 1;
+ return true;
+
+ default:
+ return false;
+ }
+ }
+
+ private static bool State16(StateContext context)
+ {
+ context.Parser.GetCharacter();
+
+ switch (context.Parser.InputCharacter)
+ {
+ case 'u':
+ context.NextState = 17;
+ return true;
+
+ default:
+ return false;
+ }
+ }
+
+ private static bool State17(StateContext context)
+ {
+ context.Parser.GetCharacter();
+
+ switch (context.Parser.InputCharacter)
+ {
+ case 'l':
+ context.NextState = 18;
+ return true;
+
+ default:
+ return false;
+ }
+ }
+
+ private static bool State18(StateContext context)
+ {
+ context.Parser.GetCharacter();
+
+ switch (context.Parser.InputCharacter)
+ {
+ case 'l':
+ context.Return = true;
+ context.NextState = 1;
+ return true;
+
+ default:
+ return false;
+ }
+ }
+
+ private static bool State19(StateContext context)
+ {
+ while (context.Parser.GetCharacter())
+ {
+ switch (context.Parser.InputCharacter)
+ {
+ case '"':
+ context.Parser.PreviousCharacter();
+ context.Return = true;
+ context.NextState = 20;
+ return true;
+
+ case '\\':
+ context.StateStack = 19;
+ context.NextState = 21;
+ return true;
+
+ default:
+ context.Parser.StringBuffer.Append((char)context.Parser.InputCharacter);
+ continue;
+ }
+ }
+
+ return true;
+ }
+
+ private static bool State20(StateContext context)
+ {
+ context.Parser.GetCharacter();
+
+ switch (context.Parser.InputCharacter)
+ {
+ case '"':
+ context.Return = true;
+ context.NextState = 1;
+ return true;
+
+ default:
+ return false;
+ }
+ }
+
+ private static bool State21(StateContext context)
+ {
+ context.Parser.GetCharacter();
+
+ switch (context.Parser.InputCharacter)
+ {
+ case 'u':
+ context.NextState = 22;
+ return true;
+
+ case '"':
+ case '\'':
+ case '/':
+ case '\\':
+ case 'b':
+ case 'f':
+ case 'n':
+ case 'r':
+ case 't':
+ context.Parser.StringBuffer.Append(ProcessEscChar(context.Parser.InputCharacter));
+ context.NextState = context.StateStack;
+ return true;
+
+ default:
+ return false;
+ }
+ }
+
+ private static bool State22(StateContext context)
+ {
+ int counter = 0;
+ int mult = 4096;
+
+ context.Parser.UnicodeCharacter = 0;
+
+ while (context.Parser.GetCharacter())
+ {
+ if (context.Parser.InputCharacter >= '0' && context.Parser.InputCharacter <= '9' ||
+ context.Parser.InputCharacter >= 'A' && context.Parser.InputCharacter <= 'F' ||
+ context.Parser.InputCharacter >= 'a' && context.Parser.InputCharacter <= 'f')
+ {
+
+ context.Parser.UnicodeCharacter += HexValue(context.Parser.InputCharacter) * mult;
+
+ counter++;
+ mult /= 16;
+
+ if (counter == 4)
+ {
+ context.Parser.StringBuffer.Append(Convert.ToChar(context.Parser.UnicodeCharacter));
+ context.NextState = context.StateStack;
+ return true;
+ }
+
+ continue;
+ }
+
+ return false;
+ }
+
+ return true;
+ }
+
+ private static bool State23(StateContext context)
+ {
+ while (context.Parser.GetCharacter())
+ {
+ switch (context.Parser.InputCharacter)
+ {
+ case '\'':
+ context.Parser.PreviousCharacter();
+ context.Return = true;
+ context.NextState = 24;
+ return true;
+
+ case '\\':
+ context.StateStack = 23;
+ context.NextState = 21;
+ return true;
+
+ default:
+ context.Parser.StringBuffer.Append((char) context.Parser.InputCharacter);
+ continue;
+ }
+ }
+
+ return true;
+ }
+
+ private static bool State24(StateContext context)
+ {
+ context.Parser.GetCharacter();
+
+ switch (context.Parser.InputCharacter)
+ {
+ case '\'':
+ context.Parser.InputCharacter = '"';
+ context.Return = true;
+ context.NextState = 1;
+ return true;
+
+ default:
+ return false;
+ }
+ }
+
+ private static bool State25(StateContext context)
+ {
+ context.Parser.GetCharacter();
+
+ switch (context.Parser.InputCharacter)
+ {
+ case '*':
+ context.NextState = 27;
+ return true;
+
+ case '/':
+ context.NextState = 26;
+ return true;
+
+ default:
+ return false;
+ }
+ }
+
+ private static bool State26(StateContext context)
+ {
+ while (context.Parser.GetCharacter())
+ {
+ if (context.Parser.InputCharacter == '\n')
+ {
+ context.NextState = 1;
+ return true;
+ }
+ }
+
+ return true;
+ }
+
+ private static bool State27(StateContext context)
+ {
+ while (context.Parser.GetCharacter())
+ {
+ if (context.Parser.InputCharacter == '*')
+ {
+ context.NextState = 28;
+ return true;
+ }
+ }
+
+ return true;
+ }
+
+ private static bool State28(StateContext context)
+ {
+ while (context.Parser.GetCharacter())
+ {
+ if (context.Parser.InputCharacter == '*')
+ continue;
+
+ if (context.Parser.InputCharacter == '/')
+ {
+ context.NextState = 1;
+ return true;
+ }
+
+ context.NextState = 27;
+ return true;
+ }
+
+ return true;
+ }
+
+ private static char ProcessEscChar(int esc_char)
+ {
+ switch (esc_char)
+ {
+ case '"':
+ case '\'':
+ case '\\':
+ case '/':
+ return Convert.ToChar(esc_char);
+
+ case 'n':
+ return '\n';
+
+ case 't':
+ return '\t';
+
+ case 'r':
+ return '\r';
+
+ case 'b':
+ return '\b';
+
+ case 'f':
+ return '\f';
+
+ default:
+ // Unreachable
+ return '?';
+ }
+ }
+
+ private static int HexValue(int digit)
+ {
+ switch (digit)
+ {
+ case 'a':
+ case 'A':
+ return 10;
+
+ case 'b':
+ case 'B':
+ return 11;
+
+ case 'c':
+ case 'C':
+ return 12;
+
+ case 'd':
+ case 'D':
+ return 13;
+
+ case 'e':
+ case 'E':
+ return 14;
+
+ case 'f':
+ case 'F':
+ return 15;
+
+ default:
+ return digit - '0';
+ }
+ }
+ }
+
+ internal class StateContext
+ {
+ public bool Return;
+ public int NextState;
+ public JsonParser Parser;
+ public int StateStack;
+ }
+}
View
33 src/SineSignal.Ottoman/Serialization/JsonParserToken.cs
@@ -0,0 +1,33 @@
+namespace SineSignal.Ottoman.Serialization
+{
+ internal enum JsonParserToken
+ {
+ // JsonLexer tokens
+ None = System.Char.MaxValue + 1,
+ Number,
+ True,
+ False,
+ Null,
+ CharSeq,
+ // Single char
+ Char,
+
+ // Parser Rules
+ Text,
+ Object,
+ ObjectPrime,
+ Pair,
+ PairRest,
+ Array,
+ ArrayPrime,
+ Value,
+ ValueRest,
+ String,
+
+ // End of input
+ End,
+
+ // The empty rule
+ Epsilon
+ }
+}
View
398 src/SineSignal.Ottoman/Serialization/JsonReader.cs
@@ -0,0 +1,398 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+
+namespace SineSignal.Ottoman.Serialization
+{
+ // This is loosely based off of LitJson's JsonReader. I modified the naming convention and also updated
+ // it a little also to take advantage of newer language features. It will do for now, until I have more
+ // time to revisit and finish the task below.
+ // TODO: Refactor when we refactor JsonParser
+ public sealed class JsonReader
+ {
+ private static IDictionary<int, IDictionary<int, int[]>> parseTable;
+
+ private Stack<int> AutomationStack { get; set; }
+ private int CurrentInput { get; set; }
+ private int CurrentSymbol { get; set; }
+ private JsonParser Parser { get; set; }
+ private bool ParserInString { get; set; }
+ private bool ParserReturn { get; set; }
+ private bool ReadStarted { get; set; }
+ private TextReader Reader { get; set; }
+ private bool ReaderIsOwned { get; set; }
+
+ public bool EndOfInput { get; private set; }
+ public bool EndOfJson { get; private set; }
+ public JsonToken CurrentToken { get; private set; }
+ public object CurrentTokenValue { get; private set; }
+
+ public bool AllowComments
+ {
+ get { return Parser.AllowComments; }
+ set { Parser.AllowComments = value; }
+ }
+
+ public bool AllowSingleQuotedStrings
+ {
+ get { return Parser.AllowSingleQuotedStrings; }
+ set { Parser.AllowSingleQuotedStrings = value; }
+ }
+
+ static JsonReader()
+ {
+ PopulateParseTable();
+ }
+
+ public JsonReader(string json) : this(new StringReader(json), true)
+ {
+ }
+
+ public JsonReader(TextReader reader) : this(reader, false)
+ {
+ }
+
+ public JsonReader(TextReader reader, bool owned)
+ {
+ if (reader == null)
+ throw new ArgumentNullException("reader");
+
+ ParserInString = false;
+ ParserReturn = false;
+
+ ReadStarted = false;
+ AutomationStack = new Stack<int>();
+ AutomationStack.Push((int)JsonParserToken.End);
+ AutomationStack.Push((int)JsonParserToken.Text);
+
+ Parser = new JsonParser(reader);
+
+ EndOfInput = false;
+ EndOfJson = false;
+
+ this.Reader = reader;
+ ReaderIsOwned = owned;
+ }
+
+ public bool Read()
+ {
+ if (EndOfInput)
+ return false;
+
+ if (EndOfJson)
+ {
+ EndOfJson = false;
+ AutomationStack.Clear();
+ AutomationStack.Push((int)JsonParserToken.End);
+ AutomationStack.Push((int)JsonParserToken.Text);
+ }
+
+ ParserInString = false;
+ ParserReturn = false;
+
+ CurrentToken = JsonToken.None;
+ CurrentTokenValue = null;
+
+ if (!ReadStarted)
+ {
+ ReadStarted = true;
+
+ if (!ReadToken())
+ return false;
+ }
+
+ int[] entry_symbols;
+
+ while (true)
+ {
+ if (ParserReturn)
+ {
+ if (AutomationStack.Peek() == (int)JsonParserToken.End)
+ EndOfJson = true;
+
+ return true;
+ }
+
+ CurrentSymbol = AutomationStack.Pop();
+
+ ProcessSymbol();
+
+ if (CurrentSymbol == CurrentInput)
+ {
+ if (!ReadToken())
+ {
+ if (AutomationStack.Peek() != (int)JsonParserToken.End)
+ throw new JsonException("Input doesn't evaluate to proper JSON text");
+
+ if (ParserReturn)
+ return true;
+
+ return false;
+ }
+
+ continue;
+ }
+
+ try
+ {
+ entry_symbols = parseTable[CurrentSymbol][CurrentInput];
+ }
+ catch(KeyNotFoundException e)
+ {
+ throw new JsonException((JsonParserToken)CurrentInput, e);
+ }
+
+ if (entry_symbols[0] == (int)JsonParserToken.Epsilon)
+ continue;
+
+ for (int index = entry_symbols.Length - 1; index >= 0; index--)
+ {
+ AutomationStack.Push(entry_symbols[index]);
+ }
+ }
+ }
+
+ public void Close()
+ {
+ if (EndOfInput)
+ return;
+
+ EndOfInput = true;
+ EndOfJson = true;
+
+ if (ReaderIsOwned)
+ Reader.Close();
+
+ Reader = null;
+ }
+
+ private bool ReadToken()
+ {
+ if (EndOfInput)
+ return false;
+
+ Parser.NextToken();
+
+ if (Parser.EndOfInput)
+ {
+ Close();
+
+ return false;
+ }
+
+ CurrentInput = Parser.Token;
+
+ return true;
+ }
+
+ private void ProcessSymbol()
+ {
+ if (CurrentSymbol == '[')
+ {
+ CurrentToken = JsonToken.ArrayStart;
+ ParserReturn = true;
+ }
+ else if (CurrentSymbol == ']')
+ {
+ CurrentToken = JsonToken.ArrayEnd;
+ ParserReturn = true;
+ }
+ else if (CurrentSymbol == '{')
+ {
+ CurrentToken = JsonToken.ObjectStart;
+ ParserReturn = true;
+ }
+ else if (CurrentSymbol == '}')
+ {
+ CurrentToken = JsonToken.ObjectEnd;
+ ParserReturn = true;
+ }
+ else if (CurrentSymbol == '"')
+ {
+ if (ParserInString)
+ {
+ ParserInString = false;
+ ParserReturn = true;
+ }
+ else
+ {
+ if (CurrentToken == JsonToken.None)
+ CurrentToken = JsonToken.String;
+
+ ParserInString = true;
+ }
+ }
+ else if (CurrentSymbol == (int)JsonParserToken.CharSeq)
+ {
+ CurrentTokenValue = Parser.StringValue;
+ }
+ else if (CurrentSymbol == (int)JsonParserToken.False)
+ {
+ CurrentToken = JsonToken.Boolean;
+ CurrentTokenValue = false;
+ ParserReturn = true;
+ }
+ else if (CurrentSymbol == (int)JsonParserToken.Null)
+ {
+ CurrentToken = JsonToken.Null;
+ ParserReturn = true;
+ }
+ else if (CurrentSymbol == (int)JsonParserToken.Number)
+ {
+ ProcessNumber(Parser.StringValue);
+ ParserReturn = true;
+ }
+ else if (CurrentSymbol == (int)JsonParserToken.Pair)
+ {
+ CurrentToken = JsonToken.MemberName;
+ }
+ else if (CurrentSymbol == (int)JsonParserToken.True)
+ {
+ CurrentToken = JsonToken.Boolean;
+ CurrentTokenValue = true;
+ ParserReturn = true;
+ }
+ }
+
+ private void ProcessNumber(string number)
+ {
+ if (number.IndexOf('.') != -1 ||
+ number.IndexOf('e') != -1 ||
+ number.IndexOf('E') != -1)
+ {
+ double n_double;
+ if (Double.TryParse(number, NumberStyles.Float, NumberFormatInfo.InvariantInfo, out n_double))