File: UI\BaseTemplateParser.cs
Project: ndp\fx\src\xsp\system\Web\System.Web.csproj (System.Web)
//------------------------------------------------------------------------------
// <copyright file="BaseTemplateParser.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>                                                                
//------------------------------------------------------------------------------
 
/*
 * Implements the ASP.NET template parser
 *
 * Copyright (c) 1998 Microsoft Corporation
 */
 
namespace System.Web.UI {
 
    using System.Text;
    using System;
    using System.IO;
    using System.Collections;
    using System.Collections.Specialized;
    using System.ComponentModel;
    using System.ComponentModel.Design;
    using System.Reflection;
    using System.Configuration;
    using System.Web.Caching;
    using System.Web.Util;
    using System.Web.Hosting;
    using System.Web.Compilation;
    using HttpException = System.Web.HttpException;
    using System.Text.RegularExpressions;
    using System.Globalization;
    using System.Security.Permissions;
 
    /*
     * Parser for Template Files (TemplateControls and PageTheme)
     */
 
    /// <internalonly/>
    /// <devdoc>
    ///    <para>[To be supplied.]</para>
    /// </devdoc>
    public abstract class BaseTemplateParser : TemplateParser {
 
        private const string _sourceString = "src";
        private const string _namespaceString = "namespace";
        private const string _tagnameString = "tagname";
 
        internal Type GetDesignTimeUserControlType(string tagPrefix, string tagName) {
            Debug.Assert(FInDesigner);
 
            Type type = typeof(UserControl);
 
            IDesignerHost host = DesignerHost;
            if (host != null) {
                IUserControlTypeResolutionService ucTypeResService =
                    (IUserControlTypeResolutionService)host.GetService(typeof(IUserControlTypeResolutionService));
                if (ucTypeResService != null) {
                    try {
                        type = ucTypeResService.GetType(tagPrefix, tagName);
                    }
                    catch {
                    }
                }
            }
 
            return type;
        }
    
        /*
         * Compile a nested .ascx file (a User Control) and return its Type
         */
 
        protected internal Type GetUserControlType(string virtualPath) {
            return GetUserControlType(VirtualPath.Create(virtualPath));
        }
 
        internal Type GetUserControlType(VirtualPath virtualPath) {
            Type t = GetReferencedType(virtualPath, false /*allowNoCompile*/);
 
            // Fail if it's a no compile uc, since it doesn't have a Type we can use
            if (t == null) {
                // First, check whether there is a PageParserFilter that can give us a type
                if (_pageParserFilter != null)
                    t = _pageParserFilter.GetNoCompileUserControlType();
 
                if (t == null)
                    ProcessError(SR.GetString(SR.Cant_use_nocompile_uc, virtualPath));
            }
            else {
                // Make sure it has the correct base type
                Util.CheckAssignableType(typeof(UserControl), t);
            }
 
            return t;
        }
 
        /*
         * Compile a .aspx/.ascx file and return its Type
         */
 
        protected Type GetReferencedType(string virtualPath) {
            return GetReferencedType(VirtualPath.Create(virtualPath));
        }
 
        internal Type GetReferencedType(VirtualPath virtualPath) {
            return GetReferencedType(virtualPath, true /*allowNoCompile*/);
        }
 
        internal Type GetReferencedType(VirtualPath virtualPath, bool allowNoCompile) {
 
            virtualPath = ResolveVirtualPath(virtualPath);
 
            // If we have a page parser filter, make sure the reference is allowed
            if (_pageParserFilter != null && !_pageParserFilter.AllowVirtualReference(CompConfig, virtualPath)) {
                ProcessError(SR.GetString(SR.Reference_not_allowed, virtualPath));
            }
 
            BuildResult result = null;
            Type t = null;
 
            try {
                result = BuildManager.GetVPathBuildResult(virtualPath);
            }
            catch (HttpCompileException e) {
                // Add the path depdencies properly so we know when
                // to invalidate the cached result.
                if (e.VirtualPathDependencies != null) {
                    foreach (string vPath in e.VirtualPathDependencies) {
                        AddSourceDependency(VirtualPath.Create(vPath));
                    }
                }
 
                throw;
            }
            catch {
                // Add the virtualPath to the dependency so that
                // we know when to check again. This could happen if the
                // virtualPath points to a file not created yet.
                // This only affects designtime code path since we do want to return
                // partial result even if there is an error, and that result is
                // cached. VSWhidbey 372585
                if (IgnoreParseErrors) {
                    AddSourceDependency(virtualPath);
                }
 
                throw;
            }
 
            // Is it a no-compile page/uc
            BuildResultNoCompileTemplateControl noCompileResult = result as BuildResultNoCompileTemplateControl;
            if (noCompileResult != null) {
 
                // If no-compile is not acceptable, return null
                if (!allowNoCompile)
                    return null;
 
                // In the no-compile case, use the base type, since we don't compile a type
                t = noCompileResult.BaseType;
            }
            else if (result is BuildResultCompiledType) {
                BuildResultCompiledType compiledResult = (BuildResultCompiledType) result;
                Debug.Assert(compiledResult != null);
 
                t = compiledResult.ResultType;
            }
            else {
                throw new HttpException(SR.GetString(SR.Invalid_typeless_reference, _sourceString));
            }
 
            Debug.Assert(t != null);
 
            // Add a dependency on the Type
            AddTypeDependency(t);
 
            // Add a dependency on the BuildResult
            AddBuildResultDependency(result);
 
            return t;
        }
 
        internal override void ProcessDirective(string directiveName, IDictionary directive) {
            if (StringUtil.EqualsIgnoreCase(directiveName, "register")) {
                // Register directive
 
                // Get the tagprefix, which is required
                string tagPrefix = Util.GetAndRemoveNonEmptyIdentifierAttribute(directive,
                    "tagprefix", true /*required*/);
 
                string tagName = Util.GetAndRemoveNonEmptyIdentifierAttribute(directive,
                    _tagnameString, false /*required*/);
 
                VirtualPath src = Util.GetAndRemoveVirtualPathAttribute(directive, 
                    _sourceString, false /*required*/);
 
                string ns = Util.GetAndRemoveNonEmptyNoSpaceAttribute(directive, 
                    _namespaceString, false /*required*/);
 
                // An Assembly can optionally be specified (ASURT 61326/VSWhidbey 87050)
                string assemblyName = Util.GetAndRemoveNonEmptyAttribute(directive, "assembly",
                    false /*required*/);
 
                RegisterDirectiveEntry registerEntry;
                if (tagName != null) {
                    // It's a user control registration
 
                    // 'src' is required
                    if (src == null) {
                        throw new HttpException(SR.GetString(SR.Missing_attr, _sourceString));
                    }
 
                    // 'namespace' is not allowed
                    if (ns != null) {
                        throw new HttpException(
                            SR.GetString(SR.Invalid_attr, _namespaceString, "tagname"));
                    }
 
                    // 'assembly' is not allowed
                    if (assemblyName != null) {
                        throw new HttpException(
                            SR.GetString(SR.Invalid_attr, "assembly", "tagname"));
                    }
 
                    UserControlRegisterEntry ucRegisterEntry = new UserControlRegisterEntry(tagPrefix, tagName);
                    ucRegisterEntry.UserControlSource = src;
                    registerEntry = ucRegisterEntry;
 
                    TypeMapper.ProcessUserControlRegistration(ucRegisterEntry);
                }
                else if (src != null) {
                    // It's missing the tagname attribute.
                    throw new HttpException(SR.GetString(SR.Missing_attr, _tagnameString));
                }
                else {
                    // It's a namespace prefix registration
                    // 'namespace' is required
                    if (ns == null) {
                        throw new HttpException(SR.GetString(SR.Missing_attr, _namespaceString));
                    }
 
                    TagNamespaceRegisterEntry nsRegisterEntry = new TagNamespaceRegisterEntry(tagPrefix, ns, assemblyName);
                    registerEntry = nsRegisterEntry;
 
                    TypeMapper.ProcessTagNamespaceRegistration(nsRegisterEntry);
                }
 
                registerEntry.Line = _lineNumber;
                registerEntry.VirtualPath = CurrentVirtualPathString;
 
                // If there are some attributes left, fail
                Util.CheckUnknownDirectiveAttributes(directiveName, directive);
            }
            else {
                base.ProcessDirective(directiveName, directive);
            }
        }
    }
 
    /*
     * Entry representing a register directive
     * e.g. <%@ Register tagprefix="tagprefix" Namespace="namespace" Assembly="assembly" %> OR
     * e.g. <%@ Register tagprefix="tagprefix" Tagname="tagname" Src="pathname" %>
     */
    internal abstract class RegisterDirectiveEntry: SourceLineInfo {
 
        internal RegisterDirectiveEntry(string tagPrefix) {
            _tagPrefix = tagPrefix;
        }
 
        private string _tagPrefix;
        internal string TagPrefix {
            get { return _tagPrefix;}
        }
    }
 
    /*
     * Entry representing the registration of a tag namespace
     * e.g. <%@ Register tagprefix="tagprefix" Namespace="namespace" Assembly="assembly" %>
     */
    internal class TagNamespaceRegisterEntry: RegisterDirectiveEntry {
 
        internal TagNamespaceRegisterEntry(string tagPrefix, string namespaceName, string assemblyName) : base(tagPrefix) {
            _ns = namespaceName;
            _assemblyName = assemblyName;
        }
 
        private string _ns;
        internal string Namespace {
            get { return _ns;}
        }
 
        private string _assemblyName;
        internal string AssemblyName {
            get { return _assemblyName;}
        }
 
#if DONT_COMPILE
        internal string Key {
            get {
                return TagPrefix + ":" + _ns + ":" + (_assemblyName == null ? String.Empty : _assemblyName);
            }
        }
#endif
    }
 
    /*
     * Entry representing the registration of a user control
     * e.g. <%@ Register tagprefix="tagprefix" Tagname="tagname" Src="pathname" %>
     */
    internal class UserControlRegisterEntry: RegisterDirectiveEntry {
 
        internal UserControlRegisterEntry(string tagPrefix, string tagName) : base(tagPrefix) {
            _tagName = tagName;
        }
 
        private string _tagName;
        internal string TagName {
            get { return _tagName;}
        }
 
        private VirtualPath _source;
        internal VirtualPath UserControlSource {
            get { return _source;}
            set { _source = value;}
        }
 
        private bool _comesFromConfig;
        internal bool ComesFromConfig {
            get { return _comesFromConfig;}
            set { _comesFromConfig = value;}
        }
 
        internal string Key {
            get {
                return TagPrefix + ":" + _tagName;
            }
        }
    }
 
    internal class TagNamespaceRegisterEntryTable : Hashtable {
 
        public TagNamespaceRegisterEntryTable() : base(StringComparer.OrdinalIgnoreCase) {
        }
 
        public override object Clone() {
            // We override clone to perform a deep copy of the hashtable contents but a shallow copy of
            // the contained arraylist itself
 
            TagNamespaceRegisterEntryTable newTable = new TagNamespaceRegisterEntryTable();
            foreach (DictionaryEntry entry in this) {
                newTable[entry.Key] = ((ArrayList)entry.Value).Clone();
            }
 
            return newTable;
        }
    }
}