[corlib] Update ValueTuple implementation
[mono.git] / mcs / class / referencesource / System.Activities.Presentation / System.Activities.Presentation / System / Activities / Presentation / Base / Core / Internal / PropertyEditing / Model / ModelPropertyMerger.cs
1 //----------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //----------------------------------------------------------------
4 namespace System.Activities.Presentation.Internal.PropertyEditing.Model
5 {
6     using System;
7     using System.Collections;
8     using System.Collections.Generic;
9     using System.Collections.ObjectModel;
10     using System.Diagnostics.CodeAnalysis;
11     using System.Text;
12     using System.Activities.Presentation.Model;
13
14     // <summary>
15     // Helper class that knows how to merge ModelProperties across multiple ModelItems
16     // </summary>
17     internal static class ModelPropertyMerger
18     {
19
20         private static IEnumerable<IList<ModelProperty>> _emptyCollection;
21
22         private static IEnumerable<IList<ModelProperty>> EmptyCollection
23         {
24             get
25             {
26                 if (_emptyCollection == null)
27                 {
28                     _emptyCollection = new List<IList<ModelProperty>>();
29                 }
30
31                 return _emptyCollection;
32             }
33         }
34
35         // <summary>
36         // Uber method that returns a list of list of ModelProperties that represent the
37         // merged set of properties across the specified ModelItems
38         // </summary>
39         // <param name="items">ModelItems to examine</param>
40         // <param name="itemCount">Count on ModelItems to examine</param>
41         // <returns>List of list of merged properties</returns>
42         public static IEnumerable<IList<ModelProperty>> GetMergedProperties(IEnumerable<ModelItem> items, int itemCount)
43         {
44             return GetMergedPropertiesHelper(new ModelItemExpander(items, itemCount));
45         }
46
47         // <summary>
48         // Uber method that returns a list of list of ModelProperties that represent the
49         // merged set of sub-properties across the values of the specified parent properties
50         // </summary>
51         // <param name="parentProperties">ModelProperties to examine</param>
52         // <returns>List of list of merged properties</returns>
53         public static IEnumerable<IList<ModelProperty>> GetMergedSubProperties(ICollection<ModelProperty> parentProperties)
54         {
55             return GetMergedPropertiesHelper(new SubPropertyExpander(parentProperties));
56         }
57
58         // <summary>
59         // Finds the consolidated default property name and returns it.  If there is no shared
60         // default property betweem the specified items, null is returned.
61         // </summary>
62         // <param name="items">Items to examine</param>
63         // <returns>Shared default property, if any.</returns>
64         public static string GetMergedDefaultProperty(IEnumerable<ModelItem> items)
65         {
66             if (items == null)
67             {
68                 return null;
69             }
70
71             bool firstIteration = true;
72             string mergedDefaultProperty = null;
73
74             foreach (ModelItem item in items)
75             {
76                 string currentDefaultProperty = ExtensibilityAccessor.GetDefaultProperty(item.ItemType);
77
78                 if (firstIteration)
79                 {
80                     mergedDefaultProperty = currentDefaultProperty;
81                 }
82                 else if (!string.Equals(currentDefaultProperty, mergedDefaultProperty))
83                 {
84                     mergedDefaultProperty = null;
85                 }
86
87                 if (string.IsNullOrEmpty(mergedDefaultProperty))
88                 {
89                     return null;
90                 }
91
92                 firstIteration = false;
93             }
94
95             return mergedDefaultProperty;
96         }
97
98         // Optimization that speeds up the common case (single selection)
99         private static IEnumerable<IList<ModelProperty>> GetMergedPropertiesHelper(PropertyExpander expander)
100         {
101             // Check empty list
102             if (expander == null || expander.ContainerCount == 0)
103             {
104                 return EmptyCollection;
105             }
106
107             if (expander.ContainerCount == 1)
108             {
109                 // Corner case - one object selected, don't bother with merging
110                 return GetFirstProperties(expander);
111             }
112             else
113             {
114                 // Calculate the list anew
115                 return GetMergedPropertiesCore(expander);
116             }
117         }
118
119         // Optimization that speeds up the common case (single selection)
120         private static IEnumerable<IList<ModelProperty>> GetFirstProperties(PropertyExpander expander)
121         {
122             IEnumerator<IEnumerable<ModelProperty>> propertyContainers = expander.GetEnumerator();
123             propertyContainers.MoveNext();
124
125             if (propertyContainers.Current != null)
126             {
127                 foreach (ModelProperty property in propertyContainers.Current)
128                 {
129                     yield return new ModelProperty[] { property };
130                 }
131             }
132         }
133
134         private static IEnumerable<IList<ModelProperty>> GetMergedPropertiesCore(PropertyExpander expander)
135         {
136
137             Dictionary<string, IList<ModelProperty>> counter = new Dictionary<string, IList<ModelProperty>>();
138
139             int containerCounter = 0;
140             foreach (IEnumerable<ModelProperty> properties in expander)
141             {
142
143                 if (properties == null)
144                 {
145                     yield break;
146                 }
147
148                 foreach (ModelProperty property in properties)
149                 {
150
151                     IList<ModelProperty> existingModelPropertiesForProperty;
152                     if (!counter.TryGetValue(property.Name, out existingModelPropertiesForProperty))
153                     {
154
155                         if (containerCounter == 0)
156                         {
157                             existingModelPropertiesForProperty = new List<ModelProperty>(expander.ContainerCount);
158                             counter[property.Name] = existingModelPropertiesForProperty;
159                         }
160                         else
161                         {
162                             // This property has not been encountered yet in the previous objects,
163                             // so skip it altogether.
164                             continue;
165                         }
166
167                     }
168
169                     if (existingModelPropertiesForProperty.Count < containerCounter)
170                     {
171                         // There has been a ModelItem in the list that didn't have this property,
172                         // so delete any record of it and skip it in the future.
173                         counter.Remove(property.Name);
174                         continue;
175                     }
176
177                     // Verify that the properties are equivalent
178                     if (containerCounter > 0 &&
179                         !ModelUtilities.AreEquivalent(
180                         existingModelPropertiesForProperty[containerCounter - 1], property))
181                     {
182                         // They are not, so scrap this property altogether
183                         counter.Remove(property.Name);
184                         continue;
185                     }
186
187                     existingModelPropertiesForProperty.Add(property);
188                 }
189
190                 containerCounter++;
191             }
192
193             foreach (KeyValuePair<string, IList<ModelProperty>> pair in counter)
194             {
195                 // Once again, if there is a property that is not shared by all
196                 // selected items, ignore it
197                 if (pair.Value.Count < containerCounter)
198                 {
199                     continue;
200                 }
201
202                 // We should not set the same instance to multiple properties, 
203                 // so ignore types that are not value type or string in case of multi-selection
204                 if (pair.Value.Count > 1 && !(pair.Value[0].PropertyType.IsValueType || pair.Value[0].PropertyType.Equals(typeof(string))))
205                 {
206                     continue;
207                 }
208
209                 yield return (IList<ModelProperty>)pair.Value;
210             }
211         }
212
213         // <summary>
214         // We use the same code to merge properties across a set of ModelItems as well
215         // as to merge sub-properties across a set of ModelProperties.  PropertyExpander
216         // class is a helper that abstracts the difference between these two inputs, so
217         // that the merge methods don't have to worry about it.
218         // </summary>
219         private abstract class PropertyExpander : IEnumerable<IEnumerable<ModelProperty>>
220         {
221             public abstract int ContainerCount
222             { get; }
223             public abstract IEnumerator<IEnumerable<ModelProperty>> GetEnumerator();
224
225             IEnumerator IEnumerable.GetEnumerator()
226             {
227                 return GetEnumerator();
228             }
229         }
230
231         // <summary>
232         // Version of PropertyExpander that returns the properties of a set of ModelItems
233         // </summary>
234         private class ModelItemExpander : PropertyExpander
235         {
236
237             private IEnumerable<ModelItem> _items;
238             private int _itemCount;
239
240             public ModelItemExpander(IEnumerable<ModelItem> items, int itemCount)
241             {
242                 _items = items;
243                 _itemCount = itemCount;
244             }
245
246             public override int ContainerCount
247             {
248                 get { return _itemCount; }
249             }
250
251             public override IEnumerator<IEnumerable<ModelProperty>> GetEnumerator()
252             {
253                 if (_items == null)
254                 {
255                     yield break;
256                 }
257
258                 foreach (ModelItem item in _items)
259                 {
260                     if (item.Properties == null)
261                     {
262                         continue;
263                     }
264
265                     yield return item.Properties;
266                 }
267             }
268         }
269
270         // <summary>
271         // Version of PropertyExpander that returns the sub-properties of a set of
272         // ModelProperty values.
273         // </summary>
274         private class SubPropertyExpander : PropertyExpander
275         {
276
277             private ICollection<ModelProperty> _parentProperties;
278
279             public SubPropertyExpander(ICollection<ModelProperty> parentProperties)
280             {
281                 _parentProperties = parentProperties;
282             }
283
284             public override int ContainerCount
285             {
286                 get { return _parentProperties == null ? 0 : _parentProperties.Count; }
287             }
288
289             public override IEnumerator<IEnumerable<ModelProperty>> GetEnumerator()
290             {
291                 if (_parentProperties == null)
292                 {
293                     yield break;
294                 }
295
296                 foreach (ModelProperty property in _parentProperties)
297                 {
298                     yield return ExtensibilityAccessor.GetSubProperties(property);
299                 }
300             }
301         }
302     }
303 }