//------------------------------------------------------------------------------ // // Copyright (c) Microsoft Corporation. All rights reserved. // //------------------------------------------------------------------------------ namespace System.Web.UI.Design.MobileControls { using System; using System.Web.UI; using System.Web.UI.HtmlControls; using System.Web.UI.MobileControls; using System.Web.UI.WebControls; using System.Collections; using System.Collections.Specialized; using System.ComponentModel; using System.ComponentModel.Design; using System.Diagnostics; using System.IO; using System.Reflection; using System.Text; using AttributeCollection = System.Web.UI.AttributeCollection; using System.Globalization; /// /// /// Provides helper functions used in persisting Controls. /// /// [ System.Security.Permissions.SecurityPermission(System.Security.Permissions.SecurityAction.Demand, Flags=System.Security.Permissions.SecurityPermissionFlag.UnmanagedCode) ] [Obsolete("The System.Web.Mobile.dll assembly has been deprecated and should no longer be used. For information about how to develop ASP.NET mobile applications, see http://go.microsoft.com/fwlink/?LinkId=157231.")] internal sealed class MobileControlPersister { /// /// We don't want instances of this class to be created, so mark /// the constructor as private. /// private MobileControlPersister() { } /// /// /// Gets the delarative type for the /// specified type. /// /// /// /// The type of the declarator. /// /// /// The services interface exposed by the webforms designer. /// private static string GetDeclarativeType(Type type, IDesignerHost host) { Debug.Assert(host != null, "Need an IDesignerHost to create declarative type names"); string declarativeType = null; if (host != null) { IWebFormReferenceManager refMgr = (IWebFormReferenceManager)host.GetService(typeof(IWebFormReferenceManager)); Debug.Assert(refMgr != null, "Did not get back IWebFormReferenceManager service from host."); if (refMgr != null) { string tagPrefix = refMgr.GetTagPrefix(type); if ((tagPrefix != null) && (tagPrefix.Length != 0)) { declarativeType = tagPrefix + ":" + type.Name; } } } if (declarativeType == null) { /* Begin AUI 7201 */ /* Original declarativeType = type.FullName; */ if (type == typeof(System.Web.UI.MobileControls.Style)) { declarativeType = type.Name; } else { declarativeType = type.FullName; } /* End AUI 7201 */ } return declarativeType; } /// /// /// Persists a collection property. /// /// /// /// The persistance mode to use. /// /// /// The string writer to use. /// /// /// The component to persist. /// /// /// A property descriptor for the collection property. /// /// /// The services interface exposed by the webforms designer. /// private static void PersistCollectionProperty(TextWriter sw, object component, PropertyDescriptor propDesc, PersistenceMode persistMode, IDesignerHost host) { Debug.Assert(typeof(ICollection).IsAssignableFrom(propDesc.PropertyType), "Invalid collection property : " + propDesc.Name); ICollection propValue = (ICollection)propDesc.GetValue(component); if ((propValue == null) || (propValue.Count == 0)) return; // Begin AUI Change #3785 // Original: sw.WriteLine(); if (!(component is DeviceSpecific)) { sw.WriteLine(); } // End of Change #3785 if (persistMode == PersistenceMode.InnerProperty) { sw.Write('<'); sw.Write(propDesc.Name); sw.WriteLine('>'); } IEnumerator e = propValue.GetEnumerator(); while (e.MoveNext()) { object collItem = e.Current; // Begin of AUI Change //string itemTypeName = GetDeclarativeType(collItem.GetType(), host); string itemTypeName; // AUI : To fix Hashtable objects used in Mobile Controls, only persist the value part if (collItem is DictionaryEntry) { collItem = ((DictionaryEntry)collItem).Value; } // AUI : First check if the control already has a Default Persist Name, // if not, use the Type as its name PersistNameAttribute pna = (PersistNameAttribute)TypeDescriptor.GetAttributes(collItem.GetType())[typeof(PersistNameAttribute)]; // attribute should returns default value if it's null. // this is unlikely to happen, but just to be on the safe side. Debug.Assert (pna != null, "PersistNameAttribute returns null!"); string persistName = (string)pna.Name; if (persistName != null && persistName.Length > 0) { itemTypeName = persistName; } /* AUI Change #3911 */ /* Original : else if (collItem is Control) */ else if (collItem is Control || collItem.GetType() == typeof(System.Web.UI.MobileControls.Style)) { itemTypeName = GetDeclarativeType(collItem.GetType(), host); } else { itemTypeName = collItem.GetType().Name; } // End of AUI Change sw.Write("<"); sw.Write(itemTypeName); PersistAttributes(sw, collItem, String.Empty, null); sw.Write(">"); if (collItem is Control) { PersistChildrenAttribute pca = (PersistChildrenAttribute)TypeDescriptor.GetAttributes(collItem.GetType())[typeof(PersistChildrenAttribute)]; if (pca.Persist == true) { // asurt 106696: ensure the parent control's visibility is set to true. Control parentControl = (Control)collItem; if (parentControl.HasControls()) { bool oldVisible = parentControl.Visible; try { parentControl.Visible = true; PersistChildControls(sw, parentControl.Controls, host); } finally { parentControl.Visible = oldVisible; } } } else { PersistInnerProperties(sw, collItem, host); } } else { PersistInnerProperties(sw, collItem, host); } sw.Write(""); } if (persistMode == PersistenceMode.InnerProperty) { sw.Write("'); } } /// /// /// Persists a complex property. /// /// /// /// The persistance mode to use. /// /// /// The string writer to use. /// /// /// The component to persist. /// /// /// A property descriptor for the complex property. /// /// /// The services interface exposed by the webforms designer. /// private static void PersistComplexProperty(TextWriter sw, object component, PropertyDescriptor propDesc, IDesignerHost host) { object propValue = propDesc.GetValue(component); if (propValue == null) { return; } StringWriter tagProps = new StringWriter(CultureInfo.InvariantCulture); StringWriter innerProps = new StringWriter(CultureInfo.InvariantCulture); PersistAttributes(tagProps, propValue, String.Empty, null); PersistInnerProperties(innerProps, propValue, host); // the rule here is that if a complex property has all its subproperties // in the default state, then it itself is in the default state. // When this is the case, there shouldn't be any tag properties or inner properties if ((tagProps.GetStringBuilder().Length != 0) || (innerProps.GetStringBuilder().Length != 0)) { sw.WriteLine(); sw.Write('<'); sw.Write(propDesc.Name); sw.Write(tagProps.ToString()); sw.WriteLine(">"); string innerPropsString = innerProps.ToString(); sw.Write(innerPropsString); if (innerPropsString.Length != 0) { // Begin AUI Change #3785 // Original: sw.WriteLine(); if (!propDesc.Name.Equals("DeviceSpecific")) { sw.WriteLine(); } // End AUI Change #3785 } sw.Write("'); } } /// /// /// Persists the data bindings of the specified control using the specified /// string writer. /// /// /// /// The string writer to use. /// /// /// The control to use. /// private static void PersistDataBindings(TextWriter sw, Control control) { DataBindingCollection bindings = ((IDataBindingsAccessor)control).DataBindings; IEnumerator bindingEnum = bindings.GetEnumerator(); while (bindingEnum.MoveNext()) { DataBinding db = (DataBinding)bindingEnum.Current; string persistPropName = db.PropertyName.Replace('.', '-'); sw.Write(" "); sw.Write(persistPropName); sw.Write("='<%# "); sw.Write(HttpUtility.HtmlEncode(db.Expression)); sw.Write(" %>'"); } } /// /// /// Gets a string that can persist the inner properties of a control. /// /// /// /// /// Gets a string that can persist the inner properties of a control. /// /// /// /// The component to persist. /// /// /// The services interface exposed by the webforms designer. /// /// /// /// A string that contains the persistable information about /// the inner properties /// of the control. /// /// internal static string PersistInnerProperties(object component, IDesignerHost host) { StringWriter sw = new StringWriter(CultureInfo.InvariantCulture); PersistInnerProperties(sw, component, host); return sw.ToString(); } /// /// /// Persists the inner properties of the control. /// /// /// /// The string writer to use. /// /// /// The component to persist. /// /// /// The services interface exposed by the webforms designer. /// internal static void PersistInnerProperties(TextWriter sw, object component, IDesignerHost host) { PropertyDescriptorCollection propDescs = TypeDescriptor.GetProperties(component); for (int i = 0; i < propDescs.Count; i++) { // Only deal with inner attributes that need to be persisted if (propDescs[i].SerializationVisibility == DesignerSerializationVisibility.Hidden) continue; PersistenceModeAttribute persistenceMode = (PersistenceModeAttribute)propDescs[i].Attributes[typeof(PersistenceModeAttribute)]; if (persistenceMode.Mode == PersistenceMode.Attribute) { continue; } if (propDescs[i].PropertyType == typeof(string)) { // String based property... DataBindingCollection dataBindings = null; if (component is IDataBindingsAccessor) { dataBindings = ((IDataBindingsAccessor)component).DataBindings; } if (dataBindings == null || dataBindings[propDescs[i].Name] == null) { PersistenceMode mode = persistenceMode.Mode; if ((mode == PersistenceMode.InnerDefaultProperty) || (mode == PersistenceMode.EncodedInnerDefaultProperty)) { PersistStringProperty(sw, component, propDescs[i], mode); } else { Debug.Fail("Cannot persist inner string property marked with PersistenceMode.InnerProperty"); } } } else if (typeof(ITemplate).IsAssignableFrom(propDescs[i].PropertyType)) { // Template based property... if (persistenceMode.Mode == PersistenceMode.InnerProperty) { PersistTemplateProperty(sw, component, propDescs[i]); } else { Debug.Fail("Cannot persist template property " + propDescs[i].Name + " not marked with PersistenceMode.InnerProperty"); } } /* AUI change 03/21/01 */ else if (propDescs[i].DisplayName.Equals("Templates") && component is DeviceSpecificChoice && typeof(IDictionary).IsAssignableFrom(propDescs[i].PropertyType)) { IDictionary templateCollection = (IDictionary)propDescs[i].GetValue(component); foreach (String templateName in templateCollection.Keys) { ITemplate template = (ITemplate)templateCollection[templateName]; PersistTemplateProperty(sw, templateName, template); } } /* End of AUI change*/ else if (typeof(ICollection).IsAssignableFrom(propDescs[i].PropertyType)) { // Collection based property... if ((persistenceMode.Mode == PersistenceMode.InnerProperty) || (persistenceMode.Mode == PersistenceMode.InnerDefaultProperty)) { PersistCollectionProperty(sw, component, propDescs[i], persistenceMode.Mode, host); } else { Debug.Fail("Cannot persist collection property " + propDescs[i].Name + " not marked with PersistenceMode.InnerProperty or PersistenceMode.InnerDefaultProperty"); } } else { // Other complex property... if (persistenceMode.Mode == PersistenceMode.InnerProperty) { PersistComplexProperty(sw, component, propDescs[i], host); } else { Debug.Fail("Cannot persist complex property " + propDescs[i].Name + " not marked with PersistenceMode.InnerProperty"); } } } } /// /// /// Persists the properties of a /// string. /// /// /// /// The persistance mode to use. /// /// /// The string writer to use. /// /// /// The component to persist. /// /// /// A property descriptor for the string properties. /// private static void PersistStringProperty(TextWriter sw, object component, PropertyDescriptor propDesc, PersistenceMode mode) { Debug.Assert(propDesc.PropertyType == typeof(string), "Invalid string property : " + propDesc.Name); Debug.Assert((mode == PersistenceMode.InnerDefaultProperty) || (mode == PersistenceMode.EncodedInnerDefaultProperty), "Inner string properties must be marked as either InnerDefaultProperty or EncodedInnerDefaultProperty"); object propValue = propDesc.GetValue(component); if (propValue == null) { return; } if (mode == PersistenceMode.InnerDefaultProperty) { sw.Write((string)propValue); } else { HttpUtility.HtmlEncode((string)propValue, sw); } } /// /// /// Persists the properties of a tag. /// /// /// /// The string writer to use. /// /// /// The component to persist. /// /// /// The prefix to store. /// /// /// A property descriptor for the tag properties. /// private static void PersistAttributes(TextWriter sw, object component, string prefix, PropertyDescriptor propDesc) { PropertyDescriptorCollection properties; string persistPrefix = String.Empty; object value = component; if (propDesc != null) { value = propDesc.GetValue(component); properties = TypeDescriptor.GetProperties(propDesc.PropertyType, new Attribute[] { PersistenceModeAttribute.Attribute }); } else { properties = TypeDescriptor.GetProperties(component, new Attribute[] { PersistenceModeAttribute.Attribute }); } if (value == null) return; if (prefix.Length != 0) persistPrefix = prefix + "-"; DataBindingCollection dataBindings = null; bool isControl = (component is Control); if ((component is IDataBindingsAccessor)) dataBindings = ((IDataBindingsAccessor)component).DataBindings; if (component is DeviceSpecificChoice) { properties = properties.Sort(new String[] {"Filter"}); } for (int i = 0; i < properties.Count; i++) { // Skip properties that are hidden to the serializer if (properties[i].SerializationVisibility == DesignerSerializationVisibility.Hidden) { continue; } // Skip design-time only properties such as DefaultModifiers and Name DesignOnlyAttribute doAttr = (DesignOnlyAttribute)properties[i].Attributes[typeof(DesignOnlyAttribute)]; if ((doAttr != null) && doAttr.IsDesignOnly) { continue; } string propName = properties[i].Name; Type propType = properties[i].PropertyType; object obj = properties[i].GetValue(value); if (obj == null) continue; DefaultValueAttribute defValAttr = (DefaultValueAttribute)properties[i].Attributes[typeof(DefaultValueAttribute)]; if ((defValAttr != null) && (obj.Equals(defValAttr.Value))) continue; string persistName = propName; /* AUI Change 3876 -- Change is taken out because of 4347 if (component is DeviceSpecificChoice && persistName.Equals("Xmlns")) { persistName = "xmlns"; } End of Change */ if (prefix.Length != 0) persistName = persistPrefix + persistName; PropertyDescriptorCollection subProps = null; if (properties[i].SerializationVisibility == DesignerSerializationVisibility.Content) { subProps = TypeDescriptor.GetProperties(propType); } if ((subProps == null) || (subProps.Count == 0)) { string persistValue = null; // DataBinding db = null; if (dataBindings != null) db = dataBindings[persistName.Replace('.', '-')]; if (db == null) { if (propType.IsEnum) { persistValue = Enum.Format(propType, obj, "G"); } else if (propType == typeof(string)) { persistValue = HttpUtility.HtmlEncode(obj.ToString()); } else { TypeConverter converter = properties[i].Converter; if (converter != null) { persistValue = converter.ConvertToInvariantString(null, obj); } else { persistValue = obj.ToString(); } persistValue = HttpUtility.HtmlEncode(persistValue); } if ((persistValue == null) || (persistValue.Equals("NotSet")) || (propType.IsArray && (persistValue.Length == 0))) continue; sw.Write(" "); sw.Write(persistName); sw.Write("=\""); sw.Write(persistValue); sw.Write("\""); } } else { /* * This will force all ListDictionary properties with DesignerSerializationVisibility.Content atttribute be * persisted as a series of attributes eg. . The * WebControlPersistor is not able to handle this case and will return undesired results. */ // AUI Change to handle DeviceSpecificChoice.Contents if (obj is ListDictionary) { IDictionaryEnumerator enumerator = ((ListDictionary)obj).GetEnumerator (); String persistValue = null; while (enumerator.MoveNext ()) { propName = enumerator.Key as String; persistValue = enumerator.Value as String; Debug.Assert (propName != null, "Non-string key in DeviceSpecificChoice Contents."); if ((propName.Length == 0) || (persistValue == null)) continue; sw.Write(" "); sw.Write(propName); sw.Write("=\""); HttpUtility.HtmlEncode((String)persistValue, sw); sw.Write("\""); } } else { // End of AUI Change // there are sub properties, don't persist this object, but // recursively persist the subproperties. PersistAttributes(sw, obj, persistName, null); } } } // Persist all the databindings on this control if (isControl) { PersistDataBindings(sw, (Control)component); AttributeCollection expandos = null; if (component is WebControl) { expandos = ((WebControl)component).Attributes; } else if (component is HtmlControl) { expandos = ((HtmlControl)component).Attributes; } else if (component is UserControl) { expandos = ((UserControl)component).Attributes; } if (expandos != null) { foreach (string key in expandos.Keys) { sw.Write(" "); sw.Write(key); sw.Write("=\""); sw.Write(expandos[key]); sw.Write("\""); } } } } /// /// /// Persists a template property including the specified persistance mode, /// string writer and property descriptor. /// /// /// /// The persistence mode to use. /// /// /// The string writer to use. /// /// /// The component to persist. /// /// /// A property descriptor for the property. /// /* AUI change 03/21/01 added support for persisting Template collection */ private static void PersistTemplateProperty(TextWriter sw, object component, PropertyDescriptor propDesc) { Debug.Assert(typeof(ITemplate).IsAssignableFrom(propDesc.PropertyType), "Invalid template property : " + propDesc.Name); ITemplate template = (ITemplate)propDesc.GetValue(component); String templateName = propDesc.Name; PersistTemplateProperty(sw, templateName, template); } /* AUI change 03/21/01 made the following code a seperate method */ private static void PersistTemplateProperty(TextWriter sw, String templateName, ITemplate template) { if (template == null) { return; } //string templateContent = ((TemplateBuilder)template).Text; string templateContent; Debug.Assert(template is TemplateBuilder, "Unexpected ITemplate implementation."); if (template is TemplateBuilder) { templateContent = ((TemplateBuilder)template).Text; } else { templateContent = String.Empty; } sw.WriteLine(); sw.Write('<'); // changed propDesc.Name to templateName sw.Write(templateName); sw.Write('>'); if (!templateContent.StartsWith("\r\n", StringComparison.Ordinal)) { sw.WriteLine(); } sw.Write(templateContent); if (!templateContent.EndsWith("\r\n", StringComparison.Ordinal)) { sw.WriteLine(); } sw.Write("'); } /// /// /// Gets a string that can /// persist a control. /// /// /// /// /// Gets a string that can /// persist a control. /// /// /// /// The control to persist. /// /// /// /// A string that contains the persistable information about /// the control. /// /// internal static string PersistControl(Control control) { StringWriter sw = new StringWriter(CultureInfo.InvariantCulture); PersistControl(sw, control); return sw.ToString(); } /// /// /// Returns a string that can /// persist a control. /// /// /// /// /// Returns a string that can /// persist a control. /// /// /// /// The control to persist. /// /// /// The services interface exposed by the webforms designer. /// /// /// /// A string that contains the persistable information about /// the control. /// /// internal static string PersistControl(Control control, IDesignerHost host) { StringWriter sw = new StringWriter(CultureInfo.InvariantCulture); PersistControl(sw, control, host); return sw.ToString(); } /// /// /// Persists a control using the /// specified string writer. /// /// /// /// The string writer to use. /// /// /// The control to persist. /// internal static void PersistControl(TextWriter sw, Control control) { if (control is LiteralControl) { PersistLiteralControl(sw, (LiteralControl)control); return; } if (control is DesignerDataBoundLiteralControl) { PersistDataBoundLiteralControl(sw, (DesignerDataBoundLiteralControl)control); return; } ISite site = control.Site; if (site == null) { IComponent baseComponent = (IComponent)control.Page; Debug.Assert(baseComponent != null, "Control does not have its Page set!"); if (baseComponent != null) { site = baseComponent.Site; } } IDesignerHost host = null; if (site != null) { host = (IDesignerHost)site.GetService(typeof(IDesignerHost)); } Debug.Assert(host != null, "Did not get a valid IDesignerHost reference. Expect persistence problems!"); PersistControl(sw, control, host); } /// /// /// Persists a control using the /// specified string writer. /// /// /// /// The string writer to use. /// /// /// The control to persist. /// /// /// The services interface exposed by the webforms designer. /// internal static void PersistControl(TextWriter sw, Control control, IDesignerHost host) { // Literals and DataBoundLiterals must be handled specially, since they // don't have a tag around them if (control is LiteralControl) { PersistLiteralControl(sw, (LiteralControl)control); return; } if (control is DesignerDataBoundLiteralControl) { PersistDataBoundLiteralControl(sw, (DesignerDataBoundLiteralControl)control); return; } Debug.Assert(host != null, "Did not get a valid IDesignerHost reference. Expect persistence problems!"); string tagName = null; bool isUserControl = false; if (control is HtmlControl) { tagName = ((HtmlControl)control).TagName; } else if (control is UserControl) { tagName = ((IUserControlDesignerAccessor)control).TagName; Debug.Assert((tagName != null) && (tagName.Length != 0)); if (tagName.Length == 0) { // not enough information to go any further... no options, other than to throw this control out return; } isUserControl = true; } else { tagName = GetDeclarativeType(control.GetType(), host); } sw.Write('<'); sw.Write(tagName); sw.Write(" runat=\"server\""); PersistAttributes(sw, control, String.Empty, null); sw.Write('>'); if (isUserControl == false) { PersistChildrenAttribute pca = (PersistChildrenAttribute)TypeDescriptor.GetAttributes(control.GetType())[typeof(PersistChildrenAttribute)]; if (pca.Persist == true) { if (control.HasControls()) { // asurt 106696: Ensure parent control's visibility is true. bool oldVisible = control.Visible; try { control.Visible = true; PersistChildControls(sw, control.Controls, host); } finally { control.Visible = oldVisible; } } } else { // controls marked with LiteralContent == true shouldn't have // children in their persisted form. They only build children // collections at runtime. PersistInnerProperties(sw, control, host); } } else { string innerText = ((IUserControlDesignerAccessor)control).InnerText; if ((innerText != null) && (innerText.Length != 0)) { sw.Write(innerText); } } sw.Write("'); } /// /// /// Persists the child controls of /// the control using the specified string writer. /// /// /// /// The string writer to use. /// /// /// The control collection to persist. /// /// /// The services interface exposed by the webforms designer. /// private static void PersistChildControls(TextWriter sw, ControlCollection controls, IDesignerHost host) { int children = controls.Count; for (int i = 0; i < children; i++) { PersistControl(sw, controls[i], host); } } private static void PersistDataBoundLiteralControl(TextWriter sw, DesignerDataBoundLiteralControl control) { Debug.Assert(((IDataBindingsAccessor)control).HasDataBindings == true); DataBindingCollection bindings = ((IDataBindingsAccessor)control).DataBindings; DataBinding textBinding = bindings["Text"]; Debug.Assert(textBinding != null, "Did not get a Text databinding from DesignerDataBoundLiteralControl"); if (textBinding != null) { sw.Write("<%# "); sw.Write(textBinding.Expression); sw.Write(" %>"); } } private static void PersistLiteralControl(TextWriter sw, LiteralControl control) { Debug.Assert(control.Text != null); sw.Write(control.Text); } } }