1 //----------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation. All rights reserved.
3 //----------------------------------------------------------------
4 namespace System.Activities.Presentation.Internal.PropertyEditing.Model
7 using System.Collections;
8 using System.Collections.Generic;
9 using System.Collections.ObjectModel;
10 using System.Diagnostics.CodeAnalysis;
12 using System.Activities.Presentation.Model;
15 // Helper class that knows how to merge ModelProperties across multiple ModelItems
17 internal static class ModelPropertyMerger
20 private static IEnumerable<IList<ModelProperty>> _emptyCollection;
22 private static IEnumerable<IList<ModelProperty>> EmptyCollection
26 if (_emptyCollection == null)
28 _emptyCollection = new List<IList<ModelProperty>>();
31 return _emptyCollection;
36 // Uber method that returns a list of list of ModelProperties that represent the
37 // merged set of properties across the specified ModelItems
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)
44 return GetMergedPropertiesHelper(new ModelItemExpander(items, itemCount));
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
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)
55 return GetMergedPropertiesHelper(new SubPropertyExpander(parentProperties));
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.
62 // <param name="items">Items to examine</param>
63 // <returns>Shared default property, if any.</returns>
64 public static string GetMergedDefaultProperty(IEnumerable<ModelItem> items)
71 bool firstIteration = true;
72 string mergedDefaultProperty = null;
74 foreach (ModelItem item in items)
76 string currentDefaultProperty = ExtensibilityAccessor.GetDefaultProperty(item.ItemType);
80 mergedDefaultProperty = currentDefaultProperty;
82 else if (!string.Equals(currentDefaultProperty, mergedDefaultProperty))
84 mergedDefaultProperty = null;
87 if (string.IsNullOrEmpty(mergedDefaultProperty))
92 firstIteration = false;
95 return mergedDefaultProperty;
98 // Optimization that speeds up the common case (single selection)
99 private static IEnumerable<IList<ModelProperty>> GetMergedPropertiesHelper(PropertyExpander expander)
102 if (expander == null || expander.ContainerCount == 0)
104 return EmptyCollection;
107 if (expander.ContainerCount == 1)
109 // Corner case - one object selected, don't bother with merging
110 return GetFirstProperties(expander);
114 // Calculate the list anew
115 return GetMergedPropertiesCore(expander);
119 // Optimization that speeds up the common case (single selection)
120 private static IEnumerable<IList<ModelProperty>> GetFirstProperties(PropertyExpander expander)
122 IEnumerator<IEnumerable<ModelProperty>> propertyContainers = expander.GetEnumerator();
123 propertyContainers.MoveNext();
125 if (propertyContainers.Current != null)
127 foreach (ModelProperty property in propertyContainers.Current)
129 yield return new ModelProperty[] { property };
134 private static IEnumerable<IList<ModelProperty>> GetMergedPropertiesCore(PropertyExpander expander)
137 Dictionary<string, IList<ModelProperty>> counter = new Dictionary<string, IList<ModelProperty>>();
139 int containerCounter = 0;
140 foreach (IEnumerable<ModelProperty> properties in expander)
143 if (properties == null)
148 foreach (ModelProperty property in properties)
151 IList<ModelProperty> existingModelPropertiesForProperty;
152 if (!counter.TryGetValue(property.Name, out existingModelPropertiesForProperty))
155 if (containerCounter == 0)
157 existingModelPropertiesForProperty = new List<ModelProperty>(expander.ContainerCount);
158 counter[property.Name] = existingModelPropertiesForProperty;
162 // This property has not been encountered yet in the previous objects,
163 // so skip it altogether.
169 if (existingModelPropertiesForProperty.Count < containerCounter)
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);
177 // Verify that the properties are equivalent
178 if (containerCounter > 0 &&
179 !ModelUtilities.AreEquivalent(
180 existingModelPropertiesForProperty[containerCounter - 1], property))
182 // They are not, so scrap this property altogether
183 counter.Remove(property.Name);
187 existingModelPropertiesForProperty.Add(property);
193 foreach (KeyValuePair<string, IList<ModelProperty>> pair in counter)
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)
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))))
209 yield return (IList<ModelProperty>)pair.Value;
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.
219 private abstract class PropertyExpander : IEnumerable<IEnumerable<ModelProperty>>
221 public abstract int ContainerCount
223 public abstract IEnumerator<IEnumerable<ModelProperty>> GetEnumerator();
225 IEnumerator IEnumerable.GetEnumerator()
227 return GetEnumerator();
232 // Version of PropertyExpander that returns the properties of a set of ModelItems
234 private class ModelItemExpander : PropertyExpander
237 private IEnumerable<ModelItem> _items;
238 private int _itemCount;
240 public ModelItemExpander(IEnumerable<ModelItem> items, int itemCount)
243 _itemCount = itemCount;
246 public override int ContainerCount
248 get { return _itemCount; }
251 public override IEnumerator<IEnumerable<ModelProperty>> GetEnumerator()
258 foreach (ModelItem item in _items)
260 if (item.Properties == null)
265 yield return item.Properties;
271 // Version of PropertyExpander that returns the sub-properties of a set of
272 // ModelProperty values.
274 private class SubPropertyExpander : PropertyExpander
277 private ICollection<ModelProperty> _parentProperties;
279 public SubPropertyExpander(ICollection<ModelProperty> parentProperties)
281 _parentProperties = parentProperties;
284 public override int ContainerCount
286 get { return _parentProperties == null ? 0 : _parentProperties.Count; }
289 public override IEnumerator<IEnumerable<ModelProperty>> GetEnumerator()
291 if (_parentProperties == null)
296 foreach (ModelProperty property in _parentProperties)
298 yield return ExtensibilityAccessor.GetSubProperties(property);