Adding reference source for part of Workflow Foundation
[mono.git] / mcs / class / referencesource / System.Activities.Presentation / System.Activities.Presentation / System / Activities / Presentation / Base / Core / Internal / PropertyEditing / Model / ModelPropertyMerger.cs
diff --git a/mcs/class/referencesource/System.Activities.Presentation/System.Activities.Presentation/System/Activities/Presentation/Base/Core/Internal/PropertyEditing/Model/ModelPropertyMerger.cs b/mcs/class/referencesource/System.Activities.Presentation/System.Activities.Presentation/System/Activities/Presentation/Base/Core/Internal/PropertyEditing/Model/ModelPropertyMerger.cs
new file mode 100644 (file)
index 0000000..f9e06d4
--- /dev/null
@@ -0,0 +1,303 @@
+//----------------------------------------------------------------
+// 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);
+                }
+            }
+        }
+    }
+}