File: AuthoringOM\Compiler\CompileXomlTask.cs
Project: ndp\cdf\src\WF\Common\System.Workflow.ComponentModel.csproj (System.Workflow.ComponentModel)
namespace System.Workflow.ComponentModel.Compiler
{
    using System;
    using System.Resources;
    using System.Reflection;
    using System.Diagnostics;
    using System.Globalization;
    using System.IO;
    using System.Xml;
    using System.Text;
    using System.Collections;
    using System.Collections.Specialized;
    using System.ComponentModel.Design;
    using System.CodeDom;
    using System.CodeDom.Compiler;
    using Microsoft.Win32;
    using Microsoft.CSharp;
    using Microsoft.VisualBasic;
    using System.Workflow.ComponentModel.Compiler;
    using System.Workflow.ComponentModel;
    using System.Workflow.ComponentModel.Design;
    using System.Workflow.ComponentModel.Serialization;
    using Microsoft.Build.Framework;
    using Microsoft.Build.Utilities;
    using System.Runtime.InteropServices;
    using Microsoft.Build.Tasks;
    using System.Collections.Generic;
    using Microsoft.Workflow.Compiler;
    using System.Runtime.Versioning;
    using System.Security;
 
    [Guid("59B2D1D0-5DB0-4F9F-9609-13F0168516D6")]
    [ComImport]
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    internal interface IVsHierarchy
    {
    }
 
    [Guid("6d5140c1-7436-11ce-8034-00aa006009fa")]
    [ComImport]
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    internal interface IOleServiceProvider
    {
        [PreserveSig]
        int QueryService(ref Guid guidService, ref Guid riid, out IntPtr ppvObject);
    }
 
    [ComImport(), Guid("8AA9644E-1F6A-4F4C-83E3-D0BAD4B2BB21"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    internal interface IWorkflowBuildHostProperties
    {
        bool SkipWorkflowCompilation { get; set; }
    }
 
    internal class ServiceProvider : IServiceProvider
    {
        private static readonly Guid IID_IUnknown = new Guid("{00000000-0000-0000-C000-000000000046}");
        private IOleServiceProvider serviceProvider;
        public ServiceProvider(IOleServiceProvider sp)
        {
            this.serviceProvider = sp;
        }
        public object GetService(Type serviceType)
        {
            if (serviceType == null)
                throw new ArgumentNullException("serviceType");
 
            IntPtr pUnk = IntPtr.Zero;
            Guid guidService = serviceType.GUID;
            Guid guidUnk = IID_IUnknown;
            int hr = this.serviceProvider.QueryService(ref guidService, ref guidUnk, out pUnk);
 
            object service = null;
            if (hr >= 0)
            {
                try
                {
                    service = Marshal.GetObjectForIUnknown(pUnk);
                }
                finally
                {
                    Marshal.Release(pUnk);
                }
            }
            return service;
        }
    }
 
    #region CompileWorkflowTask
    /// <summary>
    /// This class extends the Task class of MSBuild framework.
    /// Methods of this class are invoked by the MSBuild framework to customize
    /// the build process when compiling WinOE flavors of CSharp and VB.net projects.
    /// It provides support for compiling .xoml files into intermediate
    /// code files (either CSharp or VB). It calls into the WorkflowCompiler to do the
    /// validations and code compile unit generation.
    /// This component is used during the build process of WinOE flavor projects
    /// both from within the Visual Studio IDE and the standalone MSBuild executable.
    /// As such this component's assembly should not have direct or indirect dependencies
    /// on the Visual Studio assemblies to work in the standalone scenario.
    /// </summary>
    [Obsolete("The System.Workflow.* types are deprecated.  Instead, please use the new types from System.Activities.*")]
    public sealed class CompileWorkflowTask : Microsoft.Build.Utilities.Task, ITask
    {
 
        #region Members and Constructors
 
        private string projectExt = null;
        private string projectDirectory = null;
        private object hostObject = null;
        private string rootNamespace = null;
        private string imports = null;
        private string assemblyName = null;
        private ITaskItem[] xomlFiles = null;
        private ITaskItem[] referenceFiles = null;
        private ITaskItem[] sourceCodeFiles = null;
        private ITaskItem[] resourceFiles = null;
        private ITaskItem[] outputFiles = null; //new TaskItem[0]; // The outputs should be non-null if we bail out successfully or otherwise from the Execute method.
        private ITaskItem[] compilationOptions = null;
        private SupportedLanguages projectType;
        private StringCollection temporaryFiles = new StringCollection();
        private bool delaySign = false;
        private string targetFramework = null;
        private string keyContainer = null;
        private string keyFile = null;
 
        public CompileWorkflowTask()
            : base(new ResourceManager("System.Workflow.ComponentModel.BuildTasksStrings", Assembly.GetExecutingAssembly()))
        {
            this.BuildingProject = true;
        }
 
        #endregion
 
        #region Input parameters and property overrides
        public string ProjectDirectory
        {
            get
            {
                return this.projectDirectory;
            }
            set
            {
                this.projectDirectory = value;
            }
        }
 
        public string ProjectExtension
        {
            get
            {
                return this.projectExt;
            }
            set
            {
                this.projectExt = value;
                if (String.Compare(this.projectExt, ".csproj", StringComparison.OrdinalIgnoreCase) == 0)
                {
                    ProjectType = SupportedLanguages.CSharp;
                }
                else if (String.Compare(this.projectExt, ".vbproj", StringComparison.OrdinalIgnoreCase) == 0)
                {
                    ProjectType = SupportedLanguages.VB;
                }
            }
        }
 
        public string RootNamespace
        {
            get
            {
                return this.rootNamespace;
            }
            set
            {
                this.rootNamespace = value;
            }
        }
 
        public string AssemblyName
        {
            get
            {
                return this.assemblyName;
            }
            set
            {
                this.assemblyName = value;
            }
        }
 
        public string Imports
        {
            get
            {
                return this.imports;
            }
            set
            {
                this.imports = value;
            }
        }
 
        public ITaskItem[] WorkflowMarkupFiles
        {
            get
            {
                return xomlFiles;
            }
            set
            {
                if (value != null)
                {
                    ArrayList xomlFilesOnly = new ArrayList();
                    foreach (ITaskItem inputFile in value)
                    {
                        if (inputFile != null)
                        {
                            string fileSpec = inputFile.ItemSpec;
                            if (fileSpec != null && fileSpec.EndsWith(".xoml", StringComparison.OrdinalIgnoreCase))
                            {
                                xomlFilesOnly.Add(inputFile);
                            }
                        }
                    }
 
                    if (xomlFilesOnly.Count > 0)
                    {
                        this.xomlFiles = xomlFilesOnly.ToArray(typeof(ITaskItem)) as ITaskItem[];
                    }
                }
                else
                {
                    this.xomlFiles = value;
                }
            }
        }
 
        public ITaskItem[] ReferenceFiles
        {
            get
            {
                return this.referenceFiles;
            }
            set
            {
                this.referenceFiles = value;
            }
        }
 
        public ITaskItem[] ResourceFiles
        {
            get
            {
                return this.resourceFiles;
            }
            set
            {
                this.resourceFiles = value;
            }
        }
 
        public ITaskItem[] SourceCodeFiles
        {
            get
            {
                return this.sourceCodeFiles;
            }
            set
            {
                this.sourceCodeFiles = value;
            }
        }
 
        public ITaskItem[] CompilationOptions
        {
            get
            {
                return this.compilationOptions;
            }
            set
            {
                this.compilationOptions = value;
            }
        }
 
        public bool DelaySign
        {
            get
            {
                return this.delaySign;
            }
            set
            {
                this.delaySign = value;
            }
        }
 
        public string TargetFramework
        {
            get
            {
                return this.targetFramework;
            }
            set
            {
                this.targetFramework = value;
            }
        }
 
        public string KeyContainer
        {
            get
            {
                return this.keyContainer;
            }
            set
            {
                this.keyContainer = value;
            }
        }
 
        public string KeyFile
        {
            get
            {
                return this.keyFile;
            }
            set
            {
                this.keyFile = value;
            }
        }
 
        public new object HostObject
        {
            get
            {
                return this.hostObject;
            }
        }
 
        ITaskHost ITask.HostObject
        {
            get
            {
                return (ITaskHost)this.hostObject;
            }
            set
            {
                this.hostObject = value;
            }
        }
 
        public bool BuildingProject { get; set; }
        #endregion
 
        #region Output parameter properties
        [OutputAttribute]
        public ITaskItem[] OutputFiles
        {
            get
            {
                if (this.outputFiles == null)
                {
                    if (this.ProjectType == SupportedLanguages.VB)
                        this.outputFiles = new ITaskItem[0];
                    else
                    {
                        ArrayList oFiles = new ArrayList();
                        if (this.WorkflowMarkupFiles != null)
                            oFiles.AddRange(this.WorkflowMarkupFiles);
                        this.outputFiles = oFiles.ToArray(typeof(ITaskItem)) as ITaskItem[];
                    }
                }
                return this.outputFiles;
            }
        }
 
        [OutputAttribute]
        public string KeepTemporaryFiles
        {
            get
            {
                return ShouldKeepTempFiles().ToString();
            }
        }
 
        [OutputAttribute]
        public string[] TemporaryFiles
        {
            get
            {
                string[] tempFiles = new string[this.temporaryFiles.Count];
 
                this.temporaryFiles.CopyTo(tempFiles, 0);
                return tempFiles;
            }
        }
 
        #endregion
 
        #region Public method overrides
 
        public override bool Execute()
        {
#if DEBUG
            DumpInputParameters();
#endif
 
            // Validate the input parameters for the task.
            if (!this.ValidateParameters())
                return false;
 
            // If no .xoml files were specified, return success.
            if (this.WorkflowMarkupFiles == null)
                this.Log.LogMessageFromResources(MessageImportance.Normal, "NoXomlFiles");
 
            // Check if there are any referenced assemblies.
            if (this.ReferenceFiles == null || this.ReferenceFiles.Length == 0)
                this.Log.LogMessageFromResources(MessageImportance.Normal, "NoReferenceFiles");
 
            // Check if there are any souce code files (cs/vb).
            if (this.SourceCodeFiles == null || this.SourceCodeFiles.Length == 0)
                this.Log.LogMessageFromResources(MessageImportance.Normal, "NoSourceCodeFiles");
 
            // we return early if this is not invoked during the build phase of the project (eg project load)
            IWorkflowBuildHostProperties workflowBuildHostProperty = this.HostObject as IWorkflowBuildHostProperties;
            if (!this.BuildingProject || (workflowBuildHostProperty != null && workflowBuildHostProperty.SkipWorkflowCompilation))
            {
                return true;
            }
 
            // Create an instance of WorkflowCompilerParameters.
            int errorCount = 0, warningCount = 0;
            WorkflowCompilerParameters compilerParameters = new WorkflowCompilerParameters();
 
            // set the service provider
            IWorkflowCompilerErrorLogger workflowErrorLogger = null;
            IServiceProvider externalServiceProvider = null;
            if (this.HostObject is IOleServiceProvider)
            {
                externalServiceProvider = new ServiceProvider(this.HostObject as IOleServiceProvider);
                workflowErrorLogger = externalServiceProvider.GetService(typeof(IWorkflowCompilerErrorLogger)) as IWorkflowCompilerErrorLogger;
            }
 
            string[] userCodeFiles = GetFiles(this.SourceCodeFiles, this.ProjectDirectory);
            foreach (ITaskItem referenceFile in this.ReferenceFiles)
                compilerParameters.ReferencedAssemblies.Add(referenceFile.ItemSpec);
 
            if (string.IsNullOrEmpty(this.targetFramework))
            {
                string defaultFrameworkName = null;
 
                const string NDPSetupRegistryBranch = "SOFTWARE\\Microsoft\\NET Framework Setup\\NDP";
                const string NetFrameworkIdentifier = ".NETFramework";
 
                RegistryKey ndpSetupKey = null;
                try
                {
                    ndpSetupKey = Registry.LocalMachine.OpenSubKey(NDPSetupRegistryBranch);
 
                    if (ndpSetupKey != null)
                    {
                        string[] installedNetFxs = ndpSetupKey.GetSubKeyNames();
 
                        if (installedNetFxs != null)
                        {
                            char[] splitChars = new char[] { '.' };
                            for (int i = 0; i < installedNetFxs.Length; i++)
                            {
                                string framework = installedNetFxs[i];
                                if (framework.Length > 0)
                                {
                                    string frameworkVersion = framework.TrimStart('v', 'V');
                                    if (!string.IsNullOrEmpty(frameworkVersion))
                                    {
                                        string[] parts = frameworkVersion.Split(splitChars);
 
                                        string normalizedVersion = null;
                                        if (parts.Length > 1)
                                        {
                                            normalizedVersion = string.Format(CultureInfo.InvariantCulture, "v{0}.{1}", parts[0], parts[1]);
                                        }
                                        else
                                        {
                                            normalizedVersion = string.Format(CultureInfo.InvariantCulture, "v{0}.0", parts[0]);
                                        }
 
                                        if (string.Compare(normalizedVersion, "v3.5", StringComparison.OrdinalIgnoreCase) == 0)
                                        {
                                            defaultFrameworkName = new FrameworkName(NetFrameworkIdentifier, new Version(3, 5)).ToString();
                                            break;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
                catch (SecurityException)
                {
                }
                catch (UnauthorizedAccessException)
                {
                }
                catch (IOException)
                {
                }
                finally
                {
                    if (ndpSetupKey != null)
                    {
                        ndpSetupKey.Close();
                    }
                }
 
                if (defaultFrameworkName == null)
                {
                    defaultFrameworkName = new FrameworkName(NetFrameworkIdentifier, new Version(2, 0)).ToString();
                }
 
                compilerParameters.MultiTargetingInformation = new MultiTargetingInfo(defaultFrameworkName);
            }
            else
            {
                compilerParameters.MultiTargetingInformation = new MultiTargetingInfo(this.targetFramework);
            }
 
            CompilerOptionsBuilder optionsBuilder;
            switch (this.ProjectType)
            {
                case SupportedLanguages.VB:
                    switch (compilerParameters.CompilerVersion)
                    {
                        case MultiTargetingInfo.TargetFramework30CompilerVersion:
                            optionsBuilder = new WhidbeyVBCompilerOptionsBuilder();
                            break;
                        case MultiTargetingInfo.TargetFramework35CompilerVersion:
                            optionsBuilder = new OrcasVBCompilerOptionsBuilder();
                            break;
                        default:
                            optionsBuilder = new CompilerOptionsBuilder();
                            break;
                    }
                    break;
                default:
                    optionsBuilder = new CompilerOptionsBuilder();
                    break;
            }
            compilerParameters.CompilerOptions = this.PrepareCompilerOptions(optionsBuilder);
            compilerParameters.GenerateCodeCompileUnitOnly = true;
            compilerParameters.LanguageToUse = this.ProjectType.ToString();
            compilerParameters.TempFiles.KeepFiles = ShouldKeepTempFiles();
 
            compilerParameters.OutputAssembly = AssemblyName;
            if (!string.IsNullOrEmpty(assemblyName))
            {
                // Normalizing the assembly name. 
                // The codeDomProvider expects the proper extension to be set.
                string extension = (compilerParameters.GenerateExecutable) ? ".exe" : ".dll";
                compilerParameters.OutputAssembly += extension;
            }
 
            CodeDomProvider codeProvider = null;
            if (this.ProjectType == SupportedLanguages.VB)
                codeProvider = CompilerHelpers.CreateCodeProviderInstance(typeof(VBCodeProvider), compilerParameters.CompilerVersion);
            else
                codeProvider = CompilerHelpers.CreateCodeProviderInstance(typeof(CSharpCodeProvider), compilerParameters.CompilerVersion);
 
            using (TempFileCollection tempFileCollection = new TempFileCollection(Environment.GetEnvironmentVariable("temp", EnvironmentVariableTarget.User), true))
            {
                this.outputFiles = new TaskItem[1];
 
                // Compile and generate a temporary code file for each xoml file.
                string[] xomlFilesPaths;
                if (this.WorkflowMarkupFiles != null)
                {
                    xomlFilesPaths = new string[WorkflowMarkupFiles.GetLength(0) + userCodeFiles.Length];
                    int index = 0;
                    for (; index < this.WorkflowMarkupFiles.GetLength(0); index++)
                        xomlFilesPaths[index] = Path.Combine(ProjectDirectory, this.WorkflowMarkupFiles[index].ItemSpec);
 
                    userCodeFiles.CopyTo(xomlFilesPaths, index);
                }
                else
                {
                    xomlFilesPaths = new string[userCodeFiles.Length];
                    userCodeFiles.CopyTo(xomlFilesPaths, 0);
                }
 
                WorkflowCompilerResults compilerResults = new CompilerWrapper().Compile(compilerParameters, xomlFilesPaths);
 
                foreach (WorkflowCompilerError error in compilerResults.Errors)
                {
                    if (error.IsWarning)
                    {
                        warningCount++;
                        if (workflowErrorLogger != null)
                        {
                            error.FileName = Path.Combine(this.ProjectDirectory, error.FileName);
                            workflowErrorLogger.LogError(error);
                            workflowErrorLogger.LogMessage(error.ToString() + "\n");
                        }
                        else
                            this.Log.LogWarning(error.ErrorText, error.ErrorNumber, error.FileName, error.Line, error.Column);
                    }
                    else
                    {
                        errorCount++;
                        if (workflowErrorLogger != null)
                        {
                            error.FileName = Path.Combine(this.ProjectDirectory, error.FileName);
                            workflowErrorLogger.LogError(error);
                            workflowErrorLogger.LogMessage(error.ToString() + "\n");
                        }
                        else
                            this.Log.LogError(error.ErrorText, error.ErrorNumber, error.FileName, error.Line, error.Column);
                    }
                }
 
                if (!compilerResults.Errors.HasErrors)
                {
                    CodeCompileUnit ccu = compilerResults.CompiledUnit;
                    if (ccu != null)
                    {
                        // Fix standard namespaces and root namespace.
                        WorkflowMarkupSerializationHelpers.FixStandardNamespacesAndRootNamespace(ccu.Namespaces, this.RootNamespace, CompilerHelpers.GetSupportedLanguage(this.ProjectType.ToString())); //just add the standard namespaces
 
                        string tempFile = tempFileCollection.AddExtension(codeProvider.FileExtension);
                        using (StreamWriter fileStream = new StreamWriter(new FileStream(tempFile, FileMode.Create, FileAccess.Write), Encoding.UTF8))
                        {
                            CodeGeneratorOptions options = new CodeGeneratorOptions();
                            options.BracingStyle = "C";
                            codeProvider.GenerateCodeFromCompileUnit(ccu, fileStream, options);
                        }
 
                        this.outputFiles[0] = new TaskItem(tempFile);
                        this.temporaryFiles.Add(tempFile);
                        this.Log.LogMessageFromResources(MessageImportance.Normal, "TempCodeFile", tempFile);
                    }
                }
            }
            if ((errorCount > 0 || warningCount > 0) && workflowErrorLogger != null)
                workflowErrorLogger.LogMessage(string.Format(CultureInfo.CurrentCulture, "\nCompile complete -- {0} errors, {1} warnings \n", new object[] { errorCount, warningCount }));
 
#if DEBUG
            DumpOutputParameters();
#endif
            this.Log.LogMessageFromResources(MessageImportance.Normal, "XomlValidationCompleted", errorCount, warningCount);
            return (errorCount == 0);
        }
 
        #endregion
 
        #region Private properties and methods
        private SupportedLanguages ProjectType
        {
            get
            {
                return this.projectType;
            }
            set
            {
                this.projectType = value;
            }
        }
 
        /// <summary>
        /// This method validates all the input parameters for the custom task.
        /// </summary>
        /// <returns>True if all parameters are valid, false otherwise</returns>
        private bool ValidateParameters()
        {
            // If the project directory is not supplied then bail out with an error.
            if (ProjectDirectory == null || ProjectDirectory.Trim().Length == 0)
            {
                this.Log.LogErrorFromResources("NoProjectType");
                return false;
            }
 
            // If the project extension is not supplied then bail out with an error.
            if (ProjectExtension == null || ProjectExtension.Trim().Length == 0)
            {
                this.Log.LogErrorFromResources("NoProjectType");
                return false;
            }
 
            // If the project extension is not .csproj or .vbproj bail out with an error.
            if (String.Compare(ProjectExtension, ".csproj", StringComparison.OrdinalIgnoreCase) != 0 && String.Compare(ProjectExtension, ".vbproj", StringComparison.OrdinalIgnoreCase) != 0)
            {
                this.Log.LogErrorFromResources("UnsupportedProjectType");
                return false;
            }
 
            // All parameters are valid so return true.
            return true;
        }
 
#if DEBUG
        void DumpInputParameters()
        {
            DumpParametersLine("CompileWorkflowTask - Input Parameters:");
            DumpParametersLine("  projectExt={0}", this.projectExt);
            DumpParametersLine("  projectDirectory='{0}'", this.projectDirectory);
            DumpParametersLine("  rootNamespace={0}", this.rootNamespace);
            DumpParametersLine("  imports='{0}'", this.imports);
            DumpParametersLine("  assemblyName='{0}", this.assemblyName);
            DumpParametersTaskItems("xomlFiles", this.xomlFiles);
            DumpParametersTaskItems("sourceCodeFiles", this.sourceCodeFiles);
            DumpParametersTaskItems("resourceFiles", this.resourceFiles);
            DumpParametersTaskItems("referenceFiles", this.referenceFiles);
            DumpParametersTaskItems("compilationOptions", this.compilationOptions);
            DumpParametersLine("  delaySign={0},keyContainer='{1}',keyFile='{2}'", this.delaySign, this.keyContainer, this.keyFile);
            DumpParametersLine("  targetFramework='{0}'", this.targetFramework);
        }
        void DumpOutputParameters()
        {
            DumpParametersLine("CompileWorkflowTask - Output Parameters:");
            DumpParametersTaskItems("outputFiles", this.outputFiles);
            DumpParametersLine("  KeepTemporaryFiles={0},temporaryFiles=[{1} items]", this.KeepTemporaryFiles, this.temporaryFiles.Count);
            for (int i = 0; i < this.temporaryFiles.Count; i++)
            {
                DumpParametersLine("    '{0}' [{1}]", this.temporaryFiles[i], i);
            }
        }
        void DumpParametersTaskItems(string name, ITaskItem[] items)
        {
            if (items == null)
            {
                DumpParametersLine("  {0}=<null>", name);
            }
            else
            {
                DumpParametersLine("  {0}=[{1} items]", name, items.Length);
                for (int i = 0; i < items.Length; i++)
                {
                    ITaskItem item = items[i];
                    if (item == null)
                    {
                        DumpParametersLine("    <null> [{0}]", i);
                    }
                    else
                    {
                        DumpParametersLine("    {0} [{1}]", item.ItemSpec, i);
                        foreach (string metadataName in item.MetadataNames)
                        {
                            DumpParametersLine("      {0}='{1}'", metadataName, item.GetMetadata(metadataName));
                        }
                    }
                }
            }
        }
        void DumpParametersLine(string lineFormat, params object[] lineArguments)
        {
            if ((lineArguments != null) && (lineArguments.Length > 0))
            {
                for (int i = 0; i < lineArguments.Length; i++)
                {
                    if (lineArguments[i] == null)
                    {
                        lineArguments[i] = "<null>";
                    }
                }
            }
            this.Log.LogMessage(MessageImportance.Low, lineFormat, lineArguments);
        }
#endif
 
        /// <summary>
        /// This method is used to get the absolute paths of the files 
        /// in a project.
        /// </summary>
        /// <param name="taskItems"></param>
        /// <param name="projDir"></param>
        /// <returns></returns>
        private static string[] GetFiles(ITaskItem[] taskItems, string projDir)
        {
            if (taskItems == null)
                return new string[0];
            string[] itemSpecs = new string[taskItems.Length];
 
            for (int i = 0; i < taskItems.Length; i++)
            {
                if (projDir != null)
                {
                    itemSpecs[i] = Path.Combine(projDir, taskItems[i].ItemSpec);
                }
                else
                {
                    itemSpecs[i] = taskItems[i].ItemSpec;
                }
            }
 
            return itemSpecs;
        }
 
        private static bool HasManifestResourceName(ITaskItem resourceFile, out string manifestResourceName)
        {
            IEnumerator metadataNames = resourceFile.MetadataNames.GetEnumerator();
 
            manifestResourceName = null;
            bool hasName = false;
            while (!hasName && metadataNames.MoveNext())
            {
                string metadataName = (string)metadataNames.Current;
                if (metadataName == "ManifestResourceName")
                {
                    hasName = true;
                    manifestResourceName = resourceFile.GetMetadata(metadataName);
                }
            }
 
            return hasName;
        }
 
        //Note: Remember to prefix each option with a space. We don't want compiler options glued together.
        private string PrepareCompilerOptions(CompilerOptionsBuilder optionsBuilder)
        {
            StringBuilder compilerOptions = new StringBuilder();
 
            if (this.DelaySign == true)
                compilerOptions.Append(" /delaysign+");
 
            if (this.KeyContainer != null && this.KeyContainer.Trim().Length > 0)
                compilerOptions.AppendFormat(" /keycontainer:{0}", this.KeyContainer);
 
            if (this.KeyFile != null && this.KeyFile.Trim().Length > 0)
                compilerOptions.AppendFormat(" /keyfile:\"{0}\"", Path.Combine(this.ProjectDirectory, this.KeyFile));
 
            if (this.compilationOptions != null && this.compilationOptions.Length > 0)
            {
                foreach (ITaskItem option in this.compilationOptions)
                {
                    optionsBuilder.AddCustomOption(compilerOptions, option);
                }
            }
 
            if (this.resourceFiles != null && this.resourceFiles.Length > 0)
            {
                foreach (ITaskItem resourceFile in this.resourceFiles)
                {
                    string manifestResourceName;
 
                    if (HasManifestResourceName(resourceFile, out manifestResourceName))
                    {
                        compilerOptions.AppendFormat(" /resource:\"{0}\",{1}",
                            Path.Combine(this.ProjectDirectory, resourceFile.ItemSpec), manifestResourceName);
                    }
                    else
                    {
                        compilerOptions.AppendFormat(" /resource:\"{0}\"",
                            Path.Combine(this.ProjectDirectory, resourceFile.ItemSpec));
                    }
                }
            }
 
            if (this.ProjectType == SupportedLanguages.VB)
            {
                if (!string.IsNullOrEmpty(this.RootNamespace))
                    compilerOptions.AppendFormat(" /rootnamespace:{0}", this.RootNamespace);
                compilerOptions.AppendFormat(" /imports:{0}", this.Imports.Replace(';', ','));
            }
 
            if (compilerOptions.Length > 0)
            {
                if (char.IsWhiteSpace(compilerOptions[0]))
                {
                    compilerOptions.Remove(0, 0);
                }
            }
 
            return compilerOptions.ToString();
        }
 
        private bool ShouldKeepTempFiles()
        {
            bool retVal = false;
 
            // See comments for the CompileWorkflowCleanupTask class for reasons why we must keep the temp file for VB.
            if (this.ProjectType == SupportedLanguages.VB)
                retVal = true;
            else
            {
                try
                {
                    RegistryKey winoeKey = Registry.LocalMachine.OpenSubKey(Helpers.ProductRootRegKey);
                    if (winoeKey != null)
                    {
                        object obj = winoeKey.GetValue("KeepTempFiles");
                        retVal = (Convert.ToInt32(obj, CultureInfo.InvariantCulture) != 0);
                    }
                }
                catch
                {
                }
            }
 
            return retVal;
        }
 
        #endregion
 
        class CompilerOptionsBuilder
        {
            public CompilerOptionsBuilder()
            {
            }
 
            public void AddCustomOption(StringBuilder options, ITaskItem option)
            {
                string optionName;
                string optionValue;
                string optionDelimiter;
                GetOptionInfo(option, out optionName, out optionValue, out optionDelimiter);
                if (!string.IsNullOrWhiteSpace(optionName))
                {
                    if (string.IsNullOrEmpty(optionValue))
                    {
                        options.AppendFormat(" /{0}", optionName);
                    }
                    else if (string.IsNullOrEmpty(optionDelimiter))
                    {
                        options.AppendFormat(" /{0}{1}", optionName, optionValue);
                    }
                    else
                    {
                        options.AppendFormat(" /{0}{1}{2}", optionName, optionDelimiter, optionValue);
                    }
                }
            }
 
            protected virtual void GetOptionInfo(ITaskItem option, out string optionName, out string optionValue, out string optionDelimiter)
            {
                optionName = option.ItemSpec;
                optionValue = option.GetMetadata("value");
                optionDelimiter = option.GetMetadata("delimiter");
            }
        }
        abstract class VBCompilerOptionsBuilder : CompilerOptionsBuilder
        {
            const string SuppressWarningOption = "nowarn";
 
            protected VBCompilerOptionsBuilder()
                : base()
            {
            }
 
            sealed protected override void GetOptionInfo(ITaskItem option, out string optionName, out string optionValue, out string optionDelimiter)
            {
                base.GetOptionInfo(option, out optionName, out optionValue, out optionDelimiter);
                if ((string.Compare(optionName, SuppressWarningOption, StringComparison.OrdinalIgnoreCase) == 0) &&
                    !string.IsNullOrWhiteSpace(optionValue))
                {
                    string[] warnings = optionValue.Split(',');
                    StringBuilder validWarnings = new StringBuilder();
                    for (int i = 0; i < warnings.Length; i++)
                    {
                        string warning = warnings[i].Trim();
                        if (IsValidWarning(warning))
                        {
                            if (validWarnings.Length == 0)
                            {
                                validWarnings.Append(warning);
                            }
                            else
                            {
                                validWarnings.AppendFormat(",{0}", warning);
                            }
                        }
                    }
                    optionValue = validWarnings.ToString();
                    if (string.IsNullOrWhiteSpace(optionValue))
                    {
                        optionName = string.Empty;
                    }
                }
            }
 
            protected abstract bool IsValidWarning(string warning);
        }
        class WhidbeyVBCompilerOptionsBuilder : VBCompilerOptionsBuilder
        {
            static HashSet<string> validWarnings = new HashSet<string>(StringComparer.Ordinal)
                { "40000", "40003", "40004", "40005", "40007", "40008", "40009", "40010", "40011", "40012", "40014", "40018", "40019",
                    "40020", "40021", "40022", "40023", "40024", "40025", "40026", "40027", "40028", "40029", "40030", "40031", "40032",
                    "40033", "40034", "40035", "40038", "40039", "40040", "40041", "40042", "40043", "40046", "40047", "40048", "40049",
                    "40050", "40051", "40052", "40053", "40054", "40055", "40056", "40057", 
                    "41000", "41001", "41002", "41003", "41004", "41005", "41006", "41998", "41999",
                    "42000", "42001", "42002", "42003", "42004", "42014", "42015", "42016", "42017", "42018", "42019", "42020", "42021",
                    "42022", "42024", "42025", "42026", "42028", "42029", "42030", "42031", "42032", "42033", "42034", "42035", "42036",
                    "42101", "42102", "42104", "42105", "42106", "42107", "42108", "42109", "42200", "42203", "42204", "42205", "42206",
                    "42300", "42301", "42302", "42303", "42304", "42305", "42306", "42307", "42308", "42309", "42310", "42311", "42312",
                    "42313", "42314", "42315", "42316", "42317", "42318", "42319", "42320", "42321" };
 
            public WhidbeyVBCompilerOptionsBuilder()
                : base()
            {
            }
 
            protected override bool IsValidWarning(string warning)
            {
                return validWarnings.Contains(warning);
            }
        }
        class OrcasVBCompilerOptionsBuilder : VBCompilerOptionsBuilder
        {
            static HashSet<string> validWarnings = new HashSet<string>(StringComparer.Ordinal)
                { "40000", "40003", "40004", "40005", "40007", "40008", "40009", "40010", "40011", "40012", "40014", "40018", "40019",
                    "40020", "40021", "40022", "40023", "40024", "40025", "40026", "40027", "40028", "40029", "40030", "40031", "40032",
                    "40033", "40034", "40035", "40038", "40039", "40040", "40041", "40042", "40043", "40046", "40047", "40048", "40049",
                    "40050", "40051", "40052", "40053", "40054", "40055", "40056", "40057",
                    "41000", "41001", "41002", "41003", "41004", "41005", "41006", "41007", "41008", "41998", "41999",
                    "42000", "42001", "42002", "42004", "42014", "42015", "42016", "42017", "42018", "42019", "42020", "42021", "42022",
                    "42024", "42025", "42026", "42028", "42029", "42030", "42031", "42032", "42033", "42034", "42035", "42036", "42099",
                    "42101", "42102", "42104", "42105", "42106", "42107", "42108", "42109", "42110", "42111", "42200", "42203", "42204",
                    "42205", "42206", "42207", "42300", "42301", "42302", "42303", "42304", "42305", "42306", "42307", "42308", "42309",
                    "42310", "42311", "42312", "42313", "42314", "42315", "42316", "42317", "42318", "42319", "42320", "42321", "42322",
                    "42324", "42326", "42327", "42328" };
 
            public OrcasVBCompilerOptionsBuilder()
                : base()
            {
            }
 
            protected override bool IsValidWarning(string warning)
            {
                return validWarnings.Contains(warning);
            }
        }
    }
    #endregion
 
    internal sealed class CreateWorkflowManifestResourceNameForCSharp : CreateCSharpManifestResourceName
    {
        private bool lastAskedFileWasXoml = false;
 
        [Output]
        public new ITaskItem[] ResourceFilesWithManifestResourceNames
        {
            get
            {
                for (int i = 0; i < base.ResourceFilesWithManifestResourceNames.Length; i++)
                {
                    ITaskItem item = base.ResourceFilesWithManifestResourceNames[i];
                    item.SetMetadata("LogicalName", item.GetMetadata("ManifestResourceName"));
                }
 
                return base.ResourceFilesWithManifestResourceNames;
            }
            set
            {
                base.ResourceFilesWithManifestResourceNames = value;
            }
        }
 
        override protected string CreateManifestName(string fileName, string linkFileName, string rootNamespace, string dependentUponFileName, Stream binaryStream)
        {
            string manifestName = string.Empty;
            if (!this.lastAskedFileWasXoml)
            {
                manifestName = base.CreateManifestName(fileName, linkFileName, rootNamespace, dependentUponFileName, binaryStream);
            }
            else
            {
                manifestName = TasksHelper.GetXomlManifestName(fileName, linkFileName, rootNamespace, binaryStream);
            }
 
            string extension = Path.GetExtension(fileName);
            if (String.Compare(extension, ".rules", StringComparison.OrdinalIgnoreCase) == 0 ||
                String.Compare(extension, WorkflowDesignerLoader.DesignerLayoutFileExtension, StringComparison.OrdinalIgnoreCase) == 0)
                manifestName += extension;
 
            this.lastAskedFileWasXoml = false;
            return manifestName;
        }
 
        override protected bool IsSourceFile(string fileName)
        {
            string extension = Path.GetExtension(fileName);
            if (String.Compare(extension, ".xoml", StringComparison.OrdinalIgnoreCase) == 0)
            {
                this.lastAskedFileWasXoml = true;
                return true;
            }
            return base.IsSourceFile(fileName);
        }
    }
 
    internal sealed class CreateWorkflowManifestResourceNameForVB : CreateVisualBasicManifestResourceName
    {
        private bool lastAskedFileWasXoml = false;
 
        override protected string CreateManifestName(string fileName, string linkFileName, string rootNamespace, string dependentUponFileName, Stream binaryStream)
        {
            string manifestName = string.Empty;
            if (!this.lastAskedFileWasXoml)
            {
                manifestName = base.CreateManifestName(fileName, linkFileName, rootNamespace, dependentUponFileName, binaryStream);
            }
            else
            {
                manifestName = TasksHelper.GetXomlManifestName(fileName, linkFileName, rootNamespace, binaryStream);
            }
 
            string extension = Path.GetExtension(fileName);
            if (String.Compare(extension, ".rules", StringComparison.OrdinalIgnoreCase) == 0 ||
                String.Compare(extension, WorkflowDesignerLoader.DesignerLayoutFileExtension, StringComparison.OrdinalIgnoreCase) == 0)
                manifestName += extension;
 
            this.lastAskedFileWasXoml = false;
            return manifestName;
        }
 
        override protected bool IsSourceFile(string fileName)
        {
            string extension = Path.GetExtension(fileName);
            if (String.Compare(extension, ".xoml", StringComparison.OrdinalIgnoreCase) == 0)
            {
                this.lastAskedFileWasXoml = true;
                return true;
            }
            return base.IsSourceFile(fileName);
        }
    }
 
    internal static class TasksHelper
    {
        internal static string GetXomlManifestName(string fileName, string linkFileName, string rootNamespace, Stream binaryStream)
        {
            string manifestName = string.Empty;
 
            // Use the link file name if there is one, otherwise, fall back to file name.
            string embeddedFileName = linkFileName;
            if (embeddedFileName == null || embeddedFileName.Length == 0)
                embeddedFileName = fileName;
 
            Culture.ItemCultureInfo info = Culture.GetItemCultureInfo(embeddedFileName);
 
            if (binaryStream != null)
            {
                // Resource depends on a form. Now, get the form's class name fully 
                // qualified with a namespace.
                string name = null;
                try
                {
                    Xml.XmlTextReader reader = new Xml.XmlTextReader(binaryStream) { DtdProcessing = DtdProcessing.Prohibit };
                    if (reader.MoveToContent() == System.Xml.XmlNodeType.Element)
                    {
                        if (reader.MoveToAttribute("Class", StandardXomlKeys.Definitions_XmlNs))
                            name = reader.Value;
                    }
                }
                catch
                {
                    // ignore it for now
                }
 
                if (name != null && name.Length > 0)
                {
                    manifestName = name;
 
                    // Append the culture if there is one.        
                    if (info.culture != null && info.culture.Length > 0)
                    {
                        manifestName = manifestName + "." + info.culture;
                    }
                }
            }
 
            // If there's no manifest name at this point, then fall back to using the
            // RootNamespace+Filename_with_slashes_converted_to_dots         
            if (manifestName.Length == 0)
            {
                // If Rootnamespace was null, then it wasn't set from the project resourceFile.
                // Empty namespaces are allowed.
                if (!string.IsNullOrEmpty(rootNamespace))
                    manifestName = rootNamespace + ".";
 
                // Replace spaces in the directory name with underscores. Needed for compatibility with Everett.
                // Note that spaces in the file name itself are preserved.
                string everettCompatibleDirectoryName = CreateManifestResourceName.MakeValidEverettIdentifier(Path.GetDirectoryName(info.cultureNeutralFilename));
 
                // only strip extension for .resx files
                if (0 == String.Compare(Path.GetExtension(info.cultureNeutralFilename), ".resx", StringComparison.OrdinalIgnoreCase))
                {
                    manifestName += Path.Combine(everettCompatibleDirectoryName, Path.GetFileNameWithoutExtension(info.cultureNeutralFilename));
 
                    // Replace all '\' with '.'
                    manifestName = manifestName.Replace(Path.DirectorySeparatorChar, '.');
                    manifestName = manifestName.Replace(Path.AltDirectorySeparatorChar, '.');
 
                    // Append the culture if there is one.        
                    if (info.culture != null && info.culture.Length > 0)
                    {
                        manifestName = manifestName + "." + info.culture;
                    }
                }
                else
                {
                    manifestName += Path.Combine(everettCompatibleDirectoryName, Path.GetFileName(info.cultureNeutralFilename));
 
                    // Replace all '\' with '.'
                    manifestName = manifestName.Replace(Path.DirectorySeparatorChar, '.');
                    manifestName = manifestName.Replace(Path.AltDirectorySeparatorChar, '.');
 
                    // Prepend the culture as a subdirectory if there is one.        
                    if (info.culture != null && info.culture.Length > 0)
                    {
                        manifestName = info.culture + Path.DirectorySeparatorChar + manifestName;
                    }
                }
            }
            return manifestName;
        }
 
    }
 
    internal static class Culture
    {
        static private string[] cultureInfoStrings;
 
        internal struct ItemCultureInfo
        {
            internal string culture;
            internal string cultureNeutralFilename;
        };
 
        internal static ItemCultureInfo GetItemCultureInfo(string name)
        {
            ItemCultureInfo info;
            info.culture = null;
 
            // If the item is defined as "Strings.en-US.resx", then ...
 
            // ... base file name will be "Strings.en-US" ...
            string baseFileNameWithCulture = Path.GetFileNameWithoutExtension(name);
 
            // ... and cultureName will be ".en-US".
            string cultureName = Path.GetExtension(baseFileNameWithCulture);
 
            // See if this is a valid culture name.
            bool validCulture = false;
            if ((cultureName != null) && (cultureName.Length > 1))
            {
                // ... strip the "." to make "en-US"
                cultureName = cultureName.Substring(1);
                validCulture = IsValidCultureString(cultureName);
            }
            if (validCulture)
            {
                // A valid culture was found.
                if (info.culture == null || info.culture.Length == 0)
                {
                    info.culture = cultureName;
                }
 
                // Copy the assigned file and make it culture-neutral
                string extension = Path.GetExtension(name);
                string baseFileName = Path.GetFileNameWithoutExtension(baseFileNameWithCulture);
                string baseFolder = Path.GetDirectoryName(name);
                string fileName = baseFileName + extension;
                info.cultureNeutralFilename = Path.Combine(baseFolder, fileName);
            }
            else
            {
                // No valid culture was found. In this case, the culture-neutral
                // name is the just the original file name.
                info.cultureNeutralFilename = name;
            }
            return info;
        }
 
        private static bool IsValidCultureString(string cultureString)
        {
            if (cultureInfoStrings == null)
            {
                CultureInfo[] cultureInfos = CultureInfo.GetCultures(CultureTypes.AllCultures);
 
                cultureInfoStrings = new string[cultureInfos.Length];
                for (int i = 0; i < cultureInfos.Length; i++)
                {
                    cultureInfoStrings[i] = cultureInfos[i].ToString().ToLowerInvariant();
                }
                Array.Sort(cultureInfoStrings);
            }
 
            bool valid = true;
 
            if (Array.BinarySearch(cultureInfoStrings, cultureString.ToLowerInvariant()) < 0)
            {
                valid = false;
            }
 
            return valid;
        }
    }
 
    #region Class CompileWorkflowCleanupTask
    // This cleanup task is a work-around for VB compilation only.
 
    // Due to a limitation for VB.Net, we can not delete the temp file.  VB does back-ground compilation for
    // supporting intellisense.  It re-compiles when there is a file change event that happens to each source
    // file.  The temp file must be added to the OutputFiles collection in order for the compiler to pick it up.
    // This adds the temp file to the VB compiler project who would report an error if the file is deleted
    // when re-compilation happens in the back-ground.
 
    // However, if we don't delete the temp file, we have another problem.  When we're in code-seperation mode, 
    // we compile our xoml files on the fly and add the buffer that contains 
    // the code generated based on the xoml to the project.  This code conflicts with the code in the temp file, 
    // thus causing all sorts of type conflicting errors.  
 
    // Because the two reasons above, we wrote this cleanup task to keep the temp file but clear out the content
    // of the file, thus make it work for both cases.
    [Obsolete("The System.Workflow.* types are deprecated.  Instead, please use the new types from System.Activities.*")]
    public sealed class CompileWorkflowCleanupTask : Microsoft.Build.Utilities.Task, ITask
    {
 
        #region Members and Constructors
 
        private ITaskItem[] temporaryFiles = null;
 
        public CompileWorkflowCleanupTask()
            :
            base(new ResourceManager("System.Workflow.ComponentModel.BuildTasksStrings",
                                     Assembly.GetExecutingAssembly()))
        {
        }
 
        #endregion
 
        #region Input parameters
        public ITaskItem[] TemporaryFiles
        {
            get
            {
                return this.temporaryFiles;
            }
            set
            {
                this.temporaryFiles = value;
            }
        }
        #endregion
 
        #region Public method overrides
        public override bool Execute()
        {
            if (this.temporaryFiles != null)
            {
                foreach (ITaskItem tempFileTask in this.temporaryFiles)
                {
                    string tempFile = tempFileTask.ItemSpec;
                    if (File.Exists(tempFile))
                    {
                        FileStream fileStream = File.Open(tempFile, FileMode.Truncate);
                        fileStream.Close();
                    }
                }
            }
            return true;
        }
 
        #endregion
    }
    #endregion
}