PageRenderTime 62ms CodeModel.GetById 32ms RepoModel.GetById 0ms app.codeStats 0ms

/src/NHibernate/Linq/Visitors/PossibleValueSet.cs

https://github.com/whut/nhibernate-core
C# | 391 lines | 278 code | 67 blank | 46 comment | 84 complexity | d41028eeea8b0ea756b2b0776440e09d MD5 | raw file
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. namespace NHibernate.Linq.Visitors
  6. {
  7. /// <summary>
  8. /// Represents a possible set of values for a computation. For example, an expression may
  9. /// be null, it may be a non-null value, or we may even have a constant value that is known
  10. /// precisely. This class contains operators that know how to combine these values with
  11. /// each other. This class is intended to be used to provide static analysis of expressions
  12. /// before we hit the database. As an example for future improvement, we could handle
  13. /// ranges of numeric values. We can also improve this by handling operators such as the
  14. /// comparison operators and arithmetic operators. They are currently handled by naive
  15. /// null checks.
  16. /// </summary>
  17. public class PossibleValueSet
  18. {
  19. private System.Type ExpressionType { get; set; }
  20. private bool ContainsNull { get; set; }
  21. private bool ContainsAllNonNullValues { get; set; }
  22. private List<object> DistinctValues
  23. {
  24. get
  25. {
  26. if (_DistinctValues == null)
  27. _DistinctValues = new List<object>();
  28. return _DistinctValues;
  29. }
  30. }
  31. private List<object> _DistinctValues;
  32. private bool ContainsAnyNonNullValues
  33. {
  34. get { return ContainsAllNonNullValues || DistinctValues.Any(); }
  35. }
  36. private PossibleValueSet(System.Type expressionType)
  37. {
  38. ExpressionType = expressionType;
  39. }
  40. public bool Contains(object obj)
  41. {
  42. if (obj == null)
  43. return ContainsNull;
  44. if (obj.GetType() != ExpressionType)
  45. return false;
  46. if (ContainsAllNonNullValues)
  47. return true;
  48. return DistinctValues.Contains(obj);
  49. }
  50. #region Operations
  51. public PossibleValueSet Add(PossibleValueSet pvs, System.Type resultType)
  52. {
  53. return MathOperation(pvs, resultType);
  54. }
  55. public PossibleValueSet Divide(PossibleValueSet pvs, System.Type resultType)
  56. {
  57. return MathOperation(pvs, resultType);
  58. }
  59. public PossibleValueSet Modulo(PossibleValueSet pvs, System.Type resultType)
  60. {
  61. return MathOperation(pvs, resultType);
  62. }
  63. public PossibleValueSet Multiply(PossibleValueSet pvs, System.Type resultType)
  64. {
  65. return MathOperation(pvs, resultType);
  66. }
  67. public PossibleValueSet Power(PossibleValueSet pvs, System.Type resultType)
  68. {
  69. return MathOperation(pvs, resultType);
  70. }
  71. public PossibleValueSet Subtract(PossibleValueSet pvs, System.Type resultType)
  72. {
  73. return MathOperation(pvs, resultType);
  74. }
  75. public PossibleValueSet And(PossibleValueSet pvs, System.Type resultType)
  76. {
  77. return MathOperation(pvs, resultType);
  78. }
  79. public PossibleValueSet Or(PossibleValueSet pvs, System.Type resultType)
  80. {
  81. return MathOperation(pvs, resultType);
  82. }
  83. public PossibleValueSet ExclusiveOr(PossibleValueSet pvs, System.Type resultType)
  84. {
  85. return MathOperation(pvs, resultType);
  86. }
  87. public PossibleValueSet LeftShift(PossibleValueSet pvs, System.Type resultType)
  88. {
  89. return MathOperation(pvs, resultType);
  90. }
  91. public PossibleValueSet RightShift(PossibleValueSet pvs, System.Type resultType)
  92. {
  93. return MathOperation(pvs, resultType);
  94. }
  95. private PossibleValueSet MathOperation(PossibleValueSet pvs, System.Type resultType)
  96. {
  97. // If either side is only null, the result will be null.
  98. if (!ContainsAnyNonNullValues || !pvs.ContainsAnyNonNullValues)
  99. return CreateNull(resultType);
  100. // If neither side is null, the result cannot be null.
  101. if (!ContainsNull && !pvs.ContainsNull)
  102. return CreateAllNonNullValues(resultType);
  103. // Otherwise, the result can be anything.
  104. return CreateAllValues(resultType);
  105. }
  106. public PossibleValueSet AndAlso(PossibleValueSet pvs)
  107. {
  108. /*
  109. * T && T == T
  110. * T && F == F
  111. * T && N == N
  112. * F && T == F
  113. * F && F == F
  114. * F && N == F
  115. * N && T == N
  116. * N && F == F
  117. * N && N == N
  118. */
  119. var result = new PossibleValueSet(DetermineBoolType(pvs));
  120. if (Contains(true) && pvs.Contains(true) && !result.Contains(true)) result.DistinctValues.Add(true);
  121. if (Contains(true) && pvs.Contains(false) && !result.Contains(false)) result.DistinctValues.Add(false);
  122. if (Contains(true) && pvs.Contains(null)) result.ContainsNull = true;
  123. if (Contains(false) && pvs.Contains(true) && !result.Contains(false)) result.DistinctValues.Add(false);
  124. if (Contains(false) && pvs.Contains(false) && !result.Contains(false)) result.DistinctValues.Add(false);
  125. if (Contains(false) && pvs.Contains(null) && !result.Contains(false)) result.DistinctValues.Add(false);
  126. if (Contains(null) && pvs.Contains(true)) result.ContainsNull = true;
  127. if (Contains(null) && pvs.Contains(false) && !result.Contains(false)) result.DistinctValues.Add(false);
  128. if (Contains(null) && pvs.Contains(null)) result.ContainsNull = true;
  129. return result;
  130. }
  131. public PossibleValueSet OrElse(PossibleValueSet pvs)
  132. {
  133. /*
  134. * T || T == T
  135. * T || F == T
  136. * T || N == T
  137. * F || T == T
  138. * F || F == F
  139. * F || N == N
  140. * N || T == T
  141. * N || F == N
  142. * N || N == N
  143. */
  144. var result = new PossibleValueSet(DetermineBoolType(pvs));
  145. if (Contains(true) && pvs.Contains(true) && !result.Contains(true)) result.DistinctValues.Add(true);
  146. if (Contains(true) && pvs.Contains(false) && !result.Contains(true)) result.DistinctValues.Add(true);
  147. if (Contains(true) && pvs.Contains(null) && !result.Contains(true)) result.DistinctValues.Add(true);
  148. if (Contains(false) && pvs.Contains(true) && !result.Contains(true)) result.DistinctValues.Add(true);
  149. if (Contains(false) && pvs.Contains(false) && !result.Contains(false)) result.DistinctValues.Add(false);
  150. if (Contains(false) && pvs.Contains(null)) result.ContainsNull = true;
  151. if (Contains(null) && pvs.Contains(true) && !result.Contains(true)) result.DistinctValues.Add(true);
  152. if (Contains(null) && pvs.Contains(false)) result.ContainsNull = true;
  153. if (Contains(null) && pvs.Contains(null)) result.ContainsNull = true;
  154. return result;
  155. }
  156. public PossibleValueSet Equal(PossibleValueSet pvs)
  157. {
  158. return ComparisonOperation(pvs);
  159. }
  160. public PossibleValueSet NotEqual(PossibleValueSet pvs)
  161. {
  162. return ComparisonOperation(pvs);
  163. }
  164. public PossibleValueSet GreaterThanOrEqual(PossibleValueSet pvs)
  165. {
  166. return ComparisonOperation(pvs);
  167. }
  168. public PossibleValueSet GreaterThan(PossibleValueSet pvs)
  169. {
  170. return ComparisonOperation(pvs);
  171. }
  172. public PossibleValueSet LessThan(PossibleValueSet pvs)
  173. {
  174. return ComparisonOperation(pvs);
  175. }
  176. public PossibleValueSet LessThanOrEqual(PossibleValueSet pvs)
  177. {
  178. return ComparisonOperation(pvs);
  179. }
  180. private PossibleValueSet ComparisonOperation(PossibleValueSet pvs)
  181. {
  182. return MathOperation(pvs, typeof (bool));
  183. }
  184. public PossibleValueSet Coalesce(PossibleValueSet pvs)
  185. {
  186. if (!ContainsNull)
  187. return this;
  188. PossibleValueSet result = new PossibleValueSet(ExpressionType);
  189. result.DistinctValues.AddRange(DistinctValues);
  190. result.ContainsAllNonNullValues = ContainsAllNonNullValues;
  191. result.ContainsNull = pvs.ContainsNull;
  192. result.ContainsAllNonNullValues |= pvs.ContainsAllNonNullValues;
  193. result.DistinctValues.AddRange(pvs.DistinctValues.Except(result.DistinctValues));
  194. return result;
  195. }
  196. // Unary Operators
  197. public PossibleValueSet Not()
  198. {
  199. DetermineBoolType();
  200. var result = new PossibleValueSet(ExpressionType);
  201. result.ContainsNull = ContainsNull;
  202. result.DistinctValues.AddRange(DistinctValues.Cast<bool>().Select(v => !v).Cast<object>());
  203. return result;
  204. }
  205. public PossibleValueSet BitwiseNot(System.Type resultType)
  206. {
  207. return UnaryMathOperation(resultType);
  208. }
  209. public PossibleValueSet ArrayLength(System.Type resultType)
  210. {
  211. return CreateAllNonNullValues(typeof (int));
  212. }
  213. public PossibleValueSet Convert(System.Type resultType)
  214. {
  215. return UnaryMathOperation(resultType);
  216. }
  217. public PossibleValueSet Negate(System.Type resultType)
  218. {
  219. return UnaryMathOperation(resultType);
  220. }
  221. public PossibleValueSet UnaryPlus(System.Type resultType)
  222. {
  223. return this;
  224. }
  225. private PossibleValueSet UnaryMathOperation(System.Type resultType)
  226. {
  227. return
  228. !ContainsAnyNonNullValues ? CreateNull(resultType) :
  229. !ContainsNull ? CreateAllNonNullValues(resultType) :
  230. CreateAllValues(resultType);
  231. }
  232. // Special Operators
  233. public PossibleValueSet IsNull()
  234. {
  235. PossibleValueSet result = new PossibleValueSet(typeof(bool));
  236. if (ContainsNull)
  237. result.DistinctValues.Add(true);
  238. if (ContainsAllNonNullValues || DistinctValues.Any())
  239. result.DistinctValues.Add(false);
  240. return result;
  241. }
  242. public PossibleValueSet IsNotNull()
  243. {
  244. PossibleValueSet result = new PossibleValueSet(typeof(bool));
  245. if (ContainsNull)
  246. result.DistinctValues.Add(false);
  247. if (ContainsAllNonNullValues || DistinctValues.Any())
  248. result.DistinctValues.Add(true);
  249. return result;
  250. }
  251. public PossibleValueSet MemberAccess(System.Type resultType)
  252. {
  253. // If instance is null, member will be null too.
  254. if (!ContainsAnyNonNullValues)
  255. return CreateNull(resultType);
  256. // Otherwise, value could be anything due to value of member.
  257. return CreateAllValues(resultType);
  258. }
  259. #endregion
  260. /// <summary>
  261. /// Verify that ExpressionType of both this and the other set is bool or nullable bool,
  262. /// and return the negotiated type (nullable bool if either side is nullable).
  263. /// </summary>
  264. private System.Type DetermineBoolType(PossibleValueSet otherSet)
  265. {
  266. DetermineBoolType();
  267. otherSet.DetermineBoolType();
  268. var nullableBoolType = typeof(bool?);
  269. if (ExpressionType == nullableBoolType || otherSet.ExpressionType == nullableBoolType)
  270. return nullableBoolType;
  271. return typeof(bool);
  272. }
  273. /// <summary>
  274. /// Verify that ExpressionType is bool or nullable bool.
  275. /// </summary>
  276. private void DetermineBoolType()
  277. {
  278. var boolType = typeof(bool);
  279. var nullableBoolType = typeof(bool?);
  280. if (ExpressionType != boolType && ExpressionType != nullableBoolType)
  281. throw new AssertionFailure(
  282. "Cannot perform desired possible value set operation on expressions of type: " +
  283. ExpressionType);
  284. }
  285. public static PossibleValueSet CreateNull(System.Type expressionType)
  286. {
  287. return new PossibleValueSet(expressionType) {ContainsNull = true};
  288. }
  289. public static PossibleValueSet CreateAllNonNullValues(System.Type expressionType)
  290. {
  291. PossibleValueSet result = new PossibleValueSet(expressionType);
  292. if (expressionType == typeof(bool))
  293. {
  294. result.DistinctValues.Add(true);
  295. result.DistinctValues.Add(false);
  296. }
  297. else
  298. {
  299. result.ContainsAllNonNullValues = true;
  300. }
  301. return result;
  302. }
  303. public static PossibleValueSet CreateAllValues(System.Type expressionType)
  304. {
  305. PossibleValueSet result = CreateAllNonNullValues(expressionType);
  306. result.ContainsNull = true;
  307. return result;
  308. }
  309. public static PossibleValueSet Create(System.Type expressionType, params object[] values)
  310. {
  311. PossibleValueSet result = new PossibleValueSet(expressionType);
  312. foreach (var v in values)
  313. {
  314. if (v == null)
  315. result.ContainsNull = true;
  316. else if (v.GetType() == expressionType && !result.DistinctValues.Contains(v))
  317. result.DistinctValues.Add(v);
  318. else
  319. throw new AssertionFailure("Don't know how to add value to possible value set of type " + expressionType + ": " + v);
  320. }
  321. return result;
  322. }
  323. }
  324. }