From 82e54905b942665630e23cc4f84e06ed91e4c3d6 Mon Sep 17 00:00:00 2001 From: Matt Ward Date: Mon, 13 Apr 2009 10:37:38 +0000 Subject: [PATCH] Removed control specific code from generated code when adding an array of controls to a property in the python forms designer. git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/branches/3.0@3987 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61 --- .../PythonBinding/Project/Src/PythonForm.cs | 197 +++++++++++++----- .../Test/Designer/FindAddRangeMethodTests.cs | 95 +++++++++ ...erializableContentPropertiesTestFixture.cs | 46 ++++ .../IgnoreDesignTimePropertiesTestFixture.cs | 2 +- .../Test/Designer/IsSitedComponentTests.cs | 77 +++++++ .../Test/PythonBinding.Tests.csproj | 3 + 6 files changed, 367 insertions(+), 53 deletions(-) create mode 100644 src/AddIns/BackendBindings/Python/PythonBinding/Test/Designer/FindAddRangeMethodTests.cs create mode 100644 src/AddIns/BackendBindings/Python/PythonBinding/Test/Designer/GetSerializableContentPropertiesTestFixture.cs create mode 100644 src/AddIns/BackendBindings/Python/PythonBinding/Test/Designer/IsSitedComponentTests.cs diff --git a/src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/PythonForm.cs b/src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/PythonForm.cs index f76ccab512..bd677571ca 100644 --- a/src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/PythonForm.cs +++ b/src/AddIns/BackendBindings/Python/PythonBinding/Project/Src/PythonForm.cs @@ -29,7 +29,9 @@ namespace ICSharpCode.PythonBinding string indentString = String.Empty; int indent; IEventBindingService eventBindingService; - Attribute[] notDesignOnlyFilter = new Attribute[] { DesignOnlyAttribute.No }; + static readonly Attribute[] notDesignOnlyFilter = new Attribute[] { DesignOnlyAttribute.No }; + static readonly DesignerSerializationVisibility[] notHiddenDesignerVisibility = new DesignerSerializationVisibility[] { DesignerSerializationVisibility.Content, DesignerSerializationVisibility.Visible }; + static readonly DesignerSerializationVisibility[] contentDesignerVisibility = new DesignerSerializationVisibility[] { DesignerSerializationVisibility.Content }; /// /// Used so the EventBindingService.GetEventProperty method can be called to get the property descriptor @@ -115,11 +117,28 @@ namespace ICSharpCode.PythonBinding /// /// Gets a list of properties that should be serialized for the specified form. /// - public PropertyDescriptorCollection GetSerializableProperties(object obj) + public static PropertyDescriptorCollection GetSerializableProperties(object obj) { + return GetSerializableProperties(obj, notHiddenDesignerVisibility); + } + + /// + /// Gets a list of properties that should have their content serialized for the specified form. + /// + public static PropertyDescriptorCollection GetSerializableContentProperties(object obj) + { + return GetSerializableProperties(obj, contentDesignerVisibility); + } + + /// + /// Gets the serializable properties with the specified designer serialization visibility. + /// + public static PropertyDescriptorCollection GetSerializableProperties(object obj, DesignerSerializationVisibility[] visibility) + { + List requiredVisibility = new List(visibility); List properties = new List(); foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(obj, notDesignOnlyFilter).Sort()) { - if (property.SerializationVisibility != DesignerSerializationVisibility.Hidden) { + if (requiredVisibility.Contains(property.SerializationVisibility)) { if (property.ShouldSerializeValue(obj)) { properties.Add(property); } @@ -127,6 +146,76 @@ namespace ICSharpCode.PythonBinding } return new PropertyDescriptorCollection(properties.ToArray()); } + + /// + /// Determines whether the object is an IComponent and has a non-null ISite. + /// + public static bool IsSitedComponent(object obj) + { + IComponent component = obj as IComponent; + if (component != null) { + return component.Site != null; + } + return false; + } + + /// + /// Gets the AddRange method on the object that is not hidden from the designer. + /// + public static MethodInfo GetAddRangeSerializationMethod(object obj) + { + foreach (MethodInfo methodInfo in obj.GetType().GetMethods()) { + if (methodInfo.Name == "AddRange") { + ParameterInfo[] parameters = methodInfo.GetParameters(); + if (parameters.Length == 1) { + if (parameters[0].ParameterType.IsArray) { + if (!IsHiddenFromDesignerSerializer(methodInfo)) { + return methodInfo; + } + } + } + } + } + return null; + } + + /// + /// Gets the Add serialization method that is not hidden from the designer. + /// + public static MethodInfo GetAddSerializationMethod(object obj) + { + foreach (MethodInfo methodInfo in obj.GetType().GetMethods()) { + if (methodInfo.Name == "Add") { + return methodInfo; + } + } + return null; + } + + /// + /// Gets the type used in the array for the first parameter to the method. + /// + public static Type GetArrayParameterType(MethodInfo methodInfo) + { + if (methodInfo != null) { + ParameterInfo[] parameters = methodInfo.GetParameters(); + if (parameters.Length > 0) { + Type arrayType = parameters[0].ParameterType; + return arrayType.GetElementType(); + } + } + return null; + } + + public static bool IsHiddenFromDesignerSerializer(MethodInfo methodInfo) + { + foreach (DesignerSerializationVisibilityAttribute attribute in methodInfo.GetCustomAttributes(typeof(DesignerSerializationVisibilityAttribute), true)) { + if (attribute.Visibility == DesignerSerializationVisibility.Hidden) { + return true; + } + } + return false; + } void GenerateInitializeComponentMethodBodyInternal(Form form) { @@ -189,35 +278,17 @@ namespace ICSharpCode.PythonBinding return; } - string propertyName = GetPropertyName(propertyOwnerName, propertyDescriptor.Name); if (propertyDescriptor.SerializationVisibility == DesignerSerializationVisibility.Visible) { + string propertyName = GetPropertyName(propertyOwnerName, propertyDescriptor.Name); Control control = propertyValue as Control; if (control != null) { AppendIndentedLine(propertyName + " = self._" + control.Name); } else { AppendIndentedLine(propertyName + " = " + PythonPropertyValueAssignment.ToString(propertyValue)); } - } else { - // Content. - if (propertyDescriptor.Name == "Controls") { - Control parentControl = obj as Control; - foreach (Control childControl in parentControl.Controls) { - if (IsSitedComponent(childControl)) { - AppendIndentedLine(GetPropertyName(propertyOwnerName, "Controls") + ".Add(self._" + childControl.Name + ")"); - } - } - } else { - MenuStrip menuStrip = obj as MenuStrip; - ComboBox comboBox = obj as ComboBox; - ToolStripMenuItem menuItem = obj as ToolStripMenuItem; - if (menuStrip != null && propertyDescriptor.Name == "Items") { - AppendMenuStripItems(menuStrip); - } else if (comboBox != null && propertyDescriptor.Name == "Items") { - AppendSystemArray(comboBox.Name, "Items.AddRange", typeof(Object).FullName, comboBox.Items); - } else if (menuItem != null && propertyDescriptor.Name == "DropDownItems") { - AppendToolStripMenuItemDropDownItems(menuItem.Name, GetSitedToolStripItems(menuItem.DropDownItems)); - } - } + } else { + // DesignerSerializationVisibility.Content + AppendMethodCallWithArrayParameter(propertyOwnerName, obj, propertyDescriptor); } } @@ -373,11 +444,6 @@ namespace ICSharpCode.PythonBinding } } - static bool IsSitedComponent(IComponent component) - { - return component.Site != null; - } - bool HasSitedChildComponents(Control control) { MenuStrip menuStrip = control as MenuStrip; @@ -406,21 +472,13 @@ namespace ICSharpCode.PythonBinding return false; } - /// - /// Adds ToolStripItems that are stored on the MenuStrip. - /// - void AppendMenuStripItems(MenuStrip menuStrip) - { - List items = GetSitedToolStripItems(menuStrip.Items); - AppendMenuStripItemsAddRange(menuStrip.Name, items); - } - - void AppendSystemArray(string componentName, string methodName, string typeName, IList components) + void AppendSystemArray(string componentName, string methodName, string typeName, ICollection components) { if (components.Count > 0) { AppendIndentedLine("self._" + componentName + "." + methodName + "(System.Array[" + typeName + "]("); IncreaseIndent(); - for (int i = 0; i < components.Count; ++i) { + int i = 0; + foreach (object component in components) { if (i == 0) { AppendIndented("["); } else { @@ -428,12 +486,12 @@ namespace ICSharpCode.PythonBinding AppendLine(); AppendIndented(String.Empty); } - object component = components[i]; if (component is IComponent) { Append("self._" + ((IComponent)component).Site.Name); } else { Append(PythonPropertyValueAssignment.ToString(component)); } + ++i; } Append("]))"); AppendLine(); @@ -441,11 +499,6 @@ namespace ICSharpCode.PythonBinding } } - void AppendMenuStripItemsAddRange(string menuStripName, List items) - { - AppendSystemArray(menuStripName, "Items.AddRange", typeof(ToolStripItem).FullName, items); - } - List GetSitedToolStripItems(ToolStripItemCollection items) { List sitedItems = new List(); @@ -478,11 +531,6 @@ namespace ICSharpCode.PythonBinding } } - void AppendToolStripMenuItemDropDownItems(string menuItemName, List items) - { - AppendSystemArray(menuItemName, "DropDownItems.AddRange", typeof(ToolStripItem).FullName, items); - } - void AppendProperties(string propertyOwnerName, object obj) { foreach (PropertyDescriptor property in GetSerializableProperties(obj)) { @@ -506,5 +554,50 @@ namespace ICSharpCode.PythonBinding } } } + + /// + /// Returns the sited components in the collection. If an object in the collection is not + /// an IComponent then this is added to the collection. + /// + ICollection GetSitedComponentsAndNonComponents(ICollection components) + { + List sitedComponents = new List(); + foreach (object obj in components) { + IComponent component = obj as IComponent; + if (component == null || IsSitedComponent(component)) { + sitedComponents.Add(obj); + } + } + return sitedComponents.ToArray(); + } + + /// + /// Appends an array as a parameter and its associated method call. + /// + /// + /// Looks for the AddRange method first. If that does not exist or is hidden from the designer the + /// Add method is looked for. + /// + void AppendMethodCallWithArrayParameter(string propertyOwnerName, object propertyOwner, PropertyDescriptor propertyDescriptor) + { + IComponent component = propertyOwner as IComponent; + ICollection collectionProperty = propertyDescriptor.GetValue(propertyOwner) as ICollection; + if (collectionProperty != null) { + MethodInfo addRangeMethod = GetAddRangeSerializationMethod(collectionProperty); + if (addRangeMethod != null) { + Type arrayElementType = GetArrayParameterType(addRangeMethod); + AppendSystemArray(component.Site.Name, propertyDescriptor.Name + "." + addRangeMethod.Name, arrayElementType.FullName, GetSitedComponentsAndNonComponents(collectionProperty)); + } else { + MethodInfo addMethod = GetAddSerializationMethod(collectionProperty); + ParameterInfo[] parameters = addMethod.GetParameters(); + foreach (object item in collectionProperty) { + IComponent collectionComponent = item as IComponent; + if (IsSitedComponent(collectionComponent)) { + AppendIndentedLine(GetPropertyName(propertyOwnerName, propertyDescriptor.Name) + "." + addMethod.Name + "(self._" + collectionComponent.Site.Name + ")"); + } + } + } + } + } } } diff --git a/src/AddIns/BackendBindings/Python/PythonBinding/Test/Designer/FindAddRangeMethodTests.cs b/src/AddIns/BackendBindings/Python/PythonBinding/Test/Designer/FindAddRangeMethodTests.cs new file mode 100644 index 0000000000..72f100de4b --- /dev/null +++ b/src/AddIns/BackendBindings/Python/PythonBinding/Test/Designer/FindAddRangeMethodTests.cs @@ -0,0 +1,95 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Reflection; +using System.Windows.Forms; + +using ICSharpCode.PythonBinding; +using NUnit.Framework; + +namespace PythonBinding.Tests.Designer +{ + /// + /// Tests that the AddRange or Add method that takes an array of items can be determined. + /// + [TestFixture] + public class FindAddRangeMethodTests + { + [Test] + public void MenuStripItemsAddRangeMethod() + { + using (MenuStrip menuStrip = new MenuStrip()) { + MethodInfo expectedMethodInfo = FindMethod(menuStrip.Items, "AddRange", typeof(ToolStripItem[])); + Assert.IsNotNull(expectedMethodInfo); + + Assert.AreSame(expectedMethodInfo, PythonForm.GetAddRangeSerializationMethod(menuStrip.Items)); + } + } + + [Test] + public void GetArrayParameterType() + { + using (MenuStrip menuStrip = new MenuStrip()) { + MethodInfo methodInfo = FindMethod(menuStrip.Items, "AddRange", typeof(ToolStripItem[])); + Assert.AreEqual(typeof(ToolStripItem), PythonForm.GetArrayParameterType(methodInfo)); + } + } + + [Test] + public void GetArrayParameterTypeFromMethodWithNoParameters() + { + MethodInfo methodInfo = typeof(String).GetMethod("Clone"); + Assert.IsNull(PythonForm.GetArrayParameterType(methodInfo)); + } + + [Test] + public void GetArrayParameterTypeWithNullMethodInfo() + { + Assert.IsNull(PythonForm.GetArrayParameterType(null)); + } + + /// + /// Form.Controls.AddRange() method should not be returned since it is marked with + /// DesignerSerializationVisibility.Hidden. + /// + [Test] + public void FormControlsAddRangeMethodNotFound() + { + using (Form form = new Form()) { + Assert.IsNull(PythonForm.GetAddRangeSerializationMethod(form.Controls)); + } + } + + [Test] + public void FormControlsAddMethod() + { + using (Form form = new Form()) { + MethodInfo expectedMethodInfo = FindMethod(form.Controls, "Add", typeof(Control)); + Assert.IsNotNull(expectedMethodInfo); + + Assert.AreSame(expectedMethodInfo, PythonForm.GetAddSerializationMethod(form.Controls)); + } + } + + static MethodInfo FindMethod(object obj, string methodName, Type parameterType) + { + foreach (MethodInfo methodInfo in obj.GetType().GetMethods()) { + if (methodInfo.Name == methodName) { + ParameterInfo[] parameters = methodInfo.GetParameters(); + if (parameters.Length == 1) { + ParameterInfo param = parameters[0]; + if (param.ParameterType == parameterType) { + return methodInfo; + } + } + } + } + return null; + } + } +} diff --git a/src/AddIns/BackendBindings/Python/PythonBinding/Test/Designer/GetSerializableContentPropertiesTestFixture.cs b/src/AddIns/BackendBindings/Python/PythonBinding/Test/Designer/GetSerializableContentPropertiesTestFixture.cs new file mode 100644 index 0000000000..55342377af --- /dev/null +++ b/src/AddIns/BackendBindings/Python/PythonBinding/Test/Designer/GetSerializableContentPropertiesTestFixture.cs @@ -0,0 +1,46 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.ComponentModel; +using System.Windows.Forms; +using ICSharpCode.PythonBinding; +using NUnit.Framework; + +namespace PythonBinding.Tests.Designer +{ + /// + /// Gets properties that are marked as DesignerSerializationVisibility.Content + /// + [TestFixture] + public class GetSerializableContentPropertiesTestFixture + { + PropertyDescriptorCollection properties; + + [TestFixtureSetUp] + public void SetUpFixture() + { + using (Form form = new Form()) { + // Modify Form.Text so it is identified as needing serialization. + form.Text = "abc"; + properties = PythonForm.GetSerializableContentProperties(form); + } + } + + [Test] + public void FormControlsPropertyReturned() + { + Assert.IsNotNull(properties.Find("Controls", false), "Property not found: Controls"); + } + + [Test] + public void FormTextPropertyIsNotReturned() + { + Assert.IsNull(properties.Find("Text", false), "Property should not be found: Text"); + } + } +} diff --git a/src/AddIns/BackendBindings/Python/PythonBinding/Test/Designer/IgnoreDesignTimePropertiesTestFixture.cs b/src/AddIns/BackendBindings/Python/PythonBinding/Test/Designer/IgnoreDesignTimePropertiesTestFixture.cs index 8865dab522..b928d8a429 100644 --- a/src/AddIns/BackendBindings/Python/PythonBinding/Test/Designer/IgnoreDesignTimePropertiesTestFixture.cs +++ b/src/AddIns/BackendBindings/Python/PythonBinding/Test/Designer/IgnoreDesignTimePropertiesTestFixture.cs @@ -56,7 +56,7 @@ namespace PythonBinding.Tests.Designer PythonForm pythonForm = new PythonForm(" "); generatedCode = pythonForm.GenerateInitializeComponentMethod(form); - propertyDescriptors = pythonForm.GetSerializableProperties(form); + propertyDescriptors = PythonForm.GetSerializableProperties(form); } } diff --git a/src/AddIns/BackendBindings/Python/PythonBinding/Test/Designer/IsSitedComponentTests.cs b/src/AddIns/BackendBindings/Python/PythonBinding/Test/Designer/IsSitedComponentTests.cs new file mode 100644 index 0000000000..928ce5768c --- /dev/null +++ b/src/AddIns/BackendBindings/Python/PythonBinding/Test/Designer/IsSitedComponentTests.cs @@ -0,0 +1,77 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.ComponentModel; +using ICSharpCode.PythonBinding; +using NUnit.Framework; +using PythonBinding.Tests.Utils; + +namespace PythonBinding.Tests.Designer +{ + [TestFixture] + public class IsSitedComponentTests : ISite + { + [Test] + public void NullComponent() + { + Assert.IsFalse(PythonForm.IsSitedComponent(null)); + } + + [Test] + public void ComponentNotSited() + { + Assert.IsFalse(PythonForm.IsSitedComponent(new Component())); + } + + [Test] + public void SitedComponent() + { + Component component = new Component(); + component.Site = this; + Assert.IsTrue(PythonForm.IsSitedComponent(component)); + } + + [Test] + public void NonComponent() + { + Assert.IsFalse(PythonForm.IsSitedComponent(String.Empty)); + } + + public IComponent Component { + get { + throw new NotImplementedException(); + } + } + + public IContainer Container { + get { + throw new NotImplementedException(); + } + } + + public bool DesignMode { + get { + throw new NotImplementedException(); + } + } + + public string Name { + get { + throw new NotImplementedException(); + } + set { + throw new NotImplementedException(); + } + } + + public object GetService(Type serviceType) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/AddIns/BackendBindings/Python/PythonBinding/Test/PythonBinding.Tests.csproj b/src/AddIns/BackendBindings/Python/PythonBinding/Test/PythonBinding.Tests.csproj index 76bd6fb4fc..5e19865888 100644 --- a/src/AddIns/BackendBindings/Python/PythonBinding/Test/PythonBinding.Tests.csproj +++ b/src/AddIns/BackendBindings/Python/PythonBinding/Test/PythonBinding.Tests.csproj @@ -152,6 +152,7 @@ + @@ -175,10 +176,12 @@ + +