Index: src/DeltaShell/DeltaShell.Gui/DeltaShell.Gui.csproj =================================================================== diff -u -rc4fb042f3ea7c21852fba8fdc21bdeac96da6419 -r9f1efb36120427350936d48059c87c7e011657a9 --- src/DeltaShell/DeltaShell.Gui/DeltaShell.Gui.csproj (.../DeltaShell.Gui.csproj) (revision c4fb042f3ea7c21852fba8fdc21bdeac96da6419) +++ src/DeltaShell/DeltaShell.Gui/DeltaShell.Gui.csproj (.../DeltaShell.Gui.csproj) (revision 9f1efb36120427350936d48059c87c7e011657a9) @@ -146,6 +146,7 @@ Component + SplashScreen.xaml Index: src/DeltaShell/DeltaShell.Gui/Forms/PropertyGridView/PropertyGridView.cs =================================================================== diff -u -re0fff25db4ec9d82f4c3a39a4092ba6a30b98572 -r9f1efb36120427350936d48059c87c7e011657a9 --- src/DeltaShell/DeltaShell.Gui/Forms/PropertyGridView/PropertyGridView.cs (.../PropertyGridView.cs) (revision e0fff25db4ec9d82f4c3a39a4092ba6a30b98572) +++ src/DeltaShell/DeltaShell.Gui/Forms/PropertyGridView/PropertyGridView.cs (.../PropertyGridView.cs) (revision 9f1efb36120427350936d48059c87c7e011657a9) @@ -1,6 +1,5 @@ using System; using System.Collections; -using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Security.Permissions; @@ -9,9 +8,7 @@ using DelftTools.Shell.Core; using DelftTools.Shell.Gui; using DelftTools.Shell.Gui.Forms; -using DelftTools.Utils.PropertyBag.Dynamic; using DeltaShell.Gui.Properties; -using log4net; namespace DeltaShell.Gui.Forms.PropertyGridView { @@ -22,8 +19,6 @@ /// private delegate void ArgumentlessDelegate(); - private static readonly ILog Log = LogManager.GetLogger(typeof(PropertyGridView)); - /// /// todo: This is still an unwanted dependency. PropertyGrid uses gui to subscribe to the SelectionChanged /// delegate and in responce queries the gui.Selection @@ -56,46 +51,6 @@ } } - public object GetObjectProperties(object sourceData) - { - if (sourceData == null) - { - return null; - } - - // Obtain all property information - var propertyInfos = gui.Plugins.SelectMany(p => p.GetPropertyInfos()).ToList(); - - // 1. Match property information based on ObjectType and on AdditionalDataCheck - propertyInfos = propertyInfos.Where(pi => pi.ObjectType.IsInstanceOfType(sourceData) && (pi.AdditionalDataCheck == null || pi.AdditionalDataCheck(sourceData))).ToList(); - - // 2. Match property information based on object type inheritance - propertyInfos = FilterPropertyInfoByTypeInheritance(propertyInfos, pi => pi.ObjectType); - - // 3. Match property information based on property type inheritance - propertyInfos = FilterPropertyInfoByTypeInheritance(propertyInfos, pi => pi.PropertyType); - - if (propertyInfos.Count == 0) - { - // No (or multiple) object properties found: return 'null' so that no object properties are shown in the property grid - return null; - } - - if (propertyInfos.Count > 1) - { - // 4. We assume that the propertyInfos with AdditionalDataCheck are the most specific - propertyInfos = propertyInfos.Where(pi => pi.AdditionalDataCheck != null).ToList(); - } - - if (propertyInfos.Count == 1) - { - return CreateObjectProperties(propertyInfos.ElementAt(0), sourceData); - } - - Log.Debug(Resources.PropertyGrid_GetObjectProperties_Multiple_object_property_instances_found_for_the_same_data_object__no_object_properties_are_displayed_in_the_property_grid); - return null; - } - /// /// Raises the event. /// @@ -157,56 +112,6 @@ SelectedObject = GetObjectProperties(selection); } - private List FilterPropertyInfoByTypeInheritance(List propertyInfo, Func getTypeAction) - { - var propertyInfoCount = propertyInfo.Count; - var propertyInfoWithUnInheritedType = propertyInfo.ToList(); - - for (var i = 0; i < propertyInfoCount; i++) - { - var firstType = getTypeAction(propertyInfo.ElementAt(i)); - - for (var j = 0; j < propertyInfoCount; j++) - { - if (i == j) - { - continue; - } - - var secondType = getTypeAction(propertyInfo.ElementAt(j)); - - if (firstType != secondType && firstType.IsAssignableFrom(secondType)) - { - propertyInfoWithUnInheritedType.Remove(propertyInfo.ElementAt(i)); - - break; - } - } - } - - return propertyInfoWithUnInheritedType.Any() - ? propertyInfoWithUnInheritedType.ToList() // One or more specific property information objects found: return the filtered list - : propertyInfo; // No specific property information found: return the original list - } - - private object CreateObjectProperties(PropertyInfo propertyInfo, object sourceData) - { - try - { - // Try to create object properties for the source data - var objectProperties = propertyInfo.CreateObjectProperties(sourceData); - - // Return a dynamic property bag containing the created object properties - return new DynamicPropertyBag(objectProperties); - } - catch (Exception) - { - Log.Debug(Resources.PropertyGrid_CreateObjectProperties_Could_not_create_object_properties_for_the_data); - - return null; - } - } - #region IPropertyGrid Members public object Data @@ -235,6 +140,16 @@ public void EnsureVisible(object item) {} + public object GetObjectProperties(object sourceData) + { + if (gui != null) + { + PropertyResolver.GetObjectProperties(gui.Plugins.SelectMany(p => p.GetPropertyInfos()).ToList(), sourceData); + } + + return null; + } + public ViewInfo ViewInfo { get; set; } #endregion Index: src/DeltaShell/DeltaShell.Gui/Forms/PropertyGridView/PropertyResolver.cs =================================================================== diff -u --- src/DeltaShell/DeltaShell.Gui/Forms/PropertyGridView/PropertyResolver.cs (revision 0) +++ src/DeltaShell/DeltaShell.Gui/Forms/PropertyGridView/PropertyResolver.cs (revision 9f1efb36120427350936d48059c87c7e011657a9) @@ -0,0 +1,108 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using DelftTools.Shell.Gui; +using DelftTools.Utils.PropertyBag.Dynamic; + +namespace DeltaShell.Gui.Forms.PropertyGridView +{ + /// + /// Helper class for resolving objects. + /// + public static class PropertyResolver + { + /// + /// Returns object properties based on the provided and . + /// + /// The list of property info objects to obtain the object properties from. + /// The source data to get the object properties for. + /// An object properties object, or null when no relevant propwrties object is found. + public static object GetObjectProperties(List propertyInfos, object sourceData) + { + if (sourceData == null) + { + return null; + } + + // 1. Match property information based on ObjectType and on AdditionalDataCheck + propertyInfos = propertyInfos.Where(pi => pi.ObjectType.IsInstanceOfType(sourceData) && (pi.AdditionalDataCheck == null || pi.AdditionalDataCheck(sourceData))).ToList(); + + // 2. Match property information based on object type inheritance + propertyInfos = FilterPropertyInfoByTypeInheritance(propertyInfos, pi => pi.ObjectType); + + // 3. Match property information based on property type inheritance + propertyInfos = FilterPropertyInfoByTypeInheritance(propertyInfos, pi => pi.PropertyType); + + if (propertyInfos.Count == 0) + { + // No (or multiple) object properties found: return 'null' so that no object properties are shown in the property grid + return null; + } + + if (propertyInfos.Count > 1) + { + // 4. We assume that the propertyInfos with AdditionalDataCheck are the most specific + propertyInfos = propertyInfos.Where(pi => pi.AdditionalDataCheck != null).ToList(); + } + + if (propertyInfos.Count == 1) + { + return CreateObjectProperties(propertyInfos.ElementAt(0), sourceData); + } + + return null; + } + + private static List FilterPropertyInfoByTypeInheritance(List propertyInfo, Func getTypeAction) + { + var propertyInfoCount = propertyInfo.Count; + var propertyInfoWithUnInheritedType = propertyInfo.ToList(); + + for (var i = 0; i < propertyInfoCount; i++) + { + var firstType = getTypeAction(propertyInfo.ElementAt(i)); + + for (var j = 0; j < propertyInfoCount; j++) + { + if (i == j) + { + continue; + } + + var secondType = getTypeAction(propertyInfo.ElementAt(j)); + + if (firstType != secondType && firstType.IsAssignableFrom(secondType)) + { + propertyInfoWithUnInheritedType.Remove(propertyInfo.ElementAt(i)); + + break; + } + } + } + + return propertyInfoWithUnInheritedType.Any() + ? propertyInfoWithUnInheritedType.ToList() // One or more specific property information objects found: return the filtered list + : propertyInfo; // No specific property information found: return the original list + } + + private static object CreateObjectProperties(PropertyInfo propertyInfo, object sourceData) + { + try + { + // Try to create object properties for the source data + var objectProperties = propertyInfo.CreateObjectProperties(sourceData); + + // Return a dynamic property bag containing the created object properties + return new DynamicPropertyBag(objectProperties); + return objectProperties is DynamicPropertyBag + ? (object)objectProperties + : new DynamicPropertyBag(objectProperties); + return new DynamicPropertyBag(objectProperties); + } + catch (Exception) + { + return sourceData; + } + } + } +} Index: test/DeltaShell/DeltaShell.Tests/DeltaShell.Tests.csproj =================================================================== diff -u -r9e0b372066f8d8be8c231b3be8922d29a1c61e1f -r9f1efb36120427350936d48059c87c7e011657a9 --- test/DeltaShell/DeltaShell.Tests/DeltaShell.Tests.csproj (.../DeltaShell.Tests.csproj) (revision 9e0b372066f8d8be8c231b3be8922d29a1c61e1f) +++ test/DeltaShell/DeltaShell.Tests/DeltaShell.Tests.csproj (.../DeltaShell.Tests.csproj) (revision 9f1efb36120427350936d48059c87c7e011657a9) @@ -87,7 +87,7 @@ - + Fisheye: Tag 9f1efb36120427350936d48059c87c7e011657a9 refers to a dead (removed) revision in file `test/DeltaShell/DeltaShell.Tests/Gui/Forms/PropertyGrid/PropertyGridTest.cs'. Fisheye: No comparison available. Pass `N' to diff? Index: test/DeltaShell/DeltaShell.Tests/Gui/Forms/PropertyGrid/PropertyResolverTest.cs =================================================================== diff -u --- test/DeltaShell/DeltaShell.Tests/Gui/Forms/PropertyGrid/PropertyResolverTest.cs (revision 0) +++ test/DeltaShell/DeltaShell.Tests/Gui/Forms/PropertyGrid/PropertyResolverTest.cs (revision 9f1efb36120427350936d48059c87c7e011657a9) @@ -0,0 +1,317 @@ +using System; +using System.Collections.Generic; +using DelftTools.Shell.Gui; +using DelftTools.TestUtils; +using DelftTools.Utils.PropertyBag.Dynamic; +using DeltaShell.Gui; +using DeltaShell.Gui.Forms.PropertyGridView; +using NUnit.Framework; +using Rhino.Mocks; + +namespace DeltaShell.Tests.Gui.Forms.PropertyGrid +{ + [TestFixture] + public class PropertyResolverTest + { + # region GetObjectProperties tests + + [Test] + public void GetObjectProperties_WhenNoPropertyInfoIsFound_ReturnNull() + { + // Assert + Assert.IsNull(PropertyResolver.GetObjectProperties(new List(), 1.0)); + } + + [Test] + public void GetObjectProperties_WhenOnePropertyInfoIsFound_ReturnDynamicPropertyBagContainingOnlyThatPropertiesObject() + { + // Setup + var propertyInfos = new List + { + new PropertyInfo>() + }; + + // Call + var objectProperties = PropertyResolver.GetObjectProperties(propertyInfos, new A()); + + // Assert + Assert.IsTrue(objectProperties is DynamicPropertyBag); + Assert.AreSame(typeof(SimpleProperties), ((DynamicPropertyBag) objectProperties).GetContentType()); + } + + + [Test] + public void GetObjectProperties_WhenOnePropertyInfoIsFoundButAdditionalChecksFail_ReturnNull() + { + // Setup + var propertyInfos = new List + { + new PropertyInfo>() + { + AdditionalDataCheck = o => false + } + }; + + // Call + var objectProperties = PropertyResolver.GetObjectProperties(propertyInfos, new A()); + + // Assert + Assert.IsNull(objectProperties); + } + + [Test] + public void GetObjectProperties_WhenTwoPropertyInfoAreFoundOneWithAdditionalCheckOneWithBetterType_ReturnPropertiesObjectMatchingAdditionCheck() + { + // Setup + var propertyInfos = new List + { + new PropertyInfo>() + { + AdditionalDataCheck = o => false + }, + new PropertyInfo>() // specifically for C + }; + + + // Call + var objectProperties = PropertyResolver.GetObjectProperties(propertyInfos, new C()); //we ask for C + + // Assert + Assert.AreSame(typeof(SimpleProperties), + ((DynamicPropertyBag)objectProperties).GetContentType(), "we got A, expected C"); + } + + [Test] + public void GetObjectProperties_BasedOnDirectObjectTypeMatch_ReturnObjectPropertiesMatchingTypeD() + { + // Setup + var propertyInfos = new List + { + new PropertyInfo>(), + new PropertyInfo>() + }; + + // Call + var objectProperties = PropertyResolver.GetObjectProperties(propertyInfos, new D()); + + // Setup + Assert.IsTrue(objectProperties is DynamicPropertyBag); + Assert.AreSame(typeof(SimpleProperties), ((DynamicPropertyBag)objectProperties).GetContentType()); + } + + [Test] + public void GetObjectProperties_BasedOnDerivedObjectTypeMatch_ReturnObjectPropertiesForBaseClass() + { + // Setup + var propertyInfos = new List + { + new PropertyInfo>(), + new PropertyInfo>() + }; + + + // Call + var objectProperties = PropertyResolver.GetObjectProperties(propertyInfos, new D()); + + // Assert + Assert.IsTrue(objectProperties is DynamicPropertyBag); + Assert.AreSame(typeof(SimpleProperties), ((DynamicPropertyBag)objectProperties).GetContentType()); + } + + [Test] + public void GetObjectProperties_BasedOnDerivedObjectTypeMatchAndAdditionalDataCheck_ReturnObjectPropertiesForBaseClassMatchingAdditionCheck() + { + // Setup + var propertyInfos = new List + { + new PropertyInfo> + { + AdditionalDataCheck = o => true + }, + new PropertyInfo> + { + AdditionalDataCheck = o => true + } + }; + + // Call + var objectProperties = PropertyResolver.GetObjectProperties(propertyInfos, new D()); + + // Assert + Assert.IsTrue(objectProperties is DynamicPropertyBag); + Assert.AreSame(typeof(SimpleProperties), ((DynamicPropertyBag)objectProperties).GetContentType()); + } + + [Test] + public void GetObjectProperties_BasedOnMatchingAdditionalDataCheck_ReturnMatchingWithAdditionalDataCheck() + { + // Setup + var propertyInfos = new List + { + new PropertyInfo> + { + AdditionalDataCheck = o => true + }, // Additional data check which will be matched + new PropertyInfo>() + }; + + // Call + var objectProperties = PropertyResolver.GetObjectProperties(propertyInfos, new B()); + + // Assert + Assert.IsTrue(objectProperties is DynamicPropertyBag); + Assert.AreSame(typeof(SimpleProperties), ((DynamicPropertyBag)objectProperties).GetContentType()); + } + + [Test] + public void GetObjectProperties_BasedOnMismatchingAdditionalDataCheck_ReturnFallBackPropertiesObject() + { + // Setup + var propertyInfos = new List + { + new PropertyInfo> + { + AdditionalDataCheck = o => false + }, // Additional data check which will not be matched + new PropertyInfo>() + }; + + // Call + var objectProperties = PropertyResolver.GetObjectProperties(propertyInfos, new B()); + + // Assert + Assert.IsTrue(objectProperties is DynamicPropertyBag); + Assert.AreSame(typeof(OtherSimpleProperties), ((DynamicPropertyBag)objectProperties).GetContentType()); + } + + [Test] + public void GetObjectProperties_BasedOnDerivedPropertyTypeMatch_ReturnDerivedObjectPropertiesClass() + { + // Setup + var propertyInfos = new List + { + new PropertyInfo>(), + new PropertyInfo>() + }; + + // Call + var objectProperties = PropertyResolver.GetObjectProperties(propertyInfos, new B()); + + // Assert + Assert.IsTrue(objectProperties is DynamicPropertyBag); + Assert.AreSame(typeof(DerivedSimpleProperties), ((DynamicPropertyBag)objectProperties).GetContentType()); + } + + [Test] + public void GetObjectProperties_BasedOnDerivedPropertyTypeMatchAndAdditionalDataCheck_ReturnDerivedObjectProperties() + { + // Setup + var propertyInfos = new List + { + new PropertyInfo> + { + AdditionalDataCheck = o => true + }, + new PropertyInfo> + { + AdditionalDataCheck = o => true + } + }; + + // Call + var objectProperties = PropertyResolver.GetObjectProperties(propertyInfos, new B()); + + // Assert + Assert.IsTrue(objectProperties is DynamicPropertyBag); + Assert.AreSame(typeof(DerivedSimpleProperties), ((DynamicPropertyBag)objectProperties).GetContentType()); + } + + [Test] + public void GetObjectProperties_WhenMultiplePropertyObjectsAreFound_ReturnNull() + { + // Setup + var propertyInfos = new List + { + new PropertyInfo>(), + new PropertyInfo>() + }; + + // Call + var objectProperties = PropertyResolver.GetObjectProperties(propertyInfos, new B()); + Assert.IsNull(objectProperties); + } + + [Test] + public void GetObjectProperties_TakingAllPropertyInfoPropertiesIntoAccount_ReturnDerivedObjectPropertiesMatchingDataCheck() + { + // Setup + var propertyInfos = new List + { + new PropertyInfo> + { + AdditionalDataCheck = o => true + }, // D is not assignable from C: no candidate + new PropertyInfo> + { + AdditionalDataCheck = o => true + }, // A is less specific than C: candidate but not the most specific one + new PropertyInfo> + { + AdditionalDataCheck = o => false + }, // Additional data check is false: no candidate + new PropertyInfo> + { + AdditionalDataCheck = o => true + }, // SimpleProperties is less specific than DerivedSimpleProperties: candidate but not the most specific one + new PropertyInfo> + { + AdditionalDataCheck = o => true + } // Most specific! + }; + + // Call + var objectProperties = PropertyResolver.GetObjectProperties(propertyInfos, new C()); + + // Assert + Assert.IsTrue(objectProperties is DynamicPropertyBag); + Assert.AreSame(typeof(DerivedSimpleProperties), ((DynamicPropertyBag)objectProperties).GetContentType()); + } + + # endregion + + # region Nested types + + /* + * A + * ___^___ + * | | + * B C + * | + * D + * + * + * + * SimpleProperties OtherSimpleProperties + * ^ + * | + * DerivedSimpleProperties + * + */ + + internal class A {} + + private class B : A {} + + internal class C : A {} + + private class D : C {} + + internal class SimpleProperties : ObjectProperties {} + + private class DerivedSimpleProperties : SimpleProperties {} + + private class OtherSimpleProperties : ObjectProperties {} + + # endregion + } +} \ No newline at end of file