File: UI\TagNameToTypeMapper.cs
Project: ndp\fx\src\xsp\system\Web\System.Web.csproj (System.Web)
//------------------------------------------------------------------------------
// <copyright file="TagNameToTypeMapper.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>                                                                
//------------------------------------------------------------------------------
 
/*
 * Base Control factory implementation
 *
 * Copyright (c) 1998 Microsoft Corporation
 */
 
namespace System.Web.UI {
 
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Collections.Specialized;
    using System.ComponentModel.Design;
    using System.Reflection;
    using System.Globalization;
    using System.Web.Hosting;
    using System.Web.Util;
    using System.Web.Compilation;
    using System.Web.Configuration;
#if !FEATURE_PAL
    using System.Web.UI.Design;
#endif // !FEATURE_PAL
 
// 
 
 
 
 
    /// <devdoc>
    ///    <para>Maps a sequence of text characters to a .NET Framework 
    ///       type when an .aspx file is processed on the server.</para>
    /// </devdoc>
    internal interface ITagNameToTypeMapper {
        /*
         * Return the Type of the control that should handle a tag with the
         * passed in properties.
         */
 
        /// <devdoc>
        ///    <para>Retrieves the .NET Framework type that should process 
        ///       the control declared in the .aspx file.</para>
        ///    </devdoc>
        Type GetControlType(string tagName, IDictionary attribs);
    }
 
 
    internal class NamespaceTagNameToTypeMapper : ITagNameToTypeMapper {
        private TagNamespaceRegisterEntry _nsRegisterEntry;
        private Assembly _assembly;
        private TemplateParser _parser;
 
        internal NamespaceTagNameToTypeMapper(TagNamespaceRegisterEntry nsRegisterEntry, Assembly assembly, TemplateParser parser) {
            _nsRegisterEntry = nsRegisterEntry;
            _assembly = assembly;
            _parser = parser;
        }
 
        public TagNamespaceRegisterEntry RegisterEntry {
            get {
                return _nsRegisterEntry;
            }
        }
 
        Type ITagNameToTypeMapper.GetControlType(string tagName, IDictionary attribs) {
            return GetControlType(tagName, attribs, false);
        }
 
        internal Type GetControlType(string tagName, IDictionary attribs, bool throwOnError) {
            string typeName;
            string ns = _nsRegisterEntry.Namespace;
            if (String.IsNullOrEmpty(ns)) {
                typeName = tagName;
            }
            else {
                typeName = ns + "." + tagName;
            }
 
            // Look up the type name (case insensitive)
            if (_assembly != null) {
                Type type = null;
 
                if (throwOnError) {
                    // If loading the type from the assembly depends on a referenced assembly that cannot
                    // be loaded, we should throw the actual exception, instead of later reporting a more 
                    // generic error saying the type was not found (Devdiv 138674)
                    try {
                        type = _assembly.GetType(typeName, true /*throwOnError*/, true /*ignoreCase*/);
                    }
                    catch (System.IO.FileNotFoundException) {
                        throw;
                    }
                    catch (System.IO.FileLoadException) {
                        throw;
                    }
                    catch (BadImageFormatException) {
                        throw;
                    }
                    catch {
                        // For all other exceptions, such as when the type is not present in the assembly,
                        // we ignore the exception so that we can continue to check other assemblies to look
                        // for the type.
                    }
                }
                else {
                    type = _assembly.GetType(typeName, false /*throwOnError*/, true /*ignoreCase*/);
                }
                return type;
            }
 
#if !FEATURE_PAL
            // If we're in the designer, check the WebFormsReferenceManager and ITypeResolutionService first.
            if (_parser.FInDesigner && (_parser.DesignerHost != null)) {
                // If we are in the DesignTimeThemes Host, we can't actually go down this code path, let the TypeResolutionService try instead
                if (_parser.DesignerHost.RootComponent != null) {
                    WebFormsRootDesigner rootDesigner = _parser.DesignerHost.GetDesigner(_parser.DesignerHost.RootComponent) as WebFormsRootDesigner;
                    if (rootDesigner != null) {
                        WebFormsReferenceManager referenceManager = rootDesigner.ReferenceManager;
                        if (referenceManager != null) {
                            Type type = referenceManager.GetType(_nsRegisterEntry.TagPrefix, tagName);
                            if (type != null) {
                                // Only return the type if we found it. Otherwise fall back to the next service.
                                return type;
                            }
                        }
                    }
                }
                                
                ITypeResolutionService typeResolutionService = (ITypeResolutionService)_parser.DesignerHost.GetService(typeof(ITypeResolutionService));
                if (typeResolutionService != null) {
                    Type type = typeResolutionService.GetType(typeName, false, true);
                    if (type != null) {
                        // Only return the type if we found it. Otherwise fall back to the next service.
                        return type;
                    }
                }
            }
 
#endif // !FEATURE_PAL
 
            // Nothing more to try in non-hosted appdomains
            if (!HostingEnvironment.IsHosted)
                return null;
 
            // If the assembly was not specified, look for the type in the code assemblies (including sub code assemblies)
            return BuildManager.GetTypeFromCodeAssembly(typeName, true /*ignoreCase*/);
        }
    }
 
    internal class TagPrefixTagNameToTypeMapper : ITagNameToTypeMapper {
 
        private string _tagPrefix;
        private ArrayList _mappers;
 
        internal TagPrefixTagNameToTypeMapper(string tagPrefix) {
            _tagPrefix = tagPrefix;
            _mappers = new ArrayList();
        }
 
        internal void AddNamespaceMapper(NamespaceTagNameToTypeMapper mapper) {
            _mappers.Add(mapper);
        }
 
        Type ITagNameToTypeMapper.GetControlType(string tagName, IDictionary attribs) {
            Type foundType = null;
            Exception loadException = null;
 
            foreach (NamespaceTagNameToTypeMapper nsMapper in _mappers) {
                Type t = ((ITagNameToTypeMapper)nsMapper).GetControlType(tagName, attribs);
                if (t != null) {
                    if (foundType == null) {
                        foundType = t;
                    }
                    else if (foundType != t) {
                        throw new HttpParseException(SR.GetString(SR.Ambiguous_server_tag, _tagPrefix + ":" + tagName),
                                                     null, nsMapper.RegisterEntry.VirtualPath, null, nsMapper.RegisterEntry.Line);
                    }
                }
            }
 
            // DevDiv 168561
            // If we cannot find the type, we make a second pass and allow exceptions to be thrown so that we can
            // track the actual failure.
            if (foundType == null) {
                try {
                    foreach (NamespaceTagNameToTypeMapper nsMapper in _mappers) {
                        nsMapper.GetControlType(tagName, attribs, true /* throwOnError */);
                    }
                }
                catch (System.IO.FileNotFoundException e) {
                    loadException = e;
                }
                catch (System.IO.FileLoadException e) {
                    loadException = e;
                }
                catch (BadImageFormatException e) {
                    loadException = e;
                }
            }
 
            if (loadException != null) {
                throw new HttpException(SR.GetString(SR.ControlAdapters_TypeNotFound, _tagPrefix + ":" + tagName) + " " + loadException.Message, loadException);
            }
 
            if (foundType == null) {
                throw new HttpException(SR.GetString(SR.Unknown_server_tag, _tagPrefix + ":" + tagName));
            }
 
            return foundType;
        }
    }
 
 
    internal class MainTagNameToTypeMapper {
 
        private BaseTemplateParser _parser;
 
        // Mapping from a tag prefix to an ITagNameToTypeMapper for that prefix
        private IDictionary _prefixedMappers;
 
        // Mapping from a tag name (possibly with a prefix) to a Type
        private IDictionary _mappedTags;
 
        // Create the Html tag mapper
        private ITagNameToTypeMapper _htmlMapper = new HtmlTagNameToTypeMapper();
 
        // List of UserControl <%@ Register %> directives
        // Key = "prefix:tagname", Value = UserControlRegisterEntry
        private Hashtable _userControlRegisterEntries;
 
        // List of custom control <%@ Register %> directives
        private List<TagNamespaceRegisterEntry> _tagRegisterEntries;
 
        // <prefix, TagNamespaceRegisterEntry>
        private TagNamespaceRegisterEntryTable _tagNamespaceRegisterEntries;
 
        internal MainTagNameToTypeMapper(BaseTemplateParser parser) {
            _parser = parser;
            
            if (parser != null) {
                PagesSection pagesConfig = parser.PagesConfig;
                if (pagesConfig != null) {
                    // Clone it so we don't modify the config settings
                    _tagNamespaceRegisterEntries = pagesConfig.TagNamespaceRegisterEntriesInternal;
                    if (_tagNamespaceRegisterEntries != null) {
                        _tagNamespaceRegisterEntries = (TagNamespaceRegisterEntryTable)_tagNamespaceRegisterEntries.Clone();
                    }
 
                    _userControlRegisterEntries = pagesConfig.UserControlRegisterEntriesInternal;
                    if (_userControlRegisterEntries != null) {
                        _userControlRegisterEntries = (Hashtable)_userControlRegisterEntries.Clone();
                    }
                }
 
                // 
 
 
 
                if (parser.FInDesigner && (_tagNamespaceRegisterEntries == null)) {
                    _tagNamespaceRegisterEntries = new TagNamespaceRegisterEntryTable();
 
                    foreach (TagNamespaceRegisterEntry entry in PagesSection.DefaultTagNamespaceRegisterEntries) {
                        _tagNamespaceRegisterEntries[entry.TagPrefix] = new ArrayList(new object[] { entry });
                    }
                }
            }
        }
 
        internal ICollection UserControlRegisterEntries {
            get {
                if (_userControlRegisterEntries != null) {
                    return _userControlRegisterEntries.Values;
                }
 
                return null;
            }
        }
 
        internal List<TagNamespaceRegisterEntry> TagRegisterEntries {
            get {
                if (_tagRegisterEntries == null) {
                    _tagRegisterEntries = new List<TagNamespaceRegisterEntry>();
                }
                return _tagRegisterEntries;
            }
        }
 
        // Called to process register directive on pages
        internal void ProcessTagNamespaceRegistration(TagNamespaceRegisterEntry nsRegisterEntry) {
            string tagPrefix = nsRegisterEntry.TagPrefix;
 
            // See if there are entries registered in config with same tag prefix
            ArrayList registerEntries = null;
            if (_tagNamespaceRegisterEntries != null) {
                registerEntries = (ArrayList)_tagNamespaceRegisterEntries[tagPrefix];
            }
 
            // If there are config-based entries, and theres no mapper for them, make sure
            // a mapper has been created
            // We need all namespaces for a particular tag prefix to have a mapper, so we
            // can look for ambiguous types when a tag is parsed.
            if ((registerEntries != null) &&
                ((_prefixedMappers == null) || (_prefixedMappers[tagPrefix] == null))) {
                ProcessTagNamespaceRegistration(registerEntries);
            }
            ProcessTagNamespaceRegistrationCore(nsRegisterEntry);
        }
 
        private void ProcessTagNamespaceRegistration(ArrayList nsRegisterEntries) {
            foreach (TagNamespaceRegisterEntry nsRegisterEntry in nsRegisterEntries) {
                try {
                    ProcessTagNamespaceRegistrationCore(nsRegisterEntry);
                }
                catch (Exception e) {
                    // Make sure we throw the exception with the correct file/line info
                    throw new HttpParseException(e.Message, e,
                        nsRegisterEntry.VirtualPath, null, nsRegisterEntry.Line);
                }
            }
        }
 
        private void ProcessTagNamespaceRegistrationCore(TagNamespaceRegisterEntry nsRegisterEntry) {
            // Load the assembly if it was specified
            Assembly assembly = null;
            if (!String.IsNullOrEmpty(nsRegisterEntry.AssemblyName))
                assembly = _parser.AddAssemblyDependency(nsRegisterEntry.AssemblyName);
 
            // Import the namespace if there is one
            if (!String.IsNullOrEmpty(nsRegisterEntry.Namespace))
                _parser.AddImportEntry(nsRegisterEntry.Namespace);
 
            // Create a mapper for this specific namespace/assembly pair
            NamespaceTagNameToTypeMapper mapper = new NamespaceTagNameToTypeMapper(nsRegisterEntry, assembly, _parser);
 
            // Figure out which prefix mapper this new namespace mapper goes into
            if (_prefixedMappers == null) {
                _prefixedMappers = new Hashtable(StringComparer.OrdinalIgnoreCase);
            }
 
            TagPrefixTagNameToTypeMapper prefixMapper = (TagPrefixTagNameToTypeMapper)_prefixedMappers[nsRegisterEntry.TagPrefix];
            if (prefixMapper == null) {
                prefixMapper = new TagPrefixTagNameToTypeMapper(nsRegisterEntry.TagPrefix);
                _prefixedMappers[nsRegisterEntry.TagPrefix] = prefixMapper;
            }
 
            prefixMapper.AddNamespaceMapper(mapper);
 
            TagRegisterEntries.Add(nsRegisterEntry);
        }
 
        internal void ProcessUserControlRegistration(UserControlRegisterEntry ucRegisterEntry) {
            Type type = null;
 
            if (_parser.FInDesigner) {
                // Get the designer to load the appropriate type
                type = _parser.GetDesignTimeUserControlType(ucRegisterEntry.TagPrefix,
                    ucRegisterEntry.TagName);
            }
            else {
                // Compile it into a Type
                type = _parser.GetUserControlType(ucRegisterEntry.UserControlSource.VirtualPathString);
            }
 
            if (type == null)
                return;
 
            if (_userControlRegisterEntries == null) {  
                _userControlRegisterEntries = new Hashtable();
            }
            _userControlRegisterEntries[ucRegisterEntry.TagPrefix + ":" + ucRegisterEntry.TagName] = ucRegisterEntry;
 
            // Register the new tag, including its prefix
            RegisterTag(ucRegisterEntry.TagPrefix + ":" + ucRegisterEntry.TagName, type);
        }
 
        /*
         * Check if the tagName can be handled by a user control register directive.
         * If so, process the directive and return true.
         */
        private bool TryUserControlRegisterDirectives(string tagName) {
        
            if (_userControlRegisterEntries == null)
                return false;
 
            // Is there a registered user control for this tag
            UserControlRegisterEntry ucRegisterEntry = (UserControlRegisterEntry)
                _userControlRegisterEntries[tagName];
            if (ucRegisterEntry == null)
                return false;
 
            // Don't allow a config registered user control to be used by a
            // page that lives in the same directory, to avoid circular references.
            // More specifically, this would break batching because our simple dependency
            // parser would not be able to detect the *implicit* dependency triggered
            // by the presence of a tag (VSWhidbey 165042).
            if (ucRegisterEntry.ComesFromConfig) {
                VirtualPath ucDirectory = ucRegisterEntry.UserControlSource.Parent;
                if (ucDirectory == _parser.BaseVirtualDir) {
                    throw new HttpException(
                        SR.GetString(SR.Invalid_use_of_config_uc,
                            _parser.CurrentVirtualPath, ucRegisterEntry.UserControlSource));
                }
            }
 
            try {
                ProcessUserControlRegistration(ucRegisterEntry);
            }
            catch (Exception e) {
                // Make sure we throw the exception with the correct file/line info
                throw new HttpParseException(e.Message, e,
                    ucRegisterEntry.VirtualPath, null, ucRegisterEntry.Line);
            }
            return true;
        }
 
        /*
         * Check if the tagName can be handled by a namespace register directive.
         * If so, process the directive and return true.
         */
        private bool TryNamespaceRegisterDirectives(string prefix) {
            if (_tagNamespaceRegisterEntries == null)
                return false;
 
            // Are there registered entries for this prefix
            ArrayList entries = (ArrayList)_tagNamespaceRegisterEntries[prefix];
            if (entries == null) {
                return false;
            }
 
            ProcessTagNamespaceRegistration(entries);
            return true;
        }
 
        internal void RegisterTag(string tagName, Type type) {
            if (_mappedTags == null)
                _mappedTags = new Hashtable(StringComparer.OrdinalIgnoreCase);
 
            try {
                _mappedTags.Add(tagName, type);
            }
            catch (ArgumentException) {
                // Duplicate mapping
                throw new HttpException(SR.GetString(SR.Duplicate_registered_tag, tagName));
            }
        }
 
        internal /*public*/ Type GetControlType(string tagName, IDictionary attribs, bool fAllowHtmlTags) {
            Type type = GetControlType2(tagName, attribs, fAllowHtmlTags);
 
            if ((type != null) && _parser != null && !_parser.FInDesigner) {
                Hashtable tagMapEntries = _parser.PagesConfig.TagMapping.TagTypeMappingInternal;
                if (tagMapEntries != null) {
                    Type mappedType = (Type)tagMapEntries[type];
                    if (mappedType != null) {
                        type = mappedType;
                    }
                }
            }
 
            return type;
        }
 
        private /*public*/ Type GetControlType2(string tagName, IDictionary attribs, bool fAllowHtmlTags) {
            Type type;
 
            // First, check it the tag name has been mapped
            if (_mappedTags != null) {
                type = (Type) _mappedTags[tagName];
 
                if (type == null) {
                    // Maybe there is a register directive that we haven't yet processed
                    if (TryUserControlRegisterDirectives(tagName))
                        type = (Type) _mappedTags[tagName];
                }
 
                if (type != null) {
                    // If this is the special NoCompile UserControl specified by the PageParserFilter, give it the
                    // virtualPath via the attribute bag
                    if (_parser != null && _parser._pageParserFilter != null && _parser._pageParserFilter.GetNoCompileUserControlType() == type) {
                        UserControlRegisterEntry ucRegisterEntry = (UserControlRegisterEntry)_userControlRegisterEntries[tagName];
                        attribs["virtualpath"] = ucRegisterEntry.UserControlSource;
                    }
 
                    return type;
                }
            }
 
            // Check if there is a prefix
            int colonIndex = tagName.IndexOf(':');
            if (colonIndex >= 0) {
                // If ends with : don't try to match (88398)
                if (colonIndex == tagName.Length-1)
                    return null;
 
                // If so, parse the prefix and tagname
                string prefix = tagName.Substring(0, colonIndex);
                tagName = tagName.Substring(colonIndex+1);
 
                // Look for a mapper for the prefix
 
                ITagNameToTypeMapper mapper = null;
                if (_prefixedMappers != null)
                    mapper = (ITagNameToTypeMapper) _prefixedMappers[prefix];
 
                if (mapper == null) {
                    // Maybe there is a register directive that we haven't yet processed
                    if (TryNamespaceRegisterDirectives(prefix) && _prefixedMappers != null) {
                        mapper = (ITagNameToTypeMapper)_prefixedMappers[prefix];
                    }
                }
 
                if (mapper == null) {
                    return null;
                }
 
                // Try to get the type from the prefix mapper
                return mapper.GetControlType(tagName, attribs);
            }
            else {
                // There is no prefix.
                // Try the Html mapper if allowed
                if (fAllowHtmlTags) {
                    return _htmlMapper.GetControlType(tagName, attribs);
                }
            }
 
            return null;
        }
    }
}