//------------------------------------------------------------------------------ // // Copyright (c) Microsoft Corporation. All rights reserved. // //------------------------------------------------------------------------------ // Comment this out to get a version that doesn't need synchronized // access. This can be used for profiling, to compare whether the lock // or the late writing is more useful. using System; using System.Web.Configuration; using System.Configuration; using System.Xml; using System.Collections; using System.Diagnostics; using System.Reflection; using System.Reflection.Emit; using System.Web; using System.Web.Util; using System.Threading; using System.Web.Mobile; namespace System.Web.UI.MobileControls { // Data structure for an individual device configuration. // Included predicates, page adapter type, and a list of // control/controlAdapter pairs. [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 class IndividualDeviceConfig { internal delegate bool DeviceQualifiesDelegate(HttpContext context); private String _name; private readonly ControlsConfig _controlsConfig; private DeviceQualifiesDelegate _deviceQualifiesPredicate; private Type _pageAdapterType; private IWebObjectFactory _pageAdapterFactory; // Parent device configuration. private IndividualDeviceConfig _parentConfig; private String _parentConfigName; // ControlType --> ControlAdapterType mapping (one of these // per individual device config) private readonly Hashtable _controlAdapterTypes = new Hashtable(); // ControlType --> ControlAdapterType mapping cache, used to // store mappings that are derived from a complex lookup (one of these // per individual device config) private readonly Hashtable _controlAdapterLookupCache = new Hashtable(); // Provide synchronized access to the hashtable, allowing // multiple readers but just one writer. Here we have one per // device config. private readonly ReaderWriterLock _controlAdapterTypesLock = new ReaderWriterLock(); // The highest level to check. private static readonly Type _baseControlType = typeof(System.Web.UI.Control); // This constructor takes both a delegate that chooses this // device, and a Type to instantiate the appropriate page // adapter with. internal IndividualDeviceConfig(ControlsConfig controlsConfig, String name, DeviceQualifiesDelegate deviceQualifiesDelegate, Type pageAdapterType, String parentConfigName) { _controlsConfig = controlsConfig; _name = name; _deviceQualifiesPredicate = deviceQualifiesDelegate; _parentConfigName = parentConfigName; _parentConfig = null; PageAdapterType = pageAdapterType; } // This constructor takes just a page adapter for situations // where device selection isn't necessary (e.g., the designer). internal IndividualDeviceConfig(Type pageAdapterType) : this(null, null, null, pageAdapterType, null) { } // Given a context, see if this device config should handle // the given device. If there is no predicate, return true. internal /*public*/ bool DeviceQualifies(HttpContext context) { return _deviceQualifiesPredicate == null ? true : _deviceQualifiesPredicate(context); } // Register an adapter with the given control. internal /*public*/ void AddControl(Type controlType, Type adapterType) { // Don't need to synchronize, as this is only being called // from one thread -- the configuration section handler. _controlAdapterTypes[controlType] = FactoryGenerator.StaticFactoryGenerator.GetFactory(adapterType); } private Type PageAdapterType { get { return _pageAdapterType; } set { _pageAdapterType = value; if (value != null) { Debug.Assert(typeof(IPageAdapter).IsAssignableFrom(value)); _pageAdapterFactory = (IWebObjectFactory)FactoryGenerator.StaticFactoryGenerator.GetFactory(_pageAdapterType); } } } internal DeviceQualifiesDelegate DeviceQualifiesPredicate { get { return _deviceQualifiesPredicate; } set { _deviceQualifiesPredicate = value; } } protected IWebObjectFactory LookupControl(Type controlType) { return LookupControl(controlType, false); } private IWebObjectFactory LookupControl(Type controlType, bool lookInTypeCache) { IWebObjectFactory factory; factory = (IWebObjectFactory)_controlAdapterTypes[controlType]; if (factory == null && lookInTypeCache) { // Grab reader lock... using (new ReaderWriterLockResource(_controlAdapterTypesLock, false)) { factory = (IWebObjectFactory)_controlAdapterLookupCache[controlType]; } } return factory; } // Create a new page adapter for the device. internal /*public*/ IPageAdapter NewPageAdapter() { IPageAdapter a = _pageAdapterFactory.CreateInstance() as IPageAdapter; if (a == null) { throw new Exception( SR.GetString(SR.IndividualDeviceConfig_TypeMustSupportInterface, _pageAdapterType.FullName, "IPageAdapter")); } return a; } // Given a control's type, create a control adapter for it. internal virtual IControlAdapter NewControlAdapter(Type originalControlType) { IWebObjectFactory factory = GetAdapterFactory(originalControlType); // Should return non-null, or throw an exception. Debug.Assert(factory != null); IControlAdapter a = (IControlAdapter) factory.CreateInstance(); return a; } // Given a control's type, returns the adapter type to be used. // Note that it's legal to not register an adapter type for each // control type. // // This lookup uses the following steps: // // (1) Look up the control type directly, to see if an adapter type // has been registered for it. // (2) Walk up the control inheritance chain, to see if an adapter type // has been registered for the class. For example, if the passed // control type is a validator, check BaseValidator, Label, // TextControl, and finally MobileControl. // (3) If no adapter type has still been found, call the parent configuration, // if any, to look up the adapter type. For example, the CHTML device // configuration would call the HTML device configuration. // (4) If an adapter type is found, but is not explicitly registered for // the passed control type, add an entry to the table, so that // subsequent requests do not need to walk the hierarchy. protected IWebObjectFactory GetAdapterFactory(Type originalControlType) { Debug.Assert(_parentConfigName == null); Type controlType = originalControlType; IWebObjectFactory factory = LookupControl(controlType, true); // Look in type cache // Walk up hierarchy looking for registered adapters. // Stop when we get to the base control. while (factory == null && controlType != _baseControlType) { factory = LookupControl(controlType); if (factory == null) { controlType = controlType.BaseType; } } // Could not find one in the current hierarchy. So, look it up in // the parent config if there is one. if (factory == null && _parentConfig != null) { factory = _parentConfig.GetAdapterFactory(originalControlType); } if (factory == null) { throw new Exception( SR.GetString(SR.IndividualDeviceConfig_ControlWithIncorrectPageAdapter, controlType.FullName, _pageAdapterType.FullName)); } if (controlType != originalControlType) { // Add to lookup cache, so the next lookup won't require // traversing the hierarchy. // Grab writer lock... using (new ReaderWriterLockResource(_controlAdapterTypesLock, true)) { _controlAdapterLookupCache[originalControlType] = factory; } } return factory; } internal /*public*/ String Name { get { return _name; } } internal /*public*/ String ParentConfigName { get { return _parentConfigName; } set { _parentConfigName = null; } } internal /*public*/ IndividualDeviceConfig ParentConfig { get { return _parentConfig; } set { _parentConfig = value; } } private enum FixupState { NotFixedUp, FixingUp, FixedUp }; private FixupState _fixup = FixupState.NotFixedUp; internal /*public*/ void FixupInheritance(IndividualDeviceConfig referrer, XmlNode configNode) { if (_fixup == FixupState.FixedUp) { return; } if (_fixup == FixupState.FixingUp) { Debug.Assert(referrer != null); // Circular reference throw new Exception(SR.GetString(SR.MobileControlsSectionHandler_CircularReference, referrer.Name)); } _fixup = FixupState.FixingUp; if (ParentConfigName != null) { Debug.Assert(ParentConfigName.Length != 0 && ParentConfig == null); ParentConfig = _controlsConfig.GetDeviceConfig(ParentConfigName); if (ParentConfig == null) { throw new ConfigurationErrorsException( SR.GetString(SR.MobileControlsSectionHandler_DeviceConfigNotFound, ParentConfigName), configNode); } // Make sure parent is fixed up. ParentConfig.FixupInheritance(this, configNode); if (PageAdapterType == null) { PageAdapterType = ParentConfig.PageAdapterType; } if (DeviceQualifiesPredicate == null) { DeviceQualifiesPredicate = ParentConfig.DeviceQualifiesPredicate; } Debug.Assert(PageAdapterType != null); Debug.Assert(DeviceQualifiesPredicate != null); // Reset this since we don't need it any longer. ParentConfigName = null; } _fixup = FixupState.FixedUp; } } [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 class ReaderWriterLockResource : IDisposable { private ReaderWriterLock _lock; private bool _writerLock; internal /*public*/ ReaderWriterLockResource(ReaderWriterLock theLock, bool writerLock) { _lock = theLock; _writerLock = writerLock; if (_writerLock) { _lock.AcquireWriterLock(Timeout.Infinite); } else { _lock.AcquireReaderLock(Timeout.Infinite); } } /*public*/ void IDisposable.Dispose() { if (_writerLock) { _lock.ReleaseWriterLock(); } else { _lock.ReleaseReaderLock(); } } } }