Merge pull request #409 from Alkarex/patch-1
[mono.git] / mcs / class / System.Core / System.Linq.Parallel.QueryNodes / QuerySetNode.cs
1 //
2 // QueryMuxNode.cs
3 //
4 // Author:
5 //       Jérémie "Garuma" Laval <jeremie.laval@gmail.com>
6 //
7 // Copyright (c) 2010 Jérémie "Garuma" Laval
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining a copy
10 // of this software and associated documentation files (the "Software"), to deal
11 // in the Software without restriction, including without limitation the rights
12 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 // copies of the Software, and to permit persons to whom the Software is
14 // furnished to do so, subject to the following conditions:
15 //
16 // The above copyright notice and this permission notice shall be included in
17 // all copies or substantial portions of the Software.
18 //
19 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 // THE SOFTWARE.
26
27 #if NET_4_0 || MOBILE
28 using System;
29 using System.Linq;
30 using System.Collections.Generic;
31 using System.Collections.Concurrent;
32
33 namespace System.Linq.Parallel.QueryNodes
34 {
35         internal class QuerySetNode<TSource> : QueryMuxNode<TSource, TSource, TSource>
36         {
37                 readonly SetInclusion setInclusion;
38                 readonly IEqualityComparer<TSource> comparer;
39
40                 internal QuerySetNode (SetInclusion setInclusion, IEqualityComparer<TSource> comparer,
41                                        QueryBaseNode<TSource> first, QueryBaseNode<TSource> second)
42                         : base (first, second)
43                 {
44                         this.setInclusion = setInclusion;
45                         this.comparer = comparer;
46                 }
47
48                 internal override IEnumerable<TSource> GetSequential ()
49                 {
50                         var first = Parent.GetSequential ();
51                         var second = Second == null ? null : Second.GetSequential ();
52
53                         // We try to do some guessing based on the default
54                         switch (setInclusion) {
55                         case SetInclusionDefaults.Union:
56                                 return first.Union (second, comparer);
57                         case SetInclusionDefaults.Intersect:
58                                 return first.Intersect (second, comparer);
59                         case SetInclusionDefaults.Except:
60                                 return first.Except (second, comparer);
61                         case SetInclusionDefaults.Distinct:
62                                 return first.Distinct (comparer);
63                         }
64
65                         // Default is we return the bare source enumerable
66                         return first;
67                 }
68
69                 internal override IList<IEnumerable<TSource>> GetEnumerables (QueryOptions options)
70                 {
71                         var first = Parent.GetEnumerables (options);
72                         var second = Second.GetEnumerables (options);
73                         
74                         var checker = new ConcurrentDictionary<TSource, object> (comparer);
75                         InitConcurrentDictionary (checker, second, (e) => e);
76
77                         return first
78                                 .Select ((f, i) => GetEnumerable<TSource> (f, second[i], checker, (e) => e))
79                                 .ToList ();
80                 }
81
82                 internal override IList<IEnumerable<KeyValuePair<long, TSource>>> GetOrderedEnumerables (QueryOptions options)
83                 {
84                         var first = Parent.GetOrderedEnumerables (options);
85                         var second = Second.GetOrderedEnumerables (options);
86
87                         var checker = new ConcurrentDictionary<TSource, object> (comparer);
88                         InitConcurrentDictionary (checker, second, (e) => e.Value);
89
90                         return first
91                                 .Select ((f, i) => GetEnumerable<KeyValuePair<long, TSource>> (f, second[i], checker, (e) => e.Value))
92                                 .ToList ();
93                 }
94                                 
95                 void InitConcurrentDictionary<TExtract> (ConcurrentDictionary<TSource, object> checker,
96                                                          IList<IEnumerable<TExtract>> feeds,
97                                                          Func<TExtract, TSource> extractor)
98                 {
99                         if ((setInclusion & SetInclusion.Preload) == 0)
100                                 return;
101                         
102                         foreach (IEnumerable<TExtract> feed in feeds)
103                                 foreach (TExtract item in feed)
104                                         checker.TryAdd (extractor (item), null);
105                 }
106
107                 IEnumerable<TExtract> GetEnumerable<TExtract> (IEnumerable<TExtract> first,
108                                                                IEnumerable<TExtract> second,
109                                                                ConcurrentDictionary<TSource, object> checker,
110                                                                Func<TExtract, TSource> extractor)
111                 {
112                         IEnumerator<TExtract> eFirst = first.GetEnumerator ();
113                         IEnumerator<TExtract> eSecond = second == null ? null : second.GetEnumerator ();
114
115                         IEnumerator<TExtract> current = eFirst;
116                         bool outInclusion = (setInclusion & SetInclusion.Out) > 0;
117                         bool preload = (setInclusion & SetInclusion.Preload) > 0;
118                         bool relaxed = (setInclusion & SetInclusion.Relaxed) > 0;
119
120                         try {
121                                 while (current != null) {
122                                         while (current.MoveNext ()) {
123                                                 bool result = relaxed ?
124                                                         checker.ContainsKey (extractor (current.Current)) : checker.TryAdd (extractor (current.Current), null);
125
126                                                 if ((result && outInclusion)
127                                                     || (!result && !outInclusion))
128                                                         yield return current.Current;
129                                         }
130
131                                         if (current == eFirst && !preload)
132                                                 current = eSecond;
133                                         else
134                                                 break;
135                                 }
136                         } finally {
137                                 eFirst.Dispose ();
138                                 eSecond.Dispose ();
139                         }
140                 }
141         }
142 }
143
144 #endif