File: System\ServiceModel\Activation\ServiceParser.cs
Project: ndp\cdf\src\WCF\System.ServiceModel.Activation\System.ServiceModel.Activation.csproj (System.ServiceModel.Activation)
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//------------------------------------------------------------
 
namespace System.ServiceModel.Activation
{
    using System.CodeDom;
    using System.CodeDom.Compiler;
    using System.Collections;
    using System.Collections.Specialized;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Globalization;
    using System.IO;
    using System.Reflection;
    using System.Text.RegularExpressions;
    using System.Web;
    using System.Web.Hosting;
    using System.Web.Compilation;
    using System.Web.RegularExpressions;
    using System.ServiceModel.Activation.Diagnostics;
    using System.Security;
    using System.Runtime.Diagnostics;
    using System.Diagnostics.CodeAnalysis;
    using System.Runtime;
 
    /// <summary>
    /// This class will parse the .svc file and maintains a list of useful information that the build
    /// provider needs in order to compile the file. The parser creates a list of dependent assemblies,
    /// understands the compiler that we need to use, fully parses all the supported directives etc.
    /// </summary>
    /// <remarks>
    /// The class is not thread-safe.
    /// </remarks>
#pragma warning disable 618 // have not moved to the v4 security model yet
    [SecurityCritical(SecurityCriticalScope.Everything)]
#pragma warning restore 618
    class ServiceParser
    {
        // the delimiter for the compiled custom string
        const string Delimiter = ServiceHostingEnvironment.ServiceParserDelimiter;
 
        // attribute names
        const string DefaultDirectiveName = "ServiceHost";
        const string FactoryAttributeName = "Factory";
        const string ServiceAttributeName = "Service";
 
        // regular exression for the directive
        readonly static SimpleDirectiveRegex directiveRegex;
 
        // the build provider we will work with 
        ServiceBuildProvider buildProvider;
 
        // text for the file
        string serviceText;
 
        // the class attribute value
        string factoryAttributeValue = string.Empty;
 
        // the constructorstring
        string serviceAttributeValue = string.Empty;
 
        // the line number in file currently being parsed
        int lineNumber;
 
        // the column number in file currently being parsed
        int startColumn;
 
        // the main directive was found or not
        bool foundMainDirective;
 
        // the type of the compiler (i.e C#)
        CompilerType compilerType;
 
        // the string containing the code to be compiled,
        // it will be null when all the code is "behind"
        string sourceString;
 
        // assemblies to be linked with, we need a unique list
        // of them and we maintain a Dictionary for it.
        HybridDictionary linkedAssemblies;
 
        // the set of assemblies that the build system is 
        // telling us we will be linked with. There is no unique
        // requirement for them.
        ICollection referencedAssemblies;
 
        // used to figure out where the new lines start
        static char[] newlineChars = new char[] { '\r', '\n' };
 
        // source file dependencies
        HybridDictionary sourceDependencies;
 
        // virtual path for the file that we are parsing
        string virtualPath;
 
        [SuppressMessage(FxCop.Category.Security, FxCop.Rule.AptcaMethodsShouldOnlyCallAptcaMethods, Justification = "Users cannot pass arbitrary data to this code.")]
        static ServiceParser()
        {
            directiveRegex = new SimpleDirectiveRegex();
        }
 
        /// <summary>
        /// The Contructor needs the path to the file that it will parse and a reference to
        /// the build provider that we are using. This is necessary because there are things that 
        /// need to be set on the build provider directly as we are parsing...
        /// </summary>
        internal ServiceParser(string virtualPath, ServiceBuildProvider buildProvider)
        {
            if (DiagnosticUtility.ShouldTraceInformation)
            {
                TraceUtility.TraceEvent(TraceEventType.Information, TraceCode.WebHostCompilation, SR.TraceCodeWebHostCompilation,
                    new StringTraceRecord("VirtualPath", virtualPath), this, (Exception)null);
            }
 
            this.virtualPath = virtualPath;
            this.buildProvider = buildProvider;
        }
 
        /// <summary>
        /// Constructor that is used when the whole svc file content is provided. This is the case
        /// when the COM+ Admin tool calls into it.
        /// </summary>
        ServiceParser(string serviceText)
        {
            this.serviceText = serviceText;
            this.buildProvider = new ServiceBuildProvider();
        }
 
        /// <summary>
        /// Parsing the content of the service file and retrieve the serviceAttributeValue attribute for ComPlus.
        /// </summary>
        /// <param name="serviceText">The content of the service file.</param>
        /// <returns>The "serviceAttributeValue" attribute of the Service directive. </returns>
        /// <exception cref="System.Web.HttpParseException"/>
        internal static IDictionary<string, string> ParseServiceDirective(string serviceText)
        {
            ServiceParser parser = new ServiceParser(serviceText);
            parser.ParseString();
 
            // the list of valid attributes for ComPlus for Service Directive
            IDictionary<string, string> attributeTable = new Dictionary<string, string>(
                StringComparer.OrdinalIgnoreCase);
 
            if (!string.IsNullOrEmpty(parser.factoryAttributeValue))
                attributeTable.Add(FactoryAttributeName, parser.factoryAttributeValue);
 
            if (!string.IsNullOrEmpty(parser.serviceAttributeValue))
                attributeTable.Add(ServiceAttributeName, parser.serviceAttributeValue);
 
            return attributeTable;
        }
 
        /// <summary>
        /// </summary>
 
        // various getters for private objects that the build
        // provider will need
        //
        internal CompilerType CompilerType
        {
            get
            {
                return compilerType;
            }
        }
 
        internal ICollection AssemblyDependencies
        {
            get
            {
                if (linkedAssemblies == null)
                {
                    return null;
                }
 
                return linkedAssemblies.Keys;
            }
        }
 
        internal ICollection SourceDependencies
        {
            get
            {
                if (sourceDependencies == null)
                {
                    return null;
                }
 
                return sourceDependencies.Keys;
            }
        }
 
        internal bool HasInlineCode
        {
            get
            {
                return (sourceString != null);
            }
        }
 
        /// <summary>
        /// Parses the code file appropriately. This method is used by the
        /// build provider.
        /// </summary>
        internal void Parse(ICollection referencedAssemblies)
        {
            if (referencedAssemblies == null)
            {
                throw FxTrace.Exception.ArgumentNull("referencedAssemblies");
            }
 
            this.referencedAssemblies = referencedAssemblies;
            AddSourceDependency(virtualPath);
 
            using (TextReader reader = buildProvider.OpenReaderInternal())
            {
                this.serviceText = reader.ReadToEnd();
                ParseString();
            }
        }
 
        /// <summary>
        /// This method returns a code compile unit that will be added
        /// to the other depdnecies in order to compile
        /// </summary>
        internal CodeCompileUnit GetCodeModel()
        {
            // Do we have something to compile?
            //
            if (sourceString == null || sourceString.Length == 0)
                return null;
 
            CodeSnippetCompileUnit snippetCompileUnit = new CodeSnippetCompileUnit(sourceString);
 
            // Put in some context so that the file can be debugged.
            //
            string pragmaFile = HostingEnvironmentWrapper.MapPath(virtualPath);
            snippetCompileUnit.LinePragma = new CodeLinePragma(pragmaFile, lineNumber);
 
            return snippetCompileUnit;
        }
 
        Exception CreateParseException(string message, string sourceCode)
        {
            return CreateParseException(message, null, sourceCode);
        }
 
        Exception CreateParseException(Exception innerException, string sourceCode)
        {
            return CreateParseException(innerException.Message, innerException, sourceCode);
        }
 
        Exception CreateParseException(string message, Exception innerException, string sourceCode)
        {
            return new HttpParseException(message, innerException, this.virtualPath, sourceCode, this.lineNumber);
        }
 
        /// <summary>
        /// This method returns the custom string that is to be passed to ServiceHostingEnvironment from BuildManager.
        /// </summary>
        /// <param name="compiledAssembly">The full name of the built assembly for inline code.</param>
        internal string CreateParseString(Assembly compiledAssembly)
        {
            Type typeToPreserve = this.GetCompiledType(compiledAssembly);
            string typeToPreserveName = string.Empty;
            if (typeToPreserve != null)
                typeToPreserveName = typeToPreserve.AssemblyQualifiedName;
 
            System.Text.StringBuilder builder = new System.Text.StringBuilder();
            if (compiledAssembly != null)
            {
                builder.Append(Delimiter);
                builder.Append(compiledAssembly.FullName);
            }
 
            if (this.referencedAssemblies != null)
            {
                // CSDMain #192135
                // Minimize code change by doing 2 passes to have assembly containing type at the top of the list.
                // As a result, this assembly will get loaded first in ServiceHostFactory.CreateServiceHost.
                // In the multi-targetting scenario this prevents the runtime from trying to load a newer CLR assembly
                // and failing.  In the happy case, duplicate assembly references may occur (no effect on runtime).
                // Note that if the service type is contained in a framework assembly, this does not fix the problem.
                // Future improvement is to write fully qualified type name and let CLR handle load/search.
                if (!string.IsNullOrEmpty(serviceAttributeValue))
                {
                    foreach (Assembly assembly in this.referencedAssemblies)
                    {
                        Type serviceType;
                        try
                        {
                            serviceType = assembly.GetType(serviceAttributeValue, false);
                        }
                        catch (Exception e)
                        {
                            if (System.Runtime.Fx.IsFatal(e))
                            {
                                throw;
                            }
 
                            // log exception, but do not rethrow
                            DiagnosticUtility.TraceHandledException(e, TraceEventType.Warning);
 
                            break;
                        }
 
                        if (serviceType != null)
                        {
                            builder.Append(Delimiter);
                            builder.Append(assembly.FullName);
                            break;
                        }
                    }
                }
 
                foreach (Assembly assembly in this.referencedAssemblies)
                {
                    builder.Append(Delimiter);
                    builder.Append(assembly.FullName);
                }
            }
 
            if (this.AssemblyDependencies != null)
            {
                foreach (Assembly assembly in this.AssemblyDependencies)
                {
                    builder.Append(Delimiter);
                    builder.Append(assembly.FullName);
                }
            }
            // use application relative virtualpath instead of the absolute path 
            // so that the compliedcustomstring is applicationame independent
            return string.Concat(VirtualPathUtility.ToAppRelative(virtualPath), Delimiter,
                typeToPreserveName, Delimiter,
                serviceAttributeValue, builder.ToString());
        }
 
        void AddSourceDependency(string fileName)
        {
            if (sourceDependencies == null)
                sourceDependencies = new HybridDictionary(true);
 
            sourceDependencies.Add(fileName, fileName);
        }
 
        Type GetCompiledType(Assembly compiledAssembly)
        {
            if (string.IsNullOrEmpty(factoryAttributeValue))
            {
                return null;
            }
 
            Type type = null;
 
            // First, try to get the type from the assembly that has been built (if any)
            if (this.HasInlineCode && (compiledAssembly != null))
            {
                type = compiledAssembly.GetType(factoryAttributeValue);
            }
 
            // If not, try to get it from other assemblies
            if (type == null)
            {
                type = GetType(factoryAttributeValue);
            }
 
            return type;
        }
 
        internal IDictionary GetLinePragmasTable()
        {
            LinePragmaCodeInfo info = new LinePragmaCodeInfo(this.lineNumber, this.startColumn, 1, -1, false);
            IDictionary dictionary = new Hashtable();
            dictionary[this.lineNumber] = info;
            return dictionary;
        }
 
        /// <summary>
        /// Parses the content of the svc file for each directive line
        /// </summary>
        void ParseString()
        {
            try
            {
                int textPos = 0;
                Match match;
                lineNumber = 1;
 
                // Check for ending bracket first, MB 45013.
                if (this.serviceText.IndexOf('>') == -1)
                {
                    throw FxTrace.Exception.AsError(new HttpException(SR.Hosting_BuildProviderDirectiveEndBracketMissing(ServiceParser.DefaultDirectiveName)));
                }
 
                // First, parse all the <%@ ... %> directives
                //
                for (;;)
                {
                    match = directiveRegex.Match(this.serviceText, textPos);
 
                    // Done with the directives?
                    //
                    if (!match.Success)
                        break;
 
                    lineNumber += ServiceParserUtilities.LineCount(this.serviceText, textPos, match.Index);
                    textPos = match.Index;
 
                    // Get all the directives into a bag
                    //
                    IDictionary directive = CollectionsUtil.CreateCaseInsensitiveSortedList();
                    string directiveName = ProcessAttributes(match, directive);
 
                    // Understand the directive
                    //
                    ProcessDirective(directiveName, directive);
                    lineNumber += ServiceParserUtilities.LineCount(this.serviceText, textPos, match.Index + match.Length);
                    textPos = match.Index + match.Length;
 
                    // Fixup line and column numbers to have meaninglful errors
                    //
                    int newlineIndex = this.serviceText.LastIndexOfAny(newlineChars, textPos - 1);
                    startColumn = textPos - newlineIndex;
                }
 
                if (!foundMainDirective)
                {
                    throw FxTrace.Exception.AsError(new HttpException(SR.Hosting_BuildProviderDirectiveMissing(ServiceParser.DefaultDirectiveName)));
                }
 
                // skip the directives chunk
                //
                string remainingText = this.serviceText.Substring(textPos);
 
                // If there is something else in the file, it needs to be compiled
                //
                if (!ServiceParserUtilities.IsWhiteSpaceString(remainingText))
                {
                    sourceString = remainingText;
                }
            }
            catch (HttpException e)
            {
                // the string is set in the internal exception, no need to set it again.
                //
                Exception parseException = CreateParseException(e, this.serviceText);
                throw FxTrace.Exception.AsError(
                    new HttpCompileException(parseException.Message, parseException));
            }
        }
 
        /// <summary>
        /// Return the directive if it exists or an empty string
        /// </summary>
        string ProcessAttributes(Match match, IDictionary attribs)
        {
            // creates 3 parallel capture collections
            // for the attribute names, the attribute values and the
            // equal signs
            //
            string ret = String.Empty;
 
            CaptureCollection attrnames = match.Groups["attrname"].Captures;
            CaptureCollection attrvalues = match.Groups["attrval"].Captures;
            CaptureCollection equalsign = match.Groups["equal"].Captures;
 
            // Iterate through all of them and add then to 
            // the dictionary of attributes
            //
            for (int i = 0; i < attrnames.Count; i++)
            {
                string attribName = attrnames[i].ToString();
                string attribValue = attrvalues[i].ToString();
 
                // Check if there is an equal sign.
                //
                bool fHasEqual = (equalsign[i].ToString().Length > 0);
 
                if (attribName != null)
                {
                    // A <%@ %> block can have two formats:
                    // <%@ directive foo=1 bar=hello %>
                    // <%@ foo=1 bar=hello %>
                    // Check if we have the first format
                    //
                    if (!fHasEqual && i == 0)
                    {
                        // return the main directive
                        //
                        ret = attribName;
                        continue;
                    }
 
                    try
                    {
                        if (attribs != null)
                            attribs.Add(attribName, attribValue);
                    }
                    catch (ArgumentException)
                    {
                        throw FxTrace.Exception.AsError(new HttpException(SR.Hosting_BuildProviderDuplicateAttribute(attribName)));
                    }
                }
            }
            return ret;
        }
 
        /// <summary>
        /// This method understands the compilation parameters if any ...
        /// </summary>
        [SuppressMessage(FxCop.Category.Security, FxCop.Rule.DoNotIndirectlyExposeMethodsWithLinkDemands, Justification = "This method doesn't allow callers to access sensitive information, operations, or resources that can be used in a destructive manner.")]
        void ProcessCompilationParams(IDictionary directive, CompilerParameters compilParams)
        {
            bool debug = false;
            if (ServiceParserUtilities.GetAndRemoveBooleanAttribute(directive, "debug", ref debug))
            {
                compilParams.IncludeDebugInformation = debug;
            }
 
            int warningLevel = 0;
            if (ServiceParserUtilities.GetAndRemoveNonNegativeIntegerAttribute(directive, "warninglevel", ref warningLevel))
            {
                compilParams.WarningLevel = warningLevel;
                if (warningLevel > 0)
                    compilParams.TreatWarningsAsErrors = true;
            }
 
            string compilerOptions = ServiceParserUtilities.GetAndRemoveNonEmptyAttribute(directive, "compileroptions");
            if (compilerOptions != null)
            {
                compilParams.CompilerOptions = compilerOptions;
            }
        }
 
 
        /// <summary>
        /// Processes a directive block
        /// </summary>
        void ProcessDirective(string directiveName, IDictionary directive)
        {
            // Throw on empy, no directive specified
            //
            if (directiveName.Length == 0)
            {
                throw FxTrace.Exception.AsError(new HttpException(SR.Hosting_BuildProviderDirectiveNameMissing));
            }
 
            // Check for the main directive
            //
            if (string.Compare(directiveName, ServiceParser.DefaultDirectiveName, StringComparison.OrdinalIgnoreCase) == 0)
            {
                // Make sure the main directive was not already specified
                //
                if (foundMainDirective)
                {
                    throw FxTrace.Exception.AsError(new HttpException(SR.Hosting_BuildProviderDuplicateDirective(ServiceParser.DefaultDirectiveName)));
                }
 
                foundMainDirective = true;
 
                // Ignore 'codebehind' attribute (ASURT 4591)
                //
                directive.Remove("codebehind");
 
                string language = ServiceParserUtilities.GetAndRemoveNonEmptyAttribute(directive, "language");
 
                // Get the compiler for the specified language (if any)
                // or get the one from config
                //
                if (language != null)
                {
                    compilerType = buildProvider.GetDefaultCompilerTypeForLanguageInternal(language);
                }
                else
                {
                    compilerType = buildProvider.GetDefaultCompilerTypeInternal();
                }
 
 
                if (directive.Contains(FactoryAttributeName))
                {
                    factoryAttributeValue = ServiceParserUtilities.GetAndRemoveNonEmptyAttribute(directive, FactoryAttributeName);
                    serviceAttributeValue = ServiceParserUtilities.GetAndRemoveNonEmptyAttribute(directive, ServiceAttributeName);
                }
                else if (directive.Contains(ServiceAttributeName))
                {
                    serviceAttributeValue = ServiceParserUtilities.GetAndRemoveNonEmptyAttribute(directive, ServiceAttributeName);
                }
                else
                {
                    throw FxTrace.Exception.AsError(new HttpException(SR.Hosting_BuildProviderMainAttributeMissing));
                }
                // parse the parameters that are related to the compiler
                //
                ProcessCompilationParams(directive, compilerType.CompilerParameters);
            }
            else if (string.Compare(directiveName, "assembly", StringComparison.OrdinalIgnoreCase) == 0)
            {
                if (directive.Contains("name") && directive.Contains("src"))
                {
                    throw FxTrace.Exception.AsError(new HttpException(SR.Hosting_BuildProviderMutualExclusiveAttributes("src", "name")));
                }
                else if (directive.Contains("name"))
                {
                    string assemblyName = ServiceParserUtilities.GetAndRemoveNonEmptyAttribute(directive, "name");
                    if (assemblyName != null)
                    {
                        AddAssemblyDependency(assemblyName);
                    }
                    else
                        throw FxTrace.Exception.AsError(new HttpException(SR.Hosting_BuildProviderAttributeEmpty("name")));
                }
                else if (directive.Contains("src"))
                {
                    string srcPath = ServiceParserUtilities.GetAndRemoveNonEmptyAttribute(directive, "src");
                    if (srcPath != null)
                    {
                        ImportSourceFile(srcPath);
                    }
                    else
                        throw FxTrace.Exception.AsError(new HttpException(SR.Hosting_BuildProviderAttributeEmpty("src")));
                }
                else
                { // if (!directive.Contains("name") && !directive.Contains("src"))
                    throw FxTrace.Exception.AsError(new HttpException(SR.Hosting_BuildProviderRequiredAttributesMissing("src", "name")));
                }
            }
            else
            {
                throw FxTrace.Exception.AsError(new HttpException(SR.Hosting_BuildProviderUnknownDirective(directiveName)));
            }
 
            // check if there are any directives that you did not process 
            //
            if (directive.Count > 0)
                throw FxTrace.Exception.AsError(new HttpException(SR.Hosting_BuildProviderUnknownAttribute(ServiceParserUtilities.FirstDictionaryKey(directive))));
        }
 
        void ImportSourceFile(string path)
        {
            // Get a full path to the source file, compile it to an assembly
            // add the depedency to the assembly
            //
            string baseVirtualDir = VirtualPathUtility.GetDirectory(virtualPath);
            string fullVirtualPath = VirtualPathUtility.Combine(baseVirtualDir, path);
 
            AddSourceDependency(fullVirtualPath);
            Assembly a = BuildManager.GetCompiledAssembly(fullVirtualPath);
            AddAssemblyDependency(a);
        }
 
        void AddAssemblyDependency(string assemblyName)
        {
            // Load and keep track of the assembly
            //
            Assembly a = Assembly.Load(assemblyName);
            AddAssemblyDependency(a);
        }
 
        void AddAssemblyDependency(Assembly assembly)
        {
            if (linkedAssemblies == null)
                linkedAssemblies = new HybridDictionary(false);
 
            linkedAssemblies.Add(assembly, null);
        }
 
        /// <summary>
        /// Look for a type by name in the assemblies available to this page
        /// </summary>
        Type GetType(string typeName)
        {
            Type type;
 
            // If it contains an assembly name, just call Type.GetType (ASURT 53589)
            //
            if (ServiceParserUtilities.TypeNameIncludesAssembly(typeName))
            {
                try
                {
                    type = Type.GetType(typeName, true);
                }
                catch (ArgumentException e)
                {
                    Exception parseException = CreateParseException(e, this.sourceString);
                    throw FxTrace.Exception.AsError(
                        new HttpCompileException(parseException.Message, parseException));
                }
                catch (TargetInvocationException e)
                {
                    Exception parseException = CreateParseException(e, this.sourceString);
                    throw FxTrace.Exception.AsError(
                        new HttpCompileException(parseException.Message, parseException));
                }
                catch (TypeLoadException e)
                {
                    Exception parseException = CreateParseException(SR.Hosting_BuildProviderCouldNotCreateType(typeName), e, this.sourceString);
                    throw FxTrace.Exception.AsError(
                        new HttpCompileException(parseException.Message, parseException));
                }
 
                return type;
            }
 
            try
            {
                type = ServiceParserUtilities.GetTypeFromAssemblies(referencedAssemblies, typeName, false /*ignoreCase*/);
                if (type != null)
                    return type;
 
                type = ServiceParserUtilities.GetTypeFromAssemblies(AssemblyDependencies, typeName, false /*ignoreCase*/);
                if (type != null)
                    return type;
            }
            catch (HttpException e)
            {
                Exception parseException = CreateParseException(SR.Hosting_BuildProviderCouldNotCreateType(typeName), e, this.sourceString);
                throw FxTrace.Exception.AsError(
                        new HttpCompileException(parseException.Message, parseException));
            }
 
            Exception exception = CreateParseException(SR.Hosting_BuildProviderCouldNotCreateType(typeName), this.sourceString);
            throw FxTrace.Exception.AsError(
                        new HttpCompileException(exception.Message, exception));
        }
 
        /// <summary>
        /// This class contains static methods that are necessary to manipulate the 
        /// structures that contain the directives. The logic assumes that the parser will
        /// create a dictionary that contains all the directives and we can pull certain directives as
        /// necessary while processing/compiling the page. The directives are strings.
        /// 
        /// </summary>
        static class ServiceParserUtilities
        {
            /// <summary>
            /// Return the first key of the dictionary as a string.  Throws if it's
            /// empty or if the key is not a string.
            /// </summary>
            internal static string FirstDictionaryKey(IDictionary dictionary)
            {
                // assume that the caller has checked the dictionary before calling
                //
                IDictionaryEnumerator e = dictionary.GetEnumerator();
                e.MoveNext();
                return (string)e.Key;
            }
 
            /// <summary>
            /// Get a string value from a dictionary, and remove 
            /// it from the dictionary of attributes if it exists.
            /// </summary>
            /// <remarks>Returns null if the value was not there ...</remarks>
            static string GetAndRemove(IDictionary dictionary, string key)
            {
                string val = (string)dictionary[key];
 
                if (val != null)
                {
                    dictionary.Remove(key);
                    val = val.Trim();
                }
                else
                    return string.Empty;
 
                return val;
            }
 
            /// <summary>
            /// Get a value from a dictionary, and remove it from the dictionary if
            /// it exists.  Throw an exception if the value is a whitespace string.
            /// However, don't complain about null, which simply means the value is not
            /// in the dictionary.
            /// </summary>
            internal static string GetAndRemoveNonEmptyAttribute(IDictionary directives, string key, bool required)
            {
                string val = ServiceParserUtilities.GetAndRemove(directives, key);
 
                if (val.Length == 0)
                {
                    if (required)
                    {
                        throw FxTrace.Exception.AsError(new HttpException(SR.Hosting_BuildProviderAttributeMissing(key)));
                    }
                    return null;
                }
 
                return val;
            }
 
 
            internal static string GetAndRemoveNonEmptyAttribute(IDictionary directives, string key)
            {
                return GetAndRemoveNonEmptyAttribute(directives, key, false /*required*/);
            }
 
            /// <summary>
            /// Get a string value from a dictionary, and convert it to bool.  Throw an
            /// exception if it's not a valid bool string.
            /// However, don't complain about null, which simply means the value is not
            /// in the dictionary.
            /// The value is returned through a REF param (unchanged if null)
            /// </summary>
            /// <returns>True if attrib exists, false otherwise</returns>
            internal static bool GetAndRemoveBooleanAttribute(IDictionary directives, string key, ref bool val)
            {
                string s = ServiceParserUtilities.GetAndRemove(directives, key);
 
                if (s.Length == 0)
                    return false;
 
                try
                {
                    val = bool.Parse(s);
                }
                catch (FormatException)
                {
                    throw FxTrace.Exception.AsError(new HttpException(SR.Hosting_BuildProviderInvalidValueForBooleanAttribute(s, key)));
                }
 
                return true;
            }
 
            /// <summary>
            /// Get a string value from a dictionary, and convert it to integer.  Throw an
            /// exception if it's not a valid positive integer string.
            /// However, don't complain about null, which simply means the value is not
            /// in the dictionary.
            /// The value is returned through a REF param (unchanged if null)
            /// </summary>
            /// <returns>True if attrib exists, false otherwise</returns>
            internal static bool GetAndRemoveNonNegativeIntegerAttribute(IDictionary directives, string key, ref int val)
            {
                string s = ServiceParserUtilities.GetAndRemove(directives, key);
 
                if (s.Length == 0)
                    return false;
 
                val = GetNonNegativeIntegerAttribute(key, s);
                return true;
            }
 
            /// <summary>
            /// Parse a string attribute into a non-negative integer
            /// </summary>
            /// <param name="name">Name of the attribute, used only for the error messages</param>
            /// <param name="value">Value to convert to int</param>
            static int GetNonNegativeIntegerAttribute(string name, string value)
            {
                int ret;
 
                try
                {
                    ret = int.Parse(value, CultureInfo.InvariantCulture);
                }
                catch (FormatException)
                {
                    throw FxTrace.Exception.AsError(new HttpException(SR.Hosting_BuildProviderInvalidValueForNonNegativeIntegerAttribute(value, name)));
                }
 
                // Make sure it's not negative
                //
                if (ret < 0)
                {
                    throw FxTrace.Exception.AsError(new HttpException(SR.Hosting_BuildProviderInvalidValueForNonNegativeIntegerAttribute(value, name)));
                }
 
                return ret;
            }
 
            internal static bool IsWhiteSpaceString(string s)
            {
                return (s.Trim().Length == 0);
            }
 
            /// <summary>
            /// This method takes the code that will be compiled as a string and it 
            /// will count how many lines exist between the given offset and the final
            /// offset. 
            /// </summary>
            /// <param name="text">The text that contains the source code</param>
            /// <param name="offset">Starting offset for lookup</param>
            /// <param name="newoffset">Ending offset</param>
            /// <returns>The number of lines</returns>
            internal static int LineCount(string text, int offset, int newoffset)
            {
                int linecount = 0;
 
                while (offset < newoffset)
                {
                    if (text[offset] == '\r' || (text[offset] == '\n' && (offset == 0 || text[offset - 1] != '\r')))
                        linecount++;
 
                    offset++;
                }
 
                return linecount;
            }
 
            /// <summary>
            /// Parses a string that contains a type trying to figure out if the assembly info is there.
            /// </summary>
            /// <param name="typeName">The string to search</param>
            internal static bool TypeNameIncludesAssembly(string typeName)
            {
                return (typeName.IndexOf(",", StringComparison.Ordinal) >= 0);
            }
 
            /// <summary>
            /// Loops through a list of assemblies that are already collected by the parser/provider and 
            /// looks for the specified type.
            /// </summary>
            /// <param name="assemblies">The collection of assemblies</param>
            /// <param name="typeName">The type name</param>
            /// <param name="ignoreCase">Case sensitivity knob</param>
            /// <returns></returns>
            internal static Type GetTypeFromAssemblies(ICollection assemblies, string typeName, bool ignoreCase)
            {
                if (assemblies == null)
                    return null;
 
                Type type = null;
 
                foreach (Assembly assembly in assemblies)
                {
                    Type t = assembly.GetType(typeName, false /*throwOnError*/, ignoreCase);
 
                    if (t == null)
                        continue;
 
                    // If we had already found a different one, it's an ambiguous type reference
                    //
                    if (type != null && t != type)
                    {
                        throw FxTrace.Exception.AsError(new HttpException(
                            SR.Hosting_BuildProviderAmbiguousType(typeName, type.Assembly.FullName, t.Assembly.FullName)));
                    }
 
                    // Keep track of it
                    //
                    type = t;
                }
 
                return type;
            }
        }
    }
}