--- /dev/null
+//----------------------------------------------------------------
+// Copyright (c) Microsoft Corporation. All rights reserved.
+//----------------------------------------------------------------
+namespace System.Activities.Presentation.Internal.PropertyEditing.Model
+{
+ using System;
+ using System.Collections;
+ using System.Collections.Generic;
+ using System.Collections.ObjectModel;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Text;
+ using System.Activities.Presentation.Model;
+
+ // <summary>
+ // Helper class that knows how to merge ModelProperties across multiple ModelItems
+ // </summary>
+ internal static class ModelPropertyMerger
+ {
+
+ private static IEnumerable<IList<ModelProperty>> _emptyCollection;
+
+ private static IEnumerable<IList<ModelProperty>> EmptyCollection
+ {
+ get
+ {
+ if (_emptyCollection == null)
+ {
+ _emptyCollection = new List<IList<ModelProperty>>();
+ }
+
+ return _emptyCollection;
+ }
+ }
+
+ // <summary>
+ // Uber method that returns a list of list of ModelProperties that represent the
+ // merged set of properties across the specified ModelItems
+ // </summary>
+ // <param name="items">ModelItems to examine</param>
+ // <param name="itemCount">Count on ModelItems to examine</param>
+ // <returns>List of list of merged properties</returns>
+ public static IEnumerable<IList<ModelProperty>> GetMergedProperties(IEnumerable<ModelItem> items, int itemCount)
+ {
+ return GetMergedPropertiesHelper(new ModelItemExpander(items, itemCount));
+ }
+
+ // <summary>
+ // Uber method that returns a list of list of ModelProperties that represent the
+ // merged set of sub-properties across the values of the specified parent properties
+ // </summary>
+ // <param name="parentProperties">ModelProperties to examine</param>
+ // <returns>List of list of merged properties</returns>
+ public static IEnumerable<IList<ModelProperty>> GetMergedSubProperties(ICollection<ModelProperty> parentProperties)
+ {
+ return GetMergedPropertiesHelper(new SubPropertyExpander(parentProperties));
+ }
+
+ // <summary>
+ // Finds the consolidated default property name and returns it. If there is no shared
+ // default property betweem the specified items, null is returned.
+ // </summary>
+ // <param name="items">Items to examine</param>
+ // <returns>Shared default property, if any.</returns>
+ public static string GetMergedDefaultProperty(IEnumerable<ModelItem> items)
+ {
+ if (items == null)
+ {
+ return null;
+ }
+
+ bool firstIteration = true;
+ string mergedDefaultProperty = null;
+
+ foreach (ModelItem item in items)
+ {
+ string currentDefaultProperty = ExtensibilityAccessor.GetDefaultProperty(item.ItemType);
+
+ if (firstIteration)
+ {
+ mergedDefaultProperty = currentDefaultProperty;
+ }
+ else if (!string.Equals(currentDefaultProperty, mergedDefaultProperty))
+ {
+ mergedDefaultProperty = null;
+ }
+
+ if (string.IsNullOrEmpty(mergedDefaultProperty))
+ {
+ return null;
+ }
+
+ firstIteration = false;
+ }
+
+ return mergedDefaultProperty;
+ }
+
+ // Optimization that speeds up the common case (single selection)
+ private static IEnumerable<IList<ModelProperty>> GetMergedPropertiesHelper(PropertyExpander expander)
+ {
+ // Check empty list
+ if (expander == null || expander.ContainerCount == 0)
+ {
+ return EmptyCollection;
+ }
+
+ if (expander.ContainerCount == 1)
+ {
+ // Corner case - one object selected, don't bother with merging
+ return GetFirstProperties(expander);
+ }
+ else
+ {
+ // Calculate the list anew
+ return GetMergedPropertiesCore(expander);
+ }
+ }
+
+ // Optimization that speeds up the common case (single selection)
+ private static IEnumerable<IList<ModelProperty>> GetFirstProperties(PropertyExpander expander)
+ {
+ IEnumerator<IEnumerable<ModelProperty>> propertyContainers = expander.GetEnumerator();
+ propertyContainers.MoveNext();
+
+ if (propertyContainers.Current != null)
+ {
+ foreach (ModelProperty property in propertyContainers.Current)
+ {
+ yield return new ModelProperty[] { property };
+ }
+ }
+ }
+
+ private static IEnumerable<IList<ModelProperty>> GetMergedPropertiesCore(PropertyExpander expander)
+ {
+
+ Dictionary<string, IList<ModelProperty>> counter = new Dictionary<string, IList<ModelProperty>>();
+
+ int containerCounter = 0;
+ foreach (IEnumerable<ModelProperty> properties in expander)
+ {
+
+ if (properties == null)
+ {
+ yield break;
+ }
+
+ foreach (ModelProperty property in properties)
+ {
+
+ IList<ModelProperty> existingModelPropertiesForProperty;
+ if (!counter.TryGetValue(property.Name, out existingModelPropertiesForProperty))
+ {
+
+ if (containerCounter == 0)
+ {
+ existingModelPropertiesForProperty = new List<ModelProperty>(expander.ContainerCount);
+ counter[property.Name] = existingModelPropertiesForProperty;
+ }
+ else
+ {
+ // This property has not been encountered yet in the previous objects,
+ // so skip it altogether.
+ continue;
+ }
+
+ }
+
+ if (existingModelPropertiesForProperty.Count < containerCounter)
+ {
+ // There has been a ModelItem in the list that didn't have this property,
+ // so delete any record of it and skip it in the future.
+ counter.Remove(property.Name);
+ continue;
+ }
+
+ // Verify that the properties are equivalent
+ if (containerCounter > 0 &&
+ !ModelUtilities.AreEquivalent(
+ existingModelPropertiesForProperty[containerCounter - 1], property))
+ {
+ // They are not, so scrap this property altogether
+ counter.Remove(property.Name);
+ continue;
+ }
+
+ existingModelPropertiesForProperty.Add(property);
+ }
+
+ containerCounter++;
+ }
+
+ foreach (KeyValuePair<string, IList<ModelProperty>> pair in counter)
+ {
+ // Once again, if there is a property that is not shared by all
+ // selected items, ignore it
+ if (pair.Value.Count < containerCounter)
+ {
+ continue;
+ }
+
+ // We should not set the same instance to multiple properties,
+ // so ignore types that are not value type or string in case of multi-selection
+ if (pair.Value.Count > 1 && !(pair.Value[0].PropertyType.IsValueType || pair.Value[0].PropertyType.Equals(typeof(string))))
+ {
+ continue;
+ }
+
+ yield return (IList<ModelProperty>)pair.Value;
+ }
+ }
+
+ // <summary>
+ // We use the same code to merge properties across a set of ModelItems as well
+ // as to merge sub-properties across a set of ModelProperties. PropertyExpander
+ // class is a helper that abstracts the difference between these two inputs, so
+ // that the merge methods don't have to worry about it.
+ // </summary>
+ private abstract class PropertyExpander : IEnumerable<IEnumerable<ModelProperty>>
+ {
+ public abstract int ContainerCount
+ { get; }
+ public abstract IEnumerator<IEnumerable<ModelProperty>> GetEnumerator();
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+ }
+
+ // <summary>
+ // Version of PropertyExpander that returns the properties of a set of ModelItems
+ // </summary>
+ private class ModelItemExpander : PropertyExpander
+ {
+
+ private IEnumerable<ModelItem> _items;
+ private int _itemCount;
+
+ public ModelItemExpander(IEnumerable<ModelItem> items, int itemCount)
+ {
+ _items = items;
+ _itemCount = itemCount;
+ }
+
+ public override int ContainerCount
+ {
+ get { return _itemCount; }
+ }
+
+ public override IEnumerator<IEnumerable<ModelProperty>> GetEnumerator()
+ {
+ if (_items == null)
+ {
+ yield break;
+ }
+
+ foreach (ModelItem item in _items)
+ {
+ if (item.Properties == null)
+ {
+ continue;
+ }
+
+ yield return item.Properties;
+ }
+ }
+ }
+
+ // <summary>
+ // Version of PropertyExpander that returns the sub-properties of a set of
+ // ModelProperty values.
+ // </summary>
+ private class SubPropertyExpander : PropertyExpander
+ {
+
+ private ICollection<ModelProperty> _parentProperties;
+
+ public SubPropertyExpander(ICollection<ModelProperty> parentProperties)
+ {
+ _parentProperties = parentProperties;
+ }
+
+ public override int ContainerCount
+ {
+ get { return _parentProperties == null ? 0 : _parentProperties.Count; }
+ }
+
+ public override IEnumerator<IEnumerable<ModelProperty>> GetEnumerator()
+ {
+ if (_parentProperties == null)
+ {
+ yield break;
+ }
+
+ foreach (ModelProperty property in _parentProperties)
+ {
+ yield return ExtensibilityAccessor.GetSubProperties(property);
+ }
+ }
+ }
+ }
+}