BsonRegularExpression.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. /* Copyright 2010-present MongoDB Inc.
  2. *
  3. * Licensed under the Apache License, Version 2.0 (the "License");
  4. * you may not use this file except in compliance with the License.
  5. * You may obtain a copy of the License at
  6. *
  7. * http://www.apache.org/licenses/LICENSE-2.0
  8. *
  9. * Unless required by applicable law or agreed to in writing, software
  10. * distributed under the License is distributed on an "AS IS" BASIS,
  11. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. * See the License for the specific language governing permissions and
  13. * limitations under the License.
  14. */
  15. using System;
  16. using System.Text.RegularExpressions;
  17. namespace MongoDB.Bson
  18. {
  19. /// <summary>
  20. /// Represents a BSON regular expression value.
  21. /// </summary>
  22. [Serializable]
  23. public class BsonRegularExpression : BsonValue, IComparable<BsonRegularExpression>, IEquatable<BsonRegularExpression>
  24. {
  25. // private fields
  26. private readonly string _pattern;
  27. private readonly string _options;
  28. // constructors
  29. /// <summary>
  30. /// Initializes a new instance of the BsonRegularExpression class.
  31. /// </summary>
  32. /// <param name="pattern">A regular expression pattern.</param>
  33. public BsonRegularExpression(string pattern)
  34. {
  35. if (pattern == null)
  36. {
  37. throw new ArgumentNullException("pattern");
  38. }
  39. if (pattern.Length > 0 && pattern[0] == '/')
  40. {
  41. var index = pattern.LastIndexOf('/');
  42. var escaped = pattern.Substring(1, index - 1);
  43. var unescaped = (escaped == "(?:)") ? "" : escaped.Replace("\\/", "/");
  44. _pattern = unescaped;
  45. _options = SortOptionsIfNecessary(pattern.Substring(index + 1));
  46. }
  47. else
  48. {
  49. _pattern = pattern;
  50. _options = "";
  51. }
  52. }
  53. /// <summary>
  54. /// Initializes a new instance of the BsonRegularExpression class.
  55. /// </summary>
  56. /// <param name="pattern">A regular expression pattern.</param>
  57. /// <param name="options">Regular expression options.</param>
  58. public BsonRegularExpression(string pattern, string options)
  59. {
  60. if (pattern == null)
  61. {
  62. throw new ArgumentNullException("pattern");
  63. }
  64. _pattern = pattern;
  65. _options = SortOptionsIfNecessary(options ?? "");
  66. }
  67. /// <summary>
  68. /// Initializes a new instance of the BsonRegularExpression class.
  69. /// </summary>
  70. /// <param name="regex">A Regex.</param>
  71. public BsonRegularExpression(Regex regex)
  72. {
  73. if (regex == null)
  74. {
  75. throw new ArgumentNullException("regex");
  76. }
  77. _pattern = regex.ToString();
  78. _options = "";
  79. if ((regex.Options & RegexOptions.IgnoreCase) != 0)
  80. {
  81. _options += "i";
  82. }
  83. if ((regex.Options & RegexOptions.Multiline) != 0)
  84. {
  85. _options += "m";
  86. }
  87. if ((regex.Options & RegexOptions.Singleline) != 0)
  88. {
  89. _options += "s";
  90. }
  91. if ((regex.Options & RegexOptions.IgnorePatternWhitespace) != 0)
  92. {
  93. _options += "x";
  94. }
  95. }
  96. // public properties
  97. /// <summary>
  98. /// Gets the BsonType of this BsonValue.
  99. /// </summary>
  100. public override BsonType BsonType
  101. {
  102. get { return BsonType.RegularExpression; }
  103. }
  104. /// <summary>
  105. /// Gets the regular expression pattern.
  106. /// </summary>
  107. public string Pattern
  108. {
  109. get { return _pattern; }
  110. }
  111. /// <summary>
  112. /// Gets the regular expression options.
  113. /// </summary>
  114. public string Options
  115. {
  116. get { return _options; }
  117. }
  118. // public operators
  119. /// <summary>
  120. /// Converts a Regex to a BsonRegularExpression.
  121. /// </summary>
  122. /// <param name="value">A Regex.</param>
  123. /// <returns>A BsonRegularExpression.</returns>
  124. public static implicit operator BsonRegularExpression(Regex value)
  125. {
  126. return new BsonRegularExpression(value);
  127. }
  128. /// <summary>
  129. /// Converts a string to a BsonRegularExpression.
  130. /// </summary>
  131. /// <param name="value">A string.</param>
  132. /// <returns>A BsonRegularExpression.</returns>
  133. public static implicit operator BsonRegularExpression(string value)
  134. {
  135. return new BsonRegularExpression(value);
  136. }
  137. /// <summary>
  138. /// Compares two BsonRegularExpression values.
  139. /// </summary>
  140. /// <param name="lhs">The first BsonRegularExpression.</param>
  141. /// <param name="rhs">The other BsonRegularExpression.</param>
  142. /// <returns>True if the two BsonRegularExpression values are not equal according to ==.</returns>
  143. public static bool operator !=(BsonRegularExpression lhs, BsonRegularExpression rhs)
  144. {
  145. return !(lhs == rhs);
  146. }
  147. /// <summary>
  148. /// Compares two BsonRegularExpression values.
  149. /// </summary>
  150. /// <param name="lhs">The first BsonRegularExpression.</param>
  151. /// <param name="rhs">The other BsonRegularExpression.</param>
  152. /// <returns>True if the two BsonRegularExpression values are equal according to ==.</returns>
  153. public static bool operator ==(BsonRegularExpression lhs, BsonRegularExpression rhs)
  154. {
  155. if (object.ReferenceEquals(lhs, null)) { return object.ReferenceEquals(rhs, null); }
  156. return lhs.Equals(rhs);
  157. }
  158. // public methods
  159. /// <summary>
  160. /// Creates a new BsonRegularExpression.
  161. /// </summary>
  162. /// <param name="value">An object to be mapped to a BsonRegularExpression.</param>
  163. /// <returns>A BsonRegularExpression or null.</returns>
  164. public new static BsonRegularExpression Create(object value)
  165. {
  166. if (value == null)
  167. {
  168. throw new ArgumentNullException("value");
  169. }
  170. return (BsonRegularExpression)BsonTypeMapper.MapToBsonValue(value, BsonType.RegularExpression);
  171. }
  172. // public methods
  173. /// <summary>
  174. /// Compares this BsonRegularExpression to another BsonRegularExpression.
  175. /// </summary>
  176. /// <param name="other">The other BsonRegularExpression.</param>
  177. /// <returns>A 32-bit signed integer that indicates whether this BsonRegularExpression is less than, equal to, or greather than the other.</returns>
  178. public int CompareTo(BsonRegularExpression other)
  179. {
  180. if (other == null) { return 1; }
  181. int r = _pattern.CompareTo(other._pattern);
  182. if (r != 0) { return r; }
  183. return _options.CompareTo(other._options);
  184. }
  185. /// <summary>
  186. /// Compares the BsonRegularExpression to another BsonValue.
  187. /// </summary>
  188. /// <param name="other">The other BsonValue.</param>
  189. /// <returns>A 32-bit signed integer that indicates whether this BsonRegularExpression is less than, equal to, or greather than the other BsonValue.</returns>
  190. public override int CompareTo(BsonValue other)
  191. {
  192. if (other == null) { return 1; }
  193. var otherRegularExpression = other as BsonRegularExpression;
  194. if (otherRegularExpression != null)
  195. {
  196. return CompareTo(otherRegularExpression);
  197. }
  198. return CompareTypeTo(other);
  199. }
  200. /// <summary>
  201. /// Compares this BsonRegularExpression to another BsonRegularExpression.
  202. /// </summary>
  203. /// <param name="rhs">The other BsonRegularExpression.</param>
  204. /// <returns>True if the two BsonRegularExpression values are equal.</returns>
  205. public bool Equals(BsonRegularExpression rhs)
  206. {
  207. if (object.ReferenceEquals(rhs, null) || GetType() != rhs.GetType()) { return false; }
  208. return _pattern == rhs._pattern && _options == rhs._options;
  209. }
  210. /// <summary>
  211. /// Compares this BsonRegularExpression to another object.
  212. /// </summary>
  213. /// <param name="obj">The other object.</param>
  214. /// <returns>True if the other object is a BsonRegularExpression and equal to this one.</returns>
  215. public override bool Equals(object obj)
  216. {
  217. return Equals(obj as BsonRegularExpression); // works even if obj is null or of a different type
  218. }
  219. /// <summary>
  220. /// Gets the hash code.
  221. /// </summary>
  222. /// <returns>The hash code.</returns>
  223. public override int GetHashCode()
  224. {
  225. // see Effective Java by Joshua Bloch
  226. int hash = 17;
  227. hash = 37 * hash + BsonType.GetHashCode();
  228. hash = 37 * hash + _pattern.GetHashCode();
  229. hash = 37 * hash + _options.GetHashCode();
  230. return hash;
  231. }
  232. /// <summary>
  233. /// Converts the BsonRegularExpression to a Regex.
  234. /// </summary>
  235. /// <returns>A Regex.</returns>
  236. public Regex ToRegex()
  237. {
  238. var options = RegexOptions.None;
  239. if (_options.IndexOf('i') != -1)
  240. {
  241. options |= RegexOptions.IgnoreCase;
  242. }
  243. if (_options.IndexOf('m') != -1)
  244. {
  245. options |= RegexOptions.Multiline;
  246. }
  247. if (_options.IndexOf('s') != -1)
  248. {
  249. options |= RegexOptions.Singleline;
  250. }
  251. if (_options.IndexOf('x') != -1)
  252. {
  253. options |= RegexOptions.IgnorePatternWhitespace;
  254. }
  255. return new Regex(_pattern, options);
  256. }
  257. /// <summary>
  258. /// Returns a string representation of the value.
  259. /// </summary>
  260. /// <returns>A string representation of the value.</returns>
  261. public override string ToString()
  262. {
  263. var escaped = (_pattern == "") ? "(?:)" : _pattern.Replace("/", @"\/");
  264. return string.Format("/{0}/{1}", escaped, _options);
  265. }
  266. // private methods
  267. private string SortOptionsIfNecessary(string options)
  268. {
  269. if (IsAlreadySorted(options))
  270. {
  271. return options;
  272. }
  273. else
  274. {
  275. var sorted = options.ToCharArray();
  276. Array.Sort(sorted);
  277. return new string(sorted);
  278. }
  279. bool IsAlreadySorted(string value)
  280. {
  281. for (var i = 0; i < value.Length - 1; i++)
  282. {
  283. if (value[i] > value[i + 1])
  284. {
  285. return false;
  286. }
  287. }
  288. return true;
  289. }
  290. }
  291. }
  292. }