From 0a1371cfefec3204fb12473521a52c4ed3bb8d24 Mon Sep 17 00:00:00 2001 From: rstam Date: Wed, 6 Jul 2011 17:03:22 -0400 Subject: [PATCH] Implemented CSHARP-260. Modified JsonReader to support additional ways of representing regular expressions. --- Bson/IO/JsonReader.cs | 56 +++++++-- Bson/IO/JsonScanner.cs | 3 +- Bson/IO/JsonWriter.cs | 10 +- BsonUnitTests/BsonUnitTests.csproj | 1 + .../Serializers/BsonValueSerializerTests.cs | 4 +- BsonUnitTests/IO/BsonDocumentWriterTests.cs | 12 +- BsonUnitTests/IO/JsonReaderTests.cs | 8 +- BsonUnitTests/IO/JsonScannerTests.cs | 4 +- BsonUnitTests/IO/JsonWriterTests.cs | 10 +- BsonUnitTests/Jira/CSharp260Tests.cs | 114 ++++++++++++++++++ .../ObjectModel/BsonValueIConvertibleTests.cs | 2 +- 11 files changed, 185 insertions(+), 39 deletions(-) create mode 100644 BsonUnitTests/Jira/CSharp260Tests.cs diff --git a/Bson/IO/JsonReader.cs b/Bson/IO/JsonReader.cs index facccb213f..a511bae75f 100644 --- a/Bson/IO/JsonReader.cs +++ b/Bson/IO/JsonReader.cs @@ -221,6 +221,10 @@ namespace MongoDB.Bson.IO { currentBsonType = BsonType.ObjectId; currentValue = ParseObjectIdShell(); break; + case "RegExp": + currentBsonType = BsonType.RegularExpression; + currentValue = ParseRegularExpressionConstructor(); + break; case "new": currentBsonType = ParseNew(out currentValue); break; @@ -793,7 +797,7 @@ namespace MongoDB.Bson.IO { case "$maxkey": currentValue = ParseMaxKey(); return BsonType.MaxKey; case "$minkey": currentValue = ParseMinKey(); return BsonType.MinKey; case "$oid": currentValue = ParseObjectIdStrict(); return BsonType.ObjectId; - case "$regex": currentValue = ParseRegularExpression(); return BsonType.RegularExpression; + case "$regex": currentValue = ParseRegularExpressionStrict(); return BsonType.RegularExpression; case "$symbol": currentValue = ParseSymbol(); return BsonType.Symbol; case "$timestamp": currentValue = ParseTimestamp(); return BsonType.Timestamp; } @@ -840,6 +844,9 @@ namespace MongoDB.Bson.IO { case "ObjectId": value = ParseObjectIdShell(); return BsonType.ObjectId; + case "RegExp": + value = ParseRegularExpressionConstructor(); + return BsonType.RegularExpression; default: var message = string.Format("JSON reader expected a type name but found '{0}'.", typeToken.Lexeme); throw new FileFormatException(message); @@ -884,23 +891,52 @@ namespace MongoDB.Bson.IO { return BsonObjectId.Create(valueToken.StringValue); } - private BsonValue ParseRegularExpression() { - VerifyToken(":"); + private BsonValue ParseRegularExpressionConstructor() { + VerifyToken("("); var patternToken = PopToken(); if (patternToken.Type != JsonTokenType.String) { var message = string.Format("JSON reader expected a string but found '{0}'.", patternToken.Lexeme); throw new FileFormatException(message); } - VerifyToken(","); - VerifyString("$options"); + var options = ""; + var commaToken = PopToken(); + if (commaToken.Lexeme == ",") { + var optionsToken = PopToken(); + if (optionsToken.Type != JsonTokenType.String) { + var message = string.Format("JSON reader expected a string but found '{0}'.", optionsToken.Lexeme); + throw new FileFormatException(message); + } + options = optionsToken.StringValue; + } else { + PushToken(commaToken); + } + VerifyToken(")"); + return BsonRegularExpression.Create(patternToken.StringValue, options); + } + + private BsonValue ParseRegularExpressionStrict() { VerifyToken(":"); - var optionsToken = PopToken(); - if (optionsToken.Type != JsonTokenType.String) { - var message = string.Format("JSON reader expected a string but found '{0}'.", optionsToken.Lexeme); + var patternToken = PopToken(); + if (patternToken.Type != JsonTokenType.String) { + var message = string.Format("JSON reader expected a string but found '{0}'.", patternToken.Lexeme); throw new FileFormatException(message); } + var options = ""; + var commaToken = PopToken(); + if (commaToken.Lexeme == ",") { + VerifyString("$options"); + VerifyToken(":"); + var optionsToken = PopToken(); + if (optionsToken.Type != JsonTokenType.String) { + var message = string.Format("JSON reader expected a string but found '{0}'.", optionsToken.Lexeme); + throw new FileFormatException(message); + } + options = optionsToken.StringValue; + } else { + PushToken(commaToken); + } VerifyToken("}"); - return BsonRegularExpression.Create(patternToken.StringValue, optionsToken.StringValue); + return BsonRegularExpression.Create(patternToken.StringValue, options); } private BsonValue ParseSymbol() { @@ -954,7 +990,7 @@ namespace MongoDB.Bson.IO { string expectedString ) { var token = PopToken(); - if (token.Type != JsonTokenType.String || token.StringValue != expectedString) { + if ((token.Type != JsonTokenType.String && token.Type != JsonTokenType.UnquotedString) || token.StringValue != expectedString) { var message = string.Format("JSON reader expected '{0}' but found '{1}'.", expectedString, token.StringValue); throw new FileFormatException(message); } diff --git a/Bson/IO/JsonScanner.cs b/Bson/IO/JsonScanner.cs index 935d77612e..93f252edc0 100644 --- a/Bson/IO/JsonScanner.cs +++ b/Bson/IO/JsonScanner.cs @@ -326,9 +326,10 @@ namespace MongoDB.Bson.IO { break; case RegularExpressionState.InOptions: switch (c) { - case 'g': case 'i': case 'm': + case 'x': + case 's': state = RegularExpressionState.InOptions; break; case ',': diff --git a/Bson/IO/JsonWriter.cs b/Bson/IO/JsonWriter.cs index fd3f1c6cb3..9245c33555 100644 --- a/Bson/IO/JsonWriter.cs +++ b/Bson/IO/JsonWriter.cs @@ -440,15 +440,7 @@ namespace MongoDB.Bson.IO { var escaped = (pattern == "") ? "(?:)" : pattern.Replace(@"\", @"\\").Replace("/", @"\/"); textWriter.Write(escaped); textWriter.Write("/"); - foreach (char c in options.ToLower()) { - switch (c) { - case 'g': - case 'i': - case 'm': - textWriter.Write(c); - break; - } - } + textWriter.Write(options); break; } diff --git a/BsonUnitTests/BsonUnitTests.csproj b/BsonUnitTests/BsonUnitTests.csproj index 2c1dd8c604..ee0c56ca8a 100644 --- a/BsonUnitTests/BsonUnitTests.csproj +++ b/BsonUnitTests/BsonUnitTests.csproj @@ -123,6 +123,7 @@ + diff --git a/BsonUnitTests/DefaultSerializer/Serializers/BsonValueSerializerTests.cs b/BsonUnitTests/DefaultSerializer/Serializers/BsonValueSerializerTests.cs index 884a064fe8..e2fa836ebe 100644 --- a/BsonUnitTests/DefaultSerializer/Serializers/BsonValueSerializerTests.cs +++ b/BsonUnitTests/DefaultSerializer/Serializers/BsonValueSerializerTests.cs @@ -1062,9 +1062,9 @@ namespace MongoDB.BsonUnitTests.Serialization { [Test] public void TestWithOptions() { - var obj = new TestClass(new BsonRegularExpression("abc", "gim")); + var obj = new TestClass(new BsonRegularExpression("abc", "imxs")); var json = obj.ToJson(); - var expected = "{ 'B' : #, 'V' : # }".Replace("#", "/abc/gim").Replace("'", "\""); + var expected = "{ 'B' : #, 'V' : # }".Replace("#", "/abc/imxs").Replace("'", "\""); Assert.AreEqual(expected, json); var bson = obj.ToBson(); diff --git a/BsonUnitTests/IO/BsonDocumentWriterTests.cs b/BsonUnitTests/IO/BsonDocumentWriterTests.cs index e6e03e2ad5..c39bb34b55 100644 --- a/BsonUnitTests/IO/BsonDocumentWriterTests.cs +++ b/BsonUnitTests/IO/BsonDocumentWriterTests.cs @@ -1040,12 +1040,12 @@ namespace MongoDB.BsonUnitTests.IO { var document = new BsonDocument(); using (var writer = BsonWriter.Create(document)) { writer.WriteStartDocument(); - writer.WriteRegularExpression("a", "p", "g"); - writer.WriteRegularExpression("b", "q", "i"); + writer.WriteRegularExpression("a", "p", "i"); + writer.WriteRegularExpression("b", "q", "m"); writer.WriteEndDocument(); } var json = document.ToJson(); - var expected = "{ 'a' : /p/g, 'b' : /q/i }".Replace("'", "\""); ; + var expected = "{ 'a' : /p/i, 'b' : /q/m }".Replace("'", "\""); ; Assert.AreEqual(expected, json); } @@ -1055,13 +1055,13 @@ namespace MongoDB.BsonUnitTests.IO { using (var writer = BsonWriter.Create(document)) { writer.WriteStartDocument(); writer.WriteStartDocument("nested"); - writer.WriteRegularExpression("a", "p", "g"); - writer.WriteRegularExpression("b", "q", "i"); + writer.WriteRegularExpression("a", "p", "i"); + writer.WriteRegularExpression("b", "q", "m"); writer.WriteEndDocument(); writer.WriteEndDocument(); } var json = document.ToJson(); - var expected = "{ 'nested' : { 'a' : /p/g, 'b' : /q/i } }".Replace("'", "\""); + var expected = "{ 'nested' : { 'a' : /p/i, 'b' : /q/m } }".Replace("'", "\""); Assert.AreEqual(expected, json); } diff --git a/BsonUnitTests/IO/JsonReaderTests.cs b/BsonUnitTests/IO/JsonReaderTests.cs index 09e788b035..4ec3c29a2d 100644 --- a/BsonUnitTests/IO/JsonReaderTests.cs +++ b/BsonUnitTests/IO/JsonReaderTests.cs @@ -497,13 +497,13 @@ namespace MongoDB.BsonUnitTests.IO { [Test] public void TestRegularExpressionShell() { - var json = "/pattern/gim"; + var json = "/pattern/imxs"; using (bsonReader = BsonReader.Create(json)) { Assert.AreEqual(BsonType.RegularExpression, bsonReader.ReadBsonType()); string pattern, options; bsonReader.ReadRegularExpression(out pattern, out options); Assert.AreEqual("pattern", pattern); - Assert.AreEqual("gim", options); + Assert.AreEqual("imxs", options); Assert.AreEqual(BsonReaderState.Done, bsonReader.State); } Assert.AreEqual(json, BsonSerializer.Deserialize(new StringReader(json)).ToJson()); @@ -511,13 +511,13 @@ namespace MongoDB.BsonUnitTests.IO { [Test] public void TestRegularExpressionStrict() { - var json = "{ \"$regex\" : \"pattern\", \"$options\" : \"gim\" }"; + var json = "{ \"$regex\" : \"pattern\", \"$options\" : \"imxs\" }"; using (bsonReader = BsonReader.Create(json)) { Assert.AreEqual(BsonType.RegularExpression, bsonReader.ReadBsonType()); string pattern, options; bsonReader.ReadRegularExpression(out pattern, out options); Assert.AreEqual("pattern", pattern); - Assert.AreEqual("gim", options); + Assert.AreEqual("imxs", options); Assert.AreEqual(BsonReaderState.Done, bsonReader.State); } var settings = new JsonWriterSettings { OutputMode = JsonOutputMode.Strict }; diff --git a/BsonUnitTests/IO/JsonScannerTests.cs b/BsonUnitTests/IO/JsonScannerTests.cs index a749bfa8f5..949ae89a11 100644 --- a/BsonUnitTests/IO/JsonScannerTests.cs +++ b/BsonUnitTests/IO/JsonScannerTests.cs @@ -398,11 +398,11 @@ namespace MongoDB.BsonUnitTests.IO { [Test] public void TestRegularExpressionPatternAndOptions() { - var json = "\t /pattern/gim,"; + var json = "\t /pattern/imxs,"; var buffer = new JsonBuffer(json); var token = JsonScanner.GetNextToken(buffer); Assert.AreEqual(JsonTokenType.RegularExpression, token.Type); - Assert.AreEqual("/pattern/gim", token.Lexeme); + Assert.AreEqual("/pattern/imxs", token.Lexeme); Assert.AreEqual(',', buffer.Read()); } } diff --git a/BsonUnitTests/IO/JsonWriterTests.cs b/BsonUnitTests/IO/JsonWriterTests.cs index 68b31a5c8c..aebd28664a 100644 --- a/BsonUnitTests/IO/JsonWriterTests.cs +++ b/BsonUnitTests/IO/JsonWriterTests.cs @@ -370,10 +370,11 @@ namespace MongoDB.BsonUnitTests.IO { new TestData(BsonRegularExpression.Create("a"), "/a/"), new TestData(BsonRegularExpression.Create("a/b"), "/a\\/b/"), new TestData(BsonRegularExpression.Create("a\\b"), "/a\\\\b/"), - new TestData(BsonRegularExpression.Create("a", "g"), "/a/g"), new TestData(BsonRegularExpression.Create("a", "i"), "/a/i"), new TestData(BsonRegularExpression.Create("a", "m"), "/a/m"), - new TestData(BsonRegularExpression.Create("a", "gim"), "/a/gim"), + new TestData(BsonRegularExpression.Create("a", "x"), "/a/x"), + new TestData(BsonRegularExpression.Create("a", "s"), "/a/s"), + new TestData(BsonRegularExpression.Create("a", "imxs"), "/a/imxs"), }; foreach (var test in tests) { var json = test.Value.ToJson(); @@ -390,10 +391,11 @@ namespace MongoDB.BsonUnitTests.IO { new TestData(BsonRegularExpression.Create("a"), "{ \"$regex\" : \"a\", \"$options\" : \"\" }"), new TestData(BsonRegularExpression.Create("a/b"), "{ \"$regex\" : \"a/b\", \"$options\" : \"\" }"), new TestData(BsonRegularExpression.Create("a\\b"), "{ \"$regex\" : \"a\\\\b\", \"$options\" : \"\" }"), - new TestData(BsonRegularExpression.Create("a", "g"), "{ \"$regex\" : \"a\", \"$options\" : \"g\" }"), new TestData(BsonRegularExpression.Create("a", "i"), "{ \"$regex\" : \"a\", \"$options\" : \"i\" }"), new TestData(BsonRegularExpression.Create("a", "m"), "{ \"$regex\" : \"a\", \"$options\" : \"m\" }"), - new TestData(BsonRegularExpression.Create("a", "gim"), "{ \"$regex\" : \"a\", \"$options\" : \"gim\" }"), + new TestData(BsonRegularExpression.Create("a", "x"), "{ \"$regex\" : \"a\", \"$options\" : \"x\" }"), + new TestData(BsonRegularExpression.Create("a", "s"), "{ \"$regex\" : \"a\", \"$options\" : \"s\" }"), + new TestData(BsonRegularExpression.Create("a", "imxs"), "{ \"$regex\" : \"a\", \"$options\" : \"imxs\" }"), }; var jsonSettings = new JsonWriterSettings { OutputMode = JsonOutputMode.Strict }; foreach (var test in tests) { diff --git a/BsonUnitTests/Jira/CSharp260Tests.cs b/BsonUnitTests/Jira/CSharp260Tests.cs new file mode 100644 index 0000000000..081c25b022 --- /dev/null +++ b/BsonUnitTests/Jira/CSharp260Tests.cs @@ -0,0 +1,114 @@ +/* Copyright 2010-2011 10gen Inc. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.IO; +using System.Linq; +using System.Runtime.Serialization; +using System.Text; +using NUnit.Framework; + +using MongoDB.Bson; +using MongoDB.Bson.IO; +using MongoDB.Bson.Serialization; +using MongoDB.Bson.Serialization.Attributes; + +namespace MongoDB.BsonUnitTests.Jira.CSharp260 { + [TestFixture] + public class CSharp260Tests { + [Test] + public void TestConstantPattern() { + var json = "{ rx : /abc/ }"; + var document = BsonDocument.Parse(json); + Assert.AreEqual(BsonType.RegularExpression, document["rx"].BsonType); + var rx = document["rx"].AsBsonRegularExpression; + Assert.AreEqual("abc", rx.Pattern); + Assert.AreEqual("", rx.Options); + } + + [Test] + public void TestConstantPatternWithOptions() { + var json = "{ rx : /abc/imxs }"; + var document = BsonDocument.Parse(json); + Assert.AreEqual(BsonType.RegularExpression, document["rx"].BsonType); + var rx = document["rx"].AsBsonRegularExpression; + Assert.AreEqual("abc", rx.Pattern); + Assert.AreEqual("imxs", rx.Options); + } + + [Test] + public void TestNewRegExpPattern() { + var json = "{ rx : new RegExp('abc') }"; + var document = BsonDocument.Parse(json); + Assert.AreEqual(BsonType.RegularExpression, document["rx"].BsonType); + var rx = document["rx"].AsBsonRegularExpression; + Assert.AreEqual("abc", rx.Pattern); + Assert.AreEqual("", rx.Options); + } + + [Test] + public void TestNewRegExpPatternWithOptions() { + var json = "{ rx : new RegExp('abc', 'imxs') }"; + var document = BsonDocument.Parse(json); + Assert.AreEqual(BsonType.RegularExpression, document["rx"].BsonType); + var rx = document["rx"].AsBsonRegularExpression; + Assert.AreEqual("abc", rx.Pattern); + Assert.AreEqual("imxs", rx.Options); + } + + [Test] + public void TestRegExpPattern() { + var json = "{ rx : RegExp('abc') }"; + var document = BsonDocument.Parse(json); + Assert.AreEqual(BsonType.RegularExpression, document["rx"].BsonType); + var rx = document["rx"].AsBsonRegularExpression; + Assert.AreEqual("abc", rx.Pattern); + Assert.AreEqual("", rx.Options); + } + + [Test] + public void TestRegExpPatternWithOptions() { + var json = "{ rx : RegExp('abc', 'imxs') }"; + var document = BsonDocument.Parse(json); + Assert.AreEqual(BsonType.RegularExpression, document["rx"].BsonType); + var rx = document["rx"].AsBsonRegularExpression; + Assert.AreEqual("abc", rx.Pattern); + Assert.AreEqual("imxs", rx.Options); + } + + [Test] + public void TestStrictPattern() { + var json = "{ rx : { $regex : 'abc' } }"; + var document = BsonDocument.Parse(json); + Assert.AreEqual(BsonType.RegularExpression, document["rx"].BsonType); + var rx = document["rx"].AsBsonRegularExpression; + Assert.AreEqual("abc", rx.Pattern); + Assert.AreEqual("", rx.Options); + } + + [Test] + public void TestStrictPatternWithOptions() { + var json = "{ rx : { $regex : 'abc', $options : 'imxs' } }"; + var document = BsonDocument.Parse(json); + Assert.AreEqual(BsonType.RegularExpression, document["rx"].BsonType); + var rx = document["rx"].AsBsonRegularExpression; + Assert.AreEqual("abc", rx.Pattern); + Assert.AreEqual("imxs", rx.Options); + } + } +} diff --git a/BsonUnitTests/ObjectModel/BsonValueIConvertibleTests.cs b/BsonUnitTests/ObjectModel/BsonValueIConvertibleTests.cs index 89caa5c844..6ab4d54ccc 100644 --- a/BsonUnitTests/ObjectModel/BsonValueIConvertibleTests.cs +++ b/BsonUnitTests/ObjectModel/BsonValueIConvertibleTests.cs @@ -310,7 +310,7 @@ namespace MongoDB.BsonUnitTests { [Test] public void TestBsonRegularExpression() { - var value = BsonRegularExpression.Create("pattern", "gim"); + var value = BsonRegularExpression.Create("pattern", "imxs"); Assert.Throws(() => Convert.ToBoolean(value)); Assert.Throws(() => Convert.ToByte(value)); Assert.Throws(() => Convert.ToChar(value));